Merge branch 'jc/unleak-core-excludesfile'

The variable that holds the value read from the core.excludefile
configuration variable used to leak, which has been corrected.

* jc/unleak-core-excludesfile:
  config: do not leak excludes_file
diff --git a/.cirrus.yml b/.cirrus.yml
index 4860beb..77346a4 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,7 +1,7 @@
 env:
   CIRRUS_CLONE_DEPTH: 1
 
-freebsd_12_task:
+freebsd_task:
   env:
     GIT_PROVE_OPTS: "--timer --jobs 10"
     GIT_TEST_OPTS: "--no-chain-lint --no-bin-wrappers"
@@ -9,7 +9,7 @@
     DEFAULT_TEST_TARGET: prove
     DEVELOPER: 1
   freebsd_instance:
-    image_family: freebsd-12-3
+    image_family: freebsd-13-2
     memory: 2G
   install_script:
     pkg install -y gettext gmake perl5
@@ -19,4 +19,4 @@
   build_script:
     - su git -c gmake
   test_script:
-    - su git -c 'gmake test'
+    - su git -c 'gmake DEFAULT_UNIT_TEST_TARGET=unit-tests-prove test unit-tests'
diff --git a/.clang-format b/.clang-format
index c592dda..3ed4fac 100644
--- a/.clang-format
+++ b/.clang-format
@@ -83,9 +83,9 @@
 BreakBeforeBraces: Linux
 
 # Break after operators
-# int valuve = aaaaaaaaaaaaa +
-#              bbbbbb -
-#              ccccccccccc;
+# int value = aaaaaaaaaaaaa +
+#             bbbbbb -
+#             ccccccccccc;
 BreakBeforeBinaryOperators: None
 BreakBeforeTernaryOperators: false
 
diff --git a/.editorconfig b/.editorconfig
index f9d8196..15d6cbe 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,7 +4,7 @@
 
 # The settings for C (*.c and *.h) files are mirrored in .clang-format.  Keep
 # them in sync.
-[*.{c,h,sh,perl,pl,pm,txt}]
+[{*.{c,h,sh,perl,pl,pm,txt},config.mak.*,Makefile}]
 indent_style = tab
 tab_width = 8
 
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 952c7c3..37654cd 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,4 +4,7 @@
 bug reports. Nevertheless, you can use GitGitGadget (https://gitgitgadget.github.io/)
 to conveniently send your Pull Requests commits to our mailing list.
 
+For a single-commit pull request, please *leave the pull request description
+empty*: your commit message itself should describe your changes.
+
 Please read the "guidelines for contributing" linked above!
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index a58e2dc..a241a63 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@v3
+    - uses: actions/checkout@v4
       with:
         fetch-depth: 0
 
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
new file mode 100644
index 0000000..53cf12f
--- /dev/null
+++ b/.github/workflows/coverity.yml
@@ -0,0 +1,163 @@
+name: Coverity
+
+# This GitHub workflow automates submitting builds to Coverity Scan. To enable it,
+# set the repository variable `ENABLE_COVERITY_SCAN_FOR_BRANCHES` (for details, see
+# https://docs.github.com/en/actions/learn-github-actions/variables) to a JSON
+# string array containing the names of the branches for which the workflow should be
+# run, e.g. `["main", "next"]`.
+#
+# In addition, two repository secrets must be set (for details how to add secrets, see
+# https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions):
+# `COVERITY_SCAN_EMAIL` and `COVERITY_SCAN_TOKEN`. The former specifies the
+# email to which the Coverity reports should be sent and the latter can be
+# obtained from the Project Settings tab of the Coverity project).
+#
+# The workflow runs on `ubuntu-latest` by default. This can be overridden by setting
+# the repository variable `ENABLE_COVERITY_SCAN_ON_OS` to a JSON string array specifying
+# the operating systems, e.g. `["ubuntu-latest", "windows-latest"]`.
+#
+# By default, the builds are submitted to the Coverity project `git`. To override this,
+# set the repository variable `COVERITY_PROJECT`.
+
+on:
+  push:
+
+defaults:
+  run:
+    shell: bash
+
+jobs:
+  coverity:
+    if: contains(fromJSON(vars.ENABLE_COVERITY_SCAN_FOR_BRANCHES || '[""]'), github.ref_name)
+    strategy:
+      matrix:
+        os: ${{ fromJSON(vars.ENABLE_COVERITY_SCAN_ON_OS || '["ubuntu-latest"]') }}
+    runs-on: ${{ matrix.os }}
+    env:
+      COVERITY_PROJECT: ${{ vars.COVERITY_PROJECT || 'git' }}
+      COVERITY_LANGUAGE: cxx
+      COVERITY_PLATFORM: overridden-below
+    steps:
+      - uses: actions/checkout@v4
+      - name: install minimal Git for Windows SDK
+        if: contains(matrix.os, 'windows')
+        uses: git-for-windows/setup-git-for-windows-sdk@v1
+      - run: ci/install-dependencies.sh
+        if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
+        env:
+          runs_on_pool: ${{ matrix.os }}
+
+      # The Coverity site says the tool is usually updated twice yearly, so the
+      # MD5 of download can be used to determine whether there's been an update.
+      - name: get the Coverity Build Tool hash
+        id: lookup
+        run: |
+          case "${{ matrix.os }}" in
+          *windows*)
+            COVERITY_PLATFORM=win64
+            COVERITY_TOOL_FILENAME=cov-analysis.zip
+            MAKEFLAGS=-j$(nproc)
+            ;;
+          *macos*)
+            COVERITY_PLATFORM=macOSX
+            COVERITY_TOOL_FILENAME=cov-analysis.dmg
+            MAKEFLAGS=-j$(sysctl -n hw.physicalcpu)
+            ;;
+          *ubuntu*)
+            COVERITY_PLATFORM=linux64
+            COVERITY_TOOL_FILENAME=cov-analysis.tgz
+            MAKEFLAGS=-j$(nproc)
+            ;;
+          *)
+            echo '::error::unhandled OS ${{ matrix.os }}' >&2
+            exit 1
+            ;;
+          esac
+          echo "COVERITY_PLATFORM=$COVERITY_PLATFORM" >>$GITHUB_ENV
+          echo "COVERITY_TOOL_FILENAME=$COVERITY_TOOL_FILENAME" >>$GITHUB_ENV
+          echo "MAKEFLAGS=$MAKEFLAGS" >>$GITHUB_ENV
+          MD5=$(curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \
+                   --fail \
+                   --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+                   --form project="$COVERITY_PROJECT" \
+                   --form md5=1)
+          case $? in
+          0) ;; # okay
+          22) # 40x, i.e. access denied
+            echo "::error::incorrect token or project?" >&2
+            exit 1
+            ;;
+          *) # other error
+            echo "::error::Failed to retrieve MD5" >&2
+            exit 1
+            ;;
+          esac
+          echo "hash=$MD5" >>$GITHUB_OUTPUT
+
+      # Try to cache the tool to avoid downloading 1GB+ on every run.
+      # 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
+        with:
+          path: ${{ runner.temp }}/cov-analysis
+          key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
+      - name: download the Coverity Build Tool (${{ env.COVERITY_LANGUAGE }} / ${{ env.COVERITY_PLATFORM}})
+        if: steps.cache.outputs.cache-hit != 'true'
+        run: |
+          curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \
+            --fail --no-progress-meter \
+            --output $RUNNER_TEMP/$COVERITY_TOOL_FILENAME \
+            --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+            --form project="$COVERITY_PROJECT"
+      - name: extract the Coverity Build Tool
+        if: steps.cache.outputs.cache-hit != 'true'
+        run: |
+          case "$COVERITY_TOOL_FILENAME" in
+          *.tgz)
+            mkdir $RUNNER_TEMP/cov-analysis &&
+            tar -xzf $RUNNER_TEMP/$COVERITY_TOOL_FILENAME --strip 1 -C $RUNNER_TEMP/cov-analysis
+            ;;
+          *.dmg)
+            cd $RUNNER_TEMP &&
+            attach="$(hdiutil attach $COVERITY_TOOL_FILENAME)" &&
+            volume="$(echo "$attach" | cut -f 3 | grep /Volumes/)" &&
+            mkdir cov-analysis &&
+            cd cov-analysis &&
+            sh "$volume"/cov-analysis-macosx-*.sh &&
+            ls -l &&
+            hdiutil detach "$volume"
+            ;;
+          *.zip)
+            cd $RUNNER_TEMP &&
+            mkdir cov-analysis-tmp &&
+            unzip -d cov-analysis-tmp $COVERITY_TOOL_FILENAME &&
+            mv cov-analysis-tmp/* cov-analysis
+            ;;
+          *)
+            echo "::error::unhandled archive type: $COVERITY_TOOL_FILENAME" >&2
+            exit 1
+            ;;
+          esac
+      - name: cache the Coverity Build Tool
+        if: steps.cache.outputs.cache-hit != 'true'
+        uses: actions/cache/save@v4
+        with:
+          path: ${{ runner.temp }}/cov-analysis
+          key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
+      - name: build with cov-build
+        run: |
+          export PATH="$RUNNER_TEMP/cov-analysis/bin:$PATH" &&
+          cov-configure --gcc &&
+          cov-build --dir cov-int make
+      - name: package the build
+        run: tar -czvf cov-int.tgz cov-int
+      - name: submit the build to Coverity Scan
+        run: |
+          curl \
+            --fail \
+            --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+            --form email='${{ secrets.COVERITY_SCAN_EMAIL }}' \
+            --form file=@cov-int.tgz \
+            --form version='${{ github.sha }}' \
+            "https://scan.coverity.com/builds?project=$COVERITY_PROJECT"
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml
index 6c38496..e2c3dbd 100644
--- a/.github/workflows/l10n.yml
+++ b/.github/workflows/l10n.yml
@@ -63,9 +63,10 @@
             origin \
             ${{ github.ref }} \
             $args
-      - uses: actions/setup-go@v2
+      - uses: actions/setup-go@v5
         with:
           go-version: '>=1.16'
+          cache: false
       - name: Install git-po-helper
         run: go install github.com/git-l10n/git-po-helper@main
       - name: Install other dependencies
@@ -91,14 +92,13 @@
           cat git-po-helper.out
           exit $exit_code
       - name: Create comment in pull request for report
-        uses: mshick/add-pr-comment@v1
+        uses: mshick/add-pr-comment@v2
         if: >-
           always() &&
           github.event_name == 'pull_request_target' &&
           env.COMMENT_BODY != ''
         with:
           repo-token: ${{ secrets.GITHUB_TOKEN }}
-          repo-token-user-login: 'github-actions[bot]'
           message: >
             ${{ steps.check-commits.outcome == 'failure' && 'Errors and warnings' || 'Warnings' }}
             found by [git-po-helper](https://github.com/git-l10n/git-po-helper#readme) in workflow
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 30492ea..3428773 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -5,9 +5,23 @@
 env:
   DEVELOPER: 1
 
+# If more than one workflow run is triggered for the very same commit hash
+# (which happens when multiple branches pointing to the same commit), only
+# the first one is allowed to run, the second will be kept in the "queued"
+# state. This allows a successful completion of the first run to be reused
+# in the second run via the `skip-if-redundant` logic in the `config` job.
+#
+# The only caveat is that if a workflow run is triggered for the same commit
+# hash that another run is already being held, that latter run will be
+# canceled. For more details about the `concurrency` attribute, see:
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency
+concurrency:
+  group: ${{ github.sha }}
+
 jobs:
   ci-config:
     name: config
+    if: vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name)
     runs-on: ubuntu-latest
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
@@ -30,10 +44,13 @@
         name: check whether CI is enabled for ref
         run: |
           enabled=yes
-          if test -x config-repo/ci/config/allow-ref &&
-             ! config-repo/ci/config/allow-ref '${{ github.ref }}'
+          if test -x config-repo/ci/config/allow-ref
           then
-            enabled=no
+            echo "::warning::ci/config/allow-ref is deprecated; use CI_BRANCHES instead"
+            if ! config-repo/ci/config/allow-ref '${{ github.ref }}'
+            then
+              enabled=no
+            fi
           fi
 
           skip_concurrent=yes
@@ -46,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@v6
+        uses: actions/github-script@v7
         if: steps.check-ref.outputs.enabled == 'yes'
         with:
           github-token: ${{secrets.GITHUB_TOKEN}}
@@ -95,7 +112,7 @@
       group: windows-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: build
       shell: bash
@@ -106,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@v3
+      uses: actions/upload-artifact@v4
       with:
         name: windows-artifacts
         path: artifacts
@@ -123,7 +140,7 @@
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - name: download tracked files and build artifacts
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
         name: windows-artifacts
         path: ${{github.workspace}}
@@ -140,9 +157,9 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
-        name: failed-tests-windows
+        name: failed-tests-windows-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   vs-build:
     name: win+VS build
@@ -156,10 +173,10 @@
       group: vs-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: initialize vcpkg
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
       with:
         repository: 'microsoft/vcpkg'
         path: 'compat/vcbuild/vcpkg'
@@ -195,7 +212,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@v3
+      uses: actions/upload-artifact@v4
       with:
         name: vs-artifacts
         path: artifacts
@@ -213,7 +230,7 @@
     steps:
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: download tracked files and build artifacts
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
         name: vs-artifacts
         path: ${{github.workspace}}
@@ -231,9 +248,9 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
-        name: failed-tests-windows
+        name: failed-tests-windows-vs-${{ matrix.nr }}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   regular:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
@@ -246,10 +263,10 @@
       fail-fast: false
       matrix:
         vector:
-          - jobname: linux-clang
+          - jobname: linux-sha256
             cc: clang
             pool: ubuntu-latest
-          - jobname: linux-sha256
+          - jobname: linux-reftable
             cc: clang
             pool: ubuntu-latest
           - jobname: linux-gcc
@@ -262,22 +279,25 @@
             pool: ubuntu-20.04
           - jobname: osx-clang
             cc: clang
-            pool: macos-12
+            pool: macos-13
+          - jobname: osx-reftable
+            cc: clang
+            pool: macos-13
           - jobname: osx-gcc
             cc: gcc
-            cc_package: gcc-9
-            pool: macos-12
+            cc_package: gcc-13
+            pool: macos-13
           - jobname: linux-gcc-default
             cc: gcc
             pool: ubuntu-latest
           - jobname: linux-leaks
             cc: gcc
             pool: ubuntu-latest
-          - jobname: linux-asan
+          - jobname: linux-reftable-leaks
             cc: gcc
             pool: ubuntu-latest
-          - jobname: linux-ubsan
-            cc: gcc
+          - jobname: linux-asan-ubsan
+            cc: clang
             pool: ubuntu-latest
     env:
       CC: ${{matrix.vector.cc}}
@@ -286,7 +306,7 @@
       runs_on_pool: ${{matrix.vector.pool}}
     runs-on: ${{matrix.vector.pool}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - run: ci/install-dependencies.sh
     - run: ci/run-build-and-tests.sh
     - name: print test failures
@@ -294,10 +314,21 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
+  fuzz-smoke-test:
+    name: fuzz smoke test
+    needs: ci-config
+    if: needs.ci-config.outputs.enabled == 'yes'
+    env:
+      CC: clang
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - run: ci/install-dependencies.sh
+    - run: ci/run-build-and-minimal-fuzzers.sh
   dockerized:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
     needs: ci-config
@@ -320,9 +351,9 @@
     runs-on: ubuntu-latest
     container: ${{matrix.vector.image}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
       if: matrix.vector.jobname != 'linux32'
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
       if: matrix.vector.jobname == 'linux32'
     - run: ci/install-docker-dependencies.sh
     - run: ci/run-build-and-tests.sh
@@ -331,13 +362,13 @@
       run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname != 'linux32'
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname == 'linux32'
-      uses: actions/upload-artifact@v1
+      uses: actions/upload-artifact@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -351,7 +382,7 @@
       group: static-analysis-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - run: ci/install-dependencies.sh
     - run: ci/run-static-analysis.sh
     - run: ci/check-directional-formatting.bash
@@ -374,7 +405,7 @@
         artifact: sparse-20.04
     - name: Install the current `sparse` package
       run: sudo dpkg -i sparse-20.04/sparse_*.deb
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install other dependencies
       run: ci/install-dependencies.sh
     - run: make sparse
@@ -389,6 +420,6 @@
       jobname: Documentation
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - run: ci/install-dependencies.sh
     - run: ci/test-documentation.sh
diff --git a/.gitignore b/.gitignore
index e875c59..612c0f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,6 +135,7 @@
 /git-remote-ext
 /git-repack
 /git-replace
+/git-replay
 /git-request-pull
 /git-rerere
 /git-reset
@@ -222,6 +223,7 @@
 /TAGS
 /cscope*
 /compile_commands.json
+/.cache/
 *.hcc
 *.obj
 *.lib
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..c0fa2fe
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,104 @@
+default:
+  timeout: 2h
+
+workflow:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+    - if: $CI_COMMIT_TAG
+    - if: $CI_COMMIT_REF_PROTECTED == "true"
+
+test:linux:
+  image: $image
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - useradd builder --create-home
+    - chown -R builder "${CI_PROJECT_DIR}"
+    - sudo --preserve-env --set-home --user=builder ./ci/run-build-and-tests.sh
+  after_script:
+    - |
+      if test "$CI_JOB_STATUS" != 'success'
+      then
+        sudo --preserve-env --set-home --user=builder ./ci/print-test-failures.sh
+      fi
+  parallel:
+    matrix:
+      - jobname: linux-sha256
+        image: ubuntu:latest
+        CC: clang
+      - jobname: linux-reftable
+        image: ubuntu:latest
+        CC: clang
+      - jobname: linux-gcc
+        image: ubuntu:20.04
+        CC: gcc
+        CC_PACKAGE: gcc-8
+      - jobname: linux-TEST-vars
+        image: ubuntu:20.04
+        CC: gcc
+        CC_PACKAGE: gcc-8
+      - jobname: linux-gcc-default
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-leaks
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-reftable-leaks
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-asan-ubsan
+        image: ubuntu:latest
+        CC: clang
+      - jobname: pedantic
+        image: fedora:latest
+      - jobname: linux-musl
+        image: alpine:latest
+  artifacts:
+    paths:
+      - t/failed-test-artifacts
+    when: on_failure
+
+test:osx:
+  image: $image
+  tags:
+    - saas-macos-medium-m1
+  variables:
+    TEST_OUTPUT_DIRECTORY: "/Volumes/RAMDisk"
+  before_script:
+    # Create a 4GB RAM disk that we use to store test output on. This small hack
+    # significantly speeds up tests by more than a factor of 2 because the
+    # macOS runners use network-attached storage as disks, which is _really_
+    # slow with the many small writes that our tests do.
+    - sudo diskutil apfs create $(hdiutil attach -nomount ram://8192000) RAMDisk
+    - ./ci/install-dependencies.sh
+  script:
+    - ./ci/run-build-and-tests.sh
+  after_script:
+    - |
+      if test "$CI_JOB_STATUS" != 'success'
+      then
+        ./ci/print-test-failures.sh
+        mv "$TEST_OUTPUT_DIRECTORY"/failed-test-artifacts t/
+      fi
+  parallel:
+    matrix:
+      - jobname: osx-clang
+        image: macos-13-xcode-14
+        CC: clang
+      - jobname: osx-reftable
+        image: macos-13-xcode-14
+        CC: clang
+  artifacts:
+    paths:
+      - t/failed-test-artifacts
+    when: on_failure
+
+static-analysis:
+  image: ubuntu:22.04
+  variables:
+    jobname: StaticAnalysis
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - ./ci/run-static-analysis.sh
+    - ./ci/check-directional-formatting.bash
diff --git a/.mailmap b/.mailmap
index 95aaa1c..82129be 100644
--- a/.mailmap
+++ b/.mailmap
@@ -59,12 +59,13 @@
 David S. Miller <davem@davemloft.net>
 David Turner <novalis@novalis.org> <dturner@twopensource.com>
 David Turner <novalis@novalis.org> <dturner@twosigma.com>
-Derrick Stolee <derrickstolee@github.com> <stolee@gmail.com>
-Derrick Stolee <derrickstolee@github.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
-Derrick Stolee <derrickstolee@github.com> <dstolee@microsoft.com>
+Derrick Stolee <stolee@gmail.com> <derrickstolee@github.com>
+Derrick Stolee <stolee@gmail.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
+Derrick Stolee <stolee@gmail.com> <dstolee@microsoft.com>
 Deskin Miller <deskinm@umich.edu>
 Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh
 Dirk Süsserott <newsletter@dirk.my1.cc>
+Emily Shaffer <nasamuffin@google.com> <emilyshaffer@google.com>
 Eric Blake <eblake@redhat.com> <ebb9@byu.net>
 Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net>
 Eric S. Raymond <esr@thyrsus.com>
@@ -79,6 +80,7 @@
 Fredrik Kuivinen <frekui@gmail.com> <freku045@student.liu.se>
 Frédéric Heitzmann <frederic.heitzmann@gmail.com>
 Garry Dolley <gdolley@ucla.edu> <gdolley@arpnetworks.com>
+Glen Choo <glencbz@gmail.com> <chooglen@google.com>
 Greg Price <price@mit.edu> <price@MIT.EDU>
 Greg Price <price@mit.edu> <price@ksplice.com>
 Heiko Voigt <hvoigt@hvoigt.net> <git-list@hvoigt.net>
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 0215b1f..e58917c 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -130,11 +130,11 @@
 version 2.0, available at
 [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
 
-Community Impact Guidelines were inspired by 
+Community Impact Guidelines were inspired by
 [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
 
 For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][FAQ]. Translations are available 
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
 at [https://www.contributor-covenant.org/translations][translations].
 
 [homepage]: https://www.contributor-covenant.org
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index 1c3771e..a48448d 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -10,7 +10,6 @@
 doc.dep
 cmds-*.txt
 mergetools-*.txt
-manpage-base-url.xsl
 SubmittingPatches.txt
 tmp-doc-diff/
 GIT-ASCIIDOCFLAGS
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 9d5c278..ab39509 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -1,5 +1,5 @@
-Like other projects, we also have some guidelines to keep to the
-code.  For Git in general, a few rough rules are:
+Like other projects, we also have some guidelines for our code.  For
+Git in general, a few rough rules are:
 
  - Most importantly, we never say "It's in POSIX; we'll happily
    ignore your needs should your system not conform to it."
@@ -24,7 +24,7 @@
 
    "Once it _is_ in the tree, it's not really worth the patch noise to
    go and fix it up."
-   Cf. http://lkml.iu.edu/hypermail/linux/kernel/1001.3/01069.html
+   Cf. https://lore.kernel.org/all/20100126160632.3bdbe172.akpm@linux-foundation.org/
 
  - Log messages to explain your changes are as important as the
    changes themselves.  Clearly written code and in-code comments
@@ -40,7 +40,7 @@
 contributing to). It is always preferable to match the _local_
 convention. New code added to Git suite is expected to match
 the overall style of existing code. Modifications to existing
-code is expected to match the style the surrounding code already
+code are expected to match the style the surrounding code already
 uses (even if it doesn't match the overall style of existing code).
 
 But if you must have a list of rules, here are some language
@@ -188,6 +188,10 @@
    hopefully nobody starts using "local" before they are reimplemented
    in C ;-)
 
+ - Use octal escape sequences (e.g. "\302\242"), not hexadecimal (e.g.
+   "\xc2\xa2") in printf format strings, since hexadecimal escape
+   sequences are not portable.
+
 
 For C programs:
 
@@ -442,8 +446,41 @@
    detail.
 
  - The first #include in C files, except in platform specific compat/
-   implementations, must be either "git-compat-util.h", "cache.h" or
-   "builtin.h".  You do not have to include more than one of these.
+   implementations and sha1dc/, must be <git-compat-util.h>.  This
+   header file insulates other header files and source files from
+   platform differences, like which system header files must be
+   included in what order, and what C preprocessor feature macros must
+   be defined to trigger certain features we expect out of the system.
+   A collorary to this is that C files should not directly include
+   system header files themselves.
+
+   There are some exceptions, because certain group of files that
+   implement an API all have to include the same header file that
+   defines the API and it is convenient to include <git-compat-util.h>
+   there.  Namely:
+
+   - the implementation of the built-in commands in the "builtin/"
+     directory that include "builtin.h" for the cmd_foo() prototype
+     definition,
+
+   - the test helper programs in the "t/helper/" directory that include
+     "t/helper/test-tool.h" for the cmd__foo() prototype definition,
+
+   - the xdiff implementation in the "xdiff/" directory that includes
+     "xdiff/xinclude.h" for the xdiff machinery internals,
+
+   - the unit test programs in "t/unit-tests/" directory that include
+     "t/unit-tests/test-lib.h" that gives them the unit-tests
+     framework, and
+
+   - the source files that implement reftable in the "reftable/"
+     directory that include "reftable/system.h" for the reftable
+     internals,
+
+   are allowed to assume that they do not have to include
+   <git-compat-util.h> themselves, as it is included as the first
+   '#include' in these header files.  These headers must be the first
+   header file to be "#include"d in them, though.
 
  - A C file must directly include the header files that declare the
    functions and the types it uses, except for the functions and types
@@ -482,7 +519,7 @@
 
  - Most of the C guidelines above apply.
 
- - We try to support Perl 5.8 and later ("use Perl 5.008").
+ - We try to support Perl 5.8.1 and later ("use Perl 5.008001").
 
  - use strict and use warnings are strongly preferred.
 
@@ -510,7 +547,7 @@
 
 For Python scripts:
 
- - We follow PEP-8 (http://www.python.org/dev/peps/pep-0008/).
+ - We follow PEP-8 (https://peps.python.org/pep-0008/).
 
  - As a minimum, we aim to be compatible with Python 2.7.
 
@@ -570,7 +607,7 @@
    . The variable name describes the effect of tweaking this knob.
 
    The section and variable names that consist of multiple words are
-   formed by concatenating the words without punctuations (e.g. `-`),
+   formed by concatenating the words without punctuation marks (e.g. `-`),
    and are broken using bumpyCaps in documentation as a hint to the
    reader.
 
@@ -604,15 +641,15 @@
   - Prefer succinctness and matter-of-factly describing functionality
     in the abstract.  E.g.
 
-     --short:: Emit output in the short-format.
+     `--short`:: Emit output in the short-format.
 
     and avoid something like these overly verbose alternatives:
 
-     --short:: Use this to emit output in the short-format.
-     --short:: You can use this to get output in the short-format.
-     --short:: A user who prefers shorter output could....
-     --short:: Should a person and/or program want shorter output, he
-               she/they/it can...
+     `--short`:: Use this to emit output in the short-format.
+     `--short`:: You can use this to get output in the short-format.
+     `--short`:: A user who prefers shorter output could....
+     `--short`:: Should a person and/or program want shorter output, he
+                 she/they/it can...
 
     This practice often eliminates the need to involve human actors in
     your description, but it is a good practice regardless of the
@@ -622,12 +659,12 @@
     addressing the hypothetical user, and possibly "we" when
     discussing how the program might react to the user.  E.g.
 
-      You can use this option instead of --xyz, but we might remove
+      You can use this option instead of `--xyz`, but we might remove
       support for it in future versions.
 
     while keeping in mind that you can probably be less verbose, e.g.
 
-      Use this instead of --xyz. This option might be removed in future
+      Use this instead of `--xyz`. This option might be removed in future
       versions.
 
   - If you still need to refer to an example person that is
@@ -645,80 +682,12 @@
  The same general rule as for code applies -- imitate the existing
  conventions.
 
- A few commented examples follow to provide reference when writing or
- modifying command usage strings and synopsis sections in the manual
- pages:
 
- Placeholders are spelled in lowercase and enclosed in angle brackets:
-   <file>
-   --sort=<key>
-   --abbrev[=<n>]
+Markup:
 
- If a placeholder has multiple words, they are separated by dashes:
-   <new-branch-name>
-   --template=<template-directory>
-
- Possibility of multiple occurrences is indicated by three dots:
-   <file>...
-   (One or more of <file>.)
-
- Optional parts are enclosed in square brackets:
-   [<file>...]
-   (Zero or more of <file>.)
-
-   --exec-path[=<path>]
-   (Option with an optional argument.  Note that the "=" is inside the
-   brackets.)
-
-   [<patch>...]
-   (Zero or more of <patch>.  Note that the dots are inside, not
-   outside the brackets.)
-
- Multiple alternatives are indicated with vertical bars:
-   [-q | --quiet]
-   [--utf8 | --no-utf8]
-
- Use spacing around "|" token(s), but not immediately after opening or
- before closing a [] or () pair:
-   Do: [-q | --quiet]
-   Don't: [-q|--quiet]
-
- Don't use spacing around "|" tokens when they're used to seperate the
- alternate arguments of an option:
-    Do: --track[=(direct|inherit)]
-    Don't: --track[=(direct | inherit)]
-
- Parentheses are used for grouping:
-   [(<rev> | <range>)...]
-   (Any number of either <rev> or <range>.  Parens are needed to make
-   it clear that "..." pertains to both <rev> and <range>.)
-
-   [(-p <parent>)...]
-   (Any number of option -p, each with one <parent> argument.)
-
-   git remote set-head <name> (-a | -d | <branch>)
-   (One and only one of "-a", "-d" or "<branch>" _must_ (no square
-   brackets) be provided.)
-
- And a somewhat more contrived example:
-   --diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]
-   Here "=" is outside the brackets, because "--diff-filter=" is a
-   valid usage.  "*" has its own pair of brackets, because it can
-   (optionally) be specified only when one or more of the letters is
-   also provided.
-
-  A note on notation:
-   Use 'git' (all lowercase) when talking about commands i.e. something
-   the user would type into a shell and use 'Git' (uppercase first letter)
-   when talking about the version control system and its properties.
-
- A few commented examples follow to provide reference when writing or
- modifying paragraphs or option/command explanations that contain options
- or commands:
-
- Literal examples (e.g. use of command-line options, command names,
+ Literal parts (e.g. use of command-line options, command names,
  branch names, URLs, pathnames (files and directories), configuration and
- environment variables) must be typeset in monospace (i.e. wrapped with
+ environment variables) must be typeset as verbatim (i.e. wrapped with
  backticks):
    `--pretty=oneline`
    `git rev-list`
@@ -727,6 +696,7 @@
    `.git/config`
    `GIT_DIR`
    `HEAD`
+   `umask`(2)
 
  An environment variable must be prefixed with "$" only when referring to its
  value and not when referring to the variable itself, in this case there is
@@ -743,6 +713,97 @@
    Incorrect:
       `\--pretty=oneline`
 
+ Placeholders are spelled in lowercase and enclosed in
+ angle brackets surrounded by underscores:
+   _<file>_
+   _<commit>_
+
+ If a placeholder has multiple words, they are separated by dashes:
+   _<new-branch-name>_
+   _<template-directory>_
+
+ A placeholder is not enclosed in backticks, as it is not a literal.
+
+ When needed, use a distinctive identifier for placeholders, usually
+ made of a qualification and a type:
+   _<git-dir>_
+   _<key-id>_
+
+ When literal and placeholders are mixed, each markup is applied for
+ each sub-entity. If they are stuck, a special markup, called
+ unconstrained formatting is required.
+ Unconstrained formating for placeholders is __<like-this>__
+ Unconstrained formatting for literal formatting is ++like this++
+   `--jobs` _<n>_
+   ++--sort=++__<key>__
+   __<directory>__++/.git++
+   ++remote.++__<name>__++.mirror++
+
+ caveat: ++ unconstrained format is not verbatim and may expand
+ content. Use Asciidoc escapes inside them.
+
+Synopsis Syntax
+
+ Syntax grammar is formatted neither as literal nor as placeholder.
+
+ A few commented examples follow to provide reference when writing or
+ modifying command usage strings and synopsis sections in the manual
+ pages:
+
+ Possibility of multiple occurrences is indicated by three dots:
+   _<file>_...
+   (One or more of <file>.)
+
+ Optional parts are enclosed in square brackets:
+   [_<file>_...]
+   (Zero or more of <file>.)
+
+   ++--exec-path++[++=++__<path>__]
+   (Option with an optional argument.  Note that the "=" is inside the
+   brackets.)
+
+   [_<patch>_...]
+   (Zero or more of <patch>.  Note that the dots are inside, not
+   outside the brackets.)
+
+ Multiple alternatives are indicated with vertical bars:
+   [`-q` | `--quiet`]
+   [`--utf8` | `--no-utf8`]
+
+ Use spacing around "|" token(s), but not immediately after opening or
+ before closing a [] or () pair:
+   Do: [`-q` | `--quiet`]
+   Don't: [`-q`|`--quiet`]
+
+ Don't use spacing around "|" tokens when they're used to separate the
+ alternate arguments of an option:
+    Do: ++--track++[++=++(`direct`|`inherit`)]`
+    Don't: ++--track++[++=++(`direct` | `inherit`)]
+
+ Parentheses are used for grouping:
+   [(_<rev>_ | _<range>_)...]
+   (Any number of either <rev> or <range>.  Parens are needed to make
+   it clear that "..." pertains to both <rev> and <range>.)
+
+   [(`-p` _<parent>_)...]
+   (Any number of option -p, each with one <parent> argument.)
+
+   `git remote set-head` _<name>_ (`-a` | `-d` | _<branch>_)
+   (One and only one of "-a", "-d" or "<branch>" _must_ (no square
+   brackets) be provided.)
+
+ And a somewhat more contrived example:
+   `--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]`
+   Here "=" is outside the brackets, because "--diff-filter=" is a
+   valid usage.  "*" has its own pair of brackets, because it can
+   (optionally) be specified only when one or more of the letters is
+   also provided.
+
+  A note on notation:
+   Use 'git' (all lowercase) when talking about commands i.e. something
+   the user would type into a shell and use 'Git' (uppercase first letter)
+   when talking about the version control system and its properties.
+
  If some place in the documentation needs to typeset a command usage
  example with inline substitutions, it is fine to use +monospaced and
  inline substituted text+ instead of `monospaced literal text`, and with
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 9c67c3a..3f2383a 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -122,6 +122,7 @@
 TECH_DOCS += technical/send-pack-pipeline
 TECH_DOCS += technical/shallow
 TECH_DOCS += technical/trivial-merge
+TECH_DOCS += technical/unit-tests
 SP_ARTICLES += $(TECH_DOCS)
 SP_ARTICLES += technical/api-index
 
@@ -144,14 +145,16 @@
 man7dir = $(mandir)/man7
 # DESTDIR =
 
+GIT_DATE := $(shell git show --quiet --pretty='%as')
+
 ASCIIDOC = asciidoc
 ASCIIDOC_EXTRA =
 ASCIIDOC_HTML = xhtml11
 ASCIIDOC_DOCBOOK = docbook
 ASCIIDOC_CONF = -f asciidoc.conf
 ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA) $(ASCIIDOC_CONF) \
-		-amanversion=$(GIT_VERSION) \
-		-amanmanual='Git Manual' -amansource='Git'
+		-amanmanual='Git Manual' -amansource='Git $(GIT_VERSION)' \
+		-arevdate='$(GIT_DATE)'
 ASCIIDOC_DEPS = asciidoc.conf GIT-ASCIIDOCFLAGS
 TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
 TXT_TO_XML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_DOCBOOK)
@@ -189,15 +192,7 @@
 ifndef MAN_BASE_URL
 MAN_BASE_URL = file://$(htmldir)/
 endif
-XMLTO_EXTRA += -m manpage-base-url.xsl
-
-# If your target system uses GNU groff, it may try to render
-# apostrophes as a "pretty" apostrophe using unicode.  This breaks
-# cut&paste, so you should set GNU_ROFF to force them to be ASCII
-# apostrophes.  Unfortunately does not work with non-GNU roff.
-ifdef GNU_ROFF
-XMLTO_EXTRA += -m manpage-quote-apos.xsl
-endif
+XMLTO_EXTRA += --stringparam man.base.url.for.relative.links='$(MAN_BASE_URL)'
 
 ifdef USE_ASCIIDOCTOR
 ASCIIDOC = asciidoctor
@@ -339,7 +334,6 @@
 	$(RM) technical/*.html technical/api-index.txt
 	$(RM) SubmittingPatches.txt
 	$(RM) $(cmds_txt) $(mergetools_txt) *.made
-	$(RM) manpage-base-url.xsl
 	$(RM) GIT-ASCIIDOCFLAGS
 
 $(MAN_HTML): %.html : %.txt $(ASCIIDOC_DEPS)
@@ -348,11 +342,7 @@
 $(OBSOLETE_HTML): %.html : %.txto $(ASCIIDOC_DEPS)
 	$(QUIET_ASCIIDOC)$(TXT_TO_HTML) -o $@ $<
 
-manpage-base-url.xsl: manpage-base-url.xsl.in
-	$(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
-
-
-manpage-prereqs := manpage-base-url.xsl $(wildcard manpage*.xsl)
+manpage-prereqs := $(wildcard manpage*.xsl)
 manpage-cmd = $(QUIET_XMLTO)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
 %.1 : %.xml $(manpage-prereqs)
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index ccfd0cb..f06563e 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -35,8 +35,9 @@
 contributing are welcome to post questions here. The Git list requires
 plain-text-only emails and prefers inline and bottom-posting when replying to
 mail; you will be CC'd in all replies to you. Optionally, you can subscribe to
-the list by sending an email to majordomo@vger.kernel.org with "subscribe git"
-in the body. The https://lore.kernel.org/git[archive] of this mailing list is
+the list by sending an email to <git+subscribe@vger.kernel.org>
+(see https://subspace.kernel.org/subscribing.html for details).
+The https://lore.kernel.org/git[archive] of this mailing list is
 available to view in a browser.
 
 ==== https://groups.google.com/forum/#!forum/git-mentoring[git-mentoring@googlegroups.com]
@@ -160,10 +161,11 @@
 int cmd_psuh(int argc, const char **argv, const char *prefix);
 ----
 
-Be sure to `#include "builtin.h"` in your `psuh.c`.
+Be sure to `#include "builtin.h"` in your `psuh.c`. You'll also need to
+`#include "gettext.h"` to use functions related to printing output text.
 
-Go ahead and add some throwaway printf to that function. This is a decent
-starting point as we can now add build rules and register the command.
+Go ahead and add some throwaway printf to the `cmd_psuh` function. This is a
+decent starting point as we can now add build rules and register the command.
 
 NOTE: Your throwaway text, as well as much of the text you will be adding over
 the course of this tutorial, is user-facing. That means it needs to be
@@ -832,7 +834,7 @@
 the GitHub PR workflow. It allows contributors to open pull requests against its
 mirror of the Git project, and does some magic to turn the PR into a set of
 emails and send them out for you. It also runs the Git continuous integration
-suite for you. It's documented at http://gitgitgadget.github.io.
+suite for you. It's documented at https://gitgitgadget.github.io/.
 
 [[create-fork]]
 === Forking `git/git` on GitHub
@@ -1164,28 +1166,28 @@
 directory, alongside the v1 patches. Using a single directory makes it easy to
 refer to the old v1 patches while proofreading the v2 patches, but you will need
 to be careful to send out only the v2 patches. We will use a pattern like
-"psuh/v2-*.patch" (not "psuh/*.patch", which would match v1 and v2 patches).
+`psuh/v2-*.patch` (not `psuh/*.patch`, which would match v1 and v2 patches).
 
 Edit your cover letter again. Now is a good time to mention what's different
 between your last version and now, if it's something significant. You do not
 need the exact same body in your second cover letter; focus on explaining to
 reviewers the changes you've made that may not be as visible.
 
-You will also need to go and find the Message-Id of your previous cover letter.
+You will also need to go and find the Message-ID of your previous cover letter.
 You can either note it when you send the first series, from the output of `git
 send-email`, or you can look it up on the
 https://lore.kernel.org/git[mailing list]. Find your cover letter in the
-archives, click on it, then click "permalink" or "raw" to reveal the Message-Id
+archives, click on it, then click "permalink" or "raw" to reveal the Message-ID
 header. It should match:
 
 ----
-Message-Id: <foo.12345.author@example.com>
+Message-ID: <foo.12345.author@example.com>
 ----
 
-Your Message-Id is `<foo.12345.author@example.com>`. This example will be used
-below as well; make sure to replace it with the correct Message-Id for your
-**previous cover letter** - that is, if you're sending v2, use the Message-Id
-from v1; if you're sending v3, use the Message-Id from v2.
+Your Message-ID is `<foo.12345.author@example.com>`. This example will be used
+below as well; make sure to replace it with the correct Message-ID for your
+**previous cover letter** - that is, if you're sending v2, use the Message-ID
+from v1; if you're sending v3, use the Message-ID from v2.
 
 While you're looking at the email, you should also note who is CC'd, as it's
 common practice in the mailing list to keep all CCs on a thread. You can add
@@ -1256,6 +1258,38 @@
 [[now-what]]
 == My Patch Got Emailed - Now What?
 
+Please give reviewers enough time to process your initial patch before
+sending an updated version. That is, resist the temptation to send a new
+version immediately, because others may have already started reviewing
+your initial version.
+
+While waiting for review comments, you may find mistakes in your initial
+patch, or perhaps realize a different and better way to achieve the goal
+of the patch. In this case you may communicate your findings to other
+reviewers as follows:
+
+ - If the mistakes you found are minor, send a reply to your patch as if
+   you were a reviewer and mention that you will fix them in an
+   updated version.
+
+ - On the other hand, if you think you want to change the course so
+   drastically that reviews on the initial patch would be a waste of
+   time (for everyone involved), retract the patch immediately with
+   a reply like "I am working on a much better approach, so please
+   ignore this patch and wait for the updated version."
+
+Now, the above is a good practice if you sent your initial patch
+prematurely without polish.  But a better approach of course is to avoid
+sending your patch prematurely in the first place.
+
+Please be considerate of the time needed by reviewers to examine each
+new version of your patch. Rather than seeing the initial version right
+now (followed by several "oops, I like this version better than the
+previous one" patches over 2 days), reviewers would strongly prefer if a
+single polished version came 2 days later instead, and that version with
+fewer mistakes were the only one they would need to review.
+
+
 [[reviewing]]
 === Responding to Reviews
 
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index eee513e..dec8afe 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -41,6 +41,7 @@
  */
 
 #include "builtin.h"
+#include "trace.h"
 
 int cmd_walken(int argc, const char **argv, const char *prefix)
 {
@@ -49,12 +50,13 @@
 }
 ----
 
-NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or
-off at runtime. For the purposes of this tutorial, we will write `walken` as
-though it is intended for use as a "plumbing" command: that is, a command which
-is used primarily in scripts, rather than interactively by humans (a "porcelain"
-command). So we will send our debug output to `trace_printf()` instead. When
-running, enable trace output by setting the environment variable `GIT_TRACE`.
+NOTE: `trace_printf()`, defined in `trace.h`, differs from `printf()` in
+that it can be turned on or off at runtime. For the purposes of this
+tutorial, we will write `walken` as though it is intended for use as
+a "plumbing" command: that is, a command which is used primarily in
+scripts, rather than interactively by humans (a "porcelain" command).
+So we will send our debug output to `trace_printf()` instead.
+When running, enable trace output by setting the environment variable `GIT_TRACE`.
 
 Add usage text and `-h` handling, like all subcommands should consistently do
 (our test suite will notice and complain if you fail to do so).
@@ -124,7 +126,7 @@
 
 `nr` represents the number of `rev_cmdline_entry` present in the array.
 
-`alloc` is used by the `ALLOC_GROW` macro. Check `cache.h` - this variable is
+`alloc` is used by the `ALLOC_GROW` macro. Check `alloc.h` - this variable is
 used to track the allocated size of the list.
 
 Per entry, we find:
@@ -208,13 +210,14 @@
 
 ...
 
-static int git_walken_config(const char *var, const char *value, void *cb)
+static int git_walken_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	/*
 	 * For now, we don't have any custom configuration, so fall back to
 	 * the default config.
 	 */
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 ----
 
@@ -341,6 +344,10 @@
 `walken_commit_walk()`:
 
 ----
+#include "pretty.h"
+
+...
+
 static void walken_commit_walk(struct rev_info *rev)
 {
 	struct commit *commit;
@@ -383,10 +390,11 @@
 First some setup. Add `grep_config()` to `git_walken_config()`:
 
 ----
-static int git_walken_config(const char *var, const char *value, void *cb)
+static int git_walken_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
-	grep_config(var, value, cb);
-	return git_default_config(var, value, cb);
+	grep_config(var, value, ctx, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 ----
 
@@ -517,7 +525,7 @@
 
 We can base our work on an example. `git pack-objects` prepares all kinds of
 objects for packing into a bitmap or packfile. The work we are interested in
-resides in `builtins/pack-objects.c:get_object_list()`; examination of that
+resides in `builtin/pack-objects.c:get_object_list()`; examination of that
 function shows that the all-object walk is being performed by
 `traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two
 functions reside in `list-objects.c`; examining the source shows that, despite
@@ -726,8 +734,8 @@
 	} else {
 		trace_printf(
 			_("Filtered object walk with filterspec 'tree:1'.\n"));
-		CALLOC_ARRAY(rev->filter, 1);
-		parse_list_objects_filter(rev->filter, "tree:1");
+
+		parse_list_objects_filter(&rev->filter, "tree:1");
 	}
 	traverse_commit_list(rev, walken_show_commit,
 			     walken_show_object, NULL);
@@ -746,14 +754,20 @@
 === Counting Omitted Objects
 
 We also have the capability to enumerate all objects which were omitted by a
-filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking
-`traverse_commit_list_filtered()` to populate the `omitted` list means that our
-object walk does not perform any better than an unfiltered object walk; all
-reachable objects are walked in order to populate the list.
+filter, like with `git log --filter=<spec> --filter-print-omitted`. To do this,
+change `traverse_commit_list()` to `traverse_commit_list_filtered()`, which is
+able to populate an `omitted` list.  Asking for this list of filtered objects
+may cause performance degradations, however, because in this case, despite
+filtering objects, the possibly much larger set of all reachable objects must
+be processed in order to populate that list.
 
 First, add the `struct oidset` and related items we will use to iterate it:
 
 ----
+#include "oidset.h"
+
+...
+
 static void walken_object_walk(
 	...
 
@@ -766,8 +780,9 @@
 	...
 ----
 
-Modify the call to `traverse_commit_list_filtered()` to include your `omitted`
-object:
+Replace the call to `traverse_commit_list()` with
+`traverse_commit_list_filtered()` and pass a pointer to the `omitted` oidset
+defined and initialized above:
 
 ----
 	...
@@ -805,6 +820,10 @@
 go:
 
 ----
+#include "hex.h"
+
+...
+
 static void walken_show_commit(struct commit *cmt, void *buf)
 {
 	trace_printf("commit: %s\n", oid_to_hex(&cmt->object.oid));
@@ -829,7 +848,7 @@
 With only that change, run again (but save yourself some scrollback):
 
 ----
-$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10
+$ GIT_TRACE=1 ./bin-wrappers/git walken 2>&1 | head -n 10
 ----
 
 Take a look at the top commit with `git show` and the object ID you printed; it
@@ -857,7 +876,7 @@
 
 ----
 $ make
-$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10
+$ GIT_TRACE=1 ./bin-wrappers/git walken 2>&1 | tail -n 10
 ----
 
 The last commit object given should have the same OID as the one we saw at the
diff --git a/Documentation/RelNotes/1.6.2.txt b/Documentation/RelNotes/1.6.2.txt
index 980adfb..166d73c 100644
--- a/Documentation/RelNotes/1.6.2.txt
+++ b/Documentation/RelNotes/1.6.2.txt
@@ -10,7 +10,7 @@
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
diff --git a/Documentation/RelNotes/1.6.3.txt b/Documentation/RelNotes/1.6.3.txt
index 4bcff94..bbf177f 100644
--- a/Documentation/RelNotes/1.6.3.txt
+++ b/Documentation/RelNotes/1.6.3.txt
@@ -10,7 +10,7 @@
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
diff --git a/Documentation/RelNotes/1.6.4.txt b/Documentation/RelNotes/1.6.4.txt
index a2a34b4..0fccfb0 100644
--- a/Documentation/RelNotes/1.6.4.txt
+++ b/Documentation/RelNotes/1.6.4.txt
@@ -10,7 +10,7 @@
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
diff --git a/Documentation/RelNotes/1.6.5.txt b/Documentation/RelNotes/1.6.5.txt
index 6c7f7da..79cb1b2 100644
--- a/Documentation/RelNotes/1.6.5.txt
+++ b/Documentation/RelNotes/1.6.5.txt
@@ -21,7 +21,7 @@
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
diff --git a/Documentation/RelNotes/1.6.6.txt b/Documentation/RelNotes/1.6.6.txt
index 3ed1e01..88b86a8 100644
--- a/Documentation/RelNotes/1.6.6.txt
+++ b/Documentation/RelNotes/1.6.6.txt
@@ -63,7 +63,7 @@
 
    Please refer to:
 
-   http://git.or.cz/gitwiki/GitFaq#non-bare
+   https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
    https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
    for more details on the reason why this change is needed and the
diff --git a/Documentation/RelNotes/2.41.0.txt b/Documentation/RelNotes/2.41.0.txt
new file mode 100644
index 0000000..8a9e170
--- /dev/null
+++ b/Documentation/RelNotes/2.41.0.txt
@@ -0,0 +1,399 @@
+Git v2.41 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * Allow information carried on the WWW-Authenticate header to be
+   passed to the credential helpers.
+
+ * A new "fetch.hideRefs" option can be used to exclude specified refs
+   from "rev-list --objects --stdin --not --all" traversal for
+   checking object connectivity, most useful when there are many
+   unrelated histories in a single repository.
+
+ * "git push" has been taught to allow deletion of refs with one-level
+   names to help repairing a repository who acquired such a ref by
+   mistake.  In general, we don't encourage use of such a ref, and
+   creation or update to such a ref is rejected as before.
+
+ * Allow "git bisect reset" to check out the original branch when the
+   branch is already checked out in a different worktree linked to the
+   same repository.
+
+ * A few subcommands have been taught to stop users from working on a
+   branch that is being used in another worktree linked to the same
+   repository.
+
+ * "git format-patch" learned to write a log-message only output file
+   for empty commits.
+
+ * "git format-patch" honors the src/dst prefixes set to nonstandard
+   values with configuration variables like "diff.noprefix", causing
+   receiving end of the patch that expects the standard -p1 format to
+   break.  "format-patch" has been taught to ignore end-user configuration
+   and always use the standard prefixes.
+
+   This is a backward compatibility breaking change.
+
+ * Lift the limitation that colored prompts can only be used with
+   PROMPT_COMMAND mode.
+
+ * "git blame --contents=<file> <rev> -- <path>" used to be forbidden,
+   but now it finds the origins of lines starting at <file> contents
+   through the history that leads to <rev>.
+
+ * "git pack-redundant" gave a warning when run, as the command has
+   outlived its usefulness long ago and is nominated for future
+   removal.  Now we escalate to give an error.
+
+ * "git clone" from an empty repository learned to propagate the
+   choice of the hash algorithm from the source repository to the
+   newly created repository over any one of the v0/v1/v2 protocol.
+
+ * "git mergetool" and "git difftool" learns a new configuration
+   guiDefault to optionally favor configured guitool over non-gui-tool
+   automatically when $DISPLAY is set.
+
+ * "git branch -d origin/master" would say "no such branch", but it is
+   likely a missed "-r" if refs/remotes/origin/master exists.  The
+   command has been taught to give such a hint in its error message.
+
+ * Clean-up of the code path that deals with merge strategy option
+   handling in "git rebase".
+
+ * "git clone --local" stops copying from an original repository that
+   has symbolic links inside its $GIT_DIR; an error message when that
+   happens has been updated.
+
+ * The "--format=..." option of "git for-each-ref", "git branch", and
+   "git tag" commands learn "--omit-empty" to hide refs whose
+   formatting results in an empty string from the output.
+
+ * The sendemail-validate validate hook learned to pass the total
+   number of input files and where in the sequence each invocation is
+   via environment variables.
+
+ * When "gc" needs to retain unreachable objects, packing them into
+   cruft packs (instead of exploding them into loose object files) has
+   been offered as a more efficient option for some time.  Now the use
+   of cruft packs has been made the default and no longer considered
+   an experimental feature.
+
+ * The output given by "git blame" that attributes a line to contents
+   taken from the file specified by the "--contents" option shows it
+   differently from a line attributed to the working tree file.
+
+ * "git send-email" learned to give the e-mail headers to the validate
+   hook by passing an extra argument from the command line.
+
+ * The credential subsystem learns to help OAuth framework.
+
+ * The titles of manual pages used to be chomped at an unreasonably
+   short limit, which has been removed.
+
+ * Error messages given when working on an unborn branch that is
+   checked out in another worktree have been improved.
+
+ * The documentation was misleading about the interaction between
+   GIT_DEFAULT_HASH and "git clone", which has been clarified to
+   stress that the variable is to be ignored by the command.
+
+ * "git send-email" learned "--header-cmd=<cmd>" that can inject
+   arbitrary e-mail header lines to the outgoing messages.
+
+ * "git fsck" learned to detect bit-flip breakages in the reachability
+   bitmap files.
+
+ * The "--stdin" option of "git name-rev" has been replaced with
+   the "--annotate-stdin" option more than a year ago.  We stop
+   advertising it in the "git name-rev -h" output.
+
+ * "git push --all" gained an alias "git push --branches".
+
+ * "git fetch" learned the "--porcelain" option that emits what it did
+   in a machine-parseable format.
+
+ * "git --attr-source=<tree> cmd $args" is a new way to have any
+   command to read attributes not from the working tree but from the
+   given tree object.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Code clean-up to clarify directory traversal API.
+
+ * Code clean-up to clarify the rule that "git-compat-util.h" must be
+   the first to be included.
+
+ * More work towards -Wunused.
+
+ * Instead of forcing each command to choose to honor GPG related
+   configuration variables, make the subsystem lazily initialize
+   itself.
+
+ * Remove workaround for ancient versions of DocBook to make it work
+   correctly with groff, which has not been necessary since docbook
+   1.76 from 2010.
+
+ * Code clean-up to include and/or uninclude parse-options.h file as
+   needed.
+
+ * The code path that reports what "git fetch" did to each ref has
+   been cleaned up.
+
+ * Assorted config API updates.
+
+ * A few configuration variables to tell the cURL library that
+   different types of ssl-cert and ssl-key are in use have been added.
+
+ * Split key function and data structure definitions out of cache.h to
+   new header files and adjust the users.
+
+ * "git fetch --all" does not have to download and handle the same
+   bundleURI over and over, which has been corrected.
+
+ * "git sparse-checkout" command learns a debugging aid for the sparse
+   rule definitions.
+
+ * "git write-tree" learns to work better with sparse-index.
+
+ * The on-disk reverse index that allows mapping from the pack offset
+   to the object name for the object stored at the offset has been
+   enabled by default.
+
+ * "git fsck" learned to validate the on-disk pack reverse index files.
+
+ * strtok() and strtok_r() are banned in this codebase.
+
+ * The detect-compilers script to help auto-tweaking the build system
+   had trouble working with compilers whose version number has extra
+   suffixes.  The script has been taught that certain suffixes (like
+   "-win32" in "gcc 10-win32") can be safely stripped as they share
+   the same features and bugs with the version without the suffix.
+
+ * ctype tests have been taught to test EOF, too.
+
+ * The implementation of credential helpers used fgets() over fixed
+   size buffers to read protocol messages, causing the remainder of
+   the folded long line to trigger unexpected behaviour, which has
+   been corrected.
+
+ * The implementation of the default "negotiator", used to find common
+   ancestor over the network for object tranfer, used to be recursive;
+   it was updated to be iterative to conserve stackspace usage.
+
+ * Our custom callout formatter is no longer used in the documentation
+   formatting toolchain, as the upstream default ones give better
+   output these days.
+
+ * The tracing mechanism learned to notice and report when
+   auto-discovered bare repositories are being used, as allowing so
+   without explicitly stating the user intends to do so (with setting
+   GIT_DIR for example) can be used with social engineering as an
+   attack vector.
+
+ * "git diff-files" learned not to expand sparse-index unless needed.
+
+
+Fixes since v2.40
+-----------------
+
+ * "git fsck" learned to check the index files in other worktrees,
+   just like "git gc" honors them as anchoring points.
+   (merge 8d3e7eac52 jk/fsck-indices-in-worktrees later to maint).
+
+ * Fix a segfaulting loop.  The function and its caller may need
+   further clean-up.
+   (merge c5773dc078 ew/commit-reach-clean-up-flags-fix later to maint).
+
+ * "git restore" supports options like "--ours" that are only
+   meaningful during a conflicted merge, but these options are only
+   meaningful when updating the working tree files.  These options are
+   marked to be incompatible when both "--staged" and "--worktree" are
+   in effect.
+   (merge ee8a88826a ak/restore-both-incompatible-with-conflicts later to maint).
+
+ * Simplify UI to control progress meter given by "git bundle" command.
+   (merge 8b95521edb jk/bundle-progress later to maint).
+
+ * "git bundle" learned that "-" is a common way to say that the input
+   comes from the standard input and/or the output goes to the
+   standard output.  It used to work only for output and only from the
+   root level of the working tree.
+   (merge 0bbe10313e jk/bundle-use-dash-for-stdfiles later to maint).
+
+ * Once we start running, we assumed that the list of alternate object
+   databases would never change.  Hook into the machinery used to
+   update the list of packfiles during runtime to update this list as
+   well.
+   (merge e2d003dbed ds/reprepare-alternates-when-repreparing-packfiles later to maint).
+
+ * The code to parse "git rebase -X<opt>" was not prepared to see an
+   unparsable option string, which has been corrected.
+   (merge 15a4cc912e ab/fix-strategy-opts-parsing later to maint).
+
+ * "git add -p" while the index is unmerged sometimes failed to parse
+   the diff output it internally produces and died, which has been
+   corrected.
+   (merge 28d1122f9c jk/add-p-unmerged-fix later to maint).
+
+ * Fix for a "ls-files --format="%(path)" that produced nonsense
+   output, which was a bug in 2.38.
+   (merge cfb62dd006 aj/ls-files-format-fix later to maint).
+
+ * "git receive-pack" that responds to "git push" requests failed to
+   clean a stale lockfile when killed in the middle, which has been
+   corrected.
+   (merge c55c30669c ps/receive-pack-unlock-before-die later to maint).
+
+ * "git rev-parse --quiet foo@{u}", or anything that asks @{u} to be
+   parsed with GET_OID_QUIETLY option, did not quietly fail, which has
+   been corrected.
+   (merge dfbfdc521d fc/oid-quietly-parse-upstream later to maint).
+
+ * Transports that do not support protocol v2 did not correctly fall
+   back to protocol v0 under certain conditions, which has been
+   corrected.
+   (merge eaa0fd6584 jk/fix-proto-downgrade-to-v0 later to maint).
+
+ * time(2) on glib 2.31+, especially on Linux, goes out of sync with
+   higher resolution timers used for gettimeofday(2) and by the
+   filesystem.  Replace all calls to it with a git_time() wrapper and
+   (merge 370ddcbc89 pe/time-use-gettimeofday later to maint).
+
+ * Code clean-up to use designated initializers in parse-options API.
+   (merge 353e6d4554 sg/parse-options-h-initializers later to maint).
+
+ * A recent-ish change to allow unicode character classes to be used
+   with "grep -P" triggered a JIT bug in older pcre2 libraries.
+   The problematic change in Git built with these older libraries has
+   been disabled to work around the bug.
+   (merge 14b9a04479 mk/workaround-pcre-jit-ucp-bug later to maint).
+
+ * The wildmatch library code unlearns exponential behaviour it
+   acquired some time ago since it was borrowed from rsync.
+   (merge 3dc0b7f0dc pw/wildmatch-fixes later to maint).
+
+ * The index files can become corrupt under certain conditions when
+   the split-index feature is in use, especially together with
+   fsmonitor, which have been corrected.
+   (merge 061dd722dc js/split-index-fixes later to maint).
+
+ * Document what the pathname-looking strings in "rev-list --object"
+   output are for and what they mean.
+   (merge 15364d2a3c jk/document-rev-list-object-name later to maint).
+
+ * Fix unnecessary truncation of generation numbers used in-core.
+   (merge d3af1c193d ps/ahead-behind-truncation-fix later to maint).
+
+ * Code clean-up around the use of the_repository.
+   (merge 4a93b899c1 ab/remove-implicit-use-of-the-repository later to maint).
+
+ * Consistently spell "Message-ID" as such, not "Message-Id".
+   (merge ba4324c4e1 jc/spell-id-in-both-caps-in-message-id later to maint).
+
+ * Correct use of an uninitialized structure member.
+   (merge dc12ee77ab jx/cap-object-info-uninitialized-fix later to maint).
+
+ * Tests had a few places where we ignored PERL_PATH and blindly used
+   /usr/bin/perl, which have been corrected.
+   (merge c1917156a0 jk/use-perl-path-consistently later to maint).
+
+ * Documentation mark-up fix.
+   (merge 78b6369e67 la/mfc-markup-fix later to maint).
+
+ * Doc toolchain update to remove old workaround for AsciiDoc.
+   (merge 8806120de6 fc/remove-header-workarounds-for-asciidoc later to maint).
+
+ * The userdiff regexp patterns for various filetypes that are built
+   into the system have been updated to avoid triggering regexp errors
+   from UTF-8 aware regex engines.
+   (merge be39144954 rs/userdiff-multibyte-regex later to maint).
+
+ * The approxidate() API has been simplified by losing an extra
+   function that did the same thing as another one.
+   (merge 8a7f0b666f rs/remove-approxidate-relative later to maint).
+
+ * Code clean-up to replace a hardcoded constant with a CPP macro.
+   (merge c870de6502 rs/get-tar-commit-id-use-defined-const later to maint).
+
+ * Doc build simplification.
+   (merge 9a09ed3229 fc/doc-stop-using-manversion later to maint).
+
+ * "git archive" run from a subdirectory mishandled attributes and
+   paths outside the current directory.
+   (merge 92b1dd1b9e rs/archive-from-subdirectory-fixes later to maint).
+
+ * The code to parse capability list for v0 on-wire protocol fell into
+   an infinite loop when a capability appears multiple times, which
+   has been corrected.
+
+ * Geometric repacking ("git repack --geometric=<n>") in a repository
+   that borrows from an alternate object database had various corner
+   case bugs, which have been corrected.
+   (merge d85cd18777 ps/fix-geom-repack-with-alternates later to maint).
+
+ * The "%GT" placeholder for the "--format" option of "git log" and
+   friends caused BUG() to trigger on a commit signed with an unknown
+   key, which has been corrected.
+   (merge 7891e46585 jk/gpg-trust-level-fix later to maint).
+
+ * The completion script used to use bare "read" without the "-r"
+   option to read the contents of various state files, which risked
+   getting confused with backslashes in them.  This has been
+   corrected.
+   (merge 197152098a ek/completion-use-read-r-to-read-literally later to maint).
+
+ * A small API fix to the ort merge strategy backend.
+   (merge 000c4ceca7 en/ort-finalize-after-0-merges-fix later to maint).
+
+ * The commit object parser has been taught to be a bit more lenient
+   to parse timestamps on the author/committer line with a malformed
+   author/committer ident.
+   (merge 90ef0f14eb jk/parse-commit-with-malformed-ident later to maint).
+
+ * Retitle a test script with an overly narrow name.
+   (merge 8bb19c14fb ob/t3501-retitle later to maint).
+
+ * Doc update to clarify how text and eol attributes interact to
+   specify the end-of-line conversion.
+   (merge 6696077ace ah/doc-attributes-text later to maint).
+
+ * Gitk updates from GfW project.
+   (merge 99e70f3077 js/gitk-fixes-from-gfw later to maint).
+
+ * "git diff --dirstat" leaked memory, which has been plugged.
+   (merge 83973981eb jc/dirstat-plug-leaks later to maint).
+
+ * "git merge-tree" reads the basic configuration, which can be used
+    by git forges to disable replace-refs feature.
+   (merge b6551feadf ds/merge-tree-use-config later to maint).
+
+ * A few bugs in the sequencer machinery that results in miscounting
+   the steps have been corrected.
+   (merge 170eea9750 js/rebase-count-fixes later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge f7111175df as/doc-markup-fix later to maint).
+   (merge 90ff7c9898 fc/test-aggregation-clean-up later to maint).
+   (merge 9b0c7f308a jc/am-doc-refer-to-format-patch later to maint).
+   (merge b10cbdac4c bb/unicode-width-table-15 later to maint).
+   (merge 3457b50e8c ab/retire-scripted-add-p later to maint).
+   (merge d52fcf493b ds/p2000-fix-grep-sparse later to maint).
+   (merge ec063d2591 ss/hashmap-typofix later to maint).
+   (merge 1aaed69d11 rs/archive-mtime later to maint).
+   (merge 2da2cc9b28 ob/rollback-after-commit-lock-failure later to maint).
+   (merge 54dbd0933b ob/sequencer-save-head-simplify later to maint).
+   (merge a93cbe8d78 ar/test-cleanup-unused-file-creation later to maint).
+   (merge cc48ddd937 jk/chainlint-fixes later to maint).
+   (merge 4833b08426 ow/ref-format-remove-unused-member later to maint).
+   (merge d0ea2ca1cf dw/doc-submittingpatches-grammofix later to maint).
+   (merge fd72637423 ar/t2024-checkout-output-fix later to maint).
+   (merge d45cbe3fe0 ob/sequencer-i18n-fix later to maint).
+   (merge b734fe49fd ob/messages-capitalize-exception later to maint).
+   (merge ad353d7e77 ma/gittutorial-fixes later to maint).
+   (merge a5855fd8d4 ar/test-cleanup-unused-file-creation-part2 later to maint).
+   (merge 0c5308af30 sd/doc-gitignore-and-rm-cached later to maint).
+   (merge cbb83daeaf kh/doc-interpret-trailers-updates later to maint).
+   (merge 3d77fbb664 ar/config-count-tests-updates later to maint).
+   (merge b7cf25c8f4 jc/t9800-fix-use-of-show-s-raw later to maint).
diff --git a/Documentation/RelNotes/2.42.0.txt b/Documentation/RelNotes/2.42.0.txt
new file mode 100644
index 0000000..0f1897a
--- /dev/null
+++ b/Documentation/RelNotes/2.42.0.txt
@@ -0,0 +1,329 @@
+Git v2.42 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * "git pack-refs" learns "--include" and "--exclude" to tweak the ref
+   hierarchy to be packed using pattern matching.
+
+ * 'git worktree add' learned how to create a worktree based on an
+   orphaned branch with `--orphan`.
+
+ * "git pack-objects" learned to invoke a new hook program that
+   enumerates extra objects to be used as anchoring points to keep
+   otherwise unreachable objects in cruft packs.
+
+ * Add more "git var" for toolsmiths to learn various locations Git is
+   configured with either via the configuration or hard-coded defaults.
+
+ * 'git notes append' was taught '--separator' to specify string to insert
+   between paragraphs.
+
+ * The "git for-each-ref" family of commands learned placeholders
+   related to GPG signature verification.
+
+ * "git diff --no-index" learned to read from named pipes as if they
+   were regular files, to allow "git diff <(process) <(substitution)"
+   some shells support.
+
+ * Help newbies by suggesting that there are cases where force-pushing
+   is a valid and sensible thing to update a branch at a remote
+   repository, rather than reconciling with merge/rebase.
+
+ * "git blame --contents=file" has been taught to work in a bare
+   repository.
+
+ * "git branch -f X" to repoint the branch X said that X was "checked
+   out" in another worktree, even when branch X was not and instead
+   being bisected or rebased.  The message was reworded to say the
+   branch was "in use".
+
+ * Tone down the warning on SHA-256 repositories being an experimental
+   curiosity.  We do not have support for them to interoperate with
+   traditional SHA-1 repositories, but at this point, we do not plan
+   to make breaking changes to SHA-256 repositories and there is no
+   longer need for such a strongly phrased warning.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git diff-tree" has been taught to take advantage of the
+   sparse-index feature.
+
+ * Clang's sanitizer implementation seems to work better than GCC's.
+   (merge d88d727143 jk/ci-use-clang-for-sanitizer-jobs later to maint).
+
+ * The object traversal using reachability bitmap done by
+   "pack-object" has been tweaked to take advantage of the fact that
+   using "boundary" commits as representative of all the uninteresting
+   ones can save quite a lot of object enumeration.
+
+ * discover_git_directory() no longer touches the_repository.
+
+ * "git worktree" learned to work better with sparse index feature.
+
+ * When the external merge driver is killed by a signal, its output
+   should not be trusted as a resolution with conflicts that is
+   proposed by the driver, but the code did.
+
+ * The set-up code for the get_revision() API now allows feeding
+   options like --all and --not in the --stdin mode.
+
+ * Move functions that are not about pure string manipulation out of
+   strbuf.[ch]
+
+ * "imap-send" codepaths got cleaned up to get rid of unused
+   parameters.
+
+ * Enumerating refs in the packed-refs file, while excluding refs that
+   match certain patterns, has been optimized.
+
+ * Mark-up unused parameters in the code so that we can eventually
+   enable -Wunused-parameter by default.
+
+ * Instead of inventing a custom counter variables for debugging,
+   use existing trace2 facility in the fsync customization codepath.
+
+ * "git branch --list --format=<format>" and friends are taught
+   a new "%(describe)" placeholder.
+
+ * Clarify how to choose the starting point for a new topic in
+   developer guidance document.
+
+ * The implementation of "get_sha1_hex()" that reads a hexadecimal
+   string that spells a full object name has been extended to cope
+   with any hash function used in the repository, but the "sha1" in
+   its name survived.  Rename it to get_hash_hex(), a name that is
+   more consistent within its friends like get_hash_hex_algop().
+
+ * Command line parser fix, and a small parse-options API update.
+
+
+Fixes since v2.41
+-----------------
+
+ * "git tag" learned to leave the "$GIT_DIR/TAG_EDITMSG" file when the
+   command failed, so that the user can salvage what they typed.
+   (merge 08c12ec1d0 kh/keep-tag-editmsg-upon-failure later to maint).
+
+ * The "-s" (silent, squelch) option of the "diff" family of commands
+   did not interact with other options that specify the output format
+   well.  This has been cleaned up so that it will clear all the
+   formatting options given before.
+   (merge 9d484b92ed jc/diff-s-with-other-options later to maint).
+
+ * Update documentation regarding Coccinelle patches.
+   (merge 3bd0097cfc gc/doc-cocci-updates later to maint).
+
+ * Some atoms that can be used in "--format=<format>" for "git ls-tree"
+   were not supported by "git ls-files", even though they were relevant
+   in the context of the latter.
+   (merge 4d28c4f75f zh/ls-files-format-atoms later to maint).
+
+ * Document more pseudo-refs and teach the command line completion
+   machinery to complete AUTO_MERGE.
+   (merge 982ff3a649 pb/complete-and-document-auto-merge-and-friends later to maint).
+
+ * "git submodule" code trusted the data coming from the config (and
+   the in-tree .gitmodules file) too much without validating, leading
+   to NULL dereference if the user mucks with a repository (e.g.
+   submodule.<name>.url is removed).  This has been corrected.
+   (merge fbc806acd1 tb/submodule-null-deref-fix later to maint).
+
+ * The value of config.worktree is per-repository, but has been kept
+   in a singleton global variable per process. This has been OK as
+   most Git operations interacted with a single repository at a time,
+   but not right for operations like recursive "grep" that want to
+   access multiple repositories from a single process without forking.
+
+   The global variable has been eliminated and made into a member in
+   the per-repository data structure.
+   (merge 3867f6d650 vd/worktree-config-is-per-repository later to maint).
+
+ * "git [-c log.follow=true] log [--follow] ':(glob)f**'" used to barf.
+   (merge 8260bc5902 jk/log-follow-with-non-literal-pathspec later to maint).
+
+ * Introduce a mechanism to disable replace refs globally and per
+   repository.
+   (merge 9c7d1b057f ds/disable-replace-refs later to maint).
+
+ * "git cat-file --batch" and friends learned "-Z" that uses NUL
+   delimiter for both input and output.
+   (merge f79e18849b ps/cat-file-null-output later to maint).
+
+ * The reimplemented "git add -i" did not honor color.ui configuration.
+   (merge 6f74648cea ds/add-i-color-configuration-fix later to maint).
+
+ * Compilation fix for platforms without D_TYPE in struct dirent.
+   (merge 03bf92b9bf as/dtype-compilation-fix later to maint).
+
+ * Suggest to refrain from using hex literals that are non-portable
+   when writing printf(1) format strings.
+   (merge f0b68f0546 jt/doc-use-octal-with-printf later to maint).
+
+ * Simplify error message when run-command fails to start a command.
+   (merge 6d224ac286 rs/run-command-exec-error-on-noent later to maint).
+
+ * Gracefully deal with a stale MIDX file that lists a packfile that
+   no longer exists.
+   (merge 06f3867865 tb/open-midx-bitmap-fallback later to maint).
+
+ * Even when diff.ignoreSubmodules tells us to ignore submodule
+   changes, "git commit" with an index that already records changes to
+   submodules should include the submodule changes in the resulting
+   commit, but it did not.
+   (merge 5768478edc js/defeat-ignore-submodules-config-with-explicit-addition later to maint).
+
+ * When "git commit --trailer=..." invokes the interpret-trailers
+   machinery, it knows what it feeds to interpret-trailers is a full
+   log message without any patch, but failed to express that by
+   passing the "--no-divider" option, which has been corrected.
+   (merge be3d654343 jk/commit-use-no-divider-with-interpret-trailers later to maint).
+
+ * Avoid breakage of "git pack-objects --cruft" due to inconsistency
+   between the way the code enumerates packfiles in the repository.
+   (merge 73320e49ad tb/collect-pack-filenames-fix later to maint).
+
+ * We create .pack and then .idx, we consider only packfiles that have
+   .idx usable (those with only .pack are not ready yet), so we should
+   remove .idx before removing .pack for consistency.
+   (merge 0dd1324a73 ds/remove-idx-before-pack later to maint).
+
+ * Partially revert a sanity check that the rest of the config code
+   was not ready, to avoid triggering it in a corner case.
+   (merge a53f43f900 gc/config-partial-submodule-kvi-fix later to maint).
+
+ * "git apply" punts when it is fed too large a patch input; the error
+   message it gives when it happens has been clarified.
+   (merge 42612e18d2 pw/apply-too-large later to maint).
+
+ * During a cherry-pick or revert session that works on multiple
+   commits, "git status" did not give correct information, which has
+   been corrected.
+   (merge a096a889f4 jk/cherry-pick-revert-status later to maint).
+
+ * A few places failed to differentiate the case where the index is
+   truly empty (nothing added) and we haven't yet read from the
+   on-disk index file, which have been corrected.
+   (merge 2ee045eea1 js/empty-index-fixes later to maint).
+
+ * "git bugreport" tests did not test what it wanted to test, which
+   has been corrected.
+   (merge 1aa92b8500 ma/t0091-fixup later to maint).
+
+ * Code snippets in a tutorial document no longer compiled after
+   recent header shuffling, which have been corrected.
+   (merge bbd7c7b7c0 vd/adjust-mfow-doc-to-updated-headers later to maint).
+
+ * "git ls-files '(attr:X)D/'" that triggers the common prefix
+   optimization codepath failed to read from "D/.gitattributes",
+   which has been corrected.
+   (merge f4a8fde057 jc/pathspec-match-with-common-prefix later to maint).
+
+ * "git fsck --no-progress" still spewed noise from the commit-graph
+   subsystem, which has been corrected.
+   (merge 9281cd07f0 tb/fsck-no-progress later to maint).
+
+ * Various offset computation in the code that accesses the packfiles
+   and other data in the object layer has been hardened against
+   arithmetic overflow, especially on 32-bit systems.
+   (merge 9a25cad7e0 tb/object-access-overflow-protection later to maint).
+
+ * Names of MinGW header files are spelled in mixed case in some
+   source files, but the build host can be using case sensitive
+   filesystem with header files with their name spelled in all
+   lowercase.
+   (merge 4a53d0d0bc mh/mingw-case-sensitive-build later to maint).
+
+ * Update message mark-up for i18n in "git bundle".
+   (merge bbb6acd998 dk/bundle-i18n-more later to maint).
+
+ * "git tag --list --points-at X" showed tags that directly refers to
+   object X, but did not list a tag that points at such a tag, which
+   has been corrected.
+
+ * "./configure --with-expat=no" did not work as a way to refuse use
+   of the expat library on a system with the library installed, which
+   has been corrected.
+   (merge fb8f7269c2 ah/autoconf-fixes later to maint).
+
+ * When the user edits "rebase -i" todo file so that it starts with a
+   "fixup", which would make it invalid, the command truncated the
+   rest of the file before giving an error and returning the control
+   back to the user.  Stop truncating to make it easier to correct
+   such a malformed todo file.
+   (merge 9645a087c2 ah/sequencer-rewrite-todo-fix later to maint).
+
+ * Rewrite the description of giving a custom command to the
+   submodule.<name>.update configuration variable.
+   (merge 7cebc5bd78 pv/doc-submodule-update-settings later to maint).
+
+ * Adjust to OpenSSL 3+, which deprecates its SHA-1 functions based on
+   its traditional API, by using its EVP API instead.
+   (merge bda9c12073 ew/hash-with-openssl-evp later to maint).
+
+ * Exclude "." from the set of characters to be removed from the
+   beginning and the end of the human-readable name.
+   (merge 1c04cb0744 bc/ident-dot-is-no-longer-crud-letter later to maint).
+
+ * "git bisect visualize" stopped running "gitk" on Git for Windows
+   when the command was reimplemented in C around Git 2.34 timeframe.
+   This has been corrected.
+   (merge fff1594fa7 ma/locate-in-path-for-windows later to maint).
+
+ * "git rebase -i" with a series of squash/fixup, when one of the
+   steps stopped in conflicts and ended up getting skipped, did not
+   handle the accumulated commit log messages, which has been
+   corrected.
+   (merge 6ce7afe163 pw/rebase-skip-commit-message-fix later to maint).
+
+ * Adjust to newer Term::ReadLine to prevent it from breaking
+   the interactive prompt code in send-email.
+   (merge c016726c2d jk/send-email-with-new-readline later to maint).
+
+ * Windows updates.
+   (merge 0050f8e401 ds/maintenance-on-windows-fix later to maint).
+
+ * Correct use of lstat() that assumed a failing call would not
+   clobber the statbuf.
+   (merge 72695d8214 st/mv-lstat-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 51f9d2e563 sa/doc-ls-remote later to maint).
+   (merge c6d26a9dda jk/format-patch-message-id-unleak later to maint).
+   (merge f7e063f326 ps/fetch-cleanups later to maint).
+   (merge e4cf013468 tl/quote-problematic-arg-for-clarity later to maint).
+   (merge 20025fdfc7 tz/test-ssh-verifytime-fix later to maint).
+   (merge e48a21df65 tz/test-fix-pthreads-prereq later to maint).
+   (merge 68b51172e3 mh/commit-reach-get-reachable-plug-leak later to maint).
+   (merge aeee1408ce kh/use-default-notes-doc later to maint).
+   (merge 3b8724bce6 jc/test-modernization later to maint).
+   (merge 447a3b7331 jc/test-modernization-2 later to maint).
+   (merge d57fa7fc73 la/doc-interpret-trailers later to maint).
+   (merge 548afb0d9a la/docs-typofixes later to maint).
+   (merge 3744ffcbcd rs/doc-ls-tree-hex-literal later to maint).
+   (merge 6c26da8404 mh/credential-erase-improvements later to maint).
+   (merge 78e56cff69 tz/lib-gpg-prereq-fix later to maint).
+   (merge 80d32e84b5 rj/leakfixes later to maint).
+   (merge 0a868031ed pb/complete-diff-options later to maint).
+   (merge d4f28279ad jc/doc-hash-object-types later to maint).
+   (merge 1876a5ae15 ks/t4205-test-describe-with-abbrev-fix later to maint).
+   (merge 6e6a529b57 jk/fsck-indices-in-worktrees later to maint).
+   (merge 3e81b896f7 rs/packet-length-simplify later to maint).
+   (merge 4c9cb51fe7 mh/doc-credential-helpers later to maint).
+   (merge 3437f549dd jr/gitignore-doc-example-markup later to maint).
+   (merge 947ebd62a0 jc/am-parseopt-fix later to maint).
+   (merge e12cb98e1e jc/branch-parseopt-fix later to maint).
+   (merge d6f598e443 jc/gitignore-doc-pattern-markup later to maint).
+   (merge a2dad4868b jc/transport-parseopt-fix later to maint).
+   (merge 68cbb20e73 jc/parse-options-show-branch later to maint).
+   (merge 3821eb6c3d jc/parse-options-reset later to maint).
+   (merge c48af99a3e bb/trace2-comment-fix later to maint).
+   (merge c95ae3ff9c rs/describe-parseopt-fix later to maint).
+   (merge 36f76d2a25 rs/pack-objects-parseopt-fix later to maint).
+   (merge 30c8c55cbf jc/tree-walk-drop-base-offset later to maint).
+   (merge d089a06421 rs/bundle-parseopt-cleanup later to maint).
+   (merge 823839bda1 ew/sha256-gcrypt-leak-fixes later to maint).
+   (merge a5c01603b3 bc/ignore-clangd-cache later to maint).
+   (merge 12009a182b js/allow-t4000-to-be-indented-with-spaces later to maint).
+   (merge b3dcd24b8a jc/send-email-pre-process-fix later to maint).
diff --git a/Documentation/RelNotes/2.42.1.txt b/Documentation/RelNotes/2.42.1.txt
new file mode 100644
index 0000000..3d391b7
--- /dev/null
+++ b/Documentation/RelNotes/2.42.1.txt
@@ -0,0 +1,88 @@
+Git 2.42.1 Release Notes
+========================
+
+There is nothing exciting to see here.  Relative to Git 2.42, this
+release contains the fixes that have already been merged to the
+'master' branch of the development towards Git 2.43 that has been
+tagged as Git 2.43.0-rc0.
+
+Fixes since Git 2.42.0
+----------------------
+
+ * Tests that are known to pass with LSan are now marked as such.
+
+ * Flaky "git p4" tests, as well as "git svn" tests, are now skipped
+   in the (rather expensive) sanitizer CI job.
+
+ * Tests with LSan from time to time seem to emit harmless message
+   that makes our tests unnecessarily flaky; we work it around by
+   filtering the uninteresting output.
+
+ * GitHub CI workflow has learned to trigger Coverity check.
+
+ * Overly long label names used in the sequencer machinery are now
+   chopped to fit under filesystem limitation.
+
+ * Scalar updates.
+
+ * Tweak GitHub Actions CI so that pushing the same commit to multiple
+   branch tips at the same time will not waste building and testing
+   the same thing twice.
+
+ * The commit-graph verification code that detects mixture of zero and
+   non-zero generation numbers has been updated.
+
+ * "git diff -w --exit-code" with various options did not work
+   correctly, which is being addressed.
+
+ * transfer.unpackLimit ought to be used as a fallback, but overrode
+   fetch.unpackLimit and receive.unpackLimit instead.
+
+ * The use of API between two calls to require_clean_work_tree() from
+   the sequencer code has been cleaned up for consistency.
+
+ * "git diff --no-such-option" and other corner cases around the exit
+   status of the "diff" command has been corrected.
+
+ * "git for-each-ref --sort='contents:size'" sorts the refs according
+   to size numerically, giving a ref that points at a blob twelve-byte
+   (12) long before showing a blob hundred-byte (100) long.
+
+ * Various fixes to the behavior of "rebase -i" when the command got
+   interrupted by conflicting changes.
+
+ * References from description of the `--patch` option in various
+   manual pages have been simplified and improved.
+
+ * "git grep -e A --no-or -e B" is accepted, even though the negation
+   of "or" did not mean anything, which has been tightened.
+
+ * The completion script (in contrib/) has been taught to treat the
+   "-t" option to "git checkout" and "git switch" just like the
+   "--track" option, to complete remote-tracking branches.
+
+ * "git diff --no-index -R <(one) <(two)" did not work correctly,
+   which has been corrected.
+
+ * Update "git maintenance" timers' implementation based on systemd
+   timers to work with WSL.
+
+ * "git diff --cached" codepath did not fill the necessary stat
+   information for a file when fsmonitor knows it is clean and ended
+   up behaving as if it is not clean, which has been corrected.
+
+ * Clarify how "alias.foo = : git cmd ; aliased-command-string" should
+   be spelled with necessary whitespaces around punctuation marks to
+   work.
+
+ * HTTP Header redaction code has been adjusted for a newer version of
+   cURL library that shows its traces differently from earlier
+   versions.
+
+ * An error message given by "git send-email" when given a malformed
+   address did not give correct information, which has been corrected.
+
+ * UBSan options were not propagated through the test framework to git
+   run via the httpd, unlike ASan options, which has been corrected.
+
+Also contains various documentation updates, code clean-ups and minor fixups.
diff --git a/Documentation/RelNotes/2.43.0.txt b/Documentation/RelNotes/2.43.0.txt
new file mode 100644
index 0000000..e0e5b53
--- /dev/null
+++ b/Documentation/RelNotes/2.43.0.txt
@@ -0,0 +1,323 @@
+Git v2.43 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ * The "--rfc" option of "git format-patch" used to be a valid way to
+   override an earlier "--subject-prefix=<something>" on the command
+   line and replace it with "[RFC PATCH]", but from this release, it
+   merely prefixes the string "RFC " in front of the given subject
+   prefix.  If you are negatively affected by this change, please use
+   "--subject-prefix=PATCH --rfc" as a replacement.
+
+ * In Git 2.42, "git rev-list --stdin" learned to take non-revisions
+   (like "--not") from the standard input, but the way such a "--not" was
+   handled was quite confusing, which has been rethought.  The updated
+   rule is that "--not" given from the command line only affects revs
+   given from the command line that comes but not revs read from the
+   standard input, and "--not" read from the standard input affects
+   revs given from the standard input and not revs given from the
+   command line.
+
+UI, Workflows & Features
+
+ * A message written in olden time prevented a branch from getting
+   checked out, saying it is already checked out elsewhere. But these
+   days, we treat a branch that is being bisected or rebased just like
+   a branch that is checked out and protect it from getting modified
+   with the same codepath.  The message has been rephrased to say that
+   the branch is "in use" to avoid confusion.
+
+ * Hourly and other schedules of "git maintenance" jobs are randomly
+   distributed now.
+
+ * "git cmd -h" learned to signal which options can be negated by
+   listing such options like "--[no-]opt".
+
+ * The way authentication related data other than passwords (e.g.,
+   oauth token and password expiration data) are stored in libsecret
+   keyrings has been rethought.
+
+ * Update the libsecret and wincred credential helpers to correctly
+   match which credential to erase; they erased the wrong entry in
+   some cases.
+
+ * Git GUI updates.
+
+ * "git format-patch" learned a new "--description-file" option that
+   lets cover letter description to be fed; this can be used on
+   detached HEAD where there is no branch description available, and
+   also can override the branch description if there is one.
+
+ * Use of the "--max-pack-size" option to allow multiple packfiles to
+   be created is now supported even when we are sending unreachable
+   objects to cruft packs.
+
+ * "git format-patch --rfc --subject-prefix=<foo>" used to ignore the
+   "--subject-prefix" option and used "[RFC PATCH]"; now we will add
+   "RFC" prefix to whatever subject prefix is specified.
+
+ * "git log --format" has been taught the %(decorate) placeholder for
+   further customization over what the "--decorate" option offers.
+
+ * The default log message created by "git revert", when reverting a
+   commit that records a revert, has been tweaked, to encourage people
+   to describe complex "revert of revert of revert" situations better in
+   their own words.
+
+ * The command-line completion support (in contrib/) learned to
+   complete "git commit --trailer=" for possible trailer keys.
+
+ * "git update-index" learned the "--show-index-version" option to
+   inspect the index format version used by the on-disk index file.
+
+ * "git diff" learned the "diff.statNameWidth" configuration variable,
+   to give the default width for the name part in the "--stat" output.
+
+ * "git range-diff --notes=foo" compared "log --notes=foo --notes" of
+   the two ranges, instead of using just the specified notes tree,
+   which has been corrected to use only the specified notes tree.
+
+ * The command line completion script (in contrib/) can be told to
+   complete aliases by including ": git <cmd> ;" in the alias to tell
+   it that the alias should be completed in a similar way to how "git
+   <cmd>" is completed.  The parsing code for the alias has been
+   loosened to allow ';' without an extra space before it.
+
+ * "git for-each-ref" and friends learned to apply mailmap to
+   authorname and other fields in a more flexible way than using
+   separate placeholder letters like %a[eElL] every time we want to
+   come up with small variants.
+
+ * "git repack" machinery learned to pay attention to the "--filter="
+   option.
+
+ * "git repack" learned the "--max-cruft-size" option to prevent cruft
+   packs from growing without bounds.
+
+ * "git merge-tree" learned to take strategy backend specific options
+   via the "-X" option, like "git merge" does.
+
+ * "git log" and friends learned the "--dd" option that is a
+   short-hand for "--diff-merges=first-parent -p".
+
+ * The attribute subsystem learned to honor the "attr.tree"
+   configuration variable that specifies which tree to read the
+   .gitattributes files from.
+
+ * "git merge-file" learns a mode to read three variants of the
+   contents to be merged from blob objects.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git check-attr" has been taught to work better with sparse-index.
+
+ * It may be tempting to leave the help text NULL for a command line
+   option that is either hidden or too obvious, but "git subcmd -h"
+   and "git subcmd --help-all" would have segfaulted if done so.  Now
+   the help text is truly optional.
+
+ * Tests that are known to pass with LSan are now marked as such.
+
+ * Flaky "git p4" tests, as well as "git svn" tests, are now skipped
+   in the (rather expensive) sanitizer CI job.
+
+ * Tests with LSan from time to time seem to emit harmless messages
+   that make our tests unnecessarily flaky; we work around it by
+   filtering the uninteresting output.
+
+ * Unused parameters to functions are marked as such, and/or removed,
+   in order to bring us closer to "-Wunused-parameter" clean.
+
+ * The code to keep track of existing packs in the repository while
+   repacking has been refactored.
+
+ * The "streaming" interface used for bulk-checkin codepath has been
+   narrowed to take only blob objects for now, with no real loss of
+   functionality.
+
+ * GitHub CI workflow has learned to trigger Coverity check.
+
+ * Test coverage for trailers has been improved.
+
+ * The code to iterate over loose references has been optimized to
+   reduce the number of lstat() system calls.
+
+ * The codepaths that read "chunk" formatted files have been corrected
+   to pay attention to the chunk size and notice broken files.
+
+ * Replace macos-12 used at GitHub CI with macos-13.
+   (merge 682a868f67 js/ci-use-macos-13 later to maint).
+
+
+Fixes since v2.42
+-----------------
+
+ * Overly long label names used in the sequencer machinery are now
+   chopped to fit under filesystem limitation.
+
+ * Scalar updates.
+
+ * Tweak GitHub Actions CI so that pushing the same commit to multiple
+   branch tips at the same time will not waste building and testing
+   the same thing twice.
+
+ * The commit-graph verification code that detects a mixture of zero and
+   non-zero generation numbers has been updated.
+
+ * "git diff -w --exit-code" with various options did not work
+   correctly, which has been corrected.
+
+ * The "transfer.unpackLimit" configuration variable ought to be used
+   as a fallback, but overrode the more specific "fetch.unpackLimit"
+   and "receive.unpackLimit" configuration variables by mistake, which
+   has been corrected.
+
+ * The use of API between two calls to require_clean_work_tree() from
+   the sequencer code has been cleaned up for consistency.
+
+ * "git diff --no-such-option" and other corner cases around the exit
+   status of the "diff" command have been corrected.
+
+ * "git for-each-ref --sort='contents:size'" sorted the refs according
+   to size numerically, giving a ref that points at a blob twelve-byte
+   (12) long before showing a blob hundred-byte (100) long, which has
+   been corrected.
+
+ * We now limit the depth of the tree objects and maximum length of
+   pathnames recorded in tree objects.
+   (merge 4d5693ba05 jk/tree-name-and-depth-limit later to maint).
+
+ * Various fixes to the behavior of "rebase -i", when the command got
+   interrupted by conflicting changes, have been made.
+
+ * References from a description of the `--patch` option in various
+   manual pages have been simplified and improved.
+
+ * "git grep -e A --no-or -e B" is accepted, even though the negation
+   of the "--or" option did not mean anything, which has been tightened.
+
+ * The completion script (in contrib/) has been taught to treat the
+   "-t" option to "git checkout" and "git switch" just like the
+   "--track" option, to complete remote-tracking branches.
+
+ * "git diff --no-index -R <(one) <(two)" did not work correctly,
+   which has been corrected.
+
+ * "git maintenance" timers' implementation has been updated, based on
+   systemd timers, to work with WSL.
+
+ * "git diff --cached" codepath did not fill the necessary stat
+   information for a file when fsmonitor knows it is clean and ended
+   up behaving as if it were not clean, which has been corrected.
+
+ * How "alias.foo = : git cmd ; aliased-command-string" should be
+   spelled with necessary whitespace around punctuation marks to work
+   has been more clearly documented (but this will be moot with newer
+   versions of Git where the parsing rules have been improved).
+
+ * HTTP Header redaction code has been adjusted for a newer version of
+   cURL library that shows its traces differently from earlier
+   versions.
+
+ * An error message given by "git send-email", when given a malformed
+   address, did not show the offending address, which has been corrected.
+
+ * UBSan options were not propagated through the test framework to git
+   run via the httpd, unlike ASan options, which has been corrected.
+
+ * "checkout --merge -- path" and "update-index --unresolve path" did
+   not resurrect conflicted state that was resolved to remove path,
+   but now they do.
+   (merge 5bdedac3c7 jc/unresolve-removal later to maint).
+
+ * The display width table for unicode characters has been updated for
+   Unicode 15.1
+   (merge 872976c37e bb/unicode-width-table-15 later to maint).
+
+ * Update mailmap entry for Derrick.
+   (merge 6e5457d8c7 ds/mailmap-entry-update later to maint).
+
+ * In the ".gitmodules" files, submodules are keyed by their names,
+   and the path to the submodule whose name is $name is specified by
+   the submodule.$name.path variable.  There were a few codepaths that
+   mixed the name and path up when consulting the submodule database,
+   which have been corrected.  It took long for these bugs to be found
+   as the name of a submodule initially is the same as its path, and
+   the problem does not surface until it is moved to a different path,
+   which apparently happens very rarely.
+
+ * "git diff --merge-base X other args..." insisted that X must be a
+   commit and errored out when given an annotated tag that peels to a
+   commit, but we only need it to be a committish.  This has been
+   corrected.
+   (merge 4adceb5a29 ar/diff-index-merge-base-fix later to maint).
+
+ * "git merge-tree" used to segfault when the "--attr-source"
+   option is used, which has been corrected.
+   (merge e95bafc52f jc/merge-ort-attr-index-fix later to maint).
+
+ * Unlike "git log --pretty=%D", "git log --pretty="%(decorate)" did
+   not auto-initialize the decoration subsystem, which has been
+   corrected.
+
+ * Feeding "git stash store" with a random commit that was not created
+   by "git stash create" now errors out.
+   (merge d9b6634589 jc/fail-stash-to-store-non-stash later to maint).
+
+ * The index file has room only for the lower 32-bit of the file size in
+   the cached stat information, which means cached stat information
+   will have 0 in its sd_size member for a file whose size is a multiple
+   of 4GiB.  This is mistaken for a racily clean path.  Avoid it by
+   storing a bogus sd_size value instead for such files.
+   (merge 5143ac07b1 bc/racy-4gb-files later to maint).
+
+ * "git p4" tried to store symlinks to LFS when told, but has been
+   fixed not to do so, because it does not make sense.
+   (merge 10c89a02b0 mm/p4-symlink-with-lfs later to maint).
+
+ * The codepath to handle recipient addresses `git send-email
+   --compose` learns from the user was completely broken, which has
+   been corrected.
+   (merge 3ec6167567 jk/send-email-fix-addresses-from-composed-messages later to maint).
+
+ * "cd sub && git grep -f patterns" tried to read "patterns" file at
+   the top level of the working tree; it has been corrected to read
+   "sub/patterns" instead.
+
+ * "git reflog expire --single-worktree" has been broken for the past
+   20 months or so, which has been corrected.
+
+ * "git send-email" did not have certain pieces of data computed yet
+   when it tried to validate the outgoing messages and its recipient
+   addresses, which has been sorted out.
+
+ * "git bugreport" learned to complain when it received a command line
+   argument that it will not use.
+
+ * The codepath to traverse the commit-graph learned to notice that a
+   commit is missing (e.g., corrupt repository lost an object), even
+   though it knows something about the commit (like its parents) from
+   what is in commit-graph.
+   (merge 7a5d604443 ps/do-not-trust-commit-graph-blindly-for-existence later to maint).
+
+ * "git rev-list --missing" did not work for missing commit objects,
+   which has been corrected.
+
+ * "git rev-list --unpacked --objects" failed to exclude packed
+   non-commit objects, which has been corrected.
+   (merge 7b3c8e9f38 tb/rev-list-unpacked-fix later to maint).
+
+ * "To dereference" and "to peel" were sometimes used in in-code
+   comments and documentation but without description in the glossary.
+   (merge 893dce2ffb vd/glossary-dereference-peel later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge c2c349a15c xz/commit-title-soft-limit-doc later to maint).
+   (merge 1bd809938a tb/format-pack-doc-update later to maint).
+   (merge 8f81532599 an/clang-format-typofix later to maint).
+   (merge 3ca86adc2d la/strvec-header-fix later to maint).
+   (merge 6789275d37 jc/test-i18ngrep later to maint).
+   (merge 9972cd6004 ps/leakfixes later to maint).
+   (merge 46edab516b tz/send-email-helpfix later to maint).
diff --git a/Documentation/RelNotes/2.43.1.txt b/Documentation/RelNotes/2.43.1.txt
new file mode 100644
index 0000000..20e96f2
--- /dev/null
+++ b/Documentation/RelNotes/2.43.1.txt
@@ -0,0 +1,82 @@
+Git 2.43.1 Release Notes
+========================
+
+There is nothing exciting to see here.  Relative to Git 2.43, this
+release contains the fixes that have already been merged to the
+'master' branch of the development towards the next major release.
+
+Fixes since Git 2.43.0
+----------------------
+
+ * The way CI testing used "prove" could lead to running the test
+   suite twice needlessly, which has been corrected.
+
+ * Newer versions of Getopt::Long started giving warnings against our
+   (ab)use of it in "git send-email".  Bump the minimum version
+   requirement for Perl to 5.8.1 (from September 2002) to allow
+   simplifying our implementation.
+
+ * Earlier we stopped relying on commit-graph that (still) records
+   information about commits that are lost from the object store,
+   which has negative performance implications.  The default has been
+   flipped to disable this pessimization.
+
+ * Stale URLs have been updated to their current counterparts (or
+   archive.org) and HTTP links are replaced with working HTTPS links.
+
+ * trace2 streams used to record the URLs that potentially embed
+   authentication material, which has been corrected.
+
+ * The sample pre-commit hook that tries to catch introduction of new
+   paths that use potentially non-portable characters did not notice
+   an existing path getting renamed to such a problematic path, when
+   rename detection was enabled.
+
+ * The command line parser for the "log" family of commands was too
+   loose when parsing certain numbers, e.g., silently ignoring the
+   extra 'q' in "git log -n 1q" without complaining, which has been
+   tightened up.
+
+ * "git $cmd --end-of-options --rev -- --path" for some $cmd failed
+   to interpret "--rev" as a rev, and "--path" as a path.  This was
+   fixed for many programs like "reset" and "checkout".
+
+ * "git bisect reset" has been taught to clean up state files and refs
+   even when BISECT_START file is gone.
+
+ * Some codepaths did not correctly parse configuration variables
+   specified with valueless "true", which has been corrected.
+
+ * Code clean-up for sanity checking of command line options for "git
+   show-ref".
+
+ * The code to parse the From e-mail header has been updated to avoid
+   recursion.
+
+ * "git fetch --atomic" issued an unnecessary empty error message,
+   which has been corrected.
+
+ * Command line completion script (in contrib/) learned to work better
+   with the reftable backend.
+
+ * "git status" is taught to show both the branch being bisected and
+   being rebased when both are in effect at the same time.
+   cf. <xmqqil76kyov.fsf@gitster.g>
+
+ * "git archive --list extra garbage" silently ignored excess command
+   line parameters, which has been corrected.
+
+ * "git sparse-checkout set" added default patterns even when the
+   patterns are being fed from the standard input, which has been
+   corrected.
+
+ * Unlike other environment variables that took the usual
+   true/false/yes/no as well as 0/1, GIT_FLUSH only understood 0/1,
+   which has been corrected.
+
+ * Clearing in-core repository (happens during e.g., "git fetch
+   --recurse-submodules" with commit graph enabled) made in-core
+   commit object in an inconsistent state by discarding the necessary
+   data from commit-graph too early, which has been corrected.
+
+Also contains various documentation updates, code clean-ups and minor fixups.
diff --git a/Documentation/RelNotes/2.43.2.txt b/Documentation/RelNotes/2.43.2.txt
new file mode 100644
index 0000000..5895e23
--- /dev/null
+++ b/Documentation/RelNotes/2.43.2.txt
@@ -0,0 +1,37 @@
+Git 2.43.2 Release Notes
+========================
+
+Relative to Git 2.43.1, this release has two important fixes to allow
+"git imap-send" to be built with NO_CURL defined, and to restore the
+forced flushing behaviour when GIT_FLUSH=1 is set.  It also contains
+other, unexciting, fixes that have already been merged to the 'master'
+branch of the development towards the next major release.
+
+Fixes since Git 2.43.1
+----------------------
+
+ * Update to a new feature recently added, "git show-ref --exists".
+
+ * Rename detection logic ignored the final line of a file if it is an
+   incomplete line.
+
+ * "git diff --no-rename A B" did not disable rename detection but did
+   not trigger an error from the command line parser.
+
+ * "git diff --no-index file1 file2" segfaulted while invoking the
+   external diff driver, which has been corrected.
+
+ * Rewrite //-comments to /* comments */ in files whose comments
+   prevalently use the latter.
+
+ * A failed "git tag -s" did not necessarily result in an error
+   depending on the crypto backend, which has been corrected.
+
+ * "git stash" sometimes was silent even when it failed due to
+   unwritable index file, which has been corrected.
+
+ * Recent conversion to allow more than 0/1 in GIT_FLUSH broke the
+   mechanism by flipping what yes/no means by mistake, which has been
+   corrected.
+
+Also contains documentation updates, code clean-ups and minor fixups.
diff --git a/Documentation/RelNotes/2.43.3.txt b/Documentation/RelNotes/2.43.3.txt
new file mode 100644
index 0000000..924f205
--- /dev/null
+++ b/Documentation/RelNotes/2.43.3.txt
@@ -0,0 +1,12 @@
+Git 2.43.3 Release Notes
+========================
+
+Relative to Git 2.43.2, this release fixes one regression that
+manifests while running "git commit -v --trailer".
+
+Fixes since Git 2.43.2
+----------------------
+
+ * "git commit -v --trailer=..." was broken with recent update and
+   placed the trailer _after_ the divider line, which has been
+   corrected.
diff --git a/Documentation/RelNotes/2.44.0.txt b/Documentation/RelNotes/2.44.0.txt
new file mode 100644
index 0000000..14f9ce8
--- /dev/null
+++ b/Documentation/RelNotes/2.44.0.txt
@@ -0,0 +1,334 @@
+Git v2.44 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ * "git checkout -B <branch>" used to allow switching to a branch that
+   is in use on another worktree, but this was by mistake.  The users
+   need to use "--ignore-other-worktrees" option.
+
+
+UI, Workflows & Features
+
+ * "git add" and "git stash" learned to support the ":(attr:...)"
+   magic pathspec.
+
+ * "git rebase --autosquash" is now enabled for non-interactive rebase,
+   but it is still incompatible with the apply backend.
+
+ * Introduce "git replay", a tool meant on the server side without
+   working tree to recreate a history.
+
+ * "git merge-file" learned to take the "--diff-algorithm" option to
+   use algorithm different from the default "myers" diff.
+
+ * Command line completion (in contrib/) learned to complete path
+   arguments to the "add/set" subcommands of "git sparse-checkout"
+   better.
+
+ * "git checkout -B <branch> [<start-point>]" allowed a branch that is
+   in use in another worktree to be updated and checked out, which
+   might be a bit unexpected.  The rule has been tightened, which is a
+   breaking change.  "--ignore-other-worktrees" option is required to
+   unbreak you, if you are used to the current behaviour that "-B"
+   overrides the safety.
+
+ * The builtin_objectmode attribute is populated for each path
+   without adding anything in .gitattributes files, which would be
+   useful in magic pathspec, e.g., ":(attr:builtin_objectmode=100755)"
+   to limit to executables.
+
+ * "git fetch" learned to pay attention to "fetch.all" configuration
+   variable, which pretends as if "--all" was passed from the command
+   line when no remote parameter was given.
+
+ * In addition to (rather cryptic) Security Identifiers, show username
+   and domain in the error message when we barf on mismatch between
+   the Git directory and the current user on Windows.
+
+ * The error message given when "git branch -d branch" fails due to
+   commits unique to the branch has been split into an error and a new
+   conditional advice message.
+
+ * When given an existing but unreadable file as a configuration file,
+   gitweb behaved as if the file did not exist at all, but now it
+   errors out.  This is a change that may break backward compatibility.
+
+ * When $HOME/.gitconfig is missing but XDG config file is available, we
+   should write into the latter, not former.  "git gc" and "git
+   maintenance" wrote into a wrong "global config" file, which have
+   been corrected.
+
+ * Define "special ref" as a very narrow set that consists of
+   FETCH_HEAD and MERGE_HEAD, and clarify everything else that used to
+   be classified as such are actually just pseudorefs.
+
+ * All conditional "advice" messages show how to turn them off, which
+   becomes repetitive.  Setting advice.* configuration explicitly on
+   now omits the instruction part.
+
+ * The "disable repository discovery of a bare repository" check,
+   triggered by setting safe.bareRepository configuration variable to
+   'explicit', has been loosened to exclude the ".git/" directory inside
+   a non-bare repository from the check.  So you can do "cd .git &&
+   git cmd" to run a Git command that works on a bare repository without
+   explicitly specifying $GIT_DIR now.
+
+ * The completion script (in contrib/) learned more options that can
+   be used with "git log".
+
+ * The labels on conflict markers for the common ancestor, our version,
+   and the other version are available to custom 3-way merge driver
+   via %S, %X, and %Y placeholders.
+
+ * The write codepath for the reftable data learned to honor
+   core.fsync configuration.
+
+ * The "--fsck-objects" option of "git index-pack" now can take the
+   optional parameter to tweak severity of different fsck errors.
+
+ * The wincred credential backend has been taught to support oauth
+   refresh token the same way as credential-cache and
+   credential-libsecret backends.
+
+ * Command line completion support (in contrib/) has been
+   updated for "git bisect".
+
+ * "git branch" and friends learned to use the formatted text as
+   sorting key, not the underlying timestamp value, when the --sort
+   option is used with author or committer timestamp with a format
+   specifier (e.g., "--sort=creatordate:format:%H:%M:%S").
+
+ * The command line completion script (in contrib/) learned to
+   complete configuration variable names better.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Process to add some form of low-level unit tests has started.
+
+ * Add support for GitLab CI.
+
+ * "git for-each-ref --no-sort" still sorted the refs alphabetically
+   which paid non-trivial cost.  It has been redefined to show output
+   in an unspecified order, to allow certain optimizations to take
+   advantage of.
+
+ * Simplify API implementation to delete references by eliminating
+   duplication.
+
+ * Subject approxidate() and show_date() machinery to OSS-Fuzz.
+
+ * A new helper to let us pretend that we called lstat() when we know
+   our cache_entry is up-to-date via fsmonitor.
+
+ * The optimization based on fsmonitor in the "diff --cached"
+   codepath is resurrected with the "fake-lstat" introduced earlier.
+
+ * Test balloon to use C99 "bool" type from <stdbool.h> has been
+   added.
+
+ * "git clone" has been prepared to allow cloning a repository with
+   non-default hash function into a repository that uses the reftable
+   backend.
+
+ * Streaming spans of packfile data used to be done only from a
+   single, primary, pack in a repository with multiple packfiles.  It
+   has been extended to allow reuse from other packfiles, too.
+
+ * Comment updates to help developers not to attempt to modify
+   messages from plumbing commands that must stay constant.
+
+   It might make sense to reassess the plumbing needs every few years,
+   but that should be done as a separate effort.
+
+ * Move test-ctype helper to the unit-test framework.
+
+ * Instead of manually creating refs/ hierarchy on disk upon a
+   creation of a secondary worktree, which is only usable via the
+   files backend, use the refs API to populate it.
+
+ * CI for GitLab learned to drive macOS jobs.
+
+ * A few tests to "git commit -o <pathspec>" and "git commit -i
+   <pathspec>" has been added.
+
+ * Tests on ref API are moved around to prepare for reftable.
+
+ * The Makefile often had to say "-L$(path) -R$(path)" that repeats
+   the path to the same library directory for link time and runtime.
+   A Makefile template is used to reduce such repetition.
+
+ * The priority queue test has been migrated to the unit testing
+   framework.
+
+ * Setting `feature.experimental` opts the user into multi-pack reuse
+   experiment
+
+ * Squelch node.js 16 deprecation warnings from GitHub Actions CI
+   by updating actions/github-script and actions/checkout that use
+   node.js 20.
+
+ * The mechanism to report the filename in the source code, used by
+   the unit-test machinery, assumed that the compiler expanded __FILE__
+   to the path to the source given to the $(CC), but some compilers
+   give full path, breaking the output.  This has been corrected.
+
+
+Fixes since v2.43
+-----------------
+
+ * The way CI testing used "prove" could lead to running the test
+   suite twice needlessly, which has been corrected.
+
+ * Update ref-related tests.
+
+ * "git format-patch --encode-email-headers" ignored the option when
+   preparing the cover letter, which has been corrected.
+
+ * Newer versions of Getopt::Long started giving warnings against our
+   (ab)use of it in "git send-email".  Bump the minimum version
+   requirement for Perl to 5.8.1 (from September 2002) to allow
+   simplifying our implementation.
+
+ * Earlier we stopped relying on commit-graph that (still) records
+   information about commits that are lost from the object store,
+   which has negative performance implications.  The default has been
+   flipped to disable this pessimization.
+
+ * Stale URLs have been updated to their current counterparts (or
+   archive.org) and HTTP links are replaced with working HTTPS links.
+
+ * trace2 streams used to record the URLs that potentially embed
+   authentication material, which has been corrected.
+
+ * The sample pre-commit hook that tries to catch introduction of new
+   paths that use potentially non-portable characters did not notice
+   an existing path getting renamed to such a problematic path, when
+   rename detection was enabled.
+
+ * The command line parser for the "log" family of commands was too
+   loose when parsing certain numbers, e.g., silently ignoring the
+   extra 'q' in "git log -n 1q" without complaining, which has been
+   tightened up.
+
+ * "git $cmd --end-of-options --rev -- --path" for some $cmd failed
+   to interpret "--rev" as a rev, and "--path" as a path.  This was
+   fixed for many programs like "reset" and "checkout".
+
+ * "git bisect reset" has been taught to clean up state files and refs
+   even when BISECT_START file is gone.
+
+ * Some codepaths did not correctly parse configuration variables
+   specified with valueless "true", which has been corrected.
+
+ * Code clean-up for sanity checking of command line options for "git
+   show-ref".
+
+ * The code to parse the From e-mail header has been updated to avoid
+   recursion.
+
+ * "git fetch --atomic" issued an unnecessary empty error message,
+   which has been corrected.
+
+ * Command line completion script (in contrib/) learned to work better
+   with the reftable backend.
+
+ * "git status" is taught to show both the branch being bisected and
+   being rebased when both are in effect at the same time.
+
+ * "git archive --list extra garbage" silently ignored excess command
+   line parameters, which has been corrected.
+
+ * "git sparse-checkout set" added default patterns even when the
+   patterns are being fed from the standard input, which has been
+   corrected.
+
+ * "git sparse-checkout (add|set) --[no-]cone --end-of-options" did
+   not handle "--end-of-options" correctly after a recent update.
+
+ * Unlike other environment variables that took the usual
+   true/false/yes/no as well as 0/1, GIT_FLUSH only understood 0/1,
+   which has been corrected.
+
+ * Clearing in-core repository (happens during e.g., "git fetch
+   --recurse-submodules" with commit graph enabled) made in-core
+   commit object in an inconsistent state by discarding the necessary
+   data from commit-graph too early, which has been corrected.
+
+ * Update to a new feature recently added, "git show-ref --exists".
+
+ * oss-fuzz tests are built and run in CI.
+   (merge c4a9cf1df3 js/oss-fuzz-build-in-ci later to maint).
+
+ * Rename detection logic ignored the final line of a file if it is an
+   incomplete line.
+
+ * GitHub CI update.
+   (merge 0188b2c8e0 pb/ci-github-skip-logs-for-broken-tests later to maint).
+
+ * "git diff --no-rename A B" did not disable rename detection but did
+   not trigger an error from the command line parser.
+
+ * "git archive --remote=<remote>" learned to talk over the smart
+   http (aka stateless) transport.
+   (merge 176cd68634 jx/remote-archive-over-smart-http later to maint).
+
+ * Fetching via protocol v0 over Smart HTTP transport sometimes failed
+   to correctly auto-follow tags.
+   (merge fba732c462 jk/fetch-auto-tag-following-fix later to maint).
+
+ * The documentation for the --exclude-per-directory option marked it
+   as deprecated, which confused readers into thinking there may be a
+   plan to remove it in the future, which was not our intention.
+   (merge 0009542cab jc/ls-files-doc-update later to maint).
+
+ * "git diff --no-index file1 file2" segfaulted while invoking the
+   external diff driver, which has been corrected.
+
+ * Rewrite //-comments to /* comments */ in files whose comments
+   prevalently use the latter.
+
+ * Cirrus CI jobs started breaking because we specified version of
+   FreeBSD that is no longer available, which has been corrected.
+   (merge 81fffb66d3 cb/use-freebsd-13-2-at-cirrus-ci later to maint).
+
+ * A caller called index_file_exists() that takes a string expressed
+   as <ptr, length> with a wrong length, which has been corrected.
+   (merge 156e28b36d jh/sparse-index-expand-to-path-fix later to maint).
+
+ * A failed "git tag -s" did not necessarily result in an error
+   depending on the crypto backend, which has been corrected.
+
+ * "git stash" sometimes was silent even when it failed due to
+   unwritable index file, which has been corrected.
+
+ * "git show-ref --verify" did not show things like "CHERRY_PICK_HEAD",
+   which has been corrected.
+
+ * Recent conversion to allow more than 0/1 in GIT_FLUSH broke the
+   mechanism by flipping what yes/no means by mistake, which has been
+   corrected.
+
+ * The sequencer machinery does not use the ref API and instead
+   records names of certain objects it needs for its correct operation
+   in temporary files, which makes these objects susceptible to loss
+   by garbage collection.  These temporary files have been added as
+   starting points for reachability analysis to fix this.
+   (merge bc7f5db896 pw/gc-during-rebase later to maint).
+
+ * "git cherry-pick" invoked during "git rebase -i" session lost
+   the authorship information, which has been corrected.
+   (merge e4301f73ff vn/rebase-with-cherry-pick-authorship later to maint).
+
+ * The code paths that call repo_read_object_file() have been
+   tightened to react to errors.
+   (merge 568459bf5e js/check-null-from-read-object-file later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 5aea3955bc rj/clarify-branch-doc-m later to maint).
+   (merge 9cce3be2df bk/bisect-doc-fix later to maint).
+   (merge 8430b438f6 vd/fsck-submodule-url-test later to maint).
+   (merge 3cb4384683 jc/t0091-with-unknown-git later to maint).
+   (merge 020456cb74 rs/receive-pack-remove-find-header later to maint).
+   (merge bc47139f4f la/trailer-cleanups later to maint).
diff --git a/Documentation/RelNotes/2.45.0.txt b/Documentation/RelNotes/2.45.0.txt
new file mode 100644
index 0000000..1be72e2
--- /dev/null
+++ b/Documentation/RelNotes/2.45.0.txt
@@ -0,0 +1,375 @@
+Git v2.45 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+UI, Workflows & Features
+
+ * Integrate the reftable code into the refs framework as a backend.
+   With "git init --ref-format=reftable", hopefully it would be a lot
+   more efficient to manage a repository with many references.
+
+ * "git checkout -p" and friends learned that that "@" is a synonym
+   for "HEAD".
+
+ * Variants of vimdiff learned to honor mergetool.<variant>.layout
+   settings.
+
+ * "git reflog" learned a "list" subcommand that enumerates known reflogs.
+
+ * When a merge conflicted at a submodule, merge-ort backend used to
+   unconditionally give a lengthy message to suggest how to resolve
+   it.  Now the message can be squelched as an advice message.
+
+ * "git for-each-ref" learned "--include-root-refs" option to show
+   even the stuff outside the 'refs/' hierarchy.
+
+ * "git rev-list --missing=print" has learned to optionally take
+   "--allow-missing-tips", which allows the objects at the starting
+   points to be missing.
+
+ * "git merge-tree" has learned that the three trees involved in the
+   3-way merge only need to be trees, not necessarily commits.
+
+ * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
+   other kinds of *_HEAD pseudorefs.
+
+ * Platform specific tweaks for OS/390 has been added to
+   config.mak.uname.
+
+ * Users with safe.bareRepository=explicit can still work from within
+   $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/)
+   of the primary worktree without explicitly specifying the $GIT_DIR
+   environment variable or the --git-dir=<path> option.
+
+ * The output format for dates "iso-strict" has been tweaked to show
+   a time in the Zulu timezone with "Z" suffix, instead of "+00:00".
+
+ * "git diff" and friends learned two extra configuration variables,
+   diff.srcPrefix and diff.dstPrefix.
+
+ * The status.showUntrackedFiles configuration variable had a name
+   that tempts users to set a Boolean value expressed in our usual
+   "false", "off", and "0", but it only took "no".  This has been
+   corrected so "true" and its synonyms are taken as "normal", while
+   "false" and its synonyms are taken as "no".
+
+ * Remove an ancient and not well maintained Hg-to-git migration
+   script from contrib/.
+
+ * Hints that suggest what to do after resolving conflicts can now be
+   squelched by disabling advice.mergeConflict.
+
+ * Allow git-cherry-pick(1) to automatically drop redundant commits via
+   a new `--empty` option, similar to the `--empty` options for
+   git-rebase(1) and git-am(1). Includes a soft deprecation of
+   `--keep-redundant-commits` as well as some related docs changes and
+   sequencer code cleanup.
+
+ * "git config" learned "--comment=<message>" option to leave a
+   comment immediately after the "variable = value" on the same line
+   in the configuration file.
+
+ * core.commentChar used to be limited to a single byte, but has been
+   updated to allow an arbitrary multi-byte sequence.
+
+ * "git add -p" and other "interactive hunk selection" UI has learned to
+   skip showing the hunk immediately after it has already been shown, and
+   an additional action to explicitly ask to reshow the current hunk.
+
+ * "git pack-refs" learned the "--auto" option, which is a useful
+   addition to be triggered from "git gc --auto".
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The code to iterate over refs with the reftable backend has seen
+   some optimization.
+
+ * More tests that are marked as "ref-files only" have been updated to
+   improve test coverage of reftable backend.
+
+ * Some parts of command line completion script (in contrib/) have
+   been micro-optimized.
+
+ * The way placeholders are to be marked-up in documentation have been
+   specified; use "_<placeholder>_" to typeset the word inside a pair
+   of <angle-brakets> emphasized.
+
+ * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
+   fetching of objects from the promisor remote, which may be handy
+   for debugging.
+
+ * The implementation in "git clean" that makes "-n" and "-i" ignore
+   clean.requireForce has been simplified, together with the
+   documentation.
+
+ * The code to iterate over refs with the reftable backend has seen
+   some optimization.
+
+ * Uses of xwrite() helper have been audited and updated for better
+   error checking and simpler code.
+
+ * Some trace2 events that lacked def_param have learned to show it,
+   enriching the output.
+
+ * The parse-options code that deals with abbreviated long option
+   names have been cleaned up.
+
+ * The code in reftable backend that creates new table files works
+   better with the tempfile framework to avoid leaving cruft after a
+   failure.
+
+ * The reftable code has its own custom binary search function whose
+   comparison callback has an unusual interface, which caused the
+   binary search to degenerate into a linear search, which has been
+   corrected.
+
+ * The code to iterate over reflogs in the reftable has been optimized
+   to reduce memory allocation and deallocation.
+
+ * Work to support a repository that work with both SHA-1 and SHA-256
+   hash algorithms has started.
+
+ * A new fuzz target that exercises config parsing code has been
+   added.
+
+ * Fix the way recently added tests interpolate variables defined
+   outside them, and document the best practice to help future
+   developers.
+
+ * Introduce an experimental protocol for contributors to propose the
+   topic description to be used in the "What's cooking" report, the
+   merge commit message for the topic, and in the release notes and
+   document it in the SubmittingPatches document.
+
+ * The t/README file now gives a hint on running individual tests in
+   the "t/" directory with "make t<num>-*.sh t<num>-*.sh".
+   (merge 8d383806fc pb/test-scripts-are-build-targets later to maint).
+
+ * The "hint:" messages given by the advice mechanism, when given a
+   message with a blank line, left a line with trailing whitespace,
+   which has been cleansed.
+
+ * Documentation rules has been explicitly described how to mark-up
+   literal parts and a few manual pages have been updated as examples.
+
+ * The .editorconfig file has been taught that a Makefile uses HT
+   indentation.
+
+ * t-prio-queue test has been cleaned up by using C99 compound
+   literals; this is meant to also serve as a weather-balloon to smoke
+   out folks with compilers who have trouble compiling code that uses
+   the feature.
+
+
+Fixes since v2.44
+-----------------
+
+ * "git apply" on a filesystem without filemode support have learned
+   to take a hint from what is in the index for the path, even when
+   not working with the "--index" or "--cached" option, when checking
+   the executable bit match what is required by the preimage in the
+   patch.
+   (merge 45b625142d cp/apply-core-filemode later to maint).
+
+ * "git column" has been taught to reject negative padding value, as
+   it would lead to nonsense behaviour including division by zero.
+   (merge 76fb807faa kh/column-reject-negative-padding later to maint).
+
+ * "git am --help" now tells readers what actions are available in
+   "git am --whitespace=<action>", in addition to saying that the
+   option is passed through to the underlying "git apply".
+   (merge a171dac734 jc/am-whitespace-doc later to maint).
+
+ * "git tag --column" failed to check the exit status of its "git
+   column" invocation, which has been corrected.
+   (merge 92e66478fc rj/tag-column-fix later to maint).
+
+ * Credential helper based on libsecret (in contrib/) has been updated
+   to handle an empty password correctly.
+   (merge 8f1f2023b7 mh/libsecret-empty-password-fix later to maint).
+
+ * "git difftool --dir-diff" learned to honor the "--trust-exit-code"
+   option; it used to always exit with 0 and signalled success.
+   (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint).
+
+ * The code incorrectly attempted to use textconv cache when asked,
+   even when we are not running in a repository, which has been
+   corrected.
+   (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint).
+
+ * Remove an empty file that shouldn't have been added in the first
+   place.
+   (merge 4f66942215 js/remove-cruft-files later to maint).
+
+ * The logic to access reflog entries by date and number had ugly
+   corner cases at the boundaries, which have been cleaned up.
+   (merge 5edd126720 jk/reflog-special-cases-fix later to maint).
+
+ * An error message from "git upload-pack", which responds to "git
+   fetch" requests, had a trialing NUL in it, which has been
+   corrected.
+   (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).
+
+ * Clarify wording in the CodingGuidelines that requires <git-compat-util.h>
+   to be the first header file.
+   (merge 4e89f0e07c jc/doc-compat-util later to maint).
+
+ * "git commit -v --cleanup=scissors" used to add the scissors line
+   twice in the log message buffer, which has been corrected.
+   (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint).
+
+ * A custom remote helper no longer cannot access the newly created
+   repository during "git clone", which is a regression in Git 2.44.
+   This has been corrected.
+   (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint).
+
+ * Various parts of upload-pack has been updated to bound the resource
+   consumption relative to the size of the repository to protect from
+   abusive clients.
+   (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint).
+
+ * The upload-pack program, when talking over v2, accepted the
+   packfile-uris protocol extension from the client, even if it did
+   not advertise the capability, which has been corrected.
+   (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint).
+
+ * Make sure failure return from merge_bases_many() is properly caught.
+   (merge 25fd20eb44 js/merge-base-with-missing-commit later to maint).
+
+ * FSMonitor client code was confused when FSEvents were given in a
+   different case on a case-insensitive filesystem, which has been
+   corrected.
+   (merge 29c139ce78 jh/fsmonitor-icase-corner-case-fix later to maint).
+
+ * The "core.commentChar" configuration variable only allows an ASCII
+   character, which was not clearly documented, which has been
+   corrected.
+   (merge fb7c556f58 kh/doc-commentchar-is-a-byte later to maint).
+
+ * With release 2.44 we got rid of all uses of test_i18ngrep and there
+   is no in-flight topic that adds a new use of it.  Make a call to
+   test_i18ngrep a hard failure, so that we can remove it at the end
+   of this release cycle.
+   (merge 381a83dfa3 jc/test-i18ngrep later to maint).
+
+ * The command line completion script (in contrib/) learned to
+   complete "git reflog" better.
+   (merge 1284f9cc11 rj/complete-reflog later to maint).
+
+ * The logic to complete the command line arguments to "git worktree"
+   subcommand (in contrib/) has been updated to correctly honor things
+   like "git -C dir" etc.
+   (merge 3574816d98 rj/complete-worktree-paths-fix later to maint).
+
+ * When git refuses to create a branch because the proposed branch
+   name is not a valid refname, an advice message is given to refer
+   the user to exact naming rules.
+   (merge 8fbd903e58 kh/branch-ref-syntax-advice later to maint).
+
+ * Code simplification by getting rid of code that sets an environment
+   variable that is no longer used.
+   (merge 72a8d3f027 pw/rebase-i-ignore-cherry-pick-help-environment later to maint).
+
+ * The code to find the effective end of log message can fall into an
+   endless loop, which has been corrected.
+   (merge 2541cba2d6 fs/find-end-of-log-message-fix later to maint).
+
+ * Mark-ups used in the documentation has been improved for
+   consistency.
+   (merge 45d5ed3e50 ja/doc-markup-fixes later to maint).
+
+ * The status.showUntrackedFiles configuration variable was
+   incorrectly documented to accept "false", which has been corrected.
+
+ * Leaks from "git restore" have been plugged.
+   (merge 2f64da0790 rj/restore-plug-leaks later to maint).
+
+ * "git bugreport --no-suffix" was not supported and instead
+   segfaulted, which has been corrected.
+   (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).
+
+ * The documentation for "%(trailers[:options])" placeholder in the
+   "--pretty" option of commands in the "git log" family has been
+   updated.
+   (merge bff85a338c bl/doc-key-val-sep-fix later to maint).
+
+ * "git checkout --conflict=bad" reported a bad conflictStyle as if it
+   were given to a configuration variable; it has been corrected to
+   report that the command line option is bad.
+   (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).
+
+ * Code clean-up in the "git log" machinery that implements custom log
+   message formatting.
+   (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).
+
+ * "git config" corrupted literal HT characters written in the
+   configuration file as part of a value, which has been corrected.
+   (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).
+
+ * A unit test for reftable code tried to enumerate all files in a
+   directory after reftable operations and expected to see nothing but
+   the files it wanted to leave there, but was fooled by .nfs* cruft
+   files left, which has been corrected.
+   (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).
+
+ * The implementation and documentation of "object-format" option
+   exchange between the Git itself and its remote helpers did not
+   quite match, which has been corrected.
+
+ * The "--pretty=<shortHand>" option of the commands in the "git log"
+   family, defined as "[pretty] shortHand = <expansion>" should have
+   been looked up case insensitively, but was not, which has been
+   corrected.
+   (merge f999d5188b bl/pretty-shorthand-config-fix later to maint).
+
+ * "git apply" failed to extract the filename the patch applied to,
+   when the change was about an empty file created in or deleted from
+   a directory whose name ends with a SP, which has been corrected.
+   (merge 776ffd1a30 jc/apply-parse-diff-git-header-names-fix later to maint).
+
+ * Update a more recent tutorial doc.
+   (merge 95ab557b4b dg/myfirstobjectwalk-updates later to maint).
+
+ * The test script had an incomplete and ineffective attempt to avoid
+   clobbering the testing user's real crontab (and its equivalents),
+   which has been completed.
+   (merge 73cb87773b es/test-cron-safety later to maint).
+
+ * Use advice_if_enabled() API to rewrite a simple pattern to
+   call advise() after checking advice_enabled().
+   (merge 6412d01527 rj/use-adv-if-enabled later to maint).
+
+ * Another "set -u" fix for the bash prompt (in contrib/) script.
+   (merge d7805bc743 vs/complete-with-set-u-fix later to maint).
+
+ * "git checkout/switch --detach foo", after switching to the detached
+   HEAD state, gave the tracking information for the 'foo' branch,
+   which was pointless.
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge f0e578c69c rs/use-xstrncmpz later to maint).
+   (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
+   (merge 64562d784d jb/doc-interactive-singlekey-do-not-need-perl later to maint).
+   (merge c431a235e2 cp/t9146-use-test-path-helpers later to maint).
+   (merge 82d75402d5 ds/doc-send-email-capitalization later to maint).
+   (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint).
+   (merge 6835f0efe9 jw/remote-doc-typofix later to maint).
+   (merge 244001aa20 hs/rebase-not-in-progress later to maint).
+   (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint).
+   (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint).
+   (merge f39addd0d9 rs/name-rev-with-mempool later to maint).
+   (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint).
+   (merge 40b8076462 ak/rebase-autosquash later to maint).
+   (merge 3223204456 eg/add-uflags later to maint).
+   (merge 5f78d52dce es/config-doc-sort-sections later to maint).
+   (merge 781fb7b4c2 as/option-names-in-messages later to maint).
+   (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
+   (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
+   (merge ad538c61da jc/index-pack-fsck-levels later to maint).
+   (merge 67471bc704 ja/doc-formatting-fix later to maint).
+   (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
+   (merge 0d527842b7 az/grep-group-error-message-update later to maint).
+   (merge 7c43bdf07b rs/strbuf-expand-bad-format later to maint).
+   (merge 8b68b48d5c ds/typofix-core-config-doc later to maint).
+   (merge 39bb692152 rs/imap-send-use-xsnprintf later to maint).
diff --git a/Documentation/ReviewingGuidelines.txt b/Documentation/ReviewingGuidelines.txt
index 0e323d5..515d470 100644
--- a/Documentation/ReviewingGuidelines.txt
+++ b/Documentation/ReviewingGuidelines.txt
@@ -19,7 +19,7 @@
 Selecting patch(es) to review
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 If you are looking for a patch series in need of review, start by checking
-latest "What's cooking in git.git" email
+the latest "What's cooking in git.git" email
 (https://lore.kernel.org/git/xmqqilm1yp3m.fsf@gitster.g/[example]). The "What's
 cooking" emails & replies can be found using the query `s:"What's cooking"` on
 the https://lore.kernel.org/git/[`lore.kernel.org` mailing list archive];
@@ -126,7 +126,7 @@
 -----------
 nit: ::
 	Denotes a small issue that should be fixed, such as a typographical error
-	or mis-alignment of conditions in an `if()` statement.
+	or misalignment of conditions in an `if()` statement.
 
 aside: ::
 optional: ::
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 927f732..c647c7e 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -3,45 +3,101 @@
 
 == Guidelines
 
-Here are some guidelines for people who want to contribute their code to this
-software. There is also a link:MyFirstContribution.html[step-by-step tutorial]
+Here are some guidelines for contributing back to this
+project. There is also a link:MyFirstContribution.html[step-by-step tutorial]
 available which covers many of these same guidelines.
 
-[[base-branch]]
-=== Decide what to base your work on.
+[[choose-starting-point]]
+=== Choose a starting point.
 
-In general, always base your work on the oldest branch that your
-change is relevant to.
+As a preliminary step, you must first choose a starting point for your
+work. Typically this means choosing a branch, although technically
+speaking it is actually a particular commit (typically the HEAD, or tip,
+of the branch).
 
-* A bugfix should be based on `maint` in general. If the bug is not
-  present in `maint`, base it on `master`. For a bug that's not yet
-  in `master`, find the topic that introduces the regression, and
-  base your work on the tip of the topic.
+There are several important branches to be aware of. Namely, there are
+four integration branches as discussed in linkgit:gitworkflows[7]:
 
-* A new feature should be based on `master` in general. If the new
-  feature depends on other topics that are in `next`, but not in
-  `master`, fork a branch from the tip of `master`, merge these topics
-  to the branch, and work on that branch.  You can remind yourself of
-  how you prepared the base with `git log --first-parent master..`.
+* maint
+* master
+* next
+* seen
 
-* Corrections and enhancements to a topic not yet in `master` should
-  be based on the tip of that topic. If the topic has not been merged
-  to `next`, it's alright to add a note to squash minor corrections
-  into the series.
+The branches lower on the list are typically descendants of the ones
+that come before it. For example, `maint` is an "older" branch than
+`master` because `master` usually has patches (commits) on top of
+`maint`.
 
-* In the exceptional case that a new feature depends on several topics
-  not in `master`, start working on `next` or `seen` privately and
-  send out patches only for discussion. Once your new feature starts
-  to stabilize, you would have to rebase it (see the "depends on other
-  topics" above).
+There are also "topic" branches, which contain work from other
+contributors.  Topic branches are created by the Git maintainer (in
+their fork) to organize the current set of incoming contributions on
+the mailing list, and are itemized in the regular "What's cooking in
+git.git" announcements.  To find the tip of a topic branch, run `git log
+--first-parent master..seen` and look for the merge commit. The second
+parent of this commit is the tip of the topic branch.
 
-* Some parts of the system have dedicated maintainers with their own
-  repositories (see the section "Subsystems" below).  Changes to
-  these parts should be based on their trees.
+There is one guiding principle for choosing the right starting point: in
+general, always base your work on the oldest integration branch that
+your change is relevant to (see "Merge upwards" in
+linkgit:gitworkflows[7]).  What this principle means is that for the
+vast majority of cases, the starting point for new work should be the
+latest HEAD commit of `maint` or `master` based on the following cases:
 
-To find the tip of a topic branch, run `git log --first-parent
-master..seen` and look for the merge commit. The second parent of this
-commit is the tip of the topic branch.
+* If you are fixing bugs in the released version, use `maint` as the
+  starting point (which may mean you have to fix things without using
+  new API features on the cutting edge that recently appeared in
+  `master` but were not available in the released version).
+
+* Otherwise (such as if you are adding new features) use `master`.
+
+
+NOTE: In exceptional cases, a bug that was introduced in an old
+version may have to be fixed for users of releases that are much older
+than the recent releases.  `git describe --contains X` may describe
+`X` as `v2.30.0-rc2-gXXXXXX` for the commit `X` that introduced the
+bug, and the bug may be so high-impact that we may need to issue a new
+maintenance release for Git 2.30.x series, when "Git 2.41.0" is the
+current release.  In such a case, you may want to use the tip of the
+maintenance branch for the 2.30.x series, which may be available in the
+`maint-2.30` branch in https://github.com/gitster/git[the maintainer's
+"broken out" repo].
+
+This also means that `next` or `seen` are inappropriate starting points
+for your work, if you want your work to have a realistic chance of
+graduating to `master`.  They are simply not designed to be used as a
+base for new work; they are only there to make sure that topics in
+flight work well together. This is why both `next` and `seen` are
+frequently re-integrated with incoming patches on the mailing list and
+force-pushed to replace previous versions of themselves. A topic that is
+literally built on top of `next` cannot be merged to `master` without
+dragging in all the other topics in `next`, some of which may not be
+ready.
+
+For example, if you are making tree-wide changes, while somebody else is
+also making their own tree-wide changes, your work may have severe
+overlap with the other person's work.  This situation may tempt you to
+use `next` as your starting point (because it would have the other
+person's work included in it), but doing so would mean you'll not only
+depend on the other person's work, but all the other random things from
+other contributors that are already integrated into `next`.  And as soon
+as `next` is updated with a new version, all of your work will need to
+be rebased anyway in order for them to be cleanly applied by the
+maintainer.
+
+Under truly exceptional circumstances where you absolutely must depend
+on a select few topic branches that are already in `next` but not in
+`master`, you may want to create your own custom base-branch by forking
+`master` and merging the required topic branches into it. You could then
+work on top of this base-branch.  But keep in mind that this base-branch
+would only be known privately to you.  So when you are ready to send
+your patches to the list, be sure to communicate how you created it in
+your cover letter.  This critical piece of information would allow
+others to recreate your base-branch on their end in order for them to
+try out your work.
+
+Finally, note that some parts of the system have dedicated maintainers
+with their own separate source code repositories (see the section
+"Subsystems" below).
 
 [[separate-commits]]
 === Make separate commits for logically separate changes.
@@ -210,7 +266,7 @@
 	noticed that ...
 ....
 
-The "Copy commit summary" command of gitk can be used to obtain this
+The "Copy commit reference" command of gitk can be used to obtain this
 format (with the subject enclosed in a pair of double-quotes), or this
 invocation of `git show`:
 
@@ -299,9 +355,21 @@
   patch after a detailed analysis.
 . `Tested-by:` is used to indicate that the person applied the patch
   and found it to have the desired effect.
+. `Co-authored-by:` is used to indicate that people exchanged drafts
+   of a patch before submitting it.
+. `Helped-by:` is used to credit someone who suggested ideas for
+  changes without providing the precise changes in patch form.
+. `Mentored-by:` is used to credit someone with helping develop a
+  patch as part of a mentorship program (e.g., GSoC or Outreachy).
+. `Suggested-by:` is used to credit someone with suggesting the idea
+  for a patch.
 
-You can also create your own tag or use one that's in common usage
-such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
+While you can also create your own trailer if the situation warrants it, we
+encourage you to instead use one of the common trailers in this project
+highlighted above.
+
+Only capitalize the very first letter of tags, i.e. favor
+"Signed-off-by" over "Signed-Off-By" and "Acked-by:" over "Acked-By".
 
 [[git-tools]]
 === Generate your patch using Git tools out of your commits.
@@ -317,10 +385,13 @@
 or include any extra files which do not relate to what your patch
 is trying to achieve. Make sure to review
 your patch after generating it, to ensure accuracy.  Before
-sending out, please make sure it cleanly applies to the base you
-have chosen in the "Decide what to base your work on" section,
-and unless it targets the `master` branch (which is the default),
-mark your patches as such.
+sending out, please make sure it cleanly applies to the starting point you
+have chosen in the "Choose a starting point" section.
+
+NOTE: From the perspective of those reviewing your patch, the `master`
+branch is the default expected starting point.  So if you have chosen a
+different starting point, please communicate this choice in your cover
+letter.
 
 
 [[send-patches]]
@@ -334,8 +405,8 @@
 
 Learn to use format-patch and send-email if possible.  These commands
 are optimized for the workflow of sending patches, avoiding many ways
-your existing e-mail client that is optimized for "multipart/*" mime
-type e-mails to corrupt and render your patches unusable.
+your existing e-mail client (often optimized for "multipart/*" MIME
+type e-mails) might render your patches unusable.
 
 People on the Git mailing list need to be able to read and
 comment on the changes you are submitting.  It is important for
@@ -388,6 +459,18 @@
 Git-notes and inserted automatically following the three-dash
 line via `git format-patch --notes`.
 
+[[the-topic-summary]]
+*This is EXPERIMENTAL*.
+
+When sending a topic, you can propose a one-paragraph summary that
+should appear in the "What's cooking" report when it is picked up to
+explain the topic.  If you choose to do so, please write a 2-5 line
+paragraph that will fit well in our release notes (see many bulleted
+entries in the Documentation/RelNotes/* files for examples), and make
+it the first paragraph of the cover letter.  For a single-patch
+series, use the space between the three-dash line and the diffstat, as
+described earlier.
+
 [[attachment]]
 Do not attach the patch as a MIME attachment, compressed or not.
 Do not let your e-mail client send quoted-printable.  Do not let
@@ -456,8 +539,8 @@
 
 	git://git.ozlabs.org/~paulus/gitk
 
-   Those who are interested in improve gitk can volunteer to help Paul
-   in maintaining it cf. <YntxL/fTplFm8lr6@cleo>.
+   Those who are interested in improving gitk can volunteer to help Paul
+   maintain it, cf. <YntxL/fTplFm8lr6@cleo>.
 
 - `po/` comes from the localization coordinator, Jiang Xin:
 
@@ -497,7 +580,7 @@
 
 In any time between the (2)-(3) cycle, the maintainer may pick it up
 from the list and queue it to `seen`, in order to make it easier for
-people play with it without having to pick up and apply the patch to
+people to play with it without having to pick up and apply the patch to
 their trees themselves.
 
 [[patch-status]]
@@ -511,7 +594,7 @@
   master).
 
 * Read the Git mailing list, the maintainer regularly posts messages
-  entitled "What's cooking in git.git" and "What's in git.git" giving
+  entitled "What's cooking in git.git" giving
   the status of various proposed changes.
 
 == GitHub CI[[GHCI]]
@@ -531,11 +614,12 @@
 to your fork of Git on GitHub.  You can monitor the test state of all your
 branches here: `https://github.com/<Your GitHub handle>/git/actions/workflows/main.yml`
 
-If a branch did not pass all test cases then it is marked with a red
-cross. In that case you can click on the failing job and navigate to
-"ci/run-build-and-tests.sh" and/or "ci/print-test-failures.sh". You
-can also download "Artifacts" which are tarred (or zipped) archives
-with test data relevant for debugging.
+If a branch does not pass all test cases then it will be marked with a
+red +x+, instead of a green check. In that case, you can click on the
+failing job and navigate to "ci/run-build-and-tests.sh" and/or
+"ci/print-test-failures.sh". You can also download "Artifacts" which
+are zip archives containing tarred (or zipped) archives with test data
+relevant for debugging.
 
 Then fix the problem and push your fix to your GitHub fork. This will
 trigger a new CI build to ensure all tests pass.
@@ -543,7 +627,7 @@
 [[mua]]
 == MUA specific hints
 
-Some of patches I receive or pick up from the list share common
+Some of the patches I receive or pick up from the list share common
 patterns of breakage.  Please make sure your MUA is set up
 properly not to corrupt whitespaces.
 
@@ -627,7 +711,7 @@
 `git am`.  However, if the message is MIME encoded, what is
 piped into the program is the representation you see in your
 `*Article*` buffer after unwrapping MIME.  This is often not what
-you would want for two reasons.  It tends to screw up non ASCII
+you would want for two reasons.  It tends to screw up non-ASCII
 characters (most notably in people's names), and also
 whitespaces (fatal in patches).  Running "C-u g" to display the
 message in raw form before using "|" to run the pipe can work
diff --git a/Documentation/ToolsForGit.txt b/Documentation/ToolsForGit.txt
index 5060d0d..ae7690b 100644
--- a/Documentation/ToolsForGit.txt
+++ b/Documentation/ToolsForGit.txt
@@ -5,7 +5,7 @@
 [[summary]]
 == Summary
 
-This document gathers tips, scripts and configuration file to help people
+This document gathers tips, scripts, and configuration files to help people
 working on Git's codebase use their favorite tools while following Git's
 coding style.
 
@@ -32,7 +32,7 @@
 
 This is adapted from Linux's suggestion in its CodingStyle document:
 
-- To follow rules of the CodingGuideline, it's useful to put the following in
+- To follow the rules in CodingGuidelines, it's useful to put the following in
 GIT_CHECKOUT/.dir-locals.el, assuming you use cperl-mode:
 ----
 ;; note the first part is useful for C editing, too
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 3e4c139..60f76f4 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -51,25 +51,6 @@
 endif::doctype-manpage[]
 endif::backend-docbook[]
 
-ifdef::doctype-manpage[]
-ifdef::backend-docbook[]
-[header]
-template::[header-declarations]
-<refentry>
-<refmeta>
-<refentrytitle>{mantitle}</refentrytitle>
-<manvolnum>{manvolnum}</manvolnum>
-<refmiscinfo class="source">{mansource}</refmiscinfo>
-<refmiscinfo class="version">{manversion}</refmiscinfo>
-<refmiscinfo class="manual">{manmanual}</refmiscinfo>
-</refmeta>
-<refnamediv>
-  <refname>{manname}</refname>
-  <refpurpose>{manpurpose}</refpurpose>
-</refnamediv>
-endif::backend-docbook[]
-endif::doctype-manpage[]
-
 ifdef::backend-xhtml11[]
 [attributes]
 git-relative-html-prefix=
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 9a66353..552dcc6 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -64,11 +64,9 @@
 	manual page.
 
 --contents <file>::
-	When <rev> is not specified, the command annotates the
-	changes starting backwards from the working tree copy.
-	This flag makes the command pretend as if the working
-	tree copy has the contents of the named file (specify
-	`-` to make the command read from the standard input).
+	Annotate using the contents from the named file, starting from <rev>
+	if it is specified, and HEAD otherwise. You may specify '-' to make
+	the command read from the standard input for the file contents.
 
 --date <format>::
 	Specifies the format used to output dates. If --date is not
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0e93aef..70b448b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -11,7 +11,7 @@
 default configuration.
 
 The configuration variables are used by both the Git plumbing
-and the porcelains. The variables are divided into sections, wherein
+and the porcelain commands. The variables are divided into sections, wherein
 the fully qualified variable name of the variable itself is the last
 dot-separated segment and the section name is everything before the last
 dot. The variable names are case-insensitive, allow only alphanumeric
@@ -22,9 +22,10 @@
 Syntax
 ~~~~~~
 
-The syntax is fairly flexible and permissive; whitespaces are mostly
-ignored.  The '#' and ';' characters begin comments to the end of line,
-blank lines are ignored.
+The syntax is fairly flexible and permissive.  Whitespace characters,
+which in this context are the space character (SP) and the horizontal
+tabulation (HT), are mostly ignored.  The '#' and ';' characters begin
+comments to the end of line.  Blank lines are ignored.
 
 The file consists of sections and variables.  A section begins with
 the name of the section in square brackets and continues until the next
@@ -63,16 +64,17 @@
 The variable names are case-insensitive, allow only alphanumeric characters
 and `-`, and must start with an alphabetic character.
 
-A line that defines a value can be continued to the next line by
-ending it with a `\`; the backslash and the end-of-line are
-stripped.  Leading whitespaces after 'name =', the remainder of the
-line after the first comment character '#' or ';', and trailing
-whitespaces of the line are discarded unless they are enclosed in
-double quotes.  Internal whitespaces within the value are retained
-verbatim.
+Whitespace characters surrounding `name`, `=` and `value` are discarded.
+Internal whitespace characters within 'value' are retained verbatim.
+Comments starting with either `#` or `;` and extending to the end of line
+are discarded.  A line that defines a value can be continued to the next
+line by ending it with a backslash (`\`);  the backslash and the end-of-line
+characters are discarded.
 
-Inside double quotes, double quote `"` and backslash `\` characters
-must be escaped: use `\"` for `"` and `\\` for `\`.
+If `value` needs to contain leading or trailing whitespace characters,
+it must be enclosed in double quotation marks (`"`).  Inside double quotation
+marks, double quote (`"`) and backslash (`\`) characters must be escaped:
+use `\"` for `"` and `\\` for `\`.
 
 The following escape sequences (beside `\"` and `\\`) are recognized:
 `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
@@ -103,7 +105,7 @@
 Conditional includes
 ~~~~~~~~~~~~~~~~~~~~
 
-You can include a config file from another conditionally by setting a
+You can conditionally include a config file from another by setting an
 `includeIf.<condition>.path` variable to the name of the file to be
 included.
 
@@ -118,7 +120,7 @@
 	pattern, the include condition is met.
 +
 The .git location may be auto-discovered, or come from `$GIT_DIR`
-environment variable. If the repository is auto discovered via a .git
+environment variable. If the repository is auto-discovered via a .git
 file (e.g. from submodules, or a linked worktree), the .git location
 would be the final location where the .git directory is, not where the
 .git file is.
@@ -182,7 +184,7 @@
 the resolution of these conditions (thus, prohibiting them from
 declaring remote URLs).
 +
-As for the naming of this keyword, it is for forwards compatibiliy with
+As for the naming of this keyword, it is for forwards compatibility with
 a naming scheme that supports more variable-based include conditions,
 but currently Git only supports the exact keyword described above.
 
@@ -369,18 +371,18 @@
 names do not conflict with those that are used by Git itself and
 other popular tools, and describe them in your documentation.
 
-include::config/advice.txt[]
-
-include::config/core.txt[]
-
 include::config/add.txt[]
 
+include::config/advice.txt[]
+
 include::config/alias.txt[]
 
 include::config/am.txt[]
 
 include::config/apply.txt[]
 
+include::config/attr.txt[]
+
 include::config/blame.txt[]
 
 include::config/branch.txt[]
@@ -403,10 +405,12 @@
 
 include::config/commitgraph.txt[]
 
-include::config/credential.txt[]
-
 include::config/completion.txt[]
 
+include::config/core.txt[]
+
+include::config/credential.txt[]
+
 include::config/diff.txt[]
 
 include::config/difftool.txt[]
@@ -419,10 +423,10 @@
 
 include::config/fetch.txt[]
 
-include::config/format.txt[]
-
 include::config/filter.txt[]
 
+include::config/format.txt[]
+
 include::config/fsck.txt[]
 
 include::config/fsmonitor--daemon.txt[]
@@ -433,10 +437,10 @@
 
 include::config/gitweb.txt[]
 
-include::config/grep.txt[]
-
 include::config/gpg.txt[]
 
+include::config/grep.txt[]
+
 include::config/gui.txt[]
 
 include::config/guitool.txt[]
@@ -517,10 +521,10 @@
 
 include::config/ssh.txt[]
 
-include::config/status.txt[]
-
 include::config/stash.txt[]
 
+include::config/status.txt[]
+
 include::config/submodule.txt[]
 
 include::config/tag.txt[]
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index a00d010..0e35ae5 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -1,30 +1,65 @@
 advice.*::
 	These variables control various optional help messages designed to
-	aid new users. All 'advice.*' variables default to 'true', and you
-	can tell Git that you do not need help by setting these to 'false':
+	aid new users.  When left unconfigured, Git will give the message
+	alongside instructions on how to squelch it.  You can tell Git
+	that you do not need the help message by setting these to `false`:
 +
 --
+	addEmbeddedRepo::
+		Shown when the user accidentally adds one
+		git repo inside of another.
+	addEmptyPathspec::
+		Shown when the user runs `git add` without providing
+		the pathspec parameter.
+	addIgnoredFile::
+		Shown when the user attempts to add an ignored file to
+		the index.
+	amWorkDir::
+		Shown when linkgit:git-am[1] fails to apply a patch
+		file, to tell the user the location of the file.
 	ambiguousFetchRefspec::
-		Advice shown when fetch refspec for multiple remotes map to
+		Shown when a fetch refspec for multiple remotes maps to
 		the same remote-tracking branch namespace and causes branch
 		tracking set-up to fail.
+	checkoutAmbiguousRemoteBranchName::
+		Shown when the argument to
+		linkgit:git-checkout[1] and linkgit:git-switch[1]
+		ambiguously resolves to a
+		remote tracking branch on more than one remote in
+		situations where an unambiguous argument would have
+		otherwise caused a remote-tracking branch to be
+		checked out. See the `checkout.defaultRemote`
+		configuration variable for how to set a given remote
+		to be used by default in some situations where this
+		advice would be printed.
+	commitBeforeMerge::
+		Shown when linkgit:git-merge[1] refuses to
+		merge to avoid overwriting local changes.
+	detachedHead::
+		Shown when the user uses
+		linkgit:git-switch[1] or linkgit:git-checkout[1]
+		to move to the detached HEAD state, to tell the user how
+		to create a local branch after the fact.
+	diverging::
+		Shown when a fast-forward is not possible.
 	fetchShowForcedUpdates::
-		Advice shown when linkgit:git-fetch[1] takes a long time
+		Shown when linkgit:git-fetch[1] takes a long time
 		to calculate forced updates after ref updates, or to warn
 		that the check is disabled.
-	pushUpdateRejected::
-		Set this variable to 'false' if you want to disable
-		'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
-		'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
-		simultaneously.
-	pushNonFFCurrent::
-		Advice shown when linkgit:git-push[1] fails due to a
-		non-fast-forward update to the current branch.
-	pushNonFFMatching::
-		Advice shown when you ran linkgit:git-push[1] and pushed
-		'matching refs' explicitly (i.e. you used ':', or
-		specified a refspec that isn't your current branch) and
-		it resulted in a non-fast-forward error.
+	forceDeleteBranch::
+		Shown when the user tries to delete a not fully merged
+		branch without the force option set.
+	ignoredHook::
+		Shown when a hook is ignored because the hook is not
+		set as executable.
+	implicitIdentity::
+		Shown when the user's information is guessed from the
+		system username and domain name, to tell the user how to
+		set their identity configuration.
+	mergeConflict::
+		Shown when various commands stop because of conflicts.
+	nestedTag::
+		Shown when a user attempts to recursively tag a tag object.
 	pushAlreadyExists::
 		Shown when linkgit:git-push[1] rejects an update that
 		does not qualify for fast-forwarding (e.g., a tag.)
@@ -37,17 +72,45 @@
 		tries to overwrite a remote ref that points at an
 		object that is not a commit-ish, or make the remote
 		ref point at an object that is not a commit-ish.
+	pushNonFFCurrent::
+		Shown when linkgit:git-push[1] fails due to a
+		non-fast-forward update to the current branch.
+	pushNonFFMatching::
+		Shown when the user ran linkgit:git-push[1] and pushed
+		"matching refs" explicitly (i.e. used `:`, or
+		specified a refspec that isn't the current branch) and
+		it resulted in a non-fast-forward error.
+	pushRefNeedsUpdate::
+		Shown when linkgit:git-push[1] rejects a forced update of
+		a branch when its remote-tracking ref has updates that we
+		do not have locally.
 	pushUnqualifiedRefname::
 		Shown when linkgit:git-push[1] gives up trying to
 		guess based on the source and destination refs what
 		remote ref namespace the source belongs in, but where
 		we can still suggest that the user push to either
-		refs/heads/* or refs/tags/* based on the type of the
+		`refs/heads/*` or `refs/tags/*` based on the type of the
 		source object.
-	pushRefNeedsUpdate::
-		Shown when linkgit:git-push[1] rejects a forced update of
-		a branch when its remote-tracking ref has updates that we
-		do not have locally.
+	pushUpdateRejected::
+		Set this variable to `false` if you want to disable
+		`pushNonFFCurrent`, `pushNonFFMatching`, `pushAlreadyExists`,
+		`pushFetchFirst`, `pushNeedsForce`, and `pushRefNeedsUpdate`
+		simultaneously.
+	refSyntax::
+		Shown when the user provides an illegal ref name, to
+		tell the user about the ref syntax documentation.
+	resetNoRefresh::
+		Shown when linkgit:git-reset[1] takes more than 2
+		seconds to refresh the index after reset, to tell the user
+		that they can use the `--no-refresh` option.
+	resolveConflict::
+		Shown by various commands when conflicts
+		prevent the operation from being performed.
+	rmHints::
+		Shown on failure in the output of linkgit:git-rm[1], to
+		give directions on how to proceed from the current state.
+	sequencerInUse::
+		Shown when a sequencer command is already in progress.
 	skippedCherryPicks::
 		Shown when linkgit:git-rebase[1] skips a commit that has already
 		been cherry-picked onto the upstream branch.
@@ -63,77 +126,32 @@
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
 		by linkgit:git-switch[1] or
-		linkgit:git-checkout[1] when switching branch.
+		linkgit:git-checkout[1] when switching branches.
 	statusUoption::
-		Advise to consider using the `-u` option to linkgit:git-status[1]
-		when the command takes more than 2 seconds to enumerate untracked
-		files.
-	commitBeforeMerge::
-		Advice shown when linkgit:git-merge[1] refuses to
-		merge to avoid overwriting local changes.
-	resetNoRefresh::
-		Advice to consider using the `--no-refresh` option to
-		linkgit:git-reset[1] when the command takes more than 2 seconds
-		to refresh the index after reset.
-	resolveConflict::
-		Advice shown by various commands when conflicts
-		prevent the operation from being performed.
-	sequencerInUse::
-		Advice shown when a sequencer command is already in progress.
-	implicitIdentity::
-		Advice on how to set your identity configuration when
-		your information is guessed from the system username and
-		domain name.
-	detachedHead::
-		Advice shown when you used
-		linkgit:git-switch[1] or linkgit:git-checkout[1]
-		to move to the detach HEAD state, to instruct how to
-		create a local branch after the fact.
-	suggestDetachingHead::
-		Advice shown when linkgit:git-switch[1] refuses to detach HEAD
-		without the explicit `--detach` option.
-	checkoutAmbiguousRemoteBranchName::
-		Advice shown when the argument to
-		linkgit:git-checkout[1] and linkgit:git-switch[1]
-		ambiguously resolves to a
-		remote tracking branch on more than one remote in
-		situations where an unambiguous argument would have
-		otherwise caused a remote-tracking branch to be
-		checked out. See the `checkout.defaultRemote`
-		configuration variable for how to set a given remote
-		to used by default in some situations where this
-		advice would be printed.
-	amWorkDir::
-		Advice that shows the location of the patch file when
-		linkgit:git-am[1] fails to apply it.
-	rmHints::
-		In case of failure in the output of linkgit:git-rm[1],
-		show directions on how to proceed from the current state.
-	addEmbeddedRepo::
-		Advice on what to do when you've accidentally added one
-		git repo inside of another.
-	ignoredHook::
-		Advice shown if a hook is ignored because the hook is not
-		set as executable.
-	waitingForEditor::
-		Print a message to the terminal whenever Git is waiting for
-		editor input from the user.
-	nestedTag::
-		Advice shown if a user attempts to recursively tag a tag object.
+		Shown when linkgit:git-status[1] takes more than 2
+		seconds to enumerate untracked files, to tell the user that
+		they can use the `-u` option.
 	submoduleAlternateErrorStrategyDie::
-		Advice shown when a submodule.alternateErrorStrategy option
+		Shown when a submodule.alternateErrorStrategy option
 		configured to "die" causes a fatal error.
+	submoduleMergeConflict::
+		Advice shown when a non-trivial submodule merge conflict is
+		encountered.
 	submodulesNotUpdated::
-		Advice shown when a user runs a submodule command that fails
+		Shown when a user runs a submodule command that fails
 		because `git submodule update --init` was not run.
-	addIgnoredFile::
-		Advice shown if a user attempts to add an ignored file to
-		the index.
-	addEmptyPathspec::
-		Advice shown if a user runs the add command without providing
-		the pathspec parameter.
+	suggestDetachingHead::
+		Shown when linkgit:git-switch[1] refuses to detach HEAD
+		without the explicit `--detach` option.
 	updateSparsePath::
-		Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
+		Shown when either linkgit:git-add[1] or linkgit:git-rm[1]
 		is asked to update index entries outside the current sparse
 		checkout.
+	waitingForEditor::
+		Shown when Git is waiting for editor input. Relevant
+		when e.g. the editor is not launched inside the terminal.
+	worktreeAddOrphan::
+		Shown when the user tries to create a worktree from an
+		invalid reference, to tell the user how to create a new unborn
+		branch instead.
 --
diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt
index f1ca739..01df96f 100644
--- a/Documentation/config/alias.txt
+++ b/Documentation/config/alias.txt
@@ -4,7 +4,7 @@
 	`git last` is equivalent to `git cat-file commit HEAD`. To avoid
 	confusion and troubles with script usage, aliases that
 	hide existing Git commands are ignored. Arguments are split by
-	spaces, the usual shell quoting and escaping is supported.
+	spaces, the usual shell quoting and escaping are supported.
 	A quote pair or a backslash can be used to quote them.
 +
 Note that the first word of an alias does not necessarily have to be a
diff --git a/Documentation/config/apply.txt b/Documentation/config/apply.txt
index 8fb8ef7..f9908e2 100644
--- a/Documentation/config/apply.txt
+++ b/Documentation/config/apply.txt
@@ -2,10 +2,10 @@
 	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 tells 'git apply' to
+	When set to one of: no, none, never, false, it tells 'git apply' to
 	respect all whitespace differences.
 	See linkgit:git-apply[1].
 
 apply.whitespace::
-	Tells 'git apply' how to handle whitespaces, in the same way
+	Tells 'git apply' how to handle whitespace, in the same way
 	as the `--whitespace` option. See linkgit:git-apply[1].
diff --git a/Documentation/config/attr.txt b/Documentation/config/attr.txt
new file mode 100644
index 0000000..1a482d6
--- /dev/null
+++ b/Documentation/config/attr.txt
@@ -0,0 +1,7 @@
+attr.tree::
+	A reference to a tree in the repository from which to read attributes,
+	instead of the `.gitattributes` file in the working tree. In a bare
+	repository, this defaults to `HEAD:.gitattributes`. If the value does
+	not resolve to a valid tree object, an empty tree is used instead.
+	When the `GIT_ATTR_SOURCE` environment variable or `--attr-source`
+	command line option are used, this configuration variable has no effect.
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index 445341a..432b9cd 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -36,7 +36,7 @@
 
 branch.<name>.remote::
 	When on branch <name>, it tells 'git fetch' and 'git push'
-	which remote to fetch from/push to.  The remote to push to
+	which remote to fetch from or push to.  The remote to push to
 	may be overridden with `remote.pushDefault` (for all branches).
 	The remote to push to, for the current branch, may be further
 	overridden by `branch.<name>.pushRemote`.  If no remote is
@@ -64,7 +64,7 @@
 	handled like the remote part of a refspec, and must match a
 	ref which is fetched from the remote given by
 	"branch.<name>.remote".
-	The merge information is used by 'git pull' (which at first calls
+	The merge information is used by 'git pull' (which first calls
 	'git fetch') to lookup the default branch for merging. Without
 	this option, 'git pull' defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
@@ -99,5 +99,5 @@
 branch.<name>.description::
 	Branch description, can be edited with
 	`git branch --edit-description`. Branch description is
-	automatically added in the format-patch cover letter or
+	automatically added to the format-patch cover letter or
 	request-pull summary.
diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt
index bfbca90..a323022 100644
--- a/Documentation/config/checkout.txt
+++ b/Documentation/config/checkout.txt
@@ -30,7 +30,7 @@
 	all commands that perform checkout. E.g. checkout, clone, reset,
 	sparse-checkout, etc.
 +
-Note: parallel checkout usually delivers better performance for repositories
+Note: Parallel checkout usually delivers better performance for repositories
 located on SSDs or over NFS. For repositories on spinning disks and/or machines
 with a small number of cores, the default sequential checkout often performs
 better. The size and compression level of a repository might also influence how
@@ -39,6 +39,6 @@
 checkout.thresholdForParallelism::
 	When running parallel checkout with a small number of files, the cost
 	of subprocess spawning and inter-process communication might outweigh
-	the parallelization gains. This setting allows to define the minimum
+	the parallelization gains. This setting allows you to define the minimum
 	number of files for which parallel checkout should be attempted. The
 	default is 100.
diff --git a/Documentation/config/clean.txt b/Documentation/config/clean.txt
index a807c92..c0188ea 100644
--- a/Documentation/config/clean.txt
+++ b/Documentation/config/clean.txt
@@ -1,3 +1,3 @@
 clean.requireForce::
-	A boolean to make git-clean do nothing unless given -f,
-	-i or -n.   Defaults to true.
+	A boolean to make git-clean refuse to delete files unless -f
+	is given. Defaults to true.
diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt
index 26f4fb1..0a10efd 100644
--- a/Documentation/config/clone.txt
+++ b/Documentation/config/clone.txt
@@ -1,13 +1,23 @@
-clone.defaultRemoteName::
+`clone.defaultRemoteName`::
 	The name of the remote to create when cloning a repository.  Defaults to
-	`origin`, and can be overridden by passing the `--origin` command-line
+	`origin`.
+ifdef::git-clone[]
+	It can be overridden by passing the `--origin` command-line
+	option.
+endif::[]
+ifndef::git-clone[]
+	It can be overridden by passing the `--origin` command-line
 	option to linkgit:git-clone[1].
+endif::[]
 
-clone.rejectShallow::
-	Reject to clone a repository if it is a shallow one, can be overridden by
-	passing option `--reject-shallow` in command line. See linkgit:git-clone[1]
+`clone.rejectShallow`::
+	Reject cloning a repository if it is a shallow one; this can be overridden by
+	passing the `--reject-shallow` option on the command line.
+ifndef::git-clone[]
+	See linkgit:git-clone[1].
+endif::[]
 
-clone.filterSubmodules::
+`clone.filterSubmodules`::
 	If a partial clone filter is provided (see `--filter` in
 	linkgit:git-rev-list[1]) and `--recurse-submodules` is used, also apply
 	the filter to submodules.
diff --git a/Documentation/config/color.txt b/Documentation/config/color.txt
index 1795b2d..2f2275a 100644
--- a/Documentation/config/color.txt
+++ b/Documentation/config/color.txt
@@ -106,7 +106,7 @@
 	matching text in context lines
 `matchSelected`;;
 	matching text in selected lines. Also, used to customize the following
-	linkgit:git-log[1] subcommands: `--grep`, `--author` and `--committer`.
+	linkgit:git-log[1] subcommands: `--grep`, `--author`, and `--committer`.
 `selected`;;
 	non-matching text in selected lines. Also, used to customize the
 	following linkgit:git-log[1] subcommands: `--grep`, `--author` and
diff --git a/Documentation/config/column.txt b/Documentation/config/column.txt
index 76aa2f2..01e4198 100644
--- a/Documentation/config/column.txt
+++ b/Documentation/config/column.txt
@@ -43,7 +43,7 @@
 	See `column.ui` for details.
 
 column.clean::
-	Specify the layout when list items in `git clean -i`, which always
+	Specify the layout when listing items in `git clean -i`, which always
 	shows files and directories in columns. See `column.ui` for details.
 
 column.status::
@@ -51,5 +51,5 @@
 	See `column.ui` for details.
 
 column.tag::
-	Specify whether to output tag listing in `git tag` in columns.
+	Specify whether to output tag listings in `git tag` in columns.
 	See `column.ui` for details.
diff --git a/Documentation/config/commit.txt b/Documentation/config/commit.txt
index 2c95573..62f0d92 100644
--- a/Documentation/config/commit.txt
+++ b/Documentation/config/commit.txt
@@ -2,7 +2,7 @@
 	This setting overrides the default of the `--cleanup` option in
 	`git commit`. See linkgit:git-commit[1] for details. Changing the
 	default can be useful when you always want to keep lines that begin
-	with comment character `#` in your log message, in which case you
+	with the comment character `#` in your log message, in which case you
 	would do `git config commit.cleanup whitespace` (note that you will
 	have to remove the help lines that begin with `#` in the commit log
 	template yourself, if you do this).
@@ -25,5 +25,5 @@
 	new commit messages.
 
 commit.verbose::
-	A boolean or int to specify the level of verbose with `git commit`.
+	A boolean or int to specify the level of verbosity with `git commit`.
 	See linkgit:git-commit[1].
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index dfbdaf0..93d65e1 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -520,6 +520,7 @@
 	`GIT_EDITOR` is not set.  See linkgit:git-var[1].
 
 core.commentChar::
+core.commentString::
 	Commands such as `commit` and `tag` that let you edit
 	messages consider a line that begins with this character
 	commented, and removes them after the editor returns
@@ -527,6 +528,20 @@
 +
 If set to "auto", `git-commit` would select a character that is not
 the beginning character of any line in existing commit messages.
++
+Note that these two variables are aliases of each other, and in modern
+versions of Git you are free to use a string (e.g., `//` or `⁑⁕⁑`) with
+`commentChar`. Versions of Git prior to v2.45.0 will ignore
+`commentString` but will reject a value of `commentChar` that consists
+of more than a single ASCII byte. If you plan to use your config with
+older and newer versions of Git, you may want to specify both:
++
+    [core]
+    # single character for older versions
+    commentChar = "#"
+    # string for newer versions (which will override commentChar
+    # because it comes later in the file)
+    commentString = "//"
 
 core.filesRefLockTimeout::
 	The length of time, in milliseconds, to retry when trying to
@@ -688,7 +703,7 @@
 	will not overwrite existing objects.
 +
 On some file system/operating system combinations, this is unreliable.
-Set this config setting to 'rename' there; However, This will remove the
+Set this config setting to 'rename' there; however, this will remove the
 check that makes sure that existing object files will not get overwritten.
 
 core.notesRef::
@@ -736,3 +751,9 @@
 	If set to "no", no abbreviation is made and the object names
 	are shown in their full length.
 	The minimum length is 4.
+
+core.maxTreeDepth::
+	The maximum depth Git is willing to recurse while traversing a
+	tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
+	to allow Git to abort cleanly, and should not generally need to
+	be adjusted. The default is 4096.
diff --git a/Documentation/config/credential.txt b/Documentation/config/credential.txt
index 512f318..0221c3e 100644
--- a/Documentation/config/credential.txt
+++ b/Documentation/config/credential.txt
@@ -21,7 +21,7 @@
 
 credential.<url>.*::
 	Any of the credential.* options above can be applied selectively to
-	some credentials. For example "credential.https://example.com.username"
+	some credentials. For example, "credential.https://example.com.username"
 	would set the default username only for https connections to
 	example.com. See linkgit:gitcredentials[7] for details on how URLs are
 	matched.
@@ -31,6 +31,6 @@
 
 credentialStore.lockTimeoutMS::
 	The length of time, in milliseconds, for git-credential-store to retry
-	when trying to lock the credentials file. Value 0 means not to retry at
+	when trying to lock the credentials file. A value of 0 means not to retry at
 	all; -1 means to try indefinitely. Default is 1000 (i.e., retry for
 	1s).
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 35a7bf8..5ce7b91 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -1,6 +1,6 @@
 diff.autoRefreshIndex::
 	When using 'git diff' to compare with work tree
-	files, do not consider stat-only change as changed.
+	files, do not consider stat-only changes as changed.
 	Instead, silently run `git update-index --refresh` to
 	update the cached stat information for paths whose
 	contents in the work tree match the contents in the
@@ -52,6 +52,10 @@
 and accumulating child directory counts in the parent directories:
 `files,10,cumulative`.
 
+diff.statNameWidth::
+	Limit the width of the filename part in --stat output. If set, applies
+	to all commands generating --stat output except format-patch.
+
 diff.statGraphWidth::
 	Limit the width of the graph part in --stat output. If set, applies
 	to all commands generating --stat output except format-patch.
@@ -104,9 +108,15 @@
 `git diff --no-index a b`;;
 	compares two non-git things (1) and (2).
 
-diff.noprefix::
+diff.noPrefix::
 	If set, 'git diff' does not show any source or destination prefix.
 
+diff.srcPrefix::
+	If set, 'git diff' uses this source prefix. Defaults to "a/".
+
+diff.dstPrefix::
+	If set, 'git diff' uses this destination prefix. Defaults to "b/".
+
 diff.relative::
 	If set to 'true', 'git diff' does not show changes outside of the directory
 	and show pathnames relative to the current directory.
@@ -219,5 +229,5 @@
 
 diff.colorMovedWS::
 	When moved lines are colored using e.g. the `diff.colorMoved` setting,
-	this option controls the `<mode>` how spaces are treated
-	for details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
+	this option controls the `<mode>` how spaces are treated.
+	For details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
diff --git a/Documentation/config/difftool.txt b/Documentation/config/difftool.txt
index a3f8211..447c40d 100644
--- a/Documentation/config/difftool.txt
+++ b/Documentation/config/difftool.txt
@@ -34,3 +34,10 @@
 
 difftool.prompt::
 	Prompt before each invocation of the diff tool.
+
+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
+	default is `false`, where the `--gui` argument must be provided
+	explicitly for the `diff.guitool` to be used.
diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt
index bccaec7..38dce3d 100644
--- a/Documentation/config/extensions.txt
+++ b/Documentation/config/extensions.txt
@@ -7,6 +7,29 @@
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.compatObjectFormat::
+
+	Specify a compatitbility hash algorithm to use.  The acceptable values
+	are `sha1` and `sha256`.  The value specified must be different from the
+	value of extensions.objectFormat.  This allows client level
+	interoperability between git repositories whose objectFormat matches
+	this compatObjectFormat.  In particular when fully implemented the
+	pushes and pulls from a repository in whose objectFormat matches
+	compatObjectFormat.  As well as being able to use oids encoded in
+	compatObjectFormat in addition to oids encoded with objectFormat to
+	locally specify objects.
+
+extensions.refStorage::
+	Specify the ref storage format to use. The acceptable values are:
++
+include::../ref-storage-format.txt[]
++
+It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
+
 extensions.worktreeConfig::
 	If enabled, then worktrees will load config settings from the
 	`$GIT_DIR/config.worktree` file in addition to the
diff --git a/Documentation/config/fastimport.txt b/Documentation/config/fastimport.txt
index c1166e3..903677d 100644
--- a/Documentation/config/fastimport.txt
+++ b/Documentation/config/fastimport.txt
@@ -1,8 +1,8 @@
 fastimport.unpackLimit::
 	If the number of objects imported by linkgit:git-fast-import[1]
 	is below this limit, then the objects will be unpacked into
-	loose object files.  However if the number of imported objects
-	equals or exceeds this limit then the pack will be stored as a
+	loose object files.  However, if the number of imported objects
+	equals or exceeds this limit, then the pack will be stored as a
 	pack.  Storing the pack from a fast-import can make the import
 	operation complete faster, especially on slow filesystems.  If
 	not set, the value of `transfer.unpackLimit` is used instead.
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index e52bc6b..f061b64 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -15,8 +15,11 @@
 * `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
 skipping more commits at a time, reducing the number of round trips.
 +
-* `gc.cruftPacks=true` reduces disk space used by unreachable objects during
-garbage collection, preventing loose object explosions.
+* `pack.useBitmapBoundaryTraversal=true` may improve bitmap traversal times by
+walking fewer objects.
++
+* `pack.allowPackReuse=multi` may improve the time it takes to create a pack by
+reusing objects from multiple packs instead of just one.
 
 feature.manyFiles::
 	Enable config options that optimize for repos with many files in the
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index 568f0f7..d7dc461 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -50,10 +50,16 @@
 	refs. See also `remote.<name>.pruneTags` and the PRUNING
 	section of linkgit:git-fetch[1].
 
+fetch.all::
+	If true, fetch will attempt to update all available remotes.
+	This behavior can be overridden by passing `--no-all` or by
+	explicitly specifying one or more remote(s) to fetch from.
+	Defaults to false.
+
 fetch.output::
 	Control how ref update status is printed. Valid values are
-	`full` and `compact`. Default value is `full`. See section
-	OUTPUT in linkgit:git-fetch[1] for detail.
+	`full` and `compact`. Default value is `full`. See the
+	OUTPUT section in linkgit:git-fetch[1] for details.
 
 fetch.negotiationAlgorithm::
 	Control how information about the commits in the local repository
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 73678d8..7410e93 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -68,7 +68,7 @@
 	Defaults to true.
 
 format.pretty::
-	The default pretty format for log/show/whatchanged command,
+	The default pretty format for log/show/whatchanged command.
 	See linkgit:git-log[1], linkgit:git-show[1],
 	linkgit:git-whatchanged[1].
 
@@ -119,7 +119,7 @@
 	`--notes=<ref>`, where `ref` is the non-boolean value. Defaults
 	to false.
 +
-If one wishes to use the ref `ref/notes/true`, please use that literal
+If one wishes to use the ref `refs/notes/true`, please use that literal
 instead.
 +
 This configuration can be specified multiple times in order to allow
@@ -144,3 +144,10 @@
 format.mboxrd::
 	A boolean value which enables the robust "mboxrd" format when
 	`--stdout` is in use to escape "^>+From " lines.
+
+format.noprefix::
+	If set, do not show any source or destination prefix in patches.
+	This is equivalent to the `diff.noprefix` option used by `git
+	diff` (but which is not respected by `format-patch`). Note that
+	by setting this, the receiver of any patches you generate will
+	have to apply them using the `-p0` option.
diff --git a/Documentation/config/fsck.txt b/Documentation/config/fsck.txt
index a3c865d..8e9e508 100644
--- a/Documentation/config/fsck.txt
+++ b/Documentation/config/fsck.txt
@@ -11,13 +11,13 @@
 +
 The rest of the documentation discusses `fsck.*` for brevity, but the
 same applies for the corresponding `receive.fsck.*` and
-`fetch.<msg-id>.*`. variables.
+`fetch.fsck.*`. variables.
 +
-Unlike variables like `color.ui` and `core.editor` the
+Unlike variables like `color.ui` and `core.editor`, the
 `receive.fsck.<msg-id>` and `fetch.fsck.<msg-id>` variables will not
 fall back on the `fsck.<msg-id>` configuration if they aren't set. To
-uniformly configure the same fsck settings in different circumstances
-all three of them they must all set to the same values.
+uniformly configure the same fsck settings in different circumstances,
+all three of them must be set to the same values.
 +
 When `fsck.<msg-id>` is set, errors can be switched to warnings and
 vice versa by configuring the `fsck.<msg-id>` setting where the
@@ -36,19 +36,19 @@
 doing the same for `receive.fsck.<msg-id>` and `fetch.fsck.<msg-id>`
 will only cause git to warn.
 +
-See `Fsck Messages` section of linkgit:git-fsck[1] for supported
+See the `Fsck Messages` section of linkgit:git-fsck[1] for supported
 values of `<msg-id>`.
 
 
 fsck.skipList::
 	The path to a list of object names (i.e. one unabbreviated SHA-1 per
 	line) that are known to be broken in a non-fatal way and should
-	be ignored. On versions of Git 2.20 and later comments ('#'), empty
-	lines, and any leading and trailing whitespace is ignored. Everything
+	be ignored. On versions of Git 2.20 and later, comments ('#'), empty
+	lines, and any leading and trailing whitespace are ignored. Everything
 	but a SHA-1 per line will error out on older versions.
 +
 This feature is useful when an established project should be accepted
-despite early commits containing errors that can be safely ignored
+despite early commits containing errors that can be safely ignored,
 such as invalid committer email addresses.  Note: corrupt objects
 cannot be skipped with this setting.
 +
@@ -58,11 +58,11 @@
 Unlike variables like `color.ui` and `core.editor` the
 `receive.fsck.skipList` and `fetch.fsck.skipList` variables will not
 fall back on the `fsck.skipList` configuration if they aren't set. To
-uniformly configure the same fsck settings in different circumstances
-all three of them they must all set to the same values.
+uniformly configure the same fsck settings in different circumstances,
+all three of them must be set to the same values.
 +
 Older versions of Git (before 2.20) documented that the object names
-list should be sorted. This was never a requirement, the object names
+list should be sorted. This was never a requirement; the object names
 could appear in any order, but when reading the list we tracked whether
 the list was sorted for the purposes of an internal binary search
 implementation, which could save itself some work with an already sorted
diff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt
index c225c6c..671f9b9 100644
--- a/Documentation/config/fsmonitor--daemon.txt
+++ b/Documentation/config/fsmonitor--daemon.txt
@@ -1,5 +1,5 @@
 fsmonitor.allowRemote::
-    By default, the fsmonitor daemon refuses to work against network-mounted
+    By default, the fsmonitor daemon refuses to work with network-mounted
     repositories. Setting `fsmonitor.allowRemote` to `true` overrides this
     behavior.  Only respected when `core.fsmonitor` is set to `true`.
 
diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt
index 38fea07..664a3c2 100644
--- a/Documentation/config/gc.txt
+++ b/Documentation/config/gc.txt
@@ -24,7 +24,7 @@
 	default value is 6700.
 +
 Setting this to 0 disables not only automatic packing based on the
-number of loose objects, but any other heuristic `git gc --auto` will
+number of loose objects, but also any other heuristic `git gc --auto` will
 otherwise use to determine if there's work to do, such as
 `gc.autoPackLimit`.
 
@@ -39,15 +39,15 @@
 use, it'll affect how the auto pack limit works.
 
 gc.autoDetach::
-	Make `git gc --auto` return immediately and run in background
+	Make `git gc --auto` return immediately and run in the background
 	if the system supports it. Default is true.
 
 gc.bigPackThreshold::
-	If non-zero, all packs larger than this limit are kept when
-	`git gc` is run. This is very similar to `--keep-largest-pack`
-	except that all packs that meet the threshold are kept, not
-	just the largest pack. Defaults to zero. Common unit suffixes of
-	'k', 'm', or 'g' are supported.
+	If non-zero, all non-cruft packs larger than this limit are kept
+	when `git gc` is run. This is very similar to
+	`--keep-largest-pack` except that all non-cruft packs that meet
+	the threshold are kept, not just the largest pack. Defaults to
+	zero. Common unit suffixes of 'k', 'm', or 'g' are supported.
 +
 Note that if the number of kept packs is more than gc.autoPackLimit,
 this configuration variable is ignored, all packs except the base pack
@@ -84,7 +84,13 @@
 gc.cruftPacks::
 	Store unreachable objects in a cruft pack (see
 	linkgit:git-repack[1]) instead of as loose objects. The default
-	is `false`.
+	is `true`.
+
+gc.maxCruftSize::
+	Limit the size of new cruft packs when repacking. When
+	specified in addition to `--max-cruft-size`, the command line
+	option takes priority. See the `--max-cruft-size` option of
+	linkgit:git-repack[1].
 
 gc.pruneExpire::
 	When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'
@@ -130,6 +136,37 @@
 project most users will want to expire them sooner, which is why the
 default is more aggressive than `gc.reflogExpire`.
 
+gc.recentObjectsHook::
+	When considering whether or not to remove an object (either when
+	generating a cruft pack or storing unreachable objects as
+	loose), use the shell to execute the specified command(s).
+	Interpret their output as object IDs which Git will consider as
+	"recent", regardless of their age. By treating their mtimes as
+	"now", any objects (and their descendants) mentioned in the
+	output will be kept regardless of their true age.
++
+Output must contain exactly one hex object ID per line, and nothing
+else. Objects which cannot be found in the repository are ignored.
+Multiple hooks are supported, but all must exit successfully, else the
+operation (either generating a cruft pack or unpacking unreachable
+objects) will be halted.
+
+gc.repackFilter::
+	When repacking, use the specified filter to move certain
+	objects into a separate packfile.  See the
+	`--filter=<filter-spec>` option of linkgit:git-repack[1].
+
+gc.repackFilterTo::
+	When repacking and using a filter, see `gc.repackFilter`, the
+	specified location will be used to create the packfile
+	containing the filtered out objects. **WARNING:** The
+	specified location should be accessible, using for example the
+	Git alternates mechanism, otherwise the repo could be
+	considered corrupt by Git as it migh not be able to access the
+	objects in that packfile. See the `--filter-to=<dir>` option
+	of linkgit:git-repack[1] and the `objects/info/alternates`
+	section of linkgit:gitrepository-layout[5].
+
 gc.rerereResolved::
 	Records of conflicted merge you resolved earlier are
 	kept for this many days when 'git rerere gc' is run.
diff --git a/Documentation/config/gpg.txt b/Documentation/config/gpg.txt
index 37e2831..5cf32b1 100644
--- a/Documentation/config/gpg.txt
+++ b/Documentation/config/gpg.txt
@@ -4,7 +4,7 @@
 	same command-line interface as GPG, namely, to verify a detached
 	signature, "`gpg --verify $signature - <$file`" is run, and the
 	program is expected to signal a good signature by exiting with
-	code 0, and to generate an ASCII-armored detached signature, the
+	code 0.  To generate an ASCII-armored detached signature, the
 	standard input of "`gpg -bsau $key`" is fed with the contents to be
 	signed, and the program is expected to send the result to its
 	standard output.
@@ -25,7 +25,7 @@
 gpg.minTrustLevel::
 	Specifies a minimum trust level for signature verification.  If
 	this option is unset, then signature verification for merge
-	operations require a key with at least `marginal` trust.  Other
+	operations requires a key with at least `marginal` trust.  Other
 	operations that perform signature verification require a key
 	with at least `undefined` trust.  Setting this option overrides
 	the required trust-level for all operations.  Supported values,
@@ -38,7 +38,7 @@
 * `ultimate`
 
 gpg.ssh.defaultKeyCommand::
-	This command that will be run when user.signingkey is not set and a ssh
+	This command will be run when user.signingkey is not set and a ssh
 	signature is requested. On successful exit a valid ssh public key
 	prefixed with `key::` is expected in the first line of its output.
 	This allows for a script doing a dynamic lookup of the correct public
diff --git a/Documentation/config/grep.txt b/Documentation/config/grep.txt
index e521f20..10041f2 100644
--- a/Documentation/config/grep.txt
+++ b/Documentation/config/grep.txt
@@ -24,5 +24,5 @@
 	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
+	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/gui.txt b/Documentation/config/gui.txt
index 0c087fd..171be77 100644
--- a/Documentation/config/gui.txt
+++ b/Documentation/config/gui.txt
@@ -24,7 +24,7 @@
 	not. Default: "false".
 
 gui.newBranchTemplate::
-	Is used as suggested name when creating new branches using the
+	Is used as a suggested name when creating new branches using the
 	linkgit:git-gui[1].
 
 gui.pruneDuringFetch::
diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt
index afeeccf..2d4e0c9 100644
--- a/Documentation/config/http.txt
+++ b/Documentation/config/http.txt
@@ -246,20 +246,21 @@
 pushes.
 
 http.lowSpeedLimit, http.lowSpeedTime::
-	If the HTTP transfer speed is less than 'http.lowSpeedLimit'
-	for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
+	If the HTTP transfer speed, in bytes per second, is less than
+	'http.lowSpeedLimit' for longer than 'http.lowSpeedTime' seconds,
+	the transfer is aborted.
 	Can be overridden by the `GIT_HTTP_LOW_SPEED_LIMIT` and
 	`GIT_HTTP_LOW_SPEED_TIME` environment variables.
 
 http.noEPSV::
 	A boolean which disables using of EPSV ftp command by curl.
-	This can helpful with some "poor" ftp servers which don't
+	This can be helpful with some "poor" ftp servers which don't
 	support EPSV mode. Can be overridden by the `GIT_CURL_FTP_NO_EPSV`
 	environment variable. Default is false (curl will use EPSV).
 
 http.userAgent::
 	The HTTP USER_AGENT string presented to an HTTP server.  The default
-	value represents the version of the client Git such as git/1.7.1.
+	value represents the version of the Git client such as git/1.7.1.
 	This option allows you to override this value to a more common value
 	such as Mozilla/4.0.  This may be necessary, for instance, if
 	connecting through a firewall that restricts HTTP connections to a set
diff --git a/Documentation/config/i18n.txt b/Documentation/config/i18n.txt
index cc25621..6e72fdb 100644
--- a/Documentation/config/i18n.txt
+++ b/Documentation/config/i18n.txt
@@ -2,7 +2,7 @@
 	Character encoding the commit messages are stored in; Git itself
 	does not care per se, but this information is necessary e.g. when
 	importing commits from emails or in the gitk graphical history
-	browser (and possibly at other places in the future or in other
+	browser (and possibly in other places in the future or in other
 	porcelains). See e.g. linkgit:git-mailinfo[1]. Defaults to 'utf-8'.
 
 i18n.logOutputEncoding::
diff --git a/Documentation/config/imap.txt b/Documentation/config/imap.txt
index 06166fb..3d28f72 100644
--- a/Documentation/config/imap.txt
+++ b/Documentation/config/imap.txt
@@ -4,7 +4,7 @@
 	"[Gmail]/Drafts". Required.
 
 imap.tunnel::
-	Command used to setup a tunnel to the IMAP server through which
+	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.
 
@@ -37,7 +37,7 @@
 	format=fixed email.  Default is `false`.
 
 imap.authMethod::
-	Specify authenticate method for authentication with IMAP server.
+	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`
 	option, the only supported method is 'CRAM-MD5'. If this is not set
diff --git a/Documentation/config/index.txt b/Documentation/config/index.txt
index 23c7985..3eff420 100644
--- a/Documentation/config/index.txt
+++ b/Documentation/config/index.txt
@@ -23,7 +23,7 @@
 	Specifies the number of threads to spawn when loading the index.
 	This is meant to reduce index load time on multiprocessor machines.
 	Specifying 0 or 'true' will cause Git to auto-detect the number of
-	CPU's and set the number of threads accordingly. Specifying 1 or
+	CPUs and set the number of threads accordingly. Specifying 1 or
 	'false' will disable multithreading. Defaults to 'true'.
 
 index.version::
diff --git a/Documentation/config/init.txt b/Documentation/config/init.txt
index 79c79d6..af03acd 100644
--- a/Documentation/config/init.txt
+++ b/Documentation/config/init.txt
@@ -1,7 +1,10 @@
-init.templateDir::
-	Specify the directory from which templates will be copied.
-	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+:see-git-init:
+ifndef::git-init[]
+:see-git-init: (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+endif::[]
 
-init.defaultBranch::
+`init.templateDir`::
+	Specify the directory from which templates will be copied. {see-git-init}
+`init.defaultBranch`::
 	Allows overriding the default branch name e.g. when initializing
 	a new repository.
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index a2d3c7e..5cc2655 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -4,9 +4,7 @@
 	Currently this is used by the `--patch` mode of
 	linkgit:git-add[1], linkgit:git-checkout[1],
 	linkgit:git-restore[1], linkgit:git-commit[1],
-	linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
-	setting is silently ignored if portable keystroke input
-	is not available; requires the Perl module Term::ReadKey.
+	linkgit:git-reset[1], and linkgit:git-stash[1].
 
 interactive.diffFilter::
 	When an interactive command (such as `git add --patch`) shows
diff --git a/Documentation/config/log.txt b/Documentation/config/log.txt
index 5f96cf8..9003a82 100644
--- a/Documentation/config/log.txt
+++ b/Documentation/config/log.txt
@@ -9,7 +9,7 @@
 	`--date` option.  See linkgit:git-log[1] for details.
 +
 If the format is set to "auto:foo" and the pager is in use, format
-"foo" will be the used for the date format. Otherwise "default" will
+"foo" will be used for the date format. Otherwise, "default" will
 be used.
 
 log.decorate::
diff --git a/Documentation/config/mailinfo.txt b/Documentation/config/mailinfo.txt
index 3854d4a..ec3a5d8 100644
--- a/Documentation/config/mailinfo.txt
+++ b/Documentation/config/mailinfo.txt
@@ -1,6 +1,6 @@
 mailinfo.scissors::
 	If true, makes linkgit:git-mailinfo[1] (and therefore
 	linkgit:git-am[1]) act by default as if the --scissors option
-	was provided on the command-line. When active, this features
+	was provided on the command-line. When active, this feature
 	removes everything from the message body before a scissors
 	line (i.e. consisting mainly of ">8", "8<" and "-").
diff --git a/Documentation/config/maintenance.txt b/Documentation/config/maintenance.txt
index 18f0562..69a4f05 100644
--- a/Documentation/config/maintenance.txt
+++ b/Documentation/config/maintenance.txt
@@ -12,7 +12,7 @@
 	then that value is used instead of the one provided by
 	`maintenance.strategy`. The possible strategy strings are:
 +
-* `none`: This default setting implies no task are run at any schedule.
+* `none`: This default setting implies no tasks are run at any schedule.
 * `incremental`: This setting optimizes for performing small maintenance
   activities that do not delete any data. This does not schedule the `gc`
   task, but runs the `prefetch` and `commit-graph` tasks hourly, the
diff --git a/Documentation/config/man.txt b/Documentation/config/man.txt
index a727d98..5a0f82c 100644
--- a/Documentation/config/man.txt
+++ b/Documentation/config/man.txt
@@ -5,7 +5,7 @@
 man.<tool>.cmd::
 	Specify the command to invoke the specified man viewer. The
 	specified command is evaluated in shell with the man page
-	passed as argument. (See linkgit:git-help[1].)
+	passed as an argument. (See linkgit:git-help[1].)
 
 man.<tool>.path::
 	Override the path for the given tool that may be used to
diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
index 99e83dd..8851b6c 100644
--- a/Documentation/config/merge.txt
+++ b/Documentation/config/merge.txt
@@ -7,7 +7,7 @@
 	marker and the original text before the `=======` marker.  The
 	"merge" style tends to produce smaller conflict regions than diff3,
 	both because of the exclusion of the original text, and because
-	when a subset of lines match on the two sides they are just pulled
+	when a subset of lines match on the two sides, they are just pulled
 	out of the conflict region.  Another alternate style, "zdiff3", is
 	similar to diff3 but removes matching lines on the two sides from
 	the conflict region when those matching lines appear near either
diff --git a/Documentation/config/mergetool.txt b/Documentation/config/mergetool.txt
index e779a12..00bf665 100644
--- a/Documentation/config/mergetool.txt
+++ b/Documentation/config/mergetool.txt
@@ -22,8 +22,8 @@
 	For a custom merge command, specify whether the exit code of
 	the merge command can be used to determine whether the merge was
 	successful.  If this is not set to true then the merge target file
-	timestamp is checked and the merge assumed to have been successful
-	if the file has been updated, otherwise the user is prompted to
+	timestamp is checked, and the merge is assumed to have been successful
+	if the file has been updated; otherwise, the user is prompted to
 	indicate the success of the merge.
 
 mergetool.meld.hasOutput::
@@ -37,7 +37,7 @@
 
 mergetool.meld.useAutoMerge::
 	When the `--auto-merge` is given, meld will merge all non-conflicting
-	parts automatically, highlight the conflicting parts and wait for
+	parts automatically, highlight the conflicting parts, and wait for
 	user decision.  Setting `mergetool.meld.useAutoMerge` to `true` tells
 	Git to unconditionally use the `--auto-merge` option with `meld`.
 	Setting this value to `auto` makes git detect whether `--auto-merge`
@@ -45,17 +45,24 @@
 	value of `false` avoids using `--auto-merge` altogether, and is the
 	default value.
 
-mergetool.vimdiff.layout::
-	The vimdiff backend uses this variable to control how its split
-	windows look like. Applies even if you are using Neovim (`nvim`) or
-	gVim (`gvim`) as the merge tool. See BACKEND SPECIFIC HINTS section
-ifndef::git-mergetool[]
-	in linkgit:git-mergetool[1].
+mergetool.<vimdiff variant>.layout::
+	Configure the split window layout for vimdiff's `<variant>`, which is any of `vimdiff`,
+	`nvimdiff`, `gvimdiff`.
+	Upon launching `git mergetool` with `--tool=<variant>` (or without `--tool`
+	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`
+ifdef::git-mergetool[]
+	section.
 endif::[]
-	for details.
+ifndef::git-mergetool[]
+	section in linkgit:git-mergetool[1].
+endif::[]
 
 mergetool.hideResolved::
-	During a merge Git will automatically resolve as many conflicts as
+	During a merge, Git will automatically resolve as many conflicts as
 	possible and write the 'MERGED' file containing conflict markers around
 	any conflicts that it cannot resolve; 'LOCAL' and 'REMOTE' normally
 	represent the versions of the file from before Git's conflict
@@ -74,7 +81,7 @@
 	When invoking a custom merge tool, Git uses a set of temporary
 	files to pass to the tool. If the tool returns an error and this
 	variable is set to `true`, then these temporary files will be
-	preserved, otherwise they will be removed after the tool has
+	preserved; otherwise, they will be removed after the tool has
 	exited. Defaults to `false`.
 
 mergetool.writeToTemp::
@@ -85,3 +92,10 @@
 
 mergetool.prompt::
 	Prompt before each invocation of the merge resolution program.
+
+mergetool.guiDefault::
+	Set `true` to use the `merge.guitool` by default (equivalent to
+	specifying the `--gui` argument), or `auto` to select `merge.guitool`
+	or `merge.tool` depending on the presence of a `DISPLAY` environment
+	variable value. The default is `false`, where the `--gui` argument
+	must be provided explicitly for the `merge.guitool` to be used.
diff --git a/Documentation/config/notes.txt b/Documentation/config/notes.txt
index c7c4811..43db8e8 100644
--- a/Documentation/config/notes.txt
+++ b/Documentation/config/notes.txt
@@ -1,7 +1,7 @@
 notes.mergeStrategy::
 	Which merge strategy to choose by default when resolving notes
 	conflicts.  Must be one of `manual`, `ours`, `theirs`, `union`, or
-	`cat_sort_uniq`.  Defaults to `manual`.  See "NOTES MERGE STRATEGIES"
+	`cat_sort_uniq`.  Defaults to `manual`.  See the "NOTES MERGE STRATEGIES"
 	section of linkgit:git-notes[1] for more information on each strategy.
 +
 This setting can be overridden by passing the `--strategy` option to
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 53093d9..da52737 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -28,11 +28,16 @@
 to linkgit:git-repack[1].
 
 pack.allowPackReuse::
-	When true, and when reachability bitmaps are enabled,
-	pack-objects will try to send parts of the bitmapped packfile
-	verbatim. This can reduce memory and CPU usage to serve fetches,
-	but might result in sending a slightly larger pack. Defaults to
-	true.
+	When true or "single", and when reachability bitmaps are
+	enabled, pack-objects will try to send parts of the bitmapped
+	packfile verbatim. When "multi", and when a multi-pack
+	reachability bitmap is available, pack-objects will try to send
+	parts of all packs in the MIDX.
++
+If only a single pack bitmap is available, and `pack.allowPackReuse`
+is set to "multi", reuse parts of just the bitmapped packfile. This
+can reduce memory and CPU usage to serve fetches, but might result in
+sending a slightly larger pack. Defaults to true.
 
 pack.island::
 	An extended regular expression configuring a set of delta
@@ -74,7 +79,7 @@
 	warning. This is meant to reduce packing time on multiprocessor
 	machines. The required amount of memory for the delta search window
 	is however multiplied by the number of threads.
-	Specifying 0 will cause Git to auto-detect the number of CPU's
+	Specifying 0 will cause Git to auto-detect the number of CPUs
 	and set the number of threads accordingly.
 
 pack.indexVersion::
@@ -83,11 +88,11 @@
 	the new pack index with capabilities for packs larger than 4 GB
 	as well as proper protection against the repacking of corrupted
 	packs.  Version 2 is the default.  Note that version 2 is enforced
-	and this config option ignored whenever the corresponding pack is
+	and this config option is ignored whenever the corresponding pack is
 	larger than 2 GB.
 +
 If you have an old Git that does not understand the version 2 `*.idx` file,
-cloning or fetching over a non native protocol (e.g. "http")
+cloning or fetching over a non-native protocol (e.g. "http")
 that will copy both `*.pack` file and corresponding `*.idx` file from the
 other side may give you a repository that cannot be accessed with your
 older version of Git. If the `*.pack` file is smaller than 2 GB, however,
@@ -102,8 +107,8 @@
 	in the creation of multiple packfiles.
 +
 Note that this option is rarely useful, and may result in a larger total
-on-disk size (because Git will not store deltas between packs), as well
-as worse runtime performance (object lookup within multiple packs is
+on-disk size (because Git will not store deltas between packs) and
+worse runtime performance (object lookup within multiple packs is
 slower than a single pack, and optimizations like reachability bitmaps
 cannot cope with multiple packs).
 +
@@ -123,6 +128,23 @@
 	true. You should not generally need to turn this off unless
 	you are debugging pack bitmaps.
 
+pack.useBitmapBoundaryTraversal::
+	When true, Git will use an experimental algorithm for computing
+	reachability queries with bitmaps. Instead of building up
+	complete bitmaps for all of the negated tips and then OR-ing
+	them together, consider negated tips with existing bitmaps as
+	additive (i.e. OR-ing them into the result if they exist,
+	ignoring them otherwise), and build up a bitmap at the boundary
+	instead.
++
+When using this algorithm, Git may include too many objects as a result
+of not opening up trees belonging to certain UNINTERESTING commits. This
+inexactness matches the non-bitmap traversal algorithm.
++
+In many cases, this can provide a speed-up over the exact algorithm,
+particularly when there is poor bitmap coverage of the negated side of
+the query.
+
 pack.useSparse::
 	When true, git will default to using the '--sparse' option in
 	'git pack-objects' when the '--revs' option is present. This
@@ -171,9 +193,15 @@
 	beneficial in repositories that have relatively large bitmap
 	indexes. Defaults to false.
 
+pack.readReverseIndex::
+	When true, git will read any .rev file(s) that may be available
+	(see: linkgit:gitformat-pack[5]). When false, the reverse index
+	will be generated from scratch and stored in memory. Defaults to
+	true.
+
 pack.writeReverseIndex::
 	When true, git will write a corresponding .rev file (see:
 	linkgit:gitformat-pack[5])
 	for each new packfile that it writes in all places except for
 	linkgit:git-fast-import[1] and in the bulk checkin mechanism.
-	Defaults to false.
+	Defaults to true.
diff --git a/Documentation/config/push.txt b/Documentation/config/push.txt
index 43338b6..0acbbea 100644
--- a/Documentation/config/push.txt
+++ b/Documentation/config/push.txt
@@ -35,7 +35,7 @@
 
 * `tracking` - This is a deprecated synonym for `upstream`.
 
-* `simple` - pushes the current branch with the same name on the remote.
+* `simple` - push the current branch with the same name on the remote.
 +
 If you are working on a centralized workflow (pushing to the same repository you
 pull from, which is typically `origin`), then you need to configure an upstream
@@ -67,7 +67,7 @@
 --
 
 push.followTags::
-	If set to true enable `--follow-tags` option by default.  You
+	If set to true, enable `--follow-tags` option by default.  You
 	may override this configuration at time of push by specifying
 	`--no-follow-tags`.
 
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e..c6187ab 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -9,7 +9,9 @@
 	rebase. False by default.
 
 rebase.autoSquash::
-	If set to true enable `--autosquash` option by default.
+	If set to true, enable the `--autosquash` option of
+	linkgit:git-rebase[1] by default for interactive mode.
+	This can be overridden with the `--no-autosquash` option.
 
 rebase.autoStash::
 	When set to true, automatically create a temporary stash entry
@@ -38,7 +40,7 @@
 rebase.instructionFormat::
 	A format string, as specified in linkgit:git-log[1], to be used for the
 	todo list during an interactive rebase.  The format will
-	automatically have the long commit hash prepended to the format.
+	automatically have the commit hash prepended to the format.
 
 rebase.abbreviateCommands::
 	If set to true, `git rebase` will use abbreviated command names in the
@@ -67,3 +69,19 @@
 
 rebase.forkPoint::
 	If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+	Whether and how to set the `--rebase-merges` option by default. Can
+	be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+	true or to `no-rebase-cousins` is equivalent to
+	`--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+	equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+	equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+	command line, with or without an argument, overrides any
+	`rebase.rebaseMerges` configuration.
+
+rebase.maxLabelLength::
+	When generating label names from commit subjects, truncate the names to
+	this length. By default, the names are truncated to a little less than
+	`NAME_MAX` (to allow e.g. `.lock` files to be written for the
+	corresponding loose refs).
diff --git a/Documentation/config/receive.txt b/Documentation/config/receive.txt
index 85d5b5a..c77e55b 100644
--- a/Documentation/config/receive.txt
+++ b/Documentation/config/receive.txt
@@ -14,12 +14,12 @@
 
 receive.certNonceSeed::
 	By setting this variable to a string, `git receive-pack`
-	will accept a `git push --signed` and verifies it by using
+	will accept a `git push --signed` and verify it by using
 	a "nonce" protected by HMAC using this string as a secret
 	key.
 
 receive.certNonceSlop::
-	When a `git push --signed` sent a push certificate with a
+	When a `git push --signed` sends a push certificate with a
 	"nonce" that was issued by a receive-pack serving the same
 	repository within this many seconds, export the "nonce"
 	found in the certificate to `GIT_PUSH_CERT_NONCE` to the
diff --git a/Documentation/config/rerere.txt b/Documentation/config/rerere.txt
index 40abdf6..3a78b5e 100644
--- a/Documentation/config/rerere.txt
+++ b/Documentation/config/rerere.txt
@@ -1,7 +1,7 @@
 rerere.autoUpdate::
 	When set to true, `git-rerere` updates the index with the
 	resulting contents after it cleanly resolves conflicts using
-	previously recorded resolution.  Defaults to false.
+	previously recorded resolutions.  Defaults to false.
 
 rerere.enabled::
 	Activate recording of resolved conflicts, so that identical
diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index bde7f31..577df40 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -14,7 +14,7 @@
 within that directory.
 +
 This config setting is only respected in protected configuration (see
-<<SCOPES>>). This prevents the untrusted repository from tampering with
+<<SCOPES>>). This prevents untrusted repositories from tampering with
 this value.
 
 safe.directory::
@@ -32,7 +32,7 @@
 `safe.directory` entry with an empty value.
 +
 This config setting is only respected in protected configuration (see
-<<SCOPES>>). This prevents the untrusted repository from tampering with this
+<<SCOPES>>). This prevents untrusted repositories from tampering with this
 value.
 +
 The value of this setting is interpolated, i.e. `~/<path>` expands to a
diff --git a/Documentation/config/sendemail.txt b/Documentation/config/sendemail.txt
index 51da708..6a869d6 100644
--- a/Documentation/config/sendemail.txt
+++ b/Documentation/config/sendemail.txt
@@ -8,7 +8,7 @@
 	See linkgit:git-send-email[1] for description.  Note that this
 	setting is not subject to the 'identity' mechanism.
 
-sendemail.smtpsslcertpath::
+sendemail.smtpSSLCertPath::
 	Path to ca-certificates (either a directory or a single file).
 	Set it to an empty string to disable certificate verification.
 
@@ -36,7 +36,7 @@
 
 sendemail.aliasFileType::
 	Format of the file(s) specified in sendemail.aliasesFile. Must be
-	one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus', or 'sendmail'.
+	one of 'mutt', 'mailrc', 'pine', 'elm', 'gnus', or 'sendmail'.
 +
 What an alias file in each format looks like can be found in
 the documentation of the email program of the same name. The
@@ -61,12 +61,13 @@
 sendemail.chainReplyTo::
 sendemail.envelopeSender::
 sendemail.from::
-sendemail.signedoffbycc::
+sendemail.headerCmd::
+sendemail.signedOffByCc::
 sendemail.smtpPass::
-sendemail.suppresscc::
+sendemail.suppressCc::
 sendemail.suppressFrom::
 sendemail.to::
-sendemail.tocmd::
+sendemail.toCmd::
 sendemail.smtpDomain::
 sendemail.smtpServer::
 sendemail.smtpServerPort::
@@ -80,8 +81,8 @@
 	linkgit:git-send-email[1] command-line options. See its
 	documentation for details.
 
-sendemail.signedoffcc (deprecated)::
-	Deprecated alias for `sendemail.signedoffbycc`.
+sendemail.signedOffCc (deprecated)::
+	Deprecated alias for `sendemail.signedOffByCc`.
 
 sendemail.smtpBatchSize::
 	Number of messages to be sent per connection, after that a relogin
@@ -90,7 +91,7 @@
 	See also the `--batch-size` option of linkgit:git-send-email[1].
 
 sendemail.smtpReloginDelay::
-	Seconds wait before reconnecting to smtp server.
+	Seconds to wait before reconnecting to the smtp server.
 	See also the `--relogin-delay` option of linkgit:git-send-email[1].
 
 sendemail.forbidSendmailVariables::
diff --git a/Documentation/config/sequencer.txt b/Documentation/config/sequencer.txt
index b48d532..e664eef 100644
--- a/Documentation/config/sequencer.txt
+++ b/Documentation/config/sequencer.txt
@@ -2,4 +2,4 @@
 	Text editor used by `git rebase -i` for editing the rebase instruction file.
 	The value is meant to be interpreted by the shell when it is used.
 	It can be overridden by the `GIT_SEQUENCE_EDITOR` environment variable.
-	When not configured the default commit message editor is used instead.
+	When not configured, the default commit message editor is used instead.
diff --git a/Documentation/config/splitindex.txt b/Documentation/config/splitindex.txt
index afdb186..cfaa296 100644
--- a/Documentation/config/splitindex.txt
+++ b/Documentation/config/splitindex.txt
@@ -3,10 +3,10 @@
 	percent of entries the split index can contain compared to the
 	total number of entries in both the split index and the shared
 	index before a new shared index is written.
-	The value should be between 0 and 100. If the value is 0 then
-	a new shared index is always written, if it is 100 a new
+	The value should be between 0 and 100. If the value is 0, then
+	a new shared index is always written; if it is 100, a new
 	shared index is never written.
-	By default the value is 20, so a new shared index is written
+	By default, the value is 20, so a new shared index is written
 	if the number of entries in the split index would be greater
 	than 20 percent of the total number of entries.
 	See linkgit:git-update-index[1].
diff --git a/Documentation/config/stash.txt b/Documentation/config/stash.txt
index b9f609e..ec1edae 100644
--- a/Documentation/config/stash.txt
+++ b/Documentation/config/stash.txt
@@ -1,14 +1,14 @@
 stash.showIncludeUntracked::
 	If this is set to true, the `git stash show` command will show
 	the untracked files of a stash entry.  Defaults to false. See
-	description of 'show' command in linkgit:git-stash[1].
+	the description of the 'show' command in linkgit:git-stash[1].
 
 stash.showPatch::
 	If this is set to true, the `git stash show` command without an
 	option will show the stash entry in patch form.  Defaults to false.
-	See description of 'show' command in linkgit:git-stash[1].
+	See the description of the 'show' command in linkgit:git-stash[1].
 
 stash.showStat::
 	If this is set to true, the `git stash show` command without an
-	option will show diffstat of the stash entry.  Defaults to true.
-	See description of 'show' command in linkgit:git-stash[1].
+	option will show a diffstat of the stash entry.  Defaults to true.
+	See the description of the 'show' command in linkgit:git-stash[1].
diff --git a/Documentation/config/status.txt b/Documentation/config/status.txt
index 0fc704a..8caf90f 100644
--- a/Documentation/config/status.txt
+++ b/Documentation/config/status.txt
@@ -47,7 +47,7 @@
 	contain only untracked files, are shown with the directory name
 	only. Showing untracked files means that Git needs to lstat() all
 	the files in the whole repository, which might be slow on some
-	systems. So, this variable controls how the commands displays
+	systems. So, this variable controls how the commands display
 	the untracked files. Possible values are:
 +
 --
@@ -57,12 +57,14 @@
 --
 +
 If this variable is not specified, it defaults to 'normal'.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
 This variable can be overridden with the -u|--untracked-files option
 of linkgit:git-status[1] and linkgit:git-commit[1].
 
 status.submoduleSummary::
 	Defaults to false.
-	If this is set to a non zero number or true (identical to -1 or an
+	If this is set to a non-zero number or true (identical to -1 or an
 	unlimited number), the submodule summary will be enabled and a
 	summary of commits for modified submodules will be shown (see
 	--summary-limit option of linkgit:git-submodule[1]). Please note
diff --git a/Documentation/config/submodule.txt b/Documentation/config/submodule.txt
index 6490527..0672d99 100644
--- a/Documentation/config/submodule.txt
+++ b/Documentation/config/submodule.txt
@@ -2,7 +2,7 @@
 	The URL for a submodule. This variable is copied from the .gitmodules
 	file to the git config via 'git submodule init'. The user can change
 	the configured URL before obtaining the submodule via 'git submodule
-	update'. If neither submodule.<name>.active or submodule.active are
+	update'. If neither submodule.<name>.active nor submodule.active are
 	set, the presence of this variable is used as a fallback to indicate
 	whether the submodule is of interest to git commands.
 	See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
@@ -35,7 +35,7 @@
 	a submodule as modified. When set to "all", it will never be considered
 	modified (but it will nonetheless show up in the output of status and
 	commit when it has been staged), "dirty" will ignore all changes
-	to the submodules work tree and
+	to the submodule's work tree and
 	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.
diff --git a/Documentation/config/trace2.txt b/Documentation/config/trace2.txt
index fe1642f..3b6bca2 100644
--- a/Documentation/config/trace2.txt
+++ b/Documentation/config/trace2.txt
@@ -66,6 +66,6 @@
 
 trace2.maxFiles::
 	Integer.  When writing trace files to a target directory, do not
-	write additional traces if we would exceed this many files. Instead,
+	write additional traces if doing so would exceed this many files. Instead,
 	write a sentinel file that will block further tracing to this
 	directory. Defaults to 0, which disables this check.
diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt
index c3ac767..f1ce50f 100644
--- a/Documentation/config/transfer.txt
+++ b/Documentation/config/transfer.txt
@@ -7,7 +7,7 @@
 	and any other direct use of the configured URL.
 +
 Note that this is currently limited to detecting credentials in
-`remote.<name>.url` configuration, it won't detect credentials in
+`remote.<name>.url` configuration; it won't detect credentials in
 `remote.<name>.pushurl` configuration.
 +
 You might want to enable this to prevent inadvertent credentials
@@ -21,12 +21,12 @@
   system.
 * The git programs will pass the full URL to one another as arguments
   on the command-line, meaning the credentials will be exposed to other
-  users on OS's or systems that allow other users to see the full
+  unprivileged users on systems that allow them to see the full
   process list of other users. On linux the "hidepid" setting
   documented in procfs(5) allows for configuring this behavior.
 +
 If such concerns don't apply to you then you probably don't need to be
-concerned about credentials exposure due to storing that sensitive
+concerned about credentials exposure due to storing sensitive
 data in git's configuration files. If you do want to use this, set
 `transfer.credentialsInUrl` to one of these values:
 +
@@ -121,3 +121,7 @@
 	information from the remote server (if advertised) and download
 	bundles before continuing the clone through the Git protocol.
 	Defaults to `false`.
+
+transfer.advertiseObjectInfo::
+	When `true`, the `object-info` capability is advertised by
+	servers. Defaults to false.
diff --git a/Documentation/config/user.txt b/Documentation/config/user.txt
index ec9233b..2ffc38d 100644
--- a/Documentation/config/user.txt
+++ b/Documentation/config/user.txt
@@ -5,14 +5,14 @@
 committer.name::
 committer.email::
 	The `user.name` and `user.email` variables determine what ends
-	up in the `author` and `committer` field of commit
+	up in the `author` and `committer` fields of commit
 	objects.
 	If you need the `author` or `committer` to be different, the
-	`author.name`, `author.email`, `committer.name` or
+	`author.name`, `author.email`, `committer.name`, or
 	`committer.email` variables can be set.
-	Also, all of these can be overridden by the `GIT_AUTHOR_NAME`,
+	All of these can be overridden by the `GIT_AUTHOR_NAME`,
 	`GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`,
-	`GIT_COMMITTER_EMAIL` and `EMAIL` environment variables.
+	`GIT_COMMITTER_EMAIL`, and `EMAIL` environment variables.
 +
 Note that the `name` forms of these variables conventionally refer to
 some form of a personal name.  See linkgit:git-commit[1] and the
@@ -40,7 +40,7 @@
 	your private ssh key or the public key when ssh-agent is used.
 	Alternatively it can contain a public key prefixed with `key::`
 	directly (e.g.: "key::ssh-rsa XXXXXX identifier"). The private key
-	needs to be available via ssh-agent. If not set git will call
+	needs to be available via ssh-agent. If not set Git will call
 	gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the
 	first key available. For backward compatibility, a raw key which
 	begins with "ssh-", such as "ssh-rsa XXXXXX identifier", is treated
diff --git a/Documentation/config/versionsort.txt b/Documentation/config/versionsort.txt
index 6c7cc05..0cff090 100644
--- a/Documentation/config/versionsort.txt
+++ b/Documentation/config/versionsort.txt
@@ -19,14 +19,14 @@
 configuration, then all "1.0-preX" tags will be listed before any
 "1.0-rcX" tags.  The placement of the main release tag relative to tags
 with various suffixes can be determined by specifying the empty suffix
-among those other suffixes.  E.g. if the suffixes "-rc", "", "-ck" and
+among those other suffixes.  E.g. if the suffixes "-rc", "", "-ck", and
 "-bfs" appear in the configuration in this order, then all "v4.8-rcX" tags
 are listed first, followed by "v4.8", then "v4.8-ckX" and finally
 "v4.8-bfsX".
 +
-If more than one suffixes match the same tagname, then that tagname will
+If more than one suffix matches the same tagname, then that tagname will
 be sorted according to the suffix which starts at the earliest position in
-the tagname.  If more than one different matching suffixes start at
+the tagname.  If more than one different matching suffix starts at
 that earliest position, then that tagname will be sorted according to the
 longest of those suffixes.
 The sorting order between different suffixes is undefined if they are
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index 546adf7..4b5aa5c 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -17,7 +17,7 @@
 What the -p option produces is slightly different from the traditional
 diff format:
 
-1.   It is preceded with a "git diff" header that looks like this:
+1.   It is preceded by a "git diff" header that looks like this:
 
        diff --git a/file1 b/file2
 +
@@ -25,9 +25,9 @@
 involved.  Especially, even for a creation or a deletion,
 `/dev/null` is _not_ used in place of the `a/` or `b/` filenames.
 +
-When rename/copy is involved, `file1` and `file2` show the
+When a rename/copy is involved, `file1` and `file2` show the
 name of the source file of the rename/copy and the name of
-the file that rename/copy produces, respectively.
+the file that the rename/copy produces, respectively.
 
 2.   It is followed by one or more extended header lines:
 
@@ -77,7 +77,7 @@
 
 5.  Hunk headers mention the name of the function to which the hunk
     applies.  See "Defining a custom hunk-header" in
-    linkgit:gitattributes[5] for details of how to tailor to this to
+    linkgit:gitattributes[5] for details of how to tailor this to
     specific languages.
 
 
@@ -89,7 +89,7 @@
 format when showing merges with linkgit:git-diff[1] or
 linkgit:git-show[1]. Note also that you can give suitable
 `--diff-merges` option to any of these commands to force generation of
-diffs in specific format.
+diffs in a specific format.
 
 A "combined diff" format looks like this:
 
@@ -123,7 +123,7 @@
 		for_each_ref(get_name);
 ------------
 
-1.   It is preceded with a "git diff" header, that looks like
+1.   It is preceded by a "git diff" header, that looks like
      this (when the `-c` option is used):
 
        diff --combined file
@@ -142,22 +142,22 @@
 +
 The `mode <mode>,<mode>..<mode>` line appears only if at least one of
 the <mode> is different from the rest. Extended headers with
-information about detected contents movement (renames and
-copying detection) are designed to work with diff of two
+information about detected content movement (renames and
+copying detection) are designed to work with the diff of two
 <tree-ish> and are not used by combined diff format.
 
-3.   It is followed by two-line from-file/to-file header
+3.   It is followed by a two-line from-file/to-file header:
 
        --- a/file
        +++ b/file
 +
-Similar to two-line header for traditional 'unified' diff
+Similar to the two-line header for the traditional 'unified' diff
 format, `/dev/null` is used to signal created or deleted
 files.
 +
 However, if the --combined-all-paths option is provided, instead of a
-two-line from-file/to-file you get a N+1 line from-file/to-file header,
-where N is the number of parents in the merge commit
+two-line from-file/to-file, you get an N+1 line from-file/to-file header,
+where N is the number of parents in the merge commit:
 
        --- a/file
        --- a/file
@@ -197,7 +197,7 @@
 In the above example output, the function signature was changed
 from both files (hence two `-` removals from both file1 and
 file2, plus `++` to mean one line that was added does not appear
-in either file1 or file2).  Also eight other lines are the same
+in either file1 or file2).  Also, eight other lines are the same
 from file1 but do not appear in file2 (hence prefixed with `+`).
 
 When shown by `git diff-tree -c`, it compares the parents of a
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 7d73e97..0e94569 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -22,84 +22,94 @@
 -p::
 -u::
 --patch::
-	Generate patch (see section titled
-ifdef::git-log[]
-<<generate_patch_text_with_p, "Generating patch text with -p">>).
-endif::git-log[]
-ifndef::git-log[]
-"Generating patch text with -p").
-endif::git-log[]
+	Generate patch (see <<generate_patch_text_with_p>>).
 ifdef::git-diff[]
 	This is the default.
 endif::git-diff[]
 
 -s::
 --no-patch::
-	Suppress diff output. Useful for commands like `git show` that
-	show the patch by default, or to cancel the effect of `--patch`.
+	Suppress all output from the diff machinery.  Useful for
+	commands like `git show` that show the patch by default to
+	squelch their output, or to cancel the effect of options like
+	`--patch`, `--stat` earlier on the command line in an alias.
+
 endif::git-format-patch[]
 
 ifdef::git-log[]
---diff-merges=(off|none|on|first-parent|1|separate|m|combined|c|dense-combined|cc|remerge|r)::
+-m::
+	Show diffs for merge commits in the default format. This is
+	similar to '--diff-merges=on', except `-m` will
+	produce no output unless `-p` is given as well.
+
+-c::
+	Produce combined diff output for merge commits.
+	Shortcut for '--diff-merges=combined -p'.
+
+--cc::
+	Produce dense combined diff output for merge commits.
+	Shortcut for '--diff-merges=dense-combined -p'.
+
+--dd::
+	Produce diff with respect to first parent for both merge and
+	regular commits.
+	Shortcut for '--diff-merges=first-parent -p'.
+
+--remerge-diff::
+	Produce remerge-diff output for merge commits.
+	Shortcut for '--diff-merges=remerge -p'.
+
 --no-diff-merges::
+	Synonym for '--diff-merges=off'.
+
+--diff-merges=<format>::
 	Specify diff format to be used for merge commits. Default is
-	{diff-merges-default} unless `--first-parent` is in use, in which case
-	`first-parent` is the default.
+	{diff-merges-default} unless `--first-parent` is in use, in
+	which case `first-parent` is the default.
 +
---diff-merges=(off|none):::
---no-diff-merges:::
+The following formats are supported:
++
+--
+off, none::
 	Disable output of diffs for merge commits. Useful to override
 	implied value.
 +
---diff-merges=on:::
---diff-merges=m:::
--m:::
-	This option makes diff output for merge commits to be shown in
-	the default format. `-m` will produce the output only if `-p`
-	is given as well. The default format could be changed using
-	`log.diffMerges` configuration parameter, which default value
+on, m::
+	Make diff output for merge commits to be shown in the default
+	format. The default format can be changed using
+	`log.diffMerges` configuration variable, whose default value
 	is `separate`.
 +
---diff-merges=first-parent:::
---diff-merges=1:::
-	This option makes merge commits show the full diff with
-	respect to the first parent only.
+first-parent, 1::
+	Show full diff with respect to first parent. This is the same
+	format as `--patch` produces for non-merge commits.
 +
---diff-merges=separate:::
-	This makes merge commits show the full diff with respect to
-	each of the parents. Separate log entry and diff is generated
-	for each parent.
+separate::
+	Show full diff with respect to each of parents.
+	Separate log entry and diff is generated for each parent.
 +
---diff-merges=remerge:::
---diff-merges=r:::
---remerge-diff:::
-	With this option, two-parent merge commits are remerged to
-	create a temporary tree object -- potentially containing files
-	with conflict markers and such.  A diff is then shown between
-	that temporary tree and the actual merge commit.
+combined, c::
+	Show differences from each of the parents to the merge
+	result simultaneously instead of showing pairwise diff between
+	a parent and the result one at a time. Furthermore, it lists
+	only files which were modified from all parents.
++
+dense-combined, cc::
+	Further compress output produced by `--diff-merges=combined`
+	by omitting uninteresting hunks whose contents in the parents
+	have only two variants and the merge result picks one of them
+	without modification.
++
+remerge, r::
+	Remerge two-parent merge commits to create a temporary tree
+	object--potentially containing files with conflict markers
+	and such.  A diff is then shown between that temporary tree
+	and the actual merge commit.
 +
 The output emitted when this option is used is subject to change, and
 so is its interaction with other options (unless explicitly
 documented).
-+
---diff-merges=combined:::
---diff-merges=c:::
--c:::
-	With this option, diff output for a merge commit shows the
-	differences from each of the parents to the merge result
-	simultaneously instead of showing pairwise diff between a
-	parent and the result one at a time. Furthermore, it lists
-	only files which were modified from all parents. `-c` implies
-	`-p`.
-+
---diff-merges=dense-combined:::
---diff-merges=cc:::
---cc:::
-	With this option the output produced by
-	`--diff-merges=combined` is further compressed by omitting
-	uninteresting hunks whose contents in the parents have only
-	two variants and the merge result picks one of them without
-	modification.  `--cc` implies `-p`.
+--
 
 --combined-all-paths::
 	This flag causes combined diffs (used for merge commits) to
@@ -207,14 +217,15 @@
 	part. Maximum width defaults to terminal width, or 80 columns
 	if not connected to a terminal, and can be overridden by
 	`<width>`. The width of the filename part can be limited by
-	giving another width `<name-width>` after a comma. The width
-	of the graph part can be limited by using
-	`--stat-graph-width=<width>` (affects all commands generating
-	a stat graph) or by setting `diff.statGraphWidth=<width>`
-	(does not affect `git format-patch`).
-	By giving a third parameter `<count>`, you can limit the
-	output to the first `<count>` lines, followed by `...` if
-	there are more.
+	giving another width `<name-width>` after a comma or by setting
+	`diff.statNameWidth=<width>`. The width of the graph part can be
+	limited by using `--stat-graph-width=<width>` or by setting
+	`diff.statGraphWidth=<width>`. Using `--stat` or
+	`--stat-graph-width` affects all commands generating a stat graph,
+	while setting `diff.statNameWidth` or `diff.statGraphWidth`
+	does not affect `git format-patch`.
+	By giving a third parameter `<count>`, you can limit the output to
+	the first `<count>` lines, followed by `...` if there are more.
 +
 These parameters can also be set individually with `--stat-width=<width>`,
 `--stat-name-width=<name-width>` and `--stat-count=<count>`.
@@ -288,7 +299,7 @@
 	Synonym for --dirstat=cumulative
 
 --dirstat-by-file[=<param1,param2>...]::
-	Synonym for --dirstat=files,param1,param2...
+	Synonym for --dirstat=files,<param1>,<param2>...
 
 --summary::
 	Output a condensed summary of extended header information
@@ -303,7 +314,7 @@
 
 -z::
 ifdef::git-log[]
-	Separate the commits with NULs instead of with new newlines.
+	Separate the commits with NULs instead of newlines.
 +
 Also, when `--raw` or `--numstat` has been given, do not munge
 pathnames and use NULs as output field terminators.
@@ -735,7 +746,7 @@
 --rotate-to=<file>::
 	Discard the files before the named <file> from the output
 	(i.e. 'skip to'), or move them to the end of the output
-	(i.e. 'rotate to').  These were invented primarily for use
+	(i.e. 'rotate to').  These options were invented primarily for the use
 	of the `git difftool` command, and may not be very useful
 	otherwise.
 
@@ -852,6 +863,12 @@
 --no-prefix::
 	Do not show any source or destination prefix.
 
+--default-prefix::
+	Use the default source and destination prefixes ("a/" and "b/").
+	This overrides configuration variables such as `diff.noprefix`,
+	`diff.srcPrefix`, `diff.dstPrefix`, and `diff.mnemonicPrefix`
+	(see `git-config`(1)).
+
 --line-prefix=<prefix>::
 	Prepend an additional prefix to every line of output.
 
diff --git a/Documentation/doc-diff b/Documentation/doc-diff
index 1694300..fb09e0a 100755
--- a/Documentation/doc-diff
+++ b/Documentation/doc-diff
@@ -153,7 +153,7 @@
 		make -j$parallel -C "$tmp/worktree" \
 			$makemanflags \
 			GIT_VERSION=omitted \
-			SOURCE_DATE_EPOCH=0 \
+			GIT_DATE=1970-01-01 \
 			DESTDIR="$tmp/installed/$dname+" \
 			install-man &&
 		mv "$tmp/installed/$dname+" "$tmp/installed/$dname"
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 622bd84..e22b217 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -1,5 +1,6 @@
---all::
-	Fetch all remotes.
+--[no-]all::
+	Fetch all remotes. This overrides the configuration variable
+	`fetch.all`.
 
 -a::
 --append::
@@ -43,7 +44,7 @@
 --update-shallow::
 	By default when fetching from a shallow repository,
 	`git fetch` refuses refs that require updating
-	.git/shallow. This option updates .git/shallow and accept such
+	.git/shallow. This option updates .git/shallow and accepts such
 	refs.
 
 --negotiation-tip=<commit|glob>::
@@ -78,6 +79,13 @@
 --dry-run::
 	Show what would be done, without making any changes.
 
+--porcelain::
+	Print the output to standard output in an easy-to-parse format for
+	scripts. See section OUTPUT in linkgit:git-fetch[1] for details.
++
+This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes
+precedence over the `fetch.output` config option.
+
 ifndef::git-pull[]
 --[no-]write-fetch-head::
 	Write the list of remote refs fetched in the `FETCH_HEAD`
@@ -89,7 +97,7 @@
 
 -f::
 --force::
-	When 'git fetch' is used with `<src>:<dst>` refspec it may
+	When 'git fetch' is used with `<src>:<dst>` refspec, it may
 	refuse to update the local branch as discussed
 ifdef::git-pull[]
 	in the `<refspec>` part of the linkgit:git-fetch[1]
@@ -194,7 +202,7 @@
 	destination of an explicit refspec; see `--prune`).
 
 ifndef::git-pull[]
---recurse-submodules[=yes|on-demand|no]::
+--recurse-submodules[=(yes|on-demand|no)]::
 	This option controls if and under what conditions new commits of
 	submodules should be fetched too. When recursing through submodules,
 	`git fetch` always attempts to fetch "changed" submodules, that is, a
diff --git a/Documentation/fsck-msgids.txt b/Documentation/fsck-msgids.txt
index 12eae8a..f643585 100644
--- a/Documentation/fsck-msgids.txt
+++ b/Documentation/fsck-msgids.txt
@@ -103,6 +103,13 @@
 `hasDotgit`::
 	(WARN) A tree contains an entry named `.git`.
 
+`largePathname`::
+	(WARN) A tree contains an entry with a very long path name. If
+	the value of `fsck.largePathname` contains a colon, that value
+	is used as the maximum allowable length (e.g., "warn:10" would
+	complain about any path component of 11 or more bytes). The
+	default value is 4096.
+
 `mailmapSymlink`::
 	(INFO) `.mailmap` is a symlink.
 
@@ -125,7 +132,7 @@
 	(ERROR) Missing space before date in an author/committer line.
 
 `missingSpaceBeforeEmail`::
-	(ERROR) Missing space before the email in author/committer line.
+	(ERROR) Missing space before the email in an author/committer line.
 
 `missingTag`::
 	(ERROR) Unexpected end after `type` line in a tag object.
@@ -167,7 +174,7 @@
 	(FATAL) Missing end-of-line in the object header.
 
 `zeroPaddedDate`::
-	(ERROR) Found a zero padded date in an author/commiter line.
+	(ERROR) Found a zero padded date in an author/committer line.
 
 `zeroPaddedFilemode`::
 	(WARN) Found a zero padded filemode in a tree.
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ed44c1c..aceaa02 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -9,7 +9,7 @@
 --------
 [verse]
 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
-	  [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse]
+	  [--edit | -e] [--[no-]all | -A | --[no-]ignore-removal | [--update | -u]] [--sparse]
 	  [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
 	  [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
 	  [--] [<pathspec>...]
@@ -63,7 +63,7 @@
 	to ignore removed files; use `--no-all` option if you want
 	to add modified or new files but ignore removed ones.
 +
-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].
 
 -n::
@@ -119,10 +119,10 @@
 -u::
 --update::
 	Update the index just where it already has an entry matching
-	<pathspec>.  This removes as well as modifies index entries to
+	_<pathspec>_.  This removes as well as modifies index entries to
 	match the working tree, but adds no new files.
 +
-If no <pathspec> is given when `-u` option is used, all
+If no _<pathspec>_ is given when `-u` option is used, all
 tracked files in the entire working tree are updated (old versions
 of Git used to limit the update to the current directory and its
 subdirectories).
@@ -131,11 +131,11 @@
 --all::
 --no-ignore-removal::
 	Update the index not only where the working tree has a file
-	matching <pathspec> but also where the index already has an
+	matching _<pathspec>_ but also where the index already has an
 	entry. This adds, modifies, and removes index entries to
 	match the working tree.
 +
-If no <pathspec> is given when `-A` option is used, all
+If no _<pathspec>_ is given when `-A` option is used, all
 files in the entire working tree are updated (old versions
 of Git used to limit the update to the current directory and its
 subdirectories).
@@ -145,11 +145,11 @@
 	Update the index by adding new files that are unknown to the
 	index and files modified in the working tree, but ignore
 	files that have been removed from the working tree.  This
-	option is a no-op when no <pathspec> is used.
+	option is a no-op when no _<pathspec>_ is used.
 +
 This option is primarily to help users who are used to older
-versions of Git, whose "git add <pathspec>..." was a synonym
-for "git add --no-all <pathspec>...", i.e. ignored removed files.
+versions of Git, whose "git add _<pathspec>_..." was a synonym
+for "git add --no-all _<pathspec>_...", i.e. ignored removed files.
 
 -N::
 --intent-to-add::
@@ -198,8 +198,8 @@
 	unchanged.
 
 --pathspec-from-file=<file>::
-	Pathspec is passed in `<file>` instead of commandline args. If
-	`<file>` is exactly `-` then standard input is used. Pathspec
+	Pathspec is passed in _<file>_ instead of commandline args. If
+	_<file>_ is exactly `-` then standard input is used. Pathspec
 	elements are separated by LF or CR/LF. Pathspec elements can be
 	quoted as explained for the configuration variable `core.quotePath`
 	(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
@@ -348,6 +348,7 @@
        K - leave this hunk undecided, see previous hunk
        s - split the current hunk into smaller hunks
        e - manually edit the current hunk
+       p - print the current hunk
        ? - print help
 +
 After deciding the fate for all hunks, if there is any hunk
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0c1dfb3..624a6e6 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -12,7 +12,7 @@
 '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=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
+	 [--whitespace=<action>] [-C<n>] [-p<n>] [--directory=<dir>]
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
@@ -22,9 +22,11 @@
 
 DESCRIPTION
 -----------
-Splits mail messages in a mailbox into commit log message,
-authorship information and patches, and applies them to the
-current branch.
+Splits mail messages in a mailbox into commit log messages,
+authorship information, and patches, and applies them to the
+current branch. You could think of it as a reverse operation
+of linkgit:git-format-patch[1] run on a branch with a straight
+history without merges.
 
 OPTIONS
 -------
@@ -64,13 +66,19 @@
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
---empty=(stop|drop|keep)::
-	By default, or when the option is set to 'stop', the command
-	errors out on an input e-mail message lacking a patch
-	and stops into the middle of the current am session. When this
-	option is set to 'drop', skip such an e-mail message instead.
-	When this option is set to 'keep', create an empty commit,
-	recording the contents of the e-mail message as its log.
+--empty=(drop|keep|stop)::
+	How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+	The e-mail message will be skipped.
+`keep`;;
+	An empty commit will be created, with the contents of the e-mail
+	message as its log.
+`stop`;;
+	The command will fail, stopping in the middle of the current `am`
+	session. This is the default behavior.
+--
 
 -m::
 --message-id::
@@ -92,7 +100,7 @@
 	Pass `-u` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 	The proposed commit log message taken from the e-mail
 	is re-coded into UTF-8 encoding (configuration variable
-	`i18n.commitEncoding` can be used to specify project's
+	`i18n.commitEncoding` can be used to specify the project's
 	preferred encoding if it is not UTF-8).
 +
 This was optional in prior versions of git, but now it is the
@@ -116,7 +124,7 @@
 
 --ignore-space-change::
 --ignore-whitespace::
---whitespace=<option>::
+--whitespace=<action>::
 -C<n>::
 -p<n>::
 --directory=<dir>::
@@ -126,13 +134,16 @@
 	These flags are passed to the 'git apply' (see linkgit:git-apply[1])
 	program that applies
 	the patch.
++
+Valid <action> for the `--whitespace` option are:
+`nowarn`, `warn`, `fix`, `error`, and `error-all`.
 
 --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.
+	stgit, stgit-series, and hg.
 
 -i::
 --interactive::
@@ -190,7 +201,7 @@
 
 --abort::
 	Restore the original branch and abort the patching operation.
-	Revert contents of files involved in the am operation to their
+	Revert the contents of files involved in the am operation to their
 	pre-am state.
 
 --quit::
@@ -273,7 +284,8 @@
 
 SEE ALSO
 --------
-linkgit:git-apply[1].
+linkgit:git-apply[1],
+linkgit:git-format-patch[1].
 
 GIT
 ---
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 5e16e6d..9cce68a 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -23,8 +23,8 @@
 Reads the supplied diff output (i.e. "a patch") and applies it to files.
 When running from a subdirectory in a repository, patched paths
 outside the directory are ignored.
-With the `--index` option the patch is also applied to the index, and
-with the `--cached` option the patch is only applied to the index.
+With the `--index` option, the patch is also applied to the index, and
+with the `--cached` option, the patch is only applied to the index.
 Without these options, the command applies the patch only to files,
 and does not require them to be in a Git repository.
 
@@ -52,7 +52,7 @@
 --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.
+	headers, such as creations, renames, and mode changes.
 	Turns off "apply".
 
 --check::
@@ -140,7 +140,7 @@
 	applying a diff generated with `--unified=0`. To bypass these
 	checks use `--unidiff-zero`.
 +
-Note, for the reasons stated above usage of context-free patches is
+Note, for the reasons stated above, the usage of context-free patches is
 discouraged.
 
 --apply::
@@ -159,9 +159,9 @@
 
 --allow-binary-replacement::
 --binary::
-	Historically we did not allow binary patch applied
+	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
+	flag was the way to do so.  Currently, we always allow binary
 	patch application, so this is a no-op.
 
 --exclude=<path-pattern>::
@@ -257,7 +257,7 @@
 has no effect when `--index` or `--cached` is in use.
 
 --allow-empty::
-	Don't return error for patches containing no diff. This includes
+	Don't return an error for patches containing no diff. This includes
 	empty patches and patches with commit text only.
 
 CONFIGURATION
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index 6bab201..98526f2 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -21,14 +21,14 @@
 output.  If <prefix> is specified it is
 prepended to the filenames in the archive.
 
-'git archive' behaves differently when given a tree ID versus when
-given a commit ID or tag ID.  In the first case the current time is
-used as the modification time of each file in the archive.  In the latter
-case the commit time as recorded in the referenced commit object is
-used instead.  Additionally the commit ID is stored in a global
-extended pax header if the tar format is used; it can be extracted
-using 'git get-tar-commit-id'. In ZIP files it is stored as a file
-comment.
+'git archive' behaves differently when given a tree ID as opposed to a
+commit ID or tag ID. When a tree ID is provided, the current time is
+used as the modification time of each file in the archive. On the
+other hand, when a commit ID or tag ID is provided, the commit time as
+recorded in the referenced commit object is used instead.
+Additionally the commit ID is stored in a global extended pax header
+if the tar format is used; it can be extracted using 'git
+get-tar-commit-id'. In ZIP files it is stored as a file comment.
 
 OPTIONS
 -------
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index fbb39fb..82f944d 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -16,17 +16,17 @@
 The command takes various subcommands, and different options depending
 on the subcommand:
 
- git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
-		  [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
+ 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 | --term-bad]
+ git bisect terms [--term-(good|old) | --term-(bad|new)]
  git bisect skip [(<rev>|<range>)...]
  git bisect reset [<commit>]
  git bisect (visualize|view)
  git bisect replay <logfile>
  git bisect log
- git bisect run <cmd>...
+ git bisect run <cmd> [<arg>...]
  git bisect help
 
 This command uses a binary search algorithm to find which commit in
@@ -165,8 +165,10 @@
 git bisect terms
 ------------------------------------------------
 
-You can get just the old (respectively new) term with `git bisect terms
---term-old` or `git bisect terms --term-good`.
+You can get just the old term with `git bisect terms --term-old`
+or `git bisect terms --term-good`; `git bisect terms --term-new`
+and `git bisect terms --term-bad` can be used to learn how to call
+the commits more recent than the sought change.
 
 If you would like to use your own terms instead of "bad"/"good" or
 "new"/"old", you can choose any names you like (except existing bisect
@@ -204,9 +206,14 @@
 $ git bisect visualize
 ------------
 
-If the `DISPLAY` environment variable is not set, 'git log' is used
-instead.  You can also give command-line options such as `-p` and
-`--stat`.
+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.
+You can also give command-line options such as `-p` and `--stat`.
 
 ------------
 $ git bisect visualize --stat
@@ -294,7 +301,7 @@
 
 You can further cut down the number of trials, if you know what part of
 the tree is involved in the problem you are tracking down, by specifying
-path parameters when issuing the `bisect start` command:
+pathspec parameters when issuing the `bisect start` command:
 
 ------------
 $ git bisect start -- arch/i386 include/asm-i386
@@ -357,7 +364,7 @@
 --no-checkout::
 +
 Do not checkout the new working tree at each iteration of the bisection
-process. Instead just update a special reference named `BISECT_HEAD` to make
+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
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 4400a17..b1d7fb5 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -12,7 +12,7 @@
 	    [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
 	    [--ignore-rev <rev>] [--ignore-revs-file <file>]
 	    [--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
-	    [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
+	    [ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>
 
 DESCRIPTION
 -----------
@@ -77,7 +77,7 @@
 
 -e::
 --show-email::
-	Show the author email instead of author name (Default: off).
+	Show the author email instead of the author name (Default: off).
 	This can also be controlled via the `blame.showEmail` config
 	option.
 
@@ -100,7 +100,7 @@
 `git blame` will output annotation for each line with:
 
 - abbreviated object name for the commit the line came from;
-- author ident (by default author name and date, unless `-s` or `-e`
+- author ident (by default the author name and date, unless `-s` or `-e`
   is specified); and
 - line number
 
@@ -128,7 +128,7 @@
 - the filename in the commit that the line is attributed to.
 - the first line of the commit log message ("summary").
 
-The contents of the actual line is output after the above
+The contents of the actual line are output after the above
 header, prefixed by a TAB. This is to allow adding more
 header elements later.
 
@@ -170,7 +170,7 @@
 
 When you are not interested in changes older than version
 v2.6.18, or changes older than 3 weeks, you can use revision
-range specifiers  similar to 'git rev-list':
+range specifiers similar to 'git rev-list':
 
 	git blame v2.6.18.. -- foo
 	git blame --since=3.weeks -- foo
@@ -210,7 +210,7 @@
 
 . Each blame entry always starts with a line of:
 
-	<40-byte hex sha1> <sourceline> <resultline> <num_lines>
+	<40-byte-hex-sha1> <sourceline> <resultline> <num-lines>
 +
 Line numbers count from 1.
 
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index d382ac6..0b08442 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -156,6 +156,10 @@
 --ignore-case::
 	Sorting and filtering branches are case insensitive.
 
+--omit-empty::
+	Do not print a newline after formatted refs where the format expands
+	to the empty string.
+
 --column[=<options>]::
 --no-column::
 	Display branch listing in columns. See configuration variable
@@ -308,7 +312,8 @@
 	option is omitted, the current HEAD will be used instead.
 
 <oldbranch>::
-	The name of an existing branch to rename.
+	The name of an existing branch.  If this option is omitted,
+	the name of the current branch will be used instead.
 
 <newbranch>::
 	The new name for an existing branch. The same restrictions as for
@@ -320,7 +325,7 @@
 	multiple times, in which case the last key becomes the primary
 	key. The keys supported are the same as those in `git
 	for-each-ref`. Sort order defaults to the value configured for the
-	`branch.sort` variable if exists, or to sorting based on the
+	`branch.sort` variable if it exists, or to sorting based on the
 	full refname (including `refs/...` prefix). This lists
 	detached HEAD (if present) first, then local branches and
 	finally remote-tracking branches. See linkgit:git-config[1].
diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt
index eca726e..112658b 100644
--- a/Documentation/git-bugreport.txt
+++ b/Documentation/git-bugreport.txt
@@ -8,15 +8,17 @@
 SYNOPSIS
 --------
 [verse]
-'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
+'git bugreport' [(-o | --output-directory) <path>]
+		[(-s | --suffix) <format> | --no-suffix]
 		[--diagnose[=<mode>]]
 
 DESCRIPTION
 -----------
-Captures information about the user's machine, Git client, and repository state,
-as well as a form requesting information about the behavior the user observed,
-into a single text file which the user can then share, for example to the Git
-mailing list, in order to report an observed bug.
+Collects information about the user's machine, Git client, and repository
+state, in addition to a form requesting information about the behavior the
+user observed, and stores it in a single text file which the user can then
+share, for example to the Git mailing list, in order to report an observed
+bug.
 
 The following information is requested from the user:
 
@@ -50,16 +52,19 @@
 
 -s <format>::
 --suffix <format>::
+--no-suffix::
 	Specify an alternate suffix for the bugreport name, to create a file
-	named 'git-bugreport-<formatted suffix>'. This should take the form of a
+	named 'git-bugreport-<formatted-suffix>'. This should take the form of a
 	strftime(3) format string; the current local time will be used.
+	`--no-suffix` disables the suffix and the file is just named
+	`git-bugreport` without any disambiguation measure.
 
 --no-diagnose::
 --diagnose[=<mode>]::
 	Create a zip archive of supplemental information about the user's
 	machine, Git client, and repository state. The archive is written to the
 	same output directory as the bug report and is named
-	'git-diagnostics-<formatted suffix>'.
+	'git-diagnostics-<formatted-suffix>'.
 +
 Without `mode` specified, the diagnostic archive will contain the default set of
 statistics reported by `git diagnose`. An optional `mode` value may be specified
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index 18a022b..3ab42a1 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied]
+'git bundle' create [-q | --quiet | --progress]
 		    [--version=<version>] <file> <git-rev-list-args>
 'git bundle' verify [-q | --quiet] <file>
 'git bundle' list-heads <file> [<refname>...]
@@ -66,7 +66,7 @@
 	Used to create a bundle named 'file'.  This requires the
 	'<git-rev-list-args>' arguments to define the bundle contents.
 	'options' contains the options specific to the 'git bundle create'
-	subcommand.
+	subcommand. If 'file' is `-`, the bundle is written to stdout.
 
 verify <file>::
 	Used to check that a bundle file is valid and will apply
@@ -77,12 +77,13 @@
 	Finally, information about additional capabilities, such as "object
 	filter", is printed. See "Capabilities" in linkgit:gitformat-bundle[5]
 	for more information. The exit code is zero for success, but will
-	be nonzero if the bundle file is invalid.
+	be nonzero if the bundle file is invalid. If 'file' is `-`, the
+	bundle is read from stdin.
 
 list-heads <file>::
 	Lists the references defined in the bundle.  If followed by a
 	list of references, only references matching those given are
-	printed out.
+	printed out. If 'file' is `-`, the bundle is read from stdin.
 
 unbundle <file>::
 	Passes the objects in the bundle to 'git index-pack'
@@ -90,6 +91,7 @@
 	defined references. If a list of references is given, only
 	references matching those in the list are printed. This command is
 	really plumbing, intended to be called only by 'git fetch'.
+	If 'file' is `-`, the bundle is read from stdin.
 
 <git-rev-list-args>::
 	A list of arguments, acceptable to 'git rev-parse' and
@@ -115,22 +117,6 @@
 	is specified. This flag forces progress status even if
 	the standard error stream is not directed to a terminal.
 
---all-progress::
-	When --stdout is specified then progress report is
-	displayed during the object count and compression phases
-	but inhibited during the write-out phase. The reason is
-	that in some cases the output stream is directly linked
-	to another command which may wish to display progress
-	status of its own as it processes incoming pack data.
-	This flag is like --progress except that it forces progress
-	report for the write-out phase as well even if --stdout is
-	used.
-
---all-progress-implied::
-	This is used to imply --all-progress whenever progress display
-	is activated.  Unlike --all-progress this flag doesn't actually
-	force any progress display by itself.
-
 --version=<version>::
 	Specify the bundle version.  Version 2 is the older format and can only be
 	used with SHA-1 repositories; the newer version 3 contains capabilities that
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 411de2e..bd95a6c 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -3,8 +3,7 @@
 
 NAME
 ----
-git-cat-file - Provide content or type and size information for repository objects
-
+git-cat-file - Provide contents or details of repository objects
 
 SYNOPSIS
 --------
@@ -12,25 +11,24 @@
 'git cat-file' <type> <object>
 'git cat-file' (-e | -p) <object>
 'git cat-file' (-t | -s) [--allow-unknown-type] <object>
-'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
-	     [--buffer] [--follow-symlinks] [--unordered]
-	     [--textconv | --filters] [-z]
 'git cat-file' (--textconv | --filters)
 	     [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
+'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
+	     [--buffer] [--follow-symlinks] [--unordered]
+	     [--textconv | --filters] [-Z]
 
 DESCRIPTION
 -----------
-In its first form, the command provides the content or the type of an object in
-the repository. The type is required unless `-t` or `-p` is used to find the
-object type, or `-s` is used to find the object size, or `--textconv` or
-`--filters` is used (which imply type "blob").
+Output the contents or other properties such as size, type or delta
+information of one or more objects.
 
-In the second form, a list of objects (separated by linefeeds) is provided on
-stdin, and the SHA-1, type, and size of each object is printed on stdout. The
-output format can be overridden using the optional `<format>` argument. If
-either `--textconv` or `--filters` was specified, the input is expected to
-list the object names followed by the path name, separated by a single
-whitespace, so that the appropriate drivers can be determined.
+This command can operate in two modes, depending on whether an option
+from the `--batch` family is specified.
+
+In non-batch mode, the command provides information on an object
+named on the command line.
+
+In batch mode, arguments are read from standard input.
 
 OPTIONS
 -------
@@ -51,8 +49,8 @@
 
 -e::
 	Exit with zero status if `<object>` exists and is a valid
-	object. If `<object>` is of an invalid format exit with non-zero and
-	emits an error on stderr.
+	object. If `<object>` is of an invalid format, exit with non-zero
+	status and emit an error on stderr.
 
 -p::
 	Pretty-print the contents of `<object>` based on its type.
@@ -243,10 +241,16 @@
 	/etc/passwd
 --
 
+-Z::
+	Only meaningful with `--batch`, `--batch-check`, or
+	`--batch-command`; input and output is NUL-delimited instead of
+	newline-delimited.
+
 -z::
 	Only meaningful with `--batch`, `--batch-check`, or
 	`--batch-command`; input is NUL-delimited instead of
-	newline-delimited.
+	newline-delimited. This option is deprecated in favor of
+	`-Z` as the output can otherwise be ambiguous.
 
 
 OUTPUT
@@ -384,6 +388,11 @@
 is printed when, during symlink resolution, a file is used as a
 directory name.
 
+Alternatively, when `-Z` is passed, the line feeds in any of the above examples
+are replaced with NUL terminators. This ensures that output will be parsable if
+the output itself would contain a linefeed and is thus recommended for
+scripting purposes.
+
 CAVEATS
 -------
 
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index 6e4f3aa..cb5a6c8 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -29,7 +29,7 @@
 
 --stdin::
 	Read pathnames from the standard input, one per line,
-	instead of from the command-line.
+	instead of from the command line.
 
 -z::
 	The output format is modified to be machine-parsable.
@@ -38,7 +38,7 @@
 
 --source=<tree-ish>::
 	Check attributes against the specified tree-ish. It is common to
-	specify the source tree by naming a commit, branch or tag associated
+	specify the source tree by naming a commit, branch, or tag associated
 	with it.
 
 \--::
@@ -60,7 +60,7 @@
 
 
 <path> is the path of a file being queried, <attribute> is an attribute
-being queried and <info> can be either:
+being queried, and <info> can be either:
 
 'unspecified';; when the attribute is not defined for the path.
 'unset';;	when the attribute is defined as false.
diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt
index 2892799..3e3b4e3 100644
--- a/Documentation/git-check-ignore.txt
+++ b/Documentation/git-check-ignore.txt
@@ -50,7 +50,7 @@
 	with a NUL character instead of a linefeed character.
 
 -n, --non-matching::
-	Show given paths which don't match any pattern.	 This only
+	Show given paths which don't match any pattern.  This only
 	makes sense when `--verbose` is enabled, otherwise it would
 	not be possible to distinguish between paths which match a
 	pattern and those which don't.
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index ee6a414..2aacfd1 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -48,7 +48,7 @@
 
 . They cannot begin or end with a slash `/` or contain multiple
   consecutive slashes (see the `--normalize` option below for an
-  exception to this rule)
+  exception to this rule).
 
 . They cannot end with a dot `.`.
 
@@ -85,7 +85,7 @@
 may be stricter than what `git check-ref-format refs/heads/$name`
 says (e.g. a dash may appear at the beginning of a ref component,
 but it is explicitly forbidden at the beginning of a branch name).
-When run with `--branch` option in a repository, the input is first
+When run with the `--branch` option in a repository, the input is first
 expanded for the ``previous checkout syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
 was checked out using "git switch" or "git checkout" operation.
diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt
index 01dbd5c..faf8d6c 100644
--- a/Documentation/git-checkout-index.txt
+++ b/Documentation/git-checkout-index.txt
@@ -18,7 +18,7 @@
 
 DESCRIPTION
 -----------
-Will copy all files listed from the index to the working directory
+Copies all listed files from the index to the working directory
 (not overwriting existing files).
 
 OPTIONS
@@ -53,11 +53,11 @@
 
 --stage=<number>|all::
 	Instead of checking out unmerged entries, copy out the
-	files from named stage.  <number> must be between 1 and 3.
+	files from the named stage.  <number> must be between 1 and 3.
 	Note: --stage=all automatically implies --temp.
 
 --temp::
-	Instead of copying the files to the working directory
+	Instead of copying the files to the working directory,
 	write the content to temporary files.  The temporary name
 	associations will be written to stdout.
 
@@ -66,8 +66,8 @@
 	set.
 
 --stdin::
-	Instead of taking list of paths from the command line,
-	read list of paths from the standard input.  Paths are
+	Instead of taking a list of paths from the command line,
+	read the list of paths from the standard input.  Paths are
 	separated by LF (i.e. one path per line) by default.
 
 -z::
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 6bb32ab..8bdfa54 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -12,8 +12,10 @@
 'git checkout' [-q] [-f] [-m] --detach [<branch>]
 'git checkout' [-q] [-f] [-m] [--detach] <commit>
 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>]
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]
+'git checkout' [-f] <tree-ish> [--] <pathspec>...
+'git checkout' [-f] <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul]
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>...
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] --pathspec-from-file=<file> [--pathspec-file-nul]
 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]
 
 DESCRIPTION
@@ -41,7 +43,7 @@
 You could omit `<branch>`, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
-if exists, for the current branch.
+if it exists, for the current branch.
 
 'git checkout' -b|-B <new-branch> [<start-point>]::
 
@@ -61,7 +63,9 @@
 ------------
 +
 that is to say, the branch is not reset/created unless "git checkout" is
-successful.
+successful (e.g., when the branch is in use in another worktree, not
+just the current branch stays the same, but the branch is not reset to
+the start-point, either).
 
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
@@ -213,7 +217,7 @@
 	below for details.
 
 --orphan <new-branch>::
-	Create a new 'orphan' branch, named `<new-branch>`, started from
+	Create a new unborn branch, named `<new-branch>`, started from
 	`<start-point>` and switch to it.  The first commit made on this
 	new branch will have no parents and it will be the root of a new
 	history totally disconnected from all the other branches and
@@ -260,7 +264,8 @@
 should result in deletion of the path).
 +
 When checking out paths from the index, this option lets you recreate
-the conflicted merge in the specified paths.
+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.
 
@@ -483,14 +488,11 @@
 $ git branch foo                                 <2>
 $ git tag foo                                    <3>
 ------------
-
 <1> creates a new branch `foo`, which refers to commit `f`, and then
     updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
     be in detached `HEAD` state after this command.
-
 <2> similarly creates a new branch `foo`, which refers to commit `f`,
     but leaves `HEAD` detached.
-
 <3> creates a new tag `foo`, which refers to commit `f`,
     leaving `HEAD` detached.
 
@@ -519,84 +521,89 @@
 EXAMPLES
 --------
 
-. The following sequence checks out the `master` branch, reverts
-  the `Makefile` to two revisions back, deletes `hello.c` by
-  mistake, and gets it back from the index.
-+
+=== 1. Paths
+
+The following sequence checks out the `master` branch, reverts
+the `Makefile` to two revisions back, deletes `hello.c` by
+mistake, and gets it back from the index.
+
 ------------
 $ git checkout master             <1>
 $ git checkout master~2 Makefile  <2>
 $ rm -f hello.c
 $ git checkout hello.c            <3>
 ------------
-+
 <1> switch branch
 <2> take a file out of another commit
 <3> restore `hello.c` from the index
-+
+
 If you want to check out _all_ C source files out of the index,
 you can say
-+
+
 ------------
 $ git checkout -- '*.c'
 ------------
-+
+
 Note the quotes around `*.c`.  The file `hello.c` will also be
 checked out, even though it is no longer in the working tree,
 because the file globbing is used to match entries in the index
 (not in the working tree by the shell).
-+
+
 If you have an unfortunate branch that is named `hello.c`, this
 step would be confused as an instruction to switch to that branch.
 You should instead write:
-+
+
 ------------
 $ git checkout -- hello.c
 ------------
 
-. After working in the wrong branch, switching to the correct
-  branch would be done using:
-+
+=== 2. Merge
+
+After working in the wrong branch, switching to the correct
+branch would be done using:
+
 ------------
 $ git checkout mytopic
 ------------
-+
+
 However, your "wrong" branch and correct `mytopic` branch may
 differ in files that you have modified locally, in which case
 the above checkout would fail like this:
-+
+
 ------------
 $ git checkout mytopic
 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:
-+
+
 ------------
 $ git checkout -m mytopic
 Auto-merging frotz
 ------------
-+
+
 After this three-way merge, the local modifications are _not_
 registered in your index file, so `git diff` would show you what
 changes you made since the tip of the new branch.
 
-. When a merge conflict happens during switching branches with
-  the `-m` option, you would see something like this:
-+
+=== 3. Merge conflict
+
+When a merge conflict happens during switching branches with
+the `-m` option, you would see something like this:
+
 ------------
 $ 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
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d..81ace90 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,20 +131,36 @@
 	even without this option.  Note also, that use of this option only
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
-	previous commit are dropped.  To force the inclusion of those commits
-	use `--keep-redundant-commits`.
+	previous commit will cause the cherry-pick to fail.  To force the
+	inclusion of those commits, use `--empty=keep`.
 
 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
 	This option overrides that behavior, allowing commits with empty
 	messages to be cherry picked.
 
+--empty=(drop|keep|stop)::
+	How to handle commits being cherry-picked that are redundant with
+	changes already in the current history.
++
+--
+`drop`;;
+	The commit will be dropped.
+`keep`;;
+	The commit will be kept. Implies `--allow-empty`.
+`stop`;;
+	The cherry-pick will stop when the commit is applied, allowing
+	you to examine the commit. This is the default behavior.
+--
++
+Note that `--empty=drop` and `--empty=stop` only specify how to handle a
+commit that was not initially empty, but rather became empty due to a previous
+commit. Commits that were initially empty will still cause the cherry-pick to
+fail unless one of `--empty=keep` or `--allow-empty` are specified.
++
+
 --keep-redundant-commits::
-	If a commit being cherry picked duplicates a commit already in the
-	current history, it will become empty.  By default these
-	redundant commits cause `cherry-pick` to stop so the user can
-	examine the commit. This option overrides that behavior and
-	creates an empty commit object.  Implies `--allow-empty`.
+	Deprecated synonym for `--empty=keep`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 160d08b..fd17165 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -37,7 +37,7 @@
 --force::
 	If the Git configuration variable clean.requireForce is not set
 	to false, 'git clean' will refuse to delete files or directories
-	unless given -f or -i.  Git will refuse to modify untracked
+	unless given -f.  Git will refuse to modify untracked
 	nested git repositories (directories with a .git subdirectory)
 	unless a second -f is given.
 
@@ -45,10 +45,14 @@
 --interactive::
 	Show what would be done and clean files interactively. See
 	``Interactive mode'' for details.
+	Configuration variable `clean.requireForce` is ignored, as
+	this mode gives its own safety protection by going interactive.
 
 -n::
 --dry-run::
 	Don't actually remove anything, just show what would be done.
+	Configuration variable `clean.requireForce` is ignored, as
+	nothing will be deleted anyway.
 
 -q::
 --quiet::
@@ -103,7 +107,7 @@
    This shows the files and directories to be deleted and issues an
    "Input ignore patterns>>" prompt. You can input space-separated
    patterns to exclude files and directories from deletion.
-   E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
+   E.g. "*.c *.h" will exclude files ending with ".c" and ".h" from
    deletion. When you are satisfied with the filtered result, press
    ENTER (empty) back to the main menu.
 
@@ -127,7 +131,7 @@
 
 quit::
 
-  This lets you quit without do cleaning.
+  This lets you quit without doing any cleaning.
 
 help::
 
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index d6434d2..5de18de 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,15 +9,15 @@
 SYNOPSIS
 --------
 [verse]
-'git clone' [--template=<template-directory>]
-	  [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
-	  [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
-	  [--dissociate] [--separate-git-dir <git-dir>]
-	  [--depth <depth>] [--[no-]single-branch] [--no-tags]
-	  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
-	  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
-	  [--filter=<filter> [--also-filter-submodules]] [--] <repository>
-	  [<directory>]
+`git clone` [++--template=++__<template-directory>__]
+	  [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`]
+	  [`-o` _<name>_] [`-b` _<name>_] [`-u` _<upload-pack>_] [`--reference` _<repository>_]
+	  [`--dissociate`] [`--separate-git-dir` _<git-dir>_]
+	  [`--depth` _<depth>_] [`--`[`no-`]`single-branch`] [`--no-tags`]
+	  [++--recurse-submodules++[++=++__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
+	  [`--`[`no-`]`remote-submodules`] [`--jobs` _<n>_] [`--sparse`] [`--`[`no-`]`reject-shallow`]
+	  [++--filter=++__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
+	  [_<directory>_]
 
 DESCRIPTION
 -----------
@@ -31,7 +31,7 @@
 After the clone, a plain `git fetch` without arguments will update
 all the remote-tracking branches, and a `git pull` without
 arguments will in addition merge the remote master branch into the
-current master branch, if any (this is untrue when "--single-branch"
+current master branch, if any (this is untrue when `--single-branch`
 is given; see below).
 
 This default configuration is achieved by creating references to
@@ -42,12 +42,12 @@
 
 OPTIONS
 -------
--l::
---local::
+`-l`::
+`--local`::
 	When the repository to clone from is on a local machine,
 	this flag bypasses the normal "Git aware" transport
 	mechanism and clones the repository by making a copy of
-	HEAD and everything under objects and refs directories.
+	`HEAD` and everything under objects and refs directories.
 	The files under `.git/objects/` directory are hardlinked
 	to save space when possible.
 +
@@ -58,18 +58,23 @@
 override the default when `/path/to/repo` is given, using the regular
 Git transport instead.
 +
+If the repository's `$GIT_DIR/objects` has symbolic links or is a
+symbolic link, the clone will fail. This is a security measure to
+prevent the unintentional copying of files by dereferencing the symbolic
+links.
++
 *NOTE*: this operation can race with concurrent modification to the
 source repository, similar to running `cp -r src dst` while modifying
 `src`.
 
---no-hardlinks::
+`--no-hardlinks`::
 	Force the cloning process from a repository on a local
 	filesystem to copy the files under the `.git/objects`
 	directory instead of using hardlinks. This may be desirable
 	if you are trying to make a back-up of your repository.
 
--s::
---shared::
+`-s`::
+`--shared`::
 	When the repository to clone is on the local machine,
 	instead of using hard links, automatically setup
 	`.git/objects/info/alternates` to share the objects
@@ -96,10 +101,10 @@
 its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
---reference[-if-able] <repository>::
-	If the reference repository is on the local machine,
+`--reference`[`-if-able`] _<repository>_::
+	If the reference _<repository>_ is on the local machine,
 	automatically setup `.git/objects/info/alternates` to
-	obtain objects from the reference repository.  Using
+	obtain objects from the reference _<repository>_.  Using
 	an already existing repository as an alternate will
 	require fewer objects to be copied from the repository
 	being cloned, reducing network and local storage costs.
@@ -110,7 +115,7 @@
 *NOTE*: see the NOTE for the `--shared` option, and also the
 `--dissociate` option.
 
---dissociate::
+`--dissociate`::
 	Borrow the objects from reference repositories specified
 	with the `--reference` options only to reduce network
 	transfer, and stop borrowing from them after a clone is made
@@ -121,43 +126,43 @@
 	same repository, and this option can be used to stop the
 	borrowing.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Operate quietly.  Progress is not reported to the standard
 	error stream.
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Run verbosely. Does not affect the reporting of progress status
 	to the standard error stream.
 
---progress::
+`--progress`::
 	Progress status is reported on the standard error stream
 	by default when it is attached to a terminal, unless `--quiet`
 	is specified. This flag forces progress status even if the
 	standard error stream is not directed to a terminal.
 
---server-option=<option>::
+++--server-option=++__<option>__::
 	Transmit the given string to the server when communicating using
 	protocol version 2.  The given string must not contain a NUL or LF
 	character.  The server's handling of server options, including
 	unknown ones, is server-specific.
-	When multiple `--server-option=<option>` are given, they are all
+	When multiple ++--server-option=++__<option>__ are given, they are all
 	sent to the other side in the order listed on the command line.
 
--n::
---no-checkout::
+`-n`::
+`--no-checkout`::
 	No checkout of HEAD is performed after the clone is complete.
 
---[no-]reject-shallow::
+`--`[`no-`]`reject-shallow`::
 	Fail if the source repository is a shallow repository.
-	The 'clone.rejectShallow' configuration variable can be used to
+	The `clone.rejectShallow` configuration variable can be used to
 	specify the default.
 
---bare::
+`--bare`::
 	Make a 'bare' Git repository.  That is, instead of
-	creating `<directory>` and placing the administrative
-	files in `<directory>/.git`, make the `<directory>`
+	creating _<directory>_ and placing the administrative
+	files in _<directory>_`/.git`, make the _<directory>_
 	itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
 	because there is nowhere to check out the working tree.
 	Also the branch heads at the remote are copied directly
@@ -166,28 +171,28 @@
 	used, neither remote-tracking branches nor the related
 	configuration variables are created.
 
---sparse::
+`--sparse`::
 	Employ a sparse-checkout, with only files in the toplevel
 	directory initially being present.  The
 	linkgit:git-sparse-checkout[1] command can be used to grow the
 	working directory as needed.
 
---filter=<filter-spec>::
+++--filter=++__<filter-spec>__::
 	Use the partial clone feature and request that the server sends
 	a subset of reachable objects according to a given object filter.
-	When using `--filter`, the supplied `<filter-spec>` is used for
+	When using `--filter`, the supplied _<filter-spec>_ is used for
 	the partial clone filter. For example, `--filter=blob:none` will
 	filter out all blobs (file contents) until needed by Git. Also,
-	`--filter=blob:limit=<size>` will filter out all blobs of size
-	at least `<size>`. For more details on filter specifications, see
+	++--filter=blob:limit=++__<size>__ will filter out all blobs of size
+	at least _<size>_. For more details on filter specifications, see
 	the `--filter` option in linkgit:git-rev-list[1].
 
---also-filter-submodules::
+`--also-filter-submodules`::
 	Also apply the partial clone filter to any submodules in the repository.
 	Requires `--filter` and `--recurse-submodules`. This can be turned on by
 	default by setting the `clone.filterSubmodules` config option.
 
---mirror::
+`--mirror`::
 	Set up a mirror of the source repository.  This implies `--bare`.
 	Compared to `--bare`, `--mirror` not only maps local branches of the
 	source to local branches of the target, it maps all refs (including
@@ -195,37 +200,37 @@
 	that all these refs are overwritten by a `git remote update` in the
 	target repository.
 
--o <name>::
---origin <name>::
+`-o` _<name>_::
+`--origin` _<name>_::
 	Instead of using the remote name `origin` to keep track of the upstream
-	repository, use `<name>`.  Overrides `clone.defaultRemoteName` from the
+	repository, use _<name>_.  Overrides `clone.defaultRemoteName` from the
 	config.
 
--b <name>::
---branch <name>::
+`-b` _<name>_::
+`--branch` _<name>_::
 	Instead of pointing the newly created HEAD to the branch pointed
-	to by the cloned repository's HEAD, point to `<name>` branch
+	to by the cloned repository's HEAD, point to _<name>_ branch
 	instead. In a non-bare repository, this is the branch that will
 	be checked out.
 	`--branch` can also take tags and detaches the HEAD at that commit
 	in the resulting repository.
 
--u <upload-pack>::
---upload-pack <upload-pack>::
+`-u` _<upload-pack>_::
+`--upload-pack` _<upload-pack>_::
 	When given, and the repository to clone from is accessed
 	via ssh, this specifies a non-default path for the command
 	run on the other end.
 
---template=<template-directory>::
+++--template=++__<template-directory>__::
 	Specify the directory from which templates will be used;
 	(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
--c <key>=<value>::
---config <key>=<value>::
+`-c` __<key>__++=++__<value>__::
+`--config` __<key>__++=++__<value>__::
 	Set a configuration variable in the newly-created repository;
 	this takes effect immediately after the repository is
 	initialized, but before the remote history is fetched or any
-	files checked out.  The key is in the same format as expected by
+	files checked out.  The _<key>_ is in the same format as expected by
 	linkgit:git-config[1] (e.g., `core.eol=true`). If multiple
 	values are given for the same key, each value will be written to
 	the config file. This makes it safe, for example, to add
@@ -234,35 +239,35 @@
 Due to limitations of the current implementation, some configuration
 variables do not take effect until after the initial fetch and checkout.
 Configuration variables known to not take effect are:
-`remote.<name>.mirror` and `remote.<name>.tagOpt`.  Use the
+++remote.++__<name>__++.mirror++ and ++remote.++__<name>__++.tagOpt++.  Use the
 corresponding `--mirror` and `--no-tags` options instead.
 
---depth <depth>::
+`--depth` _<depth>_::
 	Create a 'shallow' clone with a history truncated to the
 	specified number of commits. Implies `--single-branch` unless
 	`--no-single-branch` is given to fetch the histories near the
 	tips of all branches. If you want to clone submodules shallowly,
 	also pass `--shallow-submodules`.
 
---shallow-since=<date>::
+++--shallow-since=++__<date>__::
 	Create a shallow clone with a history after the specified time.
 
---shallow-exclude=<revision>::
+++--shallow-exclude=++__<revision>__::
 	Create a shallow clone with a history, excluding commits
 	reachable from a specified remote branch or tag.  This option
 	can be specified multiple times.
 
---[no-]single-branch::
+`--`[`no-`]`single-branch`::
 	Clone only the history leading to the tip of a single branch,
 	either specified by the `--branch` option or the primary
 	branch remote's `HEAD` points at.
 	Further fetches into the resulting repository will only update the
 	remote-tracking branch for the branch this option was used for the
-	initial cloning.  If the HEAD at the remote did not point at any
+	initial cloning.  If the `HEAD` at the remote did not point at any
 	branch when `--single-branch` clone was made, no remote-tracking
 	branch is created.
 
---no-tags::
+`--no-tags`::
 	Don't clone any tags, and set
 	`remote.<remote>.tagOpt=--no-tags` in the config, ensuring
 	that future `git pull` and `git fetch` operations won't follow
@@ -274,9 +279,9 @@
 branch. This is useful e.g. to maintain minimal clones of the default
 branch of some repository for search indexing.
 
---recurse-submodules[=<pathspec>]::
+`--recurse-submodules`[`=`{empty}__<pathspec>__]::
 	After the clone is created, initialize and clone submodules
-	within based on the provided pathspec.  If no pathspec is
+	within based on the provided _<pathspec>_.  If no _=<pathspec>_ is
 	provided, all submodules are initialized and cloned.
 	This option can be given multiple times for pathspecs consisting
 	of multiple entries.  The resulting clone has `submodule.active` set to
@@ -290,42 +295,48 @@
 not have a worktree/checkout (i.e. if any of `--no-checkout`/`-n`, `--bare`,
 or `--mirror` is given)
 
---[no-]shallow-submodules::
+`--`[`no-`]`shallow-submodules`::
 	All submodules which are cloned will be shallow with a depth of 1.
 
---[no-]remote-submodules::
+`--`[`no-`]`remote-submodules`::
 	All submodules which are cloned will use the status of the submodule's
 	remote-tracking branch to update the submodule, rather than the
 	superproject's recorded SHA-1. Equivalent to passing `--remote` to
 	`git submodule update`.
 
---separate-git-dir=<git-dir>::
+`--separate-git-dir=`{empty}__<git-dir>__::
 	Instead of placing the cloned repository where it is supposed
 	to be, place the cloned repository at the specified directory,
 	then make a filesystem-agnostic Git symbolic link to there.
 	The result is Git repository can be separated from working
 	tree.
 
--j <n>::
---jobs <n>::
+`--ref-format=`{empty}__<ref-format>__::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
+`-j` _<n>_::
+`--jobs` _<n>_::
 	The number of submodules fetched at the same time.
 	Defaults to the `submodule.fetchJobs` option.
 
-<repository>::
-	The (possibly remote) repository to clone from.  See the
+_<repository>_::
+	The (possibly remote) _<repository>_ to clone from.  See the
 	<<URLS,GIT URLS>> section below for more information on specifying
 	repositories.
 
-<directory>::
+_<directory>_::
 	The name of a new directory to clone into.  The "humanish"
-	part of the source repository is used if no directory is
+	part of the source repository is used if no _<directory>_ is
 	explicitly given (`repo` for `/path/to/repo.git` and `foo`
 	for `host.xz:foo/.git`).  Cloning into an existing directory
 	is only allowed if the directory is empty.
 
---bundle-uri=<uri>::
+`--bundle-uri=`{empty}__<uri>__::
 	Before fetching from the remote, fetch a bundle from the given
-	`<uri>` and unbundle the data into the local repository. The refs
+	_<uri>_ and unbundle the data into the local repository. The refs
 	in the bundle will be stored under the hidden `refs/bundle/*`
 	namespace. This option is incompatible with `--depth`,
 	`--shallow-since`, and `--shallow-exclude`.
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index c8dbceb..903b168 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -13,7 +13,7 @@
 'git commit-graph write' [--object-dir <dir>] [--append]
 			[--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]
 			[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]
-			<split options>
+			<split-options>
 
 
 DESCRIPTION
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 225c6c9..89ecfc6 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -347,6 +347,8 @@
 	- 'normal' - Shows untracked files and directories
 	- 'all'    - Also shows individual files in untracked directories.
 
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
 The default can be changed using the status.showUntrackedFiles
 configuration variable documented in linkgit:git-config[1].
 --
@@ -541,7 +543,7 @@
 ----------
 
 Though not required, it's a good idea to begin the commit message
-with a single short (less than 50 character) line summarizing the
+with a single short (no more than 50 characters) line summarizing the
 change, followed by a blank line and then a more thorough description.
 The text up to the first blank line in a commit message is treated
 as the commit title, and that title is used throughout Git.
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 7a2bcb2..ac61113 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,9 +9,9 @@
 SYNOPSIS
 --------
 [verse]
-'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
 'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
@@ -87,6 +87,18 @@
 	values.  This is the same as providing '^$' as the `value-pattern`
 	in `--replace-all`.
 
+--comment <message>::
+	Append a comment at the end of new or modified lines.
+
+	If _<message>_ begins with one or more whitespaces followed
+	by "#", it is used as-is.  If it begins with "#", a space is
+	prepended before it is used.  Otherwise, a string " # " (a
+	space followed by a hash followed by a space) is prepended
+	to it.  And the resulting string is placed immediately after
+	the value defined for the variable.  The _<message>_ must
+	not contain linefeed characters (no multi-line comments are
+	permitted).
+
 --get::
 	Get the value for a given key (optionally filtered by a regex
 	matching the value). Returns error code 1 if the key was not
@@ -103,11 +115,11 @@
 	names are not.
 
 --get-urlmatch <name> <URL>::
-	When given a two-part name section.key, the value for
-	section.<URL>.key whose <URL> part matches the best to the
+	When given a two-part <name> as <section>.<key>, the value for
+	<section>.<URL>.<key> whose <URL> part matches the best to the
 	given URL is returned (if no such key exists, the value for
-	section.key is used as a fallback).  When given just the
-	section as name, do so for all the keys in the section and
+	<section>.<key> is used as a fallback).  When given just the
+	<section> as name, do so for all the keys in the section and
 	list them.  Returns error code 1 if no value is found.
 
 --global::
@@ -201,7 +213,7 @@
   1073741824 upon input.
 - 'bool-or-int': canonicalize according to either 'bool' or 'int', as described
   above.
-- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and
+- 'path': canonicalize by expanding a leading `~` to the value of `$HOME` and
   `~user` to the home directory for the specified user. This specifier has no
   effect when setting the value (but you can use `git config section.variable
   ~/` from the command line to let your shell do the expansion.)
@@ -275,7 +287,8 @@
 -e::
 --edit::
 	Opens an editor to modify the specified config file; either
-	`--system`, `--global`, or repository (default).
+	`--system`, `--global`, `--local` (default), `--worktree`, or
+	`--file <config-file>`.
 
 --[no-]includes::
 	Respect `include.*` directives in config files when looking up
@@ -285,7 +298,7 @@
 
 --default <value>::
   When using `--get`, and the requested variable is not found, behave as if
-  <value> were the value assigned to the that variable.
+  <value> were the value assigned to that variable.
 
 CONFIGURATION
 -------------
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index cb9b4d2..97f9f12 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -12,7 +12,7 @@
 
 DESCRIPTION
 -----------
-This counts the number of unpacked object files and disk space consumed by
+Counts the number of unpacked object files and disk space consumed by
 them, to help you decide when it is a good time to repack.
 
 
@@ -20,7 +20,7 @@
 -------
 -v::
 --verbose::
-	Report in more detail:
+	Provide more detailed reports:
 +
 count: the number of loose objects
 +
@@ -33,7 +33,7 @@
 prune-packable: the number of loose objects that are also present in
 the packs. These objects could be pruned using `git prune-packed`.
 +
-garbage: the number of files in object database that are neither valid loose
+garbage: the number of files in the object database that are neither valid loose
 objects nor valid packs
 +
 size-garbage: disk space consumed by garbage files, in KiB (unless -H is
diff --git a/Documentation/git-credential-cache.txt b/Documentation/git-credential-cache.txt
index f473994..487cc55 100644
--- a/Documentation/git-credential-cache.txt
+++ b/Documentation/git-credential-cache.txt
@@ -16,7 +16,7 @@
 
 This command caches credentials for use by future Git programs.
 The stored credentials are kept in memory of the cache-daemon
-process (instead of written to a file) and are forgotten after a
+process (instead of being written to a file) and are forgotten after a
 configurable timeout. Credentials are forgotten sooner if the
 cache-daemon dies, for example if the system restarts. The cache
 is accessible over a Unix domain socket, restricted to the current
diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
index 76b0798..71864a8 100644
--- a/Documentation/git-credential-store.txt
+++ b/Documentation/git-credential-store.txt
@@ -33,7 +33,7 @@
 
 	Use `<path>` to lookup and store credentials. The file will have its
 	filesystem permissions set to prevent other users on the system
-	from reading it, but will not be encrypted or otherwise
+	from reading it, but it will not be encrypted or otherwise
 	protected. If not specified, credentials will be searched for from
 	`~/.git-credentials` and `$XDG_CONFIG_HOME/git/credentials`, and
 	credentials will be written to `~/.git-credentials` if it exists, or
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 29d184a..918a0aa 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -39,7 +39,7 @@
 
 If the action is `reject`, git-credential will send the description to
 any configured credential helpers, which may erase any stored
-credential matching the description.
+credentials matching the description.
 
 If the action is `approve` or `reject`, no output should be emitted.
 
@@ -94,7 +94,7 @@
      that `git credential` will ask for a new password in its next
      invocation. In either case, `git credential` should be fed with
      the credential description obtained from step (2) (which also
-     contain the ones provided in step (1)).
+     contains the fields provided in step (1)).
 
 [[IOFMT]]
 INPUT/OUTPUT FORMAT
@@ -113,7 +113,13 @@
 The key may contain any bytes except `=`, newline, or NUL. The value may
 contain any bytes except newline or NUL.
 
-In both cases, all bytes are treated as-is (i.e., there is no quoting,
+Attributes with keys that end with C-style array brackets `[]` can have
+multiple values. Each instance of a multi-valued attribute forms an
+ordered list of values - the order of the repeated attributes defines
+the order of the values. An empty multi-valued attribute (`key[]=\n`)
+acts to clear any previous entries and reset the list.
+
+In all cases, all bytes are treated as-is (i.e., there is no quoting,
 and one cannot transmit a value with newline or NUL in it). The list of
 attributes is terminated by a blank line or end-of-file.
 
@@ -150,6 +156,12 @@
 	When reading credentials from helpers, `git credential fill` ignores expired
 	passwords. Represented as Unix time UTC, seconds since 1970.
 
+`oauth_refresh_token`::
+
+	An OAuth refresh token may accompany a password that is an OAuth access
+	token. Helpers must treat this attribute as confidential like the password
+	attribute. Git itself has no special behaviour for this attribute.
+
 `url`::
 
 	When this special attribute is read by `git credential`, the
@@ -166,6 +178,17 @@
 Components which are missing from the URL (e.g., there is no
 username in the example above) will be left unset.
 
+`wwwauth[]`::
+
+	When an HTTP response is received by Git that includes one or more
+	'WWW-Authenticate' authentication headers, these will be passed by Git
+	to credential helpers.
++
+Each 'WWW-Authenticate' header value is passed as a multi-valued
+attribute 'wwwauth[]', where the order of the attributes is the same as
+they appear in the HTTP response. This attribute is 'one-way' from Git
+to pass additional information to credential helpers.
+
 Unrecognised attributes are silently discarded.
 
 GIT
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index b3f2767..90fdc25 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -22,7 +22,7 @@
 deprecated; it does not work with cvsps version 3 and later.  If you are
 performing a one-shot import of a CVS repository consider using
 http://cvs2svn.tigris.org/cvs2git.html[cvs2git] or
-http://www.catb.org/esr/cvs-fast-export/[cvs-fast-export].
+https://gitlab.com/esr/cvs-fast-export[cvs-fast-export].
 
 Imports a CVS repository into Git. It will either create a new
 repository, or incrementally import into an existing one.
@@ -221,7 +221,7 @@
 If you suspect that any of these issues may apply to the repository you
 want to import, consider using cvs2git:
 
-* cvs2git (part of cvs2svn), `http://subversion.apache.org/`
+* cvs2git (part of cvs2svn), `https://subversion.apache.org/`
 
 GIT
 ---
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index 53f111b..4c475ef 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -118,7 +118,7 @@
    myuser:$5$.NqmNH1vwfzGpV8B$znZIcumu1tNLATgV2l6e1/mY8RzhUDHMOaVOeL1cxV3
 ------
 You can use the 'htpasswd' facility that comes with Apache to make these
-files, but only with the -d option (or -B if your system suports it).
+files, but only with the -d option (or -B if your system supports it).
 
 Preferably use the system specific utility that manages password hash
 creation in your platform (e.g. mkpasswd in Linux, encrypt in OpenBSD or
@@ -197,7 +197,7 @@
 5. Clients should now be able to check out the project. Use the CVS 'module'
    name to indicate what Git 'head' you want to check out.  This also sets the
    name of your newly checked-out directory, unless you tell it otherwise with
-   `-d <dir_name>`.  For example, this checks out 'master' branch to the
+   `-d <dir-name>`.  For example, this checks out 'master' branch to the
    `project-master` directory:
 +
 ------
@@ -224,7 +224,7 @@
 that the database is up to date any time 'git-cvsserver' is executed).
 
 By default it uses SQLite databases in the Git directory, named
-`gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
+`gitcvs.<module-name>.sqlite`. Note that the SQLite backend creates
 temporary files in the same directory as the database file on
 write so it might not be enough to grant the users using
 'git-cvsserver' write access to the database file without granting
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 236df51..ede7b93 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -18,7 +18,7 @@
 	     [--allow-override=<service>] [--forbid-override=<service>]
 	     [--access-hook=<path>] [--[no-]informative-errors]
 	     [--inetd |
-	      [--listen=<host_or_ipaddr>] [--port=<n>]
+	      [--listen=<host-or-ipaddr>] [--port=<n>]
 	      [--user=<user> [--group=<group>]]]
 	     [--log-destination=(stderr|syslog|none)]
 	     [<directory>...]
@@ -86,10 +86,10 @@
 	Incompatible with --detach, --port, --listen, --user and --group
 	options.
 
---listen=<host_or_ipaddr>::
+--listen=<host-or-ipaddr>::
 	Listen on a specific IP address or hostname.  IP addresses can
 	be either an IPv4 address or an IPv6 address if supported.  If IPv6
-	is not supported, then --listen=hostname is also not supported and
+	is not supported, then --listen=<hostname> is also not supported and
 	--listen must be given an IPv4 address.
 	Can be given more than once.
 	Incompatible with `--inetd` option.
@@ -138,11 +138,11 @@
 --user-path::
 --user-path=<path>::
 	Allow {tilde}user notation to be used in requests.  When
-	specified with no parameter, requests to
+	specified with no parameter, a request to
 	git://host/{tilde}alice/foo is taken as a request to access
 	'foo' repository in the home directory of user `alice`.
-	If `--user-path=path` is specified, the same request is
-	taken as a request to access `path/foo` repository in
+	If `--user-path=<path>` is specified, the same request is
+	taken as a request to access `<path>/foo` repository in
 	the home directory of user `alice`.
 
 --verbose::
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index c6a79c2..08ff715 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -140,7 +140,7 @@
 
 The number of additional commits is the number
 of commits which would be displayed by "git log v1.0.4..parent".
-The hash suffix is "-g" + an unambigous abbreviation for the tip commit
+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
@@ -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 <committ-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-diagnose.txt b/Documentation/git-diagnose.txt
index 3ec8cc7..0711959 100644
--- a/Documentation/git-diagnose.txt
+++ b/Documentation/git-diagnose.txt
@@ -45,7 +45,7 @@
 -s <format>::
 --suffix <format>::
 	Specify an alternate suffix for the diagnostics archive name, to create
-	a file named 'git-diagnostics-<formatted suffix>'. This should take the
+	a file named 'git-diagnostics-<formatted-suffix>'. This should take the
 	form of a strftime(3) format string; the current local time will be
 	used.
 
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 591e380..bf78e31 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -26,7 +26,7 @@
 -2 --ours::
 -3 --theirs::
 -0::
-	Diff against the "base" version, "our branch" or "their
+	Diff against the "base" version, "our branch", or "their
 	branch" respectively.  With these options, diffs for
 	merged entries are not shown.
 +
@@ -37,12 +37,12 @@
 -c::
 --cc::
 	This compares stage 2 (our branch), stage 3 (their
-	branch) and the working tree file and outputs a combined
+	branch), and the working tree file and outputs a combined
 	diff, similar to the way 'diff-tree' shows a merge
 	commit with these flags.
 
 -q::
-	Remain silent even on nonexistent files
+	Remain silent even for nonexistent files
 
 
 include::diff-format.txt[]
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index c30d8f0..4de1d4c 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -13,10 +13,10 @@
 
 DESCRIPTION
 -----------
-Compares the content and mode of the blobs found in a tree object
+Compare the content and mode of the blobs found in a tree object
 with the corresponding tracked files in the working tree, or with the
 corresponding paths in the index.  When <path> arguments are present,
-compares only paths matching those patterns.  Otherwise all tracked
+compare only paths matching those patterns.  Otherwise all tracked
 files are compared.
 
 OPTIONS
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 274d5ea..143318c 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -15,7 +15,7 @@
 
 DESCRIPTION
 -----------
-Compares the content and mode of the blobs found via two tree objects.
+Compare the content and mode of blobs found via two tree objects.
 
 If there is only one <tree-ish> given, the commit is compared with its parents
 (see --stdin below).
@@ -34,10 +34,10 @@
 	matching one of the provided pathspecs.
 
 -r::
-        recurse into sub-trees
+	Recurse into sub-trees.
 
 -t::
-	show tree entry itself as well as subtrees.  Implies -r.
+	Show tree entry itself as well as subtrees.  Implies -r.
 
 --root::
 	When `--root` is specified the initial commit will be shown as a big
@@ -78,7 +78,7 @@
 	By default, 'git diff-tree --stdin' shows differences,
 	either in machine-readable form (without `-p`) or in patch
 	form (with `-p`).  This output can be suppressed.  It is
-	only useful with `-v` flag.
+	only useful with the `-v` flag.
 
 -v::
 	This flag causes 'git diff-tree --stdin' to also show
@@ -104,10 +104,10 @@
 	This flag changes the way a merge commit patch is displayed,
 	in a similar way to the `-c` option. It implies the `-c`
 	and `-p` options and further compresses the patch output
-	by omitting uninteresting hunks whose the contents in the parents
+	by omitting uninteresting hunks whose contents in the parents
 	have only two variants and the merge result picks one of them
 	without modification.  When all hunks are uninteresting, the commit
-	itself and the commit log message is not shown, just like in any other
+	itself and the commit log message are not shown, just like in any other
 	"empty diff" case.
 
 --combined-all-paths::
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 52b6792..c065f02 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -102,7 +102,11 @@
 Just in case you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
 in the `--merge-base` case and in the last two forms that use `..`
-notations, can be any <tree>.
+notations, can be any <tree>. A tree of interest is the one pointed to
+by the ref named `AUTO_MERGE`, which is written by the 'ort' merge
+strategy upon hitting merge conflicts (see linkgit:git-merge[1]).
+Comparing the working tree with `AUTO_MERGE` shows changes you've made
+so far to resolve textual conflicts (see the examples below).
 
 For a more complete list of ways to spell <commit>, see
 "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
@@ -152,6 +156,7 @@
 $ git diff            <1>
 $ git diff --cached   <2>
 $ git diff HEAD       <3>
+$ git diff AUTO_MERGE <4>
 ------------
 +
 <1> Changes in the working tree not yet staged for the next commit.
@@ -159,6 +164,8 @@
     would be committing if you run `git commit` without `-a` option.
 <3> Changes in the working tree since your last commit; what you
     would be committing if you run `git commit -a`
+<4> Changes in the working tree you've made to resolve textual
+    conflicts so far.
 
 Comparing with arbitrary commits::
 +
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index 9d14c3c..a616f8b 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -36,7 +36,7 @@
 
 --rotate-to=<file>::
 	Start showing the diff for the given path,
-	the paths before it will move to end and output.
+	the paths before it will move to the end and output.
 
 --skip-to=<file>::
 	Start showing the diff for the given path, skipping all
@@ -78,7 +78,7 @@
 	Print a list of diff tools that may be used with `--tool`.
 
 --[no-]symlinks::
-	'git difftool''s default behavior is create symlinks to the
+	'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.
@@ -90,20 +90,21 @@
 --extcmd=<command>::
 	Specify a custom command for viewing diffs.
 	'git-difftool' ignores the configured defaults and runs
-	`$command $LOCAL $REMOTE` when this option is specified.
+	`<command> $LOCAL $REMOTE` when this option is specified.
 	Additionally, `$BASE` is set in the environment.
 
 -g::
 --[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`. The `--no-gui`
-	option can be used to override this setting. If `diff.guitool`
-	is not set, we will fallback in the order of `merge.guitool`,
-	`diff.tool`, `merge.tool` until a tool is found.
+	`diff.guitool` variable instead of `diff.tool`. This may be
+	selected automatically using the configuration variable
+	`difftool.guiDefault`. The `--no-gui` option can be used to
+	override these settings. If `diff.guitool` is not set, we will
+	fallback in the order of `merge.guitool`, `diff.tool`,
+	`merge.tool` until a tool is found.
 
 --[no-]trust-exit-code::
-	'git-difftool' invokes a diff tool individually on each file.
 	Errors reported by the diff tool are ignored by default.
 	Use `--trust-exit-code` to make 'git-difftool' exit when an
 	invoked diff tool returns a non-zero exit code.
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index 4643ddb..752e4b9 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -48,7 +48,7 @@
 when encountering such a tag.  With 'drop' it will omit such tags from
 the output.  With 'rewrite', if the tagged object is a commit, it will
 rewrite the tag to tag an ancestor commit (via parent rewriting; see
-linkgit:git-rev-list[1])
+linkgit:git-rev-list[1]).
 
 -M::
 -C::
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index 8b5dd6a..b260736 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -622,7 +622,7 @@
 * `100755` or `755`: A normal, but executable, file.
 * `120000`: A symlink, the content of the file will be the link target.
 * `160000`: A gitlink, SHA-1 of the object refers to a commit in
-  another repository. Git links can only be specified by SHA or through
+  another repository. Git links can only be specified either by SHA or through
   a commit mark. They are used to implement submodules.
 * `040000`: A subdirectory.  Subdirectories can only be specified by
   SHA or through a tree mark set with `--import-marks`.
@@ -745,11 +745,11 @@
 
 `notemodify`
 ^^^^^^^^^^^^
-Included in a `commit` `<notes_ref>` command to add a new note
+Included in a `commit` `<notes-ref>` command to add a new note
 annotating a `<commit-ish>` or change this annotation contents.
 Internally it is similar to filemodify 100644 on `<commit-ish>`
 path (maybe split into subdirectories). It's not advised to
-use any other commands to write to the `<notes_ref>` tree except
+use any other commands to write to the `<notes-ref>` tree except
 `filedeleteall` to delete all existing notes in this tree.
 This command has two different means of specifying the content
 of the note.
@@ -1353,7 +1353,7 @@
 accuracy and completeness of the import by comparing each Git
 commit to the corresponding source revision.
 
-Coming from a system such as Perforce or Subversion this should be
+Coming from a system such as Perforce or Subversion, this should be
 quite simple, as the fast-import mark can also be the Perforce changeset
 number or the Subversion revision number.
 
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 46747d5..b346766 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -69,7 +69,7 @@
 
 --upload-pack=<git-upload-pack>::
 	Use this to specify the path to 'git-upload-pack' on the
-	remote side, if is not found on your $PATH.
+	remote side, if it is not found on your $PATH.
 	Installations of sshd ignores the user's environment
 	setup scripts for login shells (e.g. .bash_profile) and
 	your privately installed git may not be found on the system
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index fba66f1..50900a5 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -186,8 +186,8 @@
 ------------------------------------------------
 $ git fetch origin --prune --prune-tags
 $ git fetch origin --prune 'refs/tags/*:refs/tags/*'
-$ git fetch <url of origin> --prune --prune-tags
-$ git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
+$ git fetch <url-of-origin> --prune --prune-tags
+$ git fetch <url-of-origin> --prune 'refs/tags/*:refs/tags/*'
 ------------------------------------------------
 
 OUTPUT
@@ -204,6 +204,15 @@
  <flag> <summary> <from> -> <to> [<reason>]
 -------------------------------
 
+When using `--porcelain`, the output format is intended to be
+machine-parseable. In contrast to the human-readable output formats it
+thus prints to standard output instead of standard error. Each line is
+of the form:
+
+-------------------------------
+<flag> <old-object-id> <new-object-id> <local-reference>
+-------------------------------
+
 The status of up-to-date refs is shown only if the --verbose option is
 used.
 
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 62e482a..5a4f853 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -14,7 +14,7 @@
 	[--msg-filter <command>] [--commit-filter <command>]
 	[--tag-name-filter <command>] [--prune-empty]
 	[--original <namespace>] [-d <directory>] [-f | --force]
-	[--state-branch <branch>] [--] [<rev-list options>...]
+	[--state-branch <branch>] [--] [<rev-list-options>...]
 
 WARNING
 -------
@@ -32,7 +32,7 @@
 DESCRIPTION
 -----------
 Lets you rewrite Git revision history by rewriting the branches mentioned
-in the <rev-list options>, applying custom filters on each revision.
+in the <rev-list-options>, applying custom filters on each revision.
 Those filters can modify each tree (e.g. removing a file or running
 a perl rewrite on all files) or information about each commit.
 Otherwise, all information (including original commit times or merge
@@ -624,7 +624,7 @@
      real backup; it dereferences tags first.)
 
   ** Running git-filter-branch with either --tags or --all in your
-     <rev-list options>.  In order to retain annotated tags as
+     <rev-list-options>.  In order to retain annotated tags as
      annotated, you must use --tag-name-filter (and must not have
      restored from refs/original/ in a previously botched rewrite).
 
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 6da899c..c1dd12b 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -9,10 +9,12 @@
 --------
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
-		   [(--sort=<key>)...] [--format=<format>] [<pattern>...]
+		   [(--sort=<key>)...] [--format=<format>]
+		   [--include-root-refs] [ --stdin | <pattern>... ]
 		   [--points-at=<object>]
 		   [--merged[=<object>]] [--no-merged[=<object>]]
 		   [--contains[=<object>]] [--no-contains[=<object>]]
+		   [--exclude=<pattern> ...]
 
 DESCRIPTION
 -----------
@@ -32,6 +34,10 @@
 	literally, in the latter case matching completely or from the
 	beginning up to a slash.
 
+--stdin::
+	If `--stdin` is supplied, then the list of patterns is read from
+	standard input instead of from the argument list.
+
 --count=<count>::
 	By default the command shows all refs that match
 	`<pattern>`.  This option makes it stop after showing
@@ -45,17 +51,14 @@
 	key.
 
 --format=<format>::
-	A string that interpolates `%(fieldname)` from a ref being shown
-	and the object it points at.  If `fieldname`
-	is prefixed with an asterisk (`*`) and the ref points
-	at a tag object, use the value for the field in the object
-	which the tag object refers to (instead of the field in the tag object).
-	When unspecified, `<format>` defaults to
-	`%(objectname) SPC %(objecttype) TAB %(refname)`.
-	It also interpolates `%%` to `%`, and `%xx` where `xx`
-	are hex digits interpolates to character with hex code
-	`xx`; for example `%00` interpolates to `\0` (NUL),
-	`%09` to `\t` (TAB) and `%0a` to `\n` (LF).
+	A string that interpolates `%(fieldname)` from a ref being shown and
+	the object it points at. In addition, the string literal `%%`
+	renders as `%` and `%xx` - where `xx` are hex digits - renders as
+	the character with hex code `xx`. For example, `%00` interpolates to
+	`\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF).
++
+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)
+TAB %(refname)`.
 
 --color[=<when>]::
 	Respect any colors specified in the `--format` option. The
@@ -93,6 +96,18 @@
 --ignore-case::
 	Sorting and filtering refs are case insensitive.
 
+--omit-empty::
+	Do not print a newline after formatted refs where the format expands
+	to the empty string.
+
+--exclude=<pattern>::
+	If one or more patterns are given, only refs which do not match
+	any excluded pattern(s) are shown. Matching is done using the
+	same rules as `<pattern>` above.
+
+--include-root-refs::
+	List root refs (HEAD and pseudorefs) apart from regular refs.
+
 FIELD NAMES
 -----------
 
@@ -212,11 +227,66 @@
 	`:lstrip` and `:rstrip` options in the same way as `refname`
 	above.
 
+signature::
+	The GPG signature of a commit.
+
+signature:grade::
+	Show "G" for a good (valid) signature, "B" for a bad
+	signature, "U" for a good signature with unknown validity, "X"
+	for a good signature that has expired, "Y" for a good
+	signature made by an expired key, "R" for a good signature
+	made by a revoked key, "E" if the signature cannot be
+	checked (e.g. missing key) and "N" for no signature.
+
+signature:signer::
+	The signer of the GPG signature of a commit.
+
+signature:key::
+	The key of the GPG signature of a commit.
+
+signature:fingerprint::
+	The fingerprint of the GPG signature of a commit.
+
+signature:primarykeyfingerprint::
+	The primary key fingerprint of the GPG signature of a commit.
+
+signature:trustlevel::
+	The trust level of the GPG signature of a commit. Possible
+	outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`.
+
 worktreepath::
 	The absolute path to the worktree in which the ref is checked
 	out, if it is checked out in any linked worktree. Empty string
 	otherwise.
 
+ahead-behind:<committish>::
+	Two integers, separated by a space, demonstrating the number of
+	commits ahead and behind, respectively, when comparing the output
+	ref to the `<committish>` specified in the format.
+
+describe[:options]::
+	A human-readable name, like linkgit:git-describe[1];
+	empty string for undescribable commits. The `describe` string may
+	be followed by a colon and one or more comma-separated options.
++
+--
+tags=<bool-value>;;
+	Instead of only considering annotated tags, consider
+	lightweight tags as well; see the corresponding option in
+	linkgit:git-describe[1] for details.
+abbrev=<number>;;
+	Use at least <number> hexadecimal digits; see the corresponding
+	option in linkgit:git-describe[1] for details.
+match=<pattern>;;
+	Only consider tags matching the given `glob(7)` pattern,
+	excluding the "refs/tags/" prefix; see the corresponding option
+	in linkgit:git-describe[1] for details.
+exclude=<pattern>;;
+	Do not consider tags matching the given `glob(7)` pattern,
+	excluding the "refs/tags/" prefix; see the corresponding option
+	in linkgit:git-describe[1] for details.
+--
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
@@ -228,12 +298,20 @@
 from the `committer` or `tagger` fields depending on the object type.
 These are intended for working on a mix of annotated and lightweight tags.
 
+For tag objects, a `fieldname` prefixed with an asterisk (`*`) expands to
+the `fieldname` value of the peeled object, rather than that of the tag
+object itself.
+
 Fields that have name-email-date tuple as its value (`author`,
 `committer`, and `tagger`) can be suffixed with `name`, `email`,
 and `date` to extract the named component.  For email fields (`authoremail`,
 `committeremail` and `taggeremail`), `:trim` can be appended to get the email
 without angle brackets, and `:localpart` to get the part before the `@` symbol
-out of the trimmed email.
+out of the trimmed email. In addition to these, the `:mailmap` option and the
+corresponding `:mailmap,trim` and `:mailmap,localpart` can be used (order does
+not matter) to get values of the name and email according to the .mailmap file
+or according to the file set in the mailmap.file or mailmap.blob configuration
+variable (see linkgit:gitmailmap[5]).
 
 The raw data in an object is `raw`.
 
@@ -284,9 +362,11 @@
 the object referred by the ref does not cause an error.  It
 returns an empty string instead.
 
-As a special case for the date-type fields, you may specify a format for
-the date by adding `:` followed by date format name (see the
-values the `--date` option to linkgit:git-rev-list[1] takes).
+As a special case for the date-type fields, you may specify a format for the
+date by adding `:` followed by date format name (see the values the `--date`
+option to linkgit:git-rev-list[1] takes). If this formatting is provided in
+a `--sort` key, references will be sorted according to the byte-value of the
+formatted string rather than the numeric value of the underlying timestamp.
 
 Some atoms like %(align) and %(if) always require a matching %(end).
 We call them "opening atoms" and sometimes denote them as %($open).
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index dfcc7da..728bb38 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -17,10 +17,10 @@
 		   [--signature-file=<file>]
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
-		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
+		   [--in-reply-to=<message-id>] [--suffix=.<sfx>]
 		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
-		   [--rfc] [--subject-prefix=<subject prefix>]
+		   [--rfc] [--subject-prefix=<subject-prefix>]
 		   [(--reroll-count|-v) <n>]
 		   [--to=<email>] [--cc=<email>]
 		   [--[no-]cover-letter] [--quiet]
@@ -30,8 +30,8 @@
 		   [--range-diff=<previous> [--creation-factor=<percent>]]
 		   [--filename-max-length=<n>]
 		   [--progress]
-		   [<common diff options>]
-		   [ <since> | <revision range> ]
+		   [<common-diff-options>]
+		   [ <since> | <revision-range> ]
 
 DESCRIPTION
 -----------
@@ -55,7 +55,7 @@
 * The "patch", which is the "diff -p --stat" output (see
   linkgit:git-diff[1]) between the commit and its parent.
 
-The log message and the patch is separated by a line with a
+The log message and the patch are separated by a line with a
 three-dash line.
 
 There are two ways to specify which commits to operate on.
@@ -64,7 +64,7 @@
    to the tip of the current branch that are not in the history
    that leads to the <since> to be output.
 
-2. Generic <revision range> expression (see "SPECIFYING
+2. Generic <revision-range> expression (see "SPECIFYING
    REVISIONS" section in linkgit:gitrevisions[7]) means the
    commits in the specified range.
 
@@ -99,7 +99,7 @@
 
 If given `--thread`, `git-format-patch` will generate `In-Reply-To` and
 `References` headers to make the second and subsequent patch mails appear
-as replies to the first mail; this also generates a `Message-Id` header to
+as replies to the first mail; this also generates a `Message-ID` header to
 reference.
 
 OPTIONS
@@ -163,7 +163,7 @@
 --no-thread::
 	Controls addition of `In-Reply-To` and `References` headers to
 	make the second and subsequent mails appear as replies to the
-	first.  Also controls generation of the `Message-Id` header to
+	first.  Also controls generation of the `Message-ID` header to
 	reference.
 +
 The optional <style> argument can be either `shallow` or `deep`.
@@ -173,16 +173,15 @@
 threading makes every mail a reply to the previous one.
 +
 The default is `--no-thread`, unless the `format.thread` configuration
-is set.  If `--thread` is specified without a style, it defaults to the
-style specified by `format.thread` if any, or else `shallow`.
+is set.  `--thread` without an argument is equivalent to `--thread=shallow`.
 +
 Beware that the default for 'git send-email' is to thread emails
 itself.  If you want `git format-patch` to take care of threading, you
 will want to ensure that threading is disabled for `git send-email`.
 
---in-reply-to=<message id>::
+--in-reply-to=<message-id>::
 	Make the first mail (or all the mails with `--no-thread`) appear as a
-	reply to the given <message id>, which avoids breaking threads to
+	reply to the given <message-id>, which avoids breaking threads to
 	provide a new patch series.
 
 --ignore-if-in-upstream::
@@ -216,11 +215,21 @@
 If `<mode>` is `none`, both the cover letter subject and body will be
 populated with placeholder text.
 
---subject-prefix=<subject prefix>::
+--description-file=<file>::
+	Use the contents of <file> instead of the branch's description
+	for generating the cover letter.
+
+--subject-prefix=<subject-prefix>::
 	Instead of the standard '[PATCH]' prefix in the subject
-	line, instead use '[<subject prefix>]'. This
-	allows for useful naming of a patch series, and can be
-	combined with the `--numbered` option.
+	line, instead use '[<subject-prefix>]'. This can be used
+	to name a patch series, and can be combined with the
+	`--numbered` option.
++
+The configuration variable `format.subjectPrefix` may also be used
+to configure a subject prefix to apply to a given repository for
+all patches. This is often useful on mailing lists which receive
+patches for several repositories and can be used to disambiguate
+the patches (with a value of e.g. "PATCH my-project").
 
 --filename-max-length=<n>::
 	Instead of the standard 64 bytes, chomp the generated output
@@ -230,9 +239,9 @@
 	variable, or 64 if unconfigured.
 
 --rfc::
-	Alias for `--subject-prefix="RFC PATCH"`. RFC means "Request For
-	Comments"; use this when sending an experimental patch for
-	discussion rather than application.
+	Prepends "RFC" to the subject prefix (producing "RFC PATCH" by
+	default). RFC means "Request For Comments"; use this when sending
+	an experimental patch for discussion rather than application.
 
 -v <n>::
 --reroll-count=<n>::
@@ -246,7 +255,7 @@
 	or "--reroll-count=4rev2" are allowed), but the downside of
 	using such a reroll-count is that the range-diff/interdiff
 	with the previous version does not state exactly which
-	version the new interation is compared against.
+	version the new iteration is compared against.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
@@ -394,7 +403,7 @@
 	`format.useAutoBase` configuration.
 
 --root::
-	Treat the revision argument as a <revision range>, even if it
+	Treat the revision argument as a <revision-range>, even if it
 	is just a single commit (that would normally be treated as a
 	<since>).  Note that root commits included in the specified
 	range are always formatted as creation patches, independently
@@ -601,8 +610,8 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The following Thunderbird extensions are needed:
-AboutConfig from http://aboutconfig.mozdev.org/ and
-External Editor from http://globs.org/articles.php?lng=en&pg=8
+AboutConfig from https://mjg.github.io/AboutConfig/ and
+External Editor from https://globs.org/articles.php?lng=en&pg=8
 
 1. Prepare the patch as a text file using your method of choice.
 
diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt
index b6a0f8a..5b82e46 100644
--- a/Documentation/git-fsck.txt
+++ b/Documentation/git-fsck.txt
@@ -24,7 +24,7 @@
 	An object to treat as the head of an unreachability trace.
 +
 If no objects are given, 'git fsck' defaults to using the
-index file, all SHA-1 references in `refs` namespace, and all reflogs
+index file, all SHA-1 references in the `refs` namespace, and all reflogs
 (unless --no-reflogs is given) as heads.
 
 --unreachable::
@@ -64,7 +64,7 @@
 --connectivity-only::
 	Check only the connectivity of reachable objects, making sure
 	that any objects referenced by a reachable tag, commit, or tree
-	is present. This speeds up the operation by avoiding reading
+	are present. This speeds up the operation by avoiding reading
 	blobs entirely (though it does still check that referenced blobs
 	exist). This will detect corruption in commits and trees, but
 	not do any semantic checks (e.g., for format errors). Corruption
@@ -79,7 +79,7 @@
 	recorded with g+w bit set, which was created by older
 	versions of Git.  Existing repositories, including the
 	Linux kernel, Git itself, and sparse repository have old
-	objects that triggers this check, but it is recommended
+	objects that trigger this check, but it is recommended
 	to check new projects with this flag.
 
 --verbose::
diff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt
index 8238ead..8585d19 100644
--- a/Documentation/git-fsmonitor--daemon.txt
+++ b/Documentation/git-fsmonitor--daemon.txt
@@ -70,10 +70,10 @@
 will properly ignore these extra events, so performance may be affected
 but it will not cause an incorrect result.
 
-By default, the fsmonitor daemon refuses to work against network-mounted
+By default, the fsmonitor daemon refuses to work with network-mounted
 repositories; this may be overridden by setting `fsmonitor.allowRemote` to
 `true`. Note, however, that the fsmonitor daemon is not guaranteed to work
-correctly with all network-mounted repositories and such use is considered
+correctly with all network-mounted repositories, so such use is considered
 experimental.
 
 On Mac OS, the inter-process communication (IPC) between various Git
@@ -83,10 +83,10 @@
 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.
 
-By default, the socket is created in the `.git` directory, however, if the
-`.git` directory is on a network-mounted filesystem, it will be instead be
+By default, the socket is created in the `.git` directory.  However, if the
+`.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
+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
 filesystem in which to create the socket file.
 
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index a65c9aa..b5561c4 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -54,9 +54,17 @@
 be performed as well.
 
 
---cruft::
+--[no-]cruft::
 	When expiring unreachable objects, pack them separately into a
-	cruft pack instead of storing them as loose objects.
+	cruft pack instead of storing them as loose objects. `--cruft`
+	is on by default.
+
+--max-cruft-size=<n>::
+	When packing unreachable objects into a cruft pack, limit the
+	size of new cruft packs to be at most `<n>` bytes. Overrides any
+	value specified via the `gc.maxCruftSize` configuration. See
+	the `--max-cruft-size` option of linkgit:git-repack[1] for
+	more.
 
 --prune=<date>::
 	Prune loose objects older than date (default is 2 weeks ago,
@@ -77,9 +85,10 @@
 	instance running on this repository.
 
 --keep-largest-pack::
-	All packs except the largest pack and those marked with a
-	`.keep` files are consolidated into a single pack. When this
-	option is used, `gc.bigPackThreshold` is ignored.
+	All packs except the largest non-cruft pack, any packs marked
+	with a `.keep` file, and any cruft pack(s) are consolidated into
+	a single pack. When this option is used, `gc.bigPackThreshold`
+	is ignored.
 
 AGGRESSIVE
 ----------
diff --git a/Documentation/git-get-tar-commit-id.txt b/Documentation/git-get-tar-commit-id.txt
index ac44d85..b537bb4 100644
--- a/Documentation/git-get-tar-commit-id.txt
+++ b/Documentation/git-get-tar-commit-id.txt
@@ -20,7 +20,7 @@
 1024 bytes of input, thus its runtime is not influenced by the size
 of the tar archive very much.
 
-If no commit ID is found, 'git get-tar-commit-id' quietly exists with a
+If no commit ID is found, 'git get-tar-commit-id' quietly exits with a
 return code of 1.  This can happen if the archive had not been created
 using 'git archive' or if the first parameter of 'git archive' had been
 a tree ID instead of a commit ID or tag.
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index dabdbe8..1e6d7b6 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -28,7 +28,7 @@
 	   [-f <file>] [-e] <pattern>
 	   [--and|--or|--not|(|)|-e <pattern>...]
 	   [--recurse-submodules] [--parent-basename <basename>]
-	   [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
+	   [ [--[no-]exclude-standard] [--cached | --untracked | --no-index] | <tree>...]
 	   [--] [<pathspec>...]
 
 DESCRIPTION
@@ -45,13 +45,21 @@
 	Instead of searching tracked files in the working tree, search
 	blobs registered in the index file.
 
---no-index::
-	Search files in the current directory that is not managed by Git.
-
 --untracked::
 	In addition to searching in the tracked files in the working
 	tree, search also in untracked files.
 
+--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
+	`-r` option specified, but with some additional benefits, such as
+	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::
 	Also search in ignored files by not honoring the `.gitignore`
 	mechanism. Only useful with `--untracked`.
@@ -64,9 +72,9 @@
 --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 has no effect
-	if `--no-index` is given.
+	_<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::
@@ -178,7 +186,7 @@
 	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]).
+	variable `core.quotePath` (see linkgit:git-config[1]).
 
 -o::
 --only-matching::
@@ -248,8 +256,8 @@
 	a non-zero status.
 
 --threads <num>::
-	Number of grep worker threads to use.
-	See `grep.threads` in 'CONFIGURATION' for more information.
+	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.
@@ -332,13 +340,13 @@
 NOTES ON THREADS
 ----------------
 
-The `--threads` option (and the grep.threads configuration) will be ignored when
+The `--threads` option (and the `grep.threads` configuration) will be ignored when
 `--open-files-in-pager` is used, forcing a single-threaded execution.
 
 When grepping the object store (with `--cached` or giving tree objects), running
-with multiple threads might perform slower than single threaded if `--textconv`
-is given and there're too many text conversions. So if you experience low
-performance in this case, it might be desirable to use `--threads=1`.
+with multiple threads might perform slower than single-threaded if `--textconv`
+is given and there are too many text conversions.  Thus, if low performance is
+experienced in this case, it might be desirable to use `--threads=1`.
 
 CONFIGURATION
 -------------
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index 472b5bb..ef4719a 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-hash-object - Compute object ID and optionally creates a blob from a file
+git-hash-object - Compute object ID and optionally create an object from a file
 
 
 SYNOPSIS
@@ -25,7 +25,8 @@
 -------
 
 -t <type>::
-	Specify the type (default: "blob").
+	Specify the type of object to be created (default: "blob"). Possible
+	values are `commit`, `tree`, `blob`, and `tag`.
 
 -w::
 	Actually write the object into the object database.
@@ -38,10 +39,10 @@
 	of from the command-line.
 
 --path::
-	Hash object as it were located at the given path. The location of
-	file does not directly influence on the hash value, but path is
-	used to determine what Git filters should be applied to the object
-	before it can be placed to the object database, and, as result of
+	Hash object as if it were located at the given path. The location of
+	the file does not directly influence the hash value, but the path is
+	used to determine which Git filters should be applied to the object
+	before it can be placed in the object database.  As a result of
 	applying filters, the actual blob put into the object database may
 	differ from the given file. This option is mainly useful for hashing
 	temporary files located outside of the working directory or files
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index 2b0b5e3..f0bedc1 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -42,13 +42,13 @@
 
 To display the linkgit:git[1] man page, use `git help git`.
 
-This page can be displayed with 'git help help' or `git help --help`
+This page can be displayed with 'git help help' or `git help --help`.
 
 OPTIONS
 -------
 -a::
 --all::
-	Prints all the available commands on the standard output.
+	Print all the available commands on the standard output.
 
 --no-external-commands::
 	When used with `--all`, exclude the listing of external "git-*"
@@ -59,7 +59,7 @@
 	aliases.
 
 --verbose::
-	When used with `--all` print description for all recognized
+	When used with `--all`, print description for all recognized
 	commands. This is the default.
 
 -c::
@@ -69,10 +69,10 @@
 
 -g::
 --guides::
-	Prints a list of the Git concept guides on the standard output.
+	Print a list of the Git concept guides on the standard output.
 
 --user-interfaces::
-	Prints a list of the repository, command and file interfaces
+	Print a list of the repository, command and file interfaces
 	documentation on the standard output.
 +
 In-repository file interfaces such as `.git/info/exclude` are
@@ -85,7 +85,7 @@
 described in linkgit:githooks[5].
 
 --developer-interfaces::
-	Print list of file formats, protocols and other developer
+	Print a list of file formats, protocols and other developer
 	interfaces documentation on the standard output.
 
 -i::
@@ -109,7 +109,7 @@
 	format. A web browser will be used for that purpose.
 +
 The web browser can be specified using the configuration variable
-`help.browser`, or `web.browser` if the former is not set. If none of
+`help.browser`, or `web.browser` if the former is not set. If neither of
 these config variables is set, the 'git web{litdd}browse' helper script
 (called by 'git help') will pick a suitable default. See
 linkgit:git-web{litdd}browse[1] for more information about this.
@@ -129,8 +129,8 @@
 * "info" corresponds to '-i|--info',
 * "web" or "html" correspond to '-w|--web'.
 
-help.browser, web.browser and browser.<tool>.path
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help.browser, web.browser, and browser.<tool>.path
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The `help.browser`, `web.browser` and `browser.<tool>.path` will also
 be checked if the 'web' format is chosen (either by command-line
diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt
index 3407f3c..f6cc72d 100644
--- a/Documentation/git-hook.txt
+++ b/Documentation/git-hook.txt
@@ -13,7 +13,7 @@
 DESCRIPTION
 -----------
 
-A command interface to running git hooks (see linkgit:githooks[5]),
+A command interface for running git hooks (see linkgit:githooks[5]),
 for use by other scripted git commands.
 
 SUBCOMMANDS
@@ -32,7 +32,7 @@
 -------
 
 --to-stdin::
-	For "run"; Specify a file which will be streamed into the
+	For "run"; specify a file which will be streamed into the
 	hook's stdin. The hook will receive the entire file from
 	beginning to EOF.
 
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
index 0c5c0dd..f37ddad 100644
--- a/Documentation/git-http-backend.txt
+++ b/Documentation/git-http-backend.txt
@@ -23,7 +23,7 @@
 It verifies that the directory has the magic file
 "git-daemon-export-ok", and it will refuse to export any Git directory
 that hasn't explicitly been marked for export this way (unless the
-`GIT_HTTP_EXPORT_ALL` environmental variable is set).
+`GIT_HTTP_EXPORT_ALL` environment variable is set).
 
 By default, only the `upload-pack` service is enabled, which serves
 'git fetch-pack' and 'git ls-remote' clients, which are invoked from
@@ -42,12 +42,12 @@
 	any file within the repository, including objects that are
 	no longer reachable from a branch but are still present.
 	It is enabled by default, but a repository can disable it
-	by setting this configuration item to `false`.
+	by setting this configuration value to `false`.
 
 http.uploadpack::
 	This serves 'git fetch-pack' and 'git ls-remote' clients.
 	It is enabled by default, but a repository can disable it
-	by setting this configuration item to `false`.
+	by setting this configuration value to `false`.
 
 http.receivepack::
 	This serves 'git send-pack' clients, allowing push.  It is
@@ -265,12 +265,12 @@
 * QUERY_STRING
 * REQUEST_METHOD
 
-The `GIT_HTTP_EXPORT_ALL` environmental variable may be passed to
+The `GIT_HTTP_EXPORT_ALL` environment variable may be passed to
 'git-http-backend' to bypass the check for the "git-daemon-export-ok"
 file in each repository before allowing export of that repository.
 
 The `GIT_HTTP_MAX_REQUEST_BUFFER` environment variable (or the
-`http.maxRequestBuffer` config variable) may be set to change the
+`http.maxRequestBuffer` config option) may be set to change the
 largest ref negotiation request that git will handle during a fetch; any
 fetch requiring a larger buffer will not succeed.  This value should not
 normally need to be changed, but may be helpful if you are fetching from
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index 319062c..4ec7c68 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -31,7 +31,7 @@
 	Report what is downloaded.
 
 -w <filename>::
-        Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
+	Writes the commit-id into the specified filename under $GIT_DIR/refs/<filename> on
         the local end after the transfer is complete.
 
 --stdin::
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
index 7c6a6dd..ce0d808 100644
--- a/Documentation/git-http-push.txt
+++ b/Documentation/git-http-push.txt
@@ -13,12 +13,12 @@
 
 DESCRIPTION
 -----------
-Sends missing objects to remote repository, and updates the
+Sends missing objects to the remote repository, and updates the
 remote branch.
 
 *NOTE*: This command is temporarily disabled if your libcurl
 is older than 7.16, as the combination has been reported
-not to work and sometimes corrupts repository.
+not to work and sometimes corrupts the repository.
 
 OPTIONS
 -------
@@ -44,7 +44,7 @@
 -d::
 -D::
 	Remove <ref> from remote repository.  The specified branch
-	cannot be the remote HEAD.  If -d is specified the following
+	cannot be the remote HEAD.  If -d is specified, the following
 	other conditions must also be met:
 
 	- Remote HEAD must resolve to an object that exists locally
@@ -83,8 +83,8 @@
 Without `--force`, the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
 ancestor) of <src>.  This check, known as "fast-forward check",
-is performed in order to avoid accidentally overwriting the
-remote ref and lose other peoples' commits from there.
+is performed to avoid accidentally overwriting the
+remote ref and losing other peoples' commits from there.
 
 With `--force`, the fast-forward check is disabled for all refs.
 
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index f7b1851..c8a89d7 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -135,7 +135,7 @@
 
 Thunderbird in particular is known to be problematic.  Thunderbird
 users may wish to visit this web page for more information:
-  http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
+  https://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
 
 SEE ALSO
 --------
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 4e71c25..5a20dee 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -16,10 +16,10 @@
 
 DESCRIPTION
 -----------
-Reads a packed archive (.pack) from the specified file, and
-builds a pack index file (.idx) for it. Optionally writes a
+Reads a packed archive (.pack) from the specified file,
+builds a pack index file (.idx) for it, and optionally writes a
 reverse-index (.rev) for the specified pack. The packed
-archive together with the pack index can then be placed in
+archive, together with the pack index, can then be placed in
 the objects/pack/ directory of a Git repository.
 
 
@@ -68,8 +68,8 @@
 	updated to use objects contained in the pack.
 
 --keep=<msg>::
-	Like --keep create a .keep file before moving the index into
-	its final destination, but rather than creating an empty file
+	Like --keep, create a .keep file before moving the index into
+	its final destination.  However, instead of creating an empty file
 	place '<msg>' followed by an LF into the .keep file.  The '<msg>'
 	message can later be searched for within all .keep files to
 	locate any which have outlived their usefulness.
@@ -79,8 +79,13 @@
 	to force the version for the generated pack index, and to force
 	64-bit index entries on objects located above the given offset.
 
---strict::
-	Die, if the pack contains broken objects or links.
+--strict[=<msg-id>=<severity>...]::
+	Die, if the pack contains broken objects or links. An optional
+	comma-separated list of `<msg-id>=<severity>` can be passed to change
+	the severity of some possible issues, e.g.,
+	 `--strict="missingEmail=ignore,badTagName=error"`. See the entry for the
+	`fsck.<msg-id>` configuration options in linkgit:git-fsck[1] for more
+	information on the possible values of `<msg-id>` and `<severity>`.
 
 --progress-title::
 	For internal use only.
@@ -91,13 +96,18 @@
 --check-self-contained-and-connected::
 	Die if the pack contains broken links. For internal use only.
 
---fsck-objects::
-	For internal use only.
+--fsck-objects[=<msg-id>=<severity>...]::
+	Die if the pack contains broken objects, but unlike `--strict`, don't
+	choke on broken links. If the pack contains a tree pointing to a
+	.gitmodules blob that does not exist, prints the hash of that blob
+	(for the caller to check) after the hash that goes into the name of the
+	pack/idx file (see "Notes").
 +
-Die if the pack contains broken objects. If the pack contains a tree
-pointing to a .gitmodules blob that does not exist, prints the hash of
-that blob (for the caller to check) after the hash that goes into the
-name of the pack/idx file (see "Notes").
+An optional comma-separated list of `<msg-id>=<severity>` can be passed to
+change the severity of some possible issues, e.g.,
+`--fsck-objects="missingEmail=ignore,badTagName=ignore"`. See the entry for the
+`fsck.<msg-id>` configuration options in linkgit:git-fsck[1] for more
+information on the possible values of `<msg-id>` and `<severity>`.
 
 --threads=<n>::
 	Specifies the number of threads to spawn when resolving
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 160dea1..daff93b 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -9,10 +9,11 @@
 SYNOPSIS
 --------
 [verse]
-'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
-	  [--separate-git-dir <git-dir>] [--object-format=<format>]
-	  [-b <branch-name> | --initial-branch=<branch-name>]
-	  [--shared[=<permissions>]] [<directory>]
+`git init` [`-q` | `--quiet`] [`--bare`] [++--template=++__<template-directory>__]
+	  [`--separate-git-dir` _<git-dir>_] [++--object-format=++__<format>__]
+	  [++--ref-format=++__<format>__]
+	  [`-b` _<branch-name>_ | ++--initial-branch=++__<branch-name>__]
+	  [++--shared++[++=++__<permissions>__]] [_<directory>_]
 
 
 DESCRIPTION
@@ -29,94 +30,104 @@
 
 If the object storage directory is specified via the
 `$GIT_OBJECT_DIRECTORY` environment variable then the sha1 directories
-are created underneath - otherwise the default `$GIT_DIR/objects`
+are created underneath; otherwise, the default `$GIT_DIR/objects`
 directory is used.
 
-Running 'git init' in an existing repository is safe. It will not
+Running `git init` in an existing repository is safe. It will not
 overwrite things that are already there. The primary reason for
-rerunning 'git init' is to pick up newly added templates (or to move
-the repository to another place if --separate-git-dir is given).
+rerunning `git init` is to pick up newly added templates (or to move
+the repository to another place if `--separate-git-dir` is given).
 
 OPTIONS
 -------
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 
 Only print error and warning messages; all other output will be suppressed.
 
---bare::
+`--bare`::
 
 Create a bare repository. If `GIT_DIR` environment is not set, it is set to the
 current working directory.
 
---object-format=<format>::
+++--object-format=++__<format>__::
 
-Specify the given object format (hash algorithm) for the repository.  The valid
-values are 'sha1' and (if enabled) 'sha256'.  'sha1' is the default.
+Specify the given object _<format>_ (hash algorithm) for the repository.  The valid
+values are `sha1` and (if enabled) `sha256`.  `sha1` is the default.
 +
 include::object-format-disclaimer.txt[]
 
---template=<template-directory>::
+++--ref-format=++__<format>__::
+
+Specify the given ref storage _<format>_ for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
+++--template=++__<template-directory>__::
 
 Specify the directory from which templates will be used.  (See the "TEMPLATE
 DIRECTORY" section below.)
 
---separate-git-dir=<git-dir>::
+++--separate-git-dir=++__<git-dir>__::
 
 Instead of initializing the repository as a directory to either `$GIT_DIR` or
 `./.git/`, create a text file there containing the path to the actual
-repository.  This file acts as filesystem-agnostic Git symbolic link to the
+repository.  This file acts as a filesystem-agnostic Git symbolic link to the
 repository.
 +
-If this is reinitialization, the repository will be moved to the specified path.
+If this is a reinitialization, the repository will be moved to the specified path.
 
--b <branch-name>::
---initial-branch=<branch-name>::
+`-b` _<branch-name>_::
+++--initial-branch=++__<branch-name>__::
 
-Use the specified name for the initial branch in the newly created
+Use _<branch-name>_ for the initial branch in the newly created
 repository.  If not specified, fall back to the default name (currently
 `master`, but this is subject to change in the future; the name can be
 customized via the `init.defaultBranch` configuration variable).
 
---shared[=(false|true|umask|group|all|world|everybody|<perm>)]::
+++--shared++[++=++(`false`|`true`|`umask`|`group`|`all`|`world`|`everybody`|_<perm>_)]::
 
 Specify that the Git repository is to be shared amongst several users.  This
 allows users belonging to the same group to push into that
-repository.  When specified, the config variable "core.sharedRepository" is
+repository.  When specified, the config variable `core.sharedRepository` is
 set so that files and directories under `$GIT_DIR` are created with the
 requested permissions.  When not specified, Git will use permissions reported
-by umask(2).
+by `umask`(2).
 +
-The option can have the following values, defaulting to 'group' if no value
+The option can have the following values, defaulting to `group` if no value
 is given:
 +
 --
-'umask' (or 'false')::
+`umask`::
+`false`::
 
-Use permissions reported by umask(2). The default, when `--shared` is not
+Use permissions reported by `umask`(2). The default, when `--shared` is not
 specified.
 
-'group' (or 'true')::
+`group`::
+`true`::
 
-Make the repository group-writable, (and g+sx, since the git group may be not
+Make the repository group-writable, (and `g+sx`, since the git group may not be
 the primary group of all users). This is used to loosen the permissions of an
-otherwise safe umask(2) value. Note that the umask still applies to the other
-permission bits (e.g. if umask is '0022', using 'group' will not remove read
-privileges from other (non-group) users). See '0xxx' for how to exactly specify
+otherwise safe `umask`(2) value. Note that the umask still applies to the other
+permission bits (e.g. if umask is `0022`, using `group` will not remove read
+privileges from other (non-group) users). See `0xxx` for how to exactly specify
 the repository permissions.
 
-'all' (or 'world' or 'everybody')::
+`all`::
+`world`::
+`everybody`::
 
-Same as 'group', but make the repository readable by all users.
+Same as `group`, but make the repository readable by all users.
 
-'<perm>'::
+_<perm>_::
 
-'<perm>' is a 3-digit octal number prefixed with `0` and each file
-will have mode '<perm>'. '<perm>' will override users' umask(2)
-value (and not only loosen permissions as 'group' and 'all'
-does). '0640' will create a repository which is group-readable, but
-not group-writable or accessible to others. '0660' will create a repo
+_<perm>_ is a 3-digit octal number prefixed with `0` and each file
+will have mode _<perm>_. _<perm>_ will override users' `umask`(2)
+value (and not only loosen permissions as `group` and `all`
+do). `0640` will create a repository which is group-readable, but
+not group-writable or accessible to others. `0660` will create a repo
 that is readable and writable to the current user and group, but
 inaccessible to others (directories and executable files get their
 `x` bit from the `r` bit for corresponding classes of users).
@@ -126,7 +137,7 @@
 in shared repositories, so that you cannot force a non fast-forwarding push
 into it.
 
-If you provide a 'directory', the command is run inside it. If this directory
+If you provide a _<directory>_, the command is run inside it. If this directory
 does not exist, it will be created.
 
 TEMPLATE DIRECTORY
@@ -165,7 +176,7 @@
 $ git commit    <3>
 ----------------
 +
-<1> Create a /path/to/my/codebase/.git directory.
+<1> Create a `/path/to/my/codebase/.git` directory.
 <2> Add all existing files to the index.
 <3> Record the pristine state as the first commit in the history.
 
@@ -174,6 +185,8 @@
 
 include::includes/cmd-config-section-all.txt[]
 
+:git-init:
+
 include::config/init.txt[]
 
 GIT
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 22ff3a6..d9dfb75 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -9,68 +9,105 @@
 --------
 [verse]
 'git interpret-trailers' [--in-place] [--trim-empty]
-			[(--trailer <token>[(=|:)<value>])...]
+			[(--trailer (<key>|<key-alias>)[(=|:)<value>])...]
 			[--parse] [<file>...]
 
 DESCRIPTION
 -----------
-Help parsing or adding 'trailers' lines, that look similar to RFC 822 e-mail
+Add or parse 'trailer' lines that look similar to RFC 822 e-mail
 headers, at the end of the otherwise free-form part of a commit
-message.
+message. For example, in the following commit message
 
-This command reads some patches or commit messages from either the
-<file> arguments or the standard input if no <file> is specified. If
-`--parse` is specified, the output consists of the parsed trailers.
+------------------------------------------------
+subject
 
-Otherwise, this command applies the arguments passed using the
-`--trailer` option, if any, to the commit message part of each input
-file. The result is emitted on the standard output.
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+Signed-off-by: Alice <alice@example.com>
+Signed-off-by: Bob <bob@example.com>
+------------------------------------------------
+
+the last two lines starting with "Signed-off-by" are trailers.
+
+This command reads commit messages from either the
+<file> arguments or the standard input if no <file> is specified.
+If `--parse` is specified, the output consists of the parsed trailers
+coming from the input, without influencing them with any command line
+options or configuration variables.
+
+Otherwise, this command applies `trailer.*` configuration variables
+(which could potentially add new trailers, as well as reposition them),
+as well as any command line arguments that can override configuration
+variables (such as `--trailer=...` which could also add new trailers),
+to each input file. The result is emitted on the standard output.
+
+This command can also operate on the output of linkgit:git-format-patch[1],
+which is more elaborate than a plain commit message. Namely, such output
+includes a commit message (as above), a "---" divider line, and a patch part.
+For these inputs, the divider and patch parts are not modified by
+this command and are emitted as is on the output, unless
+`--no-divider` is specified.
 
 Some configuration variables control the way the `--trailer` arguments
-are applied to each commit message and the way any existing trailer in
-the commit message is changed. They also make it possible to
+are applied to each input and the way any existing trailer in
+the input is changed. They also make it possible to
 automatically add some trailers.
 
-By default, a '<token>=<value>' or '<token>:<value>' argument given
+By default, a '<key>=<value>' or '<key>:<value>' argument given
 using `--trailer` will be appended after the existing trailers only if
-the last trailer has a different (<token>, <value>) pair (or if there
-is no existing trailer). The <token> and <value> parts will be trimmed
+the last trailer has a different (<key>, <value>) pair (or if there
+is no existing trailer). The <key> and <value> parts will be trimmed
 to remove starting and trailing whitespace, and the resulting trimmed
-<token> and <value> will appear in the message like this:
+<key> and <value> will appear in the output like this:
 
 ------------------------------------------------
-token: value
+key: value
 ------------------------------------------------
 
-This means that the trimmed <token> and <value> will be separated by
+This means that the trimmed <key> and <value> will be separated by
 `': '` (one colon followed by one space).
 
+For convenience, a <key-alias> can be configured to make using `--trailer`
+shorter to type on the command line. This can be configured using the
+'trailer.<key-alias>.key' configuration variable. The <keyAlias> must be a prefix
+of the full <key> string, although case sensitivity does not matter. For
+example, if you have
+
+------------------------------------------------
+trailer.sign.key "Signed-off-by: "
+------------------------------------------------
+
+in your configuration, you only need to specify `--trailer="sign: foo"`
+on the command line instead of `--trailer="Signed-off-by: foo"`.
+
 By default the new trailer will appear at the end of all the existing
 trailers. If there is no existing trailer, the new trailer will appear
-after the commit message part of the output, and, if there is no line
-with only spaces at the end of the commit message part, one blank line
-will be added before the new trailer.
+at the end of the input. A blank line will be added before the new
+trailer if there isn't one already.
 
-Existing trailers are extracted from the input message by looking for
+Existing trailers are extracted from the input by looking for
 a group of one or more lines that (i) is all trailers, or (ii) contains at
 least one Git-generated or user-configured trailer and consists of at
 least 25% trailers.
 The group must be preceded by one or more empty (or whitespace-only) lines.
-The group must either be at the end of the message or be the last
+The group must either be at the end of the input or be the last
 non-whitespace lines before a line that starts with '---' (followed by a
-space or the end of the line). Such three minus signs start the patch
-part of the message. See also `--no-divider` below.
+space or the end of the line).
 
 When reading trailers, there can be no whitespace before or inside the
-token, but any number of regular space and tab characters are allowed
-between the token and the separator. There can be whitespaces before,
-inside or after the value. The value may be split over multiple lines
+<key>, but any number of regular space and tab characters are allowed
+between the <key> and the separator. There can be whitespaces before,
+inside or after the <value>. The <value> may be split over multiple lines
 with each subsequent line starting with at least one whitespace, like
-the "folding" in RFC 822.
+the "folding" in RFC 822. Example:
 
-Note that 'trailers' do not follow and are not intended to follow many
-rules for RFC 822 headers. For example they do not follow
-the encoding rules and probably many other rules.
+------------------------------------------------
+key: This is a very long value, with spaces and
+  newlines in it.
+------------------------------------------------
+
+Note that trailers do not follow (nor are they intended to follow) many of the
+rules for RFC 822 headers. For example they do not follow the encoding rule.
 
 OPTIONS
 -------
@@ -79,38 +116,47 @@
 
 --trim-empty::
 	If the <value> part of any trailer contains only whitespace,
-	the whole trailer will be removed from the resulting message.
+	the whole trailer will be removed from the output.
 	This applies to existing trailers as well as new trailers.
 
---trailer <token>[(=|:)<value>]::
-	Specify a (<token>, <value>) pair that should be applied as a
-	trailer to the input messages. See the description of this
+--trailer <key>[(=|:)<value>]::
+	Specify a (<key>, <value>) pair that should be applied as a
+	trailer to the inputs. See the description of this
 	command.
 
 --where <placement>::
 --no-where::
 	Specify where all new trailers will be added.  A setting
-	provided with '--where' overrides all configuration variables
+	provided with '--where' overrides the `trailer.where` and any
+	applicable `trailer.<keyAlias>.where` configuration variables
 	and applies to all '--trailer' options until the next occurrence of
-	'--where' or '--no-where'. Possible values are `after`, `before`,
-	`end` or `start`.
+	'--where' or '--no-where'. Upon encountering '--no-where', clear the
+	effect of any previous use of '--where', such that the relevant configuration
+	variables are no longer overridden. Possible placements are `after`,
+	`before`, `end` or `start`.
 
 --if-exists <action>::
 --no-if-exists::
 	Specify what action will be performed when there is already at
-	least one trailer with the same <token> in the message.  A setting
-	provided with '--if-exists' overrides all configuration variables
+	least one trailer with the same <key> in the input.  A setting
+	provided with '--if-exists' overrides the `trailer.ifExists` and any
+	applicable `trailer.<keyAlias>.ifExists` configuration variables
 	and applies to all '--trailer' options until the next occurrence of
-	'--if-exists' or '--no-if-exists'. Possible actions are `addIfDifferent`,
+	'--if-exists' or '--no-if-exists'. Upon encountering '--no-if-exists, clear the
+	effect of any previous use of '--if-exists, such that the relevant configuration
+	variables are no longer overridden. Possible actions are `addIfDifferent`,
 	`addIfDifferentNeighbor`, `add`, `replace` and `doNothing`.
 
 --if-missing <action>::
 --no-if-missing::
 	Specify what action will be performed when there is no other
-	trailer with the same <token> in the message.  A setting
-	provided with '--if-missing' overrides all configuration variables
+	trailer with the same <key> in the input.  A setting
+	provided with '--if-missing' overrides the `trailer.ifMissing` and any
+	applicable `trailer.<keyAlias>.ifMissing` configuration variables
 	and applies to all '--trailer' options until the next occurrence of
-	'--if-missing' or '--no-if-missing'. Possible actions are `doNothing`
+	'--if-missing' or '--no-if-missing'. Upon encountering '--no-if-missing,
+	clear the effect of any previous use of '--if-missing, such that the relevant
+	configuration variables are no longer overridden. Possible actions are `doNothing`
 	or `add`.
 
 --only-trailers::
@@ -118,16 +164,19 @@
 
 --only-input::
 	Output only trailers that exist in the input; do not add any
-	from the command-line or by following configured `trailer.*`
-	rules.
+	from the command-line or by applying `trailer.*` configuration
+	variables.
 
 --unfold::
-	Remove any whitespace-continuation in trailers, so that each
-	trailer appears on a line by itself with its full content.
+	If a trailer has a value that runs over multiple lines (aka "folded"),
+	reformat the value into a single line.
 
 --parse::
 	A convenience alias for `--only-trailers --only-input
-	--unfold`.
+	--unfold`. This makes it easier to only see the trailers coming from the
+	input without influencing them with any command line options or
+	configuration variables, while also making the output machine-friendly with
+	--unfold.
 
 --no-divider::
 	Do not treat `---` as the end of the commit message. Use this
@@ -148,11 +197,11 @@
 trailer.
 +
 For example, if the value for this option is "%=$", then only lines
-using the format '<token><sep><value>' with <sep> containing '%', '='
+using the format '<key><sep><value>' with <sep> containing '%', '='
 or '$' and then spaces will be considered trailers. And '%' will be
 the default separator used, so by default trailers will appear like:
-'<token>% <value>' (one percent sign and one space will appear between
-the token and the value).
+'<key>% <value>' (one percent sign and one space will appear between
+the key and the value).
 
 trailer.where::
 	This option tells where a new trailer will be added.
@@ -166,41 +215,41 @@
 instead of the end, of the existing trailers.
 +
 If it is `after`, then each new trailer will appear just after the
-last trailer with the same <token>.
+last trailer with the same <key>.
 +
 If it is `before`, then each new trailer will appear just before the
-first trailer with the same <token>.
+first trailer with the same <key>.
 
 trailer.ifexists::
 	This option makes it possible to choose what action will be
 	performed when there is already at least one trailer with the
-	same <token> in the message.
+	same <key> in the input.
 +
 The valid values for this option are: `addIfDifferentNeighbor` (this
 is the default), `addIfDifferent`, `add`, `replace` or `doNothing`.
 +
 With `addIfDifferentNeighbor`, a new trailer will be added only if no
-trailer with the same (<token>, <value>) pair is above or below the line
+trailer with the same (<key>, <value>) pair is above or below the line
 where the new trailer will be added.
 +
 With `addIfDifferent`, a new trailer will be added only if no trailer
-with the same (<token>, <value>) pair is already in the message.
+with the same (<key>, <value>) pair is already in the input.
 +
 With `add`, a new trailer will be added, even if some trailers with
-the same (<token>, <value>) pair are already in the message.
+the same (<key>, <value>) pair are already in the input.
 +
-With `replace`, an existing trailer with the same <token> will be
+With `replace`, an existing trailer with the same <key> will be
 deleted and the new trailer will be added. The deleted trailer will be
-the closest one (with the same <token>) to the place where the new one
+the closest one (with the same <key>) to the place where the new one
 will be added.
 +
 With `doNothing`, nothing will be done; that is no new trailer will be
-added if there is already one with the same <token> in the message.
+added if there is already one with the same <key> in the input.
 
 trailer.ifmissing::
 	This option makes it possible to choose what action will be
 	performed when there is not yet any trailer with the same
-	<token> in the message.
+	<key> in the input.
 +
 The valid values for this option are: `add` (this is the default) and
 `doNothing`.
@@ -209,100 +258,106 @@
 +
 With `doNothing`, nothing will be done.
 
-trailer.<token>.key::
-	This `key` will be used instead of <token> in the trailer. At
-	the end of this key, a separator can appear and then some
-	space characters. By default the only valid separator is ':',
-	but this can be changed using the `trailer.separators` config
-	variable.
+trailer.<keyAlias>.key::
+	Defines a <keyAlias> for the <key>. The <keyAlias> must be a
+	prefix (case does not matter) of the <key>. For example, in `git
+	config trailer.ack.key "Acked-by"` the "Acked-by" is the <key> and
+	the "ack" is the <keyAlias>. This configuration allows the shorter
+	`--trailer "ack:..."` invocation on the command line using the "ack"
+	<keyAlias> instead of the longer `--trailer "Acked-by:..."`.
 +
-If there is a separator, then the key will be used instead of both the
-<token> and the default separator when adding the trailer.
+At the end of the <key>, a separator can appear and then some
+space characters. By default the only valid separator is ':',
+but this can be changed using the `trailer.separators` config
+variable.
++
+If there is a separator in the key, then it overrides the default
+separator when adding the trailer.
 
-trailer.<token>.where::
+trailer.<keyAlias>.where::
 	This option takes the same values as the 'trailer.where'
 	configuration variable and it overrides what is specified by
-	that option for trailers with the specified <token>.
+	that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.ifexists::
+trailer.<keyAlias>.ifexists::
 	This option takes the same values as the 'trailer.ifexists'
 	configuration variable and it overrides what is specified by
-	that option for trailers with the specified <token>.
+	that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.ifmissing::
+trailer.<keyAlias>.ifmissing::
 	This option takes the same values as the 'trailer.ifmissing'
 	configuration variable and it overrides what is specified by
-	that option for trailers with the specified <token>.
+	that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.command::
-	This option behaves in the same way as 'trailer.<token>.cmd', except
+trailer.<keyAlias>.command::
+	Deprecated in favor of 'trailer.<keyAlias>.cmd'.
+	This option behaves in the same way as 'trailer.<keyAlias>.cmd', except
 	that it doesn't pass anything as argument to the specified command.
 	Instead the first occurrence of substring $ARG is replaced by the
-	value that would be passed as argument.
+	<value> that would be passed as argument.
 +
-The 'trailer.<token>.command' option has been deprecated in favor of
-'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+Note that $ARG in the user's command is
 only replaced once and that the original way of replacing $ARG is not safe.
 +
-When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
-for the same <token>, 'trailer.<token>.cmd' is used and
-'trailer.<token>.command' is ignored.
+When both 'trailer.<keyAlias>.cmd' and 'trailer.<keyAlias>.command' are given
+for the same <keyAlias>, 'trailer.<keyAlias>.cmd' is used and
+'trailer.<keyAlias>.command' is ignored.
 
-trailer.<token>.cmd::
-	This option can be used to specify a shell command that will be called:
-	once to automatically add a trailer with the specified <token>, and then
-	each time a '--trailer <token>=<value>' argument to modify the <value> of
-	the trailer that this option would produce.
+trailer.<keyAlias>.cmd::
+	This option can be used to specify a shell command that will be called
+	once to automatically add a trailer with the specified <keyAlias>, and then
+	called each time a '--trailer <keyAlias>=<value>' argument is specified to
+	modify the <value> of the trailer that this option would produce.
 +
 When the specified command is first called to add a trailer
-with the specified <token>, the behavior is as if a special
-'--trailer <token>=<value>' argument was added at the beginning
+with the specified <keyAlias>, the behavior is as if a special
+'--trailer <keyAlias>=<value>' argument was added at the beginning
 of the "git interpret-trailers" command, where <value>
 is taken to be the standard output of the command with any
 leading and trailing whitespace trimmed off.
 +
-If some '--trailer <token>=<value>' arguments are also passed
+If some '--trailer <keyAlias>=<value>' arguments are also passed
 on the command line, the command is called again once for each
-of these arguments with the same <token>. And the <value> part
+of these arguments with the same <keyAlias>. And the <value> part
 of these arguments, if any, will be passed to the command as its
 first argument. This way the command can produce a <value> computed
-from the <value> passed in the '--trailer <token>=<value>' argument.
+from the <value> passed in the '--trailer <keyAlias>=<value>' argument.
 
 EXAMPLES
 --------
 
 * Configure a 'sign' trailer with a 'Signed-off-by' key, and then
-  add two of these trailers to a message:
+  add two of these trailers to a commit message file:
 +
 ------------
 $ git config trailer.sign.key "Signed-off-by"
 $ cat msg.txt
 subject
 
-message
-$ cat msg.txt | git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>'
+body text
+$ git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>' <msg.txt
 subject
 
-message
+body text
 
 Signed-off-by: Alice <alice@example.com>
 Signed-off-by: Bob <bob@example.com>
 ------------
 
-* Use the `--in-place` option to edit a message file in place:
+* Use the `--in-place` option to edit a commit message file in place:
 +
 ------------
 $ cat msg.txt
 subject
 
-message
+body text
 
 Signed-off-by: Bob <bob@example.com>
 $ git interpret-trailers --trailer 'Acked-by: Alice <alice@example.com>' --in-place msg.txt
 $ cat msg.txt
 subject
 
-message
+body text
 
 Signed-off-by: Bob <bob@example.com>
 Acked-by: Alice <alice@example.com>
@@ -322,17 +377,30 @@
   'Signed-off-by: ' already, and show how it works:
 +
 ------------
+$ cat msg1.txt
+subject
+
+body text
 $ git config trailer.sign.key "Signed-off-by: "
 $ git config trailer.sign.ifmissing add
 $ git config trailer.sign.ifexists doNothing
-$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
-$ git interpret-trailers <<EOF
-> EOF
+$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
+$ git interpret-trailers --trailer sign <msg1.txt
+subject
+
+body text
 
 Signed-off-by: Bob <bob@example.com>
-$ git interpret-trailers <<EOF
-> Signed-off-by: Alice <alice@example.com>
-> EOF
+$ cat msg2.txt
+subject
+
+body text
+
+Signed-off-by: Alice <alice@example.com>
+$ git interpret-trailers --trailer sign <msg2.txt
+subject
+
+body text
 
 Signed-off-by: Alice <alice@example.com>
 ------------
@@ -357,18 +425,17 @@
 $ cat ~/bin/glog-find-author
 #!/bin/sh
 test -n "$1" && git log --author="$1" --pretty="%an <%ae>" -1 || true
+$ cat msg.txt
+subject
+
+body text
 $ git config trailer.help.key "Helped-by: "
 $ git config trailer.help.ifExists "addIfDifferentNeighbor"
 $ git config trailer.help.cmd "~/bin/glog-find-author"
-$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <<EOF
-> subject
->
-> message
->
-> EOF
+$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <msg.txt
 subject
 
-message
+body text
 
 Helped-by: Junio C Hamano <gitster@pobox.com>
 Helped-by: Christian Couder <christian.couder@gmail.com>
@@ -382,18 +449,17 @@
 $ cat ~/bin/glog-grep
 #!/bin/sh
 test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
+$ cat msg.txt
+subject
+
+body text
 $ git config trailer.ref.key "Reference-to: "
 $ git config trailer.ref.ifExists "replace"
 $ git config trailer.ref.cmd "~/bin/glog-grep"
-$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
-> subject
->
-> message
->
-> EOF
+$ git interpret-trailers --trailer="ref:Add copyright notices." <msg.txt
 subject
 
-message
+body text
 
 Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
 ------------
@@ -402,20 +468,23 @@
   commit that is related, and show how it works:
 +
 ------------
+$ cat msg.txt
+subject
+
+body text
+
+see: HEAD~2
+$ cat ~/bin/glog-ref
+#!/bin/sh
+git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14
 $ git config trailer.see.key "See-also: "
 $ git config trailer.see.ifExists "replace"
 $ git config trailer.see.ifMissing "doNothing"
-$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
-$ git interpret-trailers <<EOF
-> subject
-> 
-> message
-> 
-> see: HEAD~2
-> EOF
+$ git config trailer.see.cmd "glog-ref"
+$ git interpret-trailers --trailer=see <msg.txt
 subject
 
-message
+body text
 
 See-also: fe3187489d69c4 (subject of related commit)
 ------------
@@ -427,22 +496,21 @@
   to add a 'git-version' trailer:
 +
 ------------
-$ sed -e 's/ Z$/ /' >commit_template.txt <<EOF
-> ***subject***
-> 
-> ***message***
-> 
-> Fixes: Z
-> Cc: Z
-> Reviewed-by: Z
-> Signed-off-by: Z
-> EOF
+$ cat temp.txt
+***subject***
+
+***message***
+
+Fixes: Z
+Cc: Z
+Reviewed-by: Z
+Signed-off-by: Z
+$ sed -e 's/ Z$/ /' temp.txt > commit_template.txt
 $ git config commit.template commit_template.txt
-$ cat >.git/hooks/commit-msg <<EOF
-> #!/bin/sh
-> git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
-> mv "\$1.new" "\$1"
-> EOF
+$ cat .git/hooks/commit-msg
+#!/bin/sh
+git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
+mv "\$1.new" "\$1"
 $ chmod +x .git/hooks/commit-msg
 ------------
 
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 2a66cf8..5796821 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -120,11 +120,11 @@
 below can be used to show the changes made by each commit.
 
 Note that unless one of `--diff-merges` variants (including short
-`-m`, `-c`, and `--cc` options) is explicitly given, merge commits
+`-m`, `-c`, `--cc`, and `--dd` options) is explicitly given, merge commits
 will not show a diff, even if a diff format like `--patch` is
 selected, nor will they match search options like `-S`. The exception
 is when `--first-parent` is in use, in which case `first-parent` is
-the default format.
+the default format for merge commits.
 
 :git-log: 1
 :diff-merges-default: `off`
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 1abdd3c..d08c7da 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -25,12 +25,12 @@
 
 DESCRIPTION
 -----------
-This merges the file listing in the index with the actual working
+This command merges the file listing in the index with the actual working
 directory list, and shows different combinations of the two.
 
-One or more of the options below may be used to determine the files
+Several flags can be used to determine which files are
 shown, and each file may be printed multiple times if there are
-multiple entries in the index or multiple statuses are applicable for
+multiple entries in the index or if multiple statuses are applicable for
 the relevant file selection options.
 
 OPTIONS
@@ -62,7 +62,7 @@
 	matching an exclude pattern.  When showing "other" files
 	(i.e. when used with '-o'), show only those matched by an
 	exclude pattern.  Standard ignore rules are not automatically
-	activated, therefore at least one of the `--exclude*` options
+	activated; therefore, at least one of the `--exclude*` options
 	is required.
 
 -s::
@@ -119,8 +119,10 @@
 
 --exclude-per-directory=<file>::
 	Read additional exclude patterns that apply only to the
-	directory and its subdirectories in <file>.  Deprecated; use
-	--exclude-standard instead.
+	directory and its subdirectories in <file>.  If you are
+	trying to emulate the way Porcelain commands work, using
+	the `--exclude-standard` option instead is easier and more
+	thorough.
 
 --exclude-standard::
 	Add the standard Git exclusions: .git/info/exclude, .gitignore
@@ -141,7 +143,7 @@
 	Show status tags together with filenames.  Note that for
 	scripting purposes, linkgit:git-status[1] `--porcelain` and
 	linkgit:git-diff-files[1] `--name-status` are almost always
-	superior alternatives, and users should look at
+	superior alternatives; users should look at
 	linkgit:git-status[1] `--short` or linkgit:git-diff[1]
 	`--name-status` for more user-friendly alternatives.
 +
@@ -270,8 +272,14 @@
 
 objectmode::
 	The mode of the file which is recorded in the index.
+objecttype::
+	The object type of the file which is recorded in the index.
 objectname::
 	The name of the file which is recorded in the index.
+objectsize[:padded]::
+	The object size of the file which is recorded in the index
+	("-" if the object is a `commit` or `tree`).
+	It also supports a padded format of size with "%(objectsize:padded)".
 stage::
 	The stage of the file which is recorded in the index.
 eolinfo:index::
@@ -292,9 +300,8 @@
 flags --others or --ignored are specified.  linkgit:gitignore[5]
 specifies the format of exclude patterns.
 
-Generally, you should just use --exclude-standard, but for historical
-reasons the exclude patterns can be specified from the following
-places, in order:
+These exclude patterns can be specified from the following places,
+in order:
 
   1. The command-line flag --exclude=<pattern> specifies a
      single pattern.  Patterns are ordered in the same order
@@ -316,6 +323,18 @@
 by --exclude-per-directory is relative to the directory that the
 pattern file appears in.
 
+Generally, you should be able to use `--exclude-standard` when you
+want the exclude rules applied the same way as what Porcelain
+commands do.  To emulate what `--exclude-standard` specifies, you
+can give `--exclude-per-directory=.gitignore`, and then specify:
+
+  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.
+
+via the `--exclude-from=` option.
+
 SEE ALSO
 --------
 linkgit:git-read-tree[1], linkgit:gitignore[5]
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index ff3da54..1c4f696 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -96,27 +96,51 @@
 	separator (so `bar` matches `refs/heads/bar` but not
 	`refs/heads/foobar`).
 
+OUTPUT
+------
+
+The output is in the format:
+
+------------
+<oid> TAB <ref> LF
+------------
+
+When showing an annotated tag, unless `--refs` is given, two such
+lines are shown: one with the refname for the tag itself as `<ref>`,
+and another with `<ref>` followed by `^{}`. The `<oid>` on the latter
+line shows the name of the object the tag points at.
+
 EXAMPLES
 --------
 
+* List all references (including symbolics and pseudorefs), peeling
+  tags:
++
 ----
-$ git ls-remote --tags .
-d6602ec5194c87b0fc87103ca4d67251c76f233a	refs/tags/v0.99
-f25a265a342aed6041ab0cc484224d9ca54b6f41	refs/tags/v0.99.1
-7ceca275d047c90c0c7d5afb13ab97efdf51bd6e	refs/tags/v0.99.3
-c5db5456ae3b0873fc659c19fafdde22313cc441	refs/tags/v0.99.2
-0918385dbd9656cab0d1d81ba7453d49bbc16250	refs/tags/junio-gpg-pub
+$ git ls-remote
+27d43aaaf50ef0ae014b88bba294f93658016a2e	HEAD
+950264636c68591989456e3ba0a5442f93152c1a	refs/heads/main
+d9ab777d41f92a8c1684c91cfb02053d7dd1046b	refs/heads/next
+d4ca2e3147b409459955613c152220f4db848ee1	refs/tags/v2.40.0
+73876f4861cd3d187a4682290ab75c9dccadbc56	refs/tags/v2.40.0^{}
+----
 
+* List all references matching given patterns:
++
+----
 $ git ls-remote http://www.kernel.org/pub/scm/git/git.git master seen rc
 5fe978a5381f1fbad26a80e682ddd2a401966740	refs/heads/master
 c781a84b5204fb294c9ccc79f8b3baceeb32c061	refs/heads/seen
+----
 
-$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
-$ git ls-remote --tags korg v\*
-d6602ec5194c87b0fc87103ca4d67251c76f233a	refs/tags/v0.99
-f25a265a342aed6041ab0cc484224d9ca54b6f41	refs/tags/v0.99.1
-c5db5456ae3b0873fc659c19fafdde22313cc441	refs/tags/v0.99.2
-7ceca275d047c90c0c7d5afb13ab97efdf51bd6e	refs/tags/v0.99.3
+* List only tags matching a given wildcard pattern:
++
+----
+$ git ls-remote --tags http://www.kernel.org/pub/scm/git/git.git v\*
+485a869c64a68cc5795dd99689797c5900f4716d	refs/tags/v2.39.2
+cbf04937d5b9fcf0a76c28f69e6294e9e3ecd7e6	refs/tags/v2.39.2^{}
+d4ca2e3147b409459955613c152220f4db848ee1	refs/tags/v2.40.0
+73876f4861cd3d187a4682290ab75c9dccadbc56	refs/tags/v2.40.0^{}
 ----
 
 SEE ALSO
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index 0240adb..6572095 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -86,9 +86,9 @@
 --format=<format>::
 	A string that interpolates `%(fieldname)` from the result
 	being shown. It also interpolates `%%` to `%`, and
-	`%xx` where `xx` are hex digits interpolates to character
-	with hex code `xx`; for example `%00` interpolates to
-	`\0` (NUL), `%09` to `\t` (TAB) and `%0a` to `\n` (LF).
+	`%xNN` where `NN` are hex digits interpolates to character
+	with hex code `NN`; for example `%x00` interpolates to
+	`\0` (NUL), `%x09` to `\t` (TAB) and `%x0a` to `\n` (LF).
 	When specified, `--format` cannot be combined with other
 	format-altering options, including `--long`, `--name-only`
 	and `--object-only`.
@@ -145,7 +145,7 @@
 -----------
 
 Various values from structured fields can be used to interpolate
-into the resulting output. For each outputing line, the following
+into the resulting output. For each outputting line, the following
 names can be used:
 
 objectmode::
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index e3b2a88..3f0a666 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -34,7 +34,7 @@
 
 -b::
 	If any file doesn't begin with a From line, assume it is a
-	single mail message instead of signaling error.
+	single mail message instead of signaling an error.
 
 -d<prec>::
 	Instead of the default 4 digits with leading zeros,
diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt
index 805e5a2..51d0f7e 100644
--- a/Documentation/git-maintenance.txt
+++ b/Documentation/git-maintenance.txt
@@ -102,9 +102,9 @@
 	requested refs within `refs/prefetch/`. Also, tags are not updated.
 +
 This is done to avoid disrupting the remote-tracking branches. The end users
-expect these refs to stay unmoved unless they initiate a fetch.  With prefetch
-task, however, the objects necessary to complete a later real fetch would
-already be obtained, so the real fetch would go faster.  In the ideal case,
+expect these refs to stay unmoved unless they initiate a fetch.  However,
+with the prefetch task, the objects necessary to complete a later real fetch
+would already be obtained, making the real fetch faster.  In the ideal case,
 it will just become an update to a bunch of remote-tracking branches without
 any object transfer.
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index b01ba3d..5ab957c 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -18,7 +18,7 @@
 DESCRIPTION
 -----------
 
-'git merge-base' finds best common ancestor(s) between two commits to use
+'git merge-base' finds the best common ancestor(s) between two commits to use
 in a three-way merge.  One common ancestor is 'better' than another common
 ancestor if the latter is an ancestor of the former.  A common ancestor
 that does not have any better common ancestor is a 'best common
@@ -28,7 +28,7 @@
 OPERATION MODES
 ---------------
 
-As the most common special case, specifying only two commits on the
+In the most common special case, specifying only two commits on the
 command line means computing the merge base between the given two commits.
 
 More generally, among the two commits to compute the merge base from,
@@ -64,7 +64,7 @@
 	the two commits, but also takes into account the reflog of
 	<ref> to see if the history leading to <commit> forked from
 	an earlier incarnation of the branch <ref> (see discussion
-	on this mode below).
+	of this mode below).
 
 OPTIONS
 -------
@@ -88,7 +88,7 @@
 
 the merge base between 'A' and 'B' is '1'.
 
-Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the
+Given three commits 'A', 'B', and 'C', `git merge-base A B C` will compute the
 merge base between 'A' and a hypothetical commit 'M', which is a merge
 between 'B' and 'C'.  For example, with this topology:
 
@@ -130,7 +130,7 @@
 ---2---o---o---B
 ....
 
-both '1' and '2' are merge-bases of A and B.  Neither one is better than
+both '1' and '2' are merge bases of A and B.  Neither one is better than
 the other (both are 'best' merge bases).  When the `--all` option is not given,
 it is unspecified which best one is output.
 
@@ -204,7 +204,7 @@
 
     $ git rebase --onto origin/master $fork_point topic
 
-will replay D0, D1 and D on top of B to create a new history of this
+will replay D0, D1, and D on top of B to create a new history of this
 shape:
 
 ....
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 7e9093f..71915a0 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -11,19 +11,20 @@
 [verse]
 'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
 	[--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
-	[--[no-]diff3] <current-file> <base-file> <other-file>
+	[--[no-]diff3] [--object-id] <current> <base> <other>
 
 
 DESCRIPTION
 -----------
-'git merge-file' incorporates all changes that lead from the `<base-file>`
-to `<other-file>` into `<current-file>`. The result ordinarily goes into
-`<current-file>`. 'git merge-file' is useful for combining separate changes
-to an original. Suppose `<base-file>` is the original, and both
-`<current-file>` and `<other-file>` are modifications of `<base-file>`,
+Given three files `<current>`, `<base>` and `<other>`,
+'git merge-file' incorporates all changes that lead from `<base>`
+to `<other>` into `<current>`. The result ordinarily goes into
+`<current>`. 'git merge-file' is useful for combining separate changes
+to an original. Suppose `<base>` is the original, and both
+`<current>` and `<other>` are modifications of `<base>`,
 then 'git merge-file' combines both changes.
 
-A conflict occurs if both `<current-file>` and `<other-file>` have changes
+A conflict occurs if both `<current>` and `<other>` have changes
 in a common segment of lines. If a conflict is found, 'git merge-file'
 normally outputs a warning and brackets the conflict with lines containing
 <<<<<<< and >>>>>>> markers. A typical conflict will look like this:
@@ -36,10 +37,14 @@
 
 If there are conflicts, the user should edit the result and delete one of
 the alternatives.  When `--ours`, `--theirs`, or `--union` option is in effect,
-however, these conflicts are resolved favouring lines from `<current-file>`,
-lines from `<other-file>`, or lines from both respectively.  The length of the
+however, these conflicts are resolved favouring lines from `<current>`,
+lines from `<other>`, or lines from both respectively.  The length of the
 conflict markers can be given with the `--marker-size` option.
 
+If `--object-id` is specified, exactly the same behavior occurs, except that
+instead of specifying what to merge as files, it is specified as a list of
+object IDs referring to blobs.
+
 The exit value of this program is negative on error, and the number of
 conflicts otherwise (truncated to 127 if there are more than that many
 conflicts). If the merge was clean, the exit value is 0.
@@ -52,6 +57,14 @@
 OPTIONS
 -------
 
+--object-id::
+	Specify the contents to merge as blobs in the current repository instead of
+	files.  In this case, the operation must take place within a valid repository.
++
+If the `-p` option is specified, the merged file (including conflicts, if any)
+goes to standard output as normal; otherwise, the merged file is written to the
+object store and the object ID of its blob is written to standard output.
+
 -L <label>::
 	This option may be given up to three times, and
 	specifies labels to be used in place of the
@@ -62,7 +75,7 @@
 
 -p::
 	Send results to standard output instead of overwriting
-	`<current-file>`.
+	`<current>`.
 
 -q::
 	Quiet; do not warn about conflicts.
@@ -79,6 +92,12 @@
 	Instead of leaving conflicts in the file, resolve conflicts
 	favouring our (or their or both) side of the lines.
 
+--diff-algorithm={patience|minimal|histogram|myers}::
+	Use a different diff algorithm while merging. The current default is "myers",
+	but selecting more recent algorithm such as "histogram" can help
+	avoid mismerges that occur due to unimportant matching lines
+	(such as braces from distinct functions). See also
+	linkgit:git-diff[1] `--diff-algorithm`.
 
 EXAMPLES
 --------
@@ -93,6 +112,11 @@
 	merges tmp/a123 and tmp/c345 with the base tmp/b234, but uses labels
 	`a` and `c` instead of `tmp/a123` and `tmp/c345`.
 
+`git merge-file -p --object-id abc1234 def567 890abcd`::
+
+	combines the changes of the blob abc1234 and 890abcd since def567,
+	tries to merge them and writes the result to standard output
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index 88ee942..dd388fa 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -19,12 +19,12 @@
 This command has a modern `--write-tree` mode and a deprecated
 `--trivial-merge` mode.  With the exception of the
 <<DEPMERGE,DEPRECATED DESCRIPTION>> section at the end, the rest of
-this documentation describes modern `--write-tree` mode.
+this documentation describes the modern `--write-tree` mode.
 
 Performs a merge, but does not make any new commits and does not read
 from or write to either the working tree or index.
 
-The performed merge will use the same feature as the "real"
+The performed merge will use the same features as the "real"
 linkgit:git-merge[1], including:
 
   * three way content merges of individual files
@@ -64,10 +64,13 @@
 	share no common history.  This flag can be given to override that
 	check and make the merge proceed anyway.
 
---merge-base=<commit>::
+--merge-base=<tree-ish>::
 	Instead of finding the merge-bases for <branch1> and <branch2>,
 	specify a merge-base for the merge, and specifying multiple bases is
 	currently not supported. This option is incompatible with `--stdin`.
++
+As the merge-base is provided directly, <branch1> and <branch2> do not need
+to specify commits; trees are enough.
 
 [[OUTPUT]]
 OUTPUT
@@ -108,7 +111,7 @@
 
      0: merge had conflicts
      1: merge was clean
-     &lt;0: something prevented the merge from running (e.g. access to repository
+     <0: something prevented the merge from running (e.g. access to repository
 	 objects denied by filesystem)
 
 [[OIDTLT]]
@@ -253,7 +256,7 @@
 the <<CFI,Conflicted file info>> list.  The information there is
 insufficient to do so.  For example: Rename/rename(1to2) conflicts (both
 sides renamed the same file differently) will result in three different
-file having higher order stages (but each only has one higher order
+files having higher order stages (but each only has one higher order
 stage), with no way (short of the <<IM,Informational messages>> section)
 to determine which three files are related.  File/directory conflicts
 also result in a file with exactly one higher order stage.
@@ -263,7 +266,7 @@
 <<IM,Informational messages>> section has the necessary info, though it
 is not designed to be machine parseable.
 
-Do NOT assume that each paths from <<CFI,Conflicted file info>>, and
+Do NOT assume that each path from <<CFI,Conflicted file info>>, and
 the logical conflicts in the <<IM,Informational messages>> have a
 one-to-one mapping, nor that there is a one-to-many mapping, nor a
 many-to-one mapping.  Many-to-many mappings exist, meaning that each
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 0aeff57..1ab69f6 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -20,12 +20,12 @@
 -----------
 Incorporates changes from the named commits (since the time their
 histories diverged from the current branch) into the current
-branch.  This command is used by 'git pull' to incorporate changes
+branch.  This command is used by `git pull` to incorporate changes
 from another repository and can be used by hand to merge changes
 from one branch into another.
 
 Assume the following history exists and the current branch is
-"`master`":
+`master`:
 
 ------------
 	  A---B---C topic
@@ -33,7 +33,7 @@
     D---E---F---G master
 ------------
 
-Then "`git merge topic`" will replay the changes made on the
+Then `git merge topic` will replay the changes made on the
 `topic` branch since it diverged from `master` (i.e., `E`) until
 its current commit (`C`) on top of `master`, and record the result
 in a new commit along with the names of the two parent commits and
@@ -46,21 +46,21 @@
     D---E---F---G---H master
 ------------
 
-The second syntax ("`git merge --abort`") can only be run after the
-merge has resulted in conflicts. 'git merge --abort' will abort the
-merge process and try to reconstruct the pre-merge state. However,
-if there were uncommitted changes when the merge started (and
-especially if those changes were further modified after the merge
-was started), 'git merge --abort' will in some cases be unable to
-reconstruct the original (pre-merge) changes. Therefore:
+A merge stops if there's a conflict that cannot be resolved
+automatically or if `--no-commit` was provided when initiating the
+merge. At that point you can run `git merge --abort` or `git merge
+--continue`.
 
-*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+`git merge --abort` will abort the merge process and try to reconstruct
+the pre-merge state. However, if there were uncommitted changes when the
+merge started (and especially if those changes were further modified
+after the merge was started), `git merge --abort` will in some cases be
+unable to reconstruct the original (pre-merge) changes. Therefore:
+
+*Warning*: Running `git merge` with non-trivial uncommitted changes is
 discouraged: while possible, it may leave you in a state that is hard to
 back out of in the case of a conflict.
 
-The third syntax ("`git merge --continue`") can only be run after the
-merge has resulted in conflicts.
-
 OPTIONS
 -------
 :git-merge: 1
@@ -74,8 +74,8 @@
 If `--log` is specified, a shortlog of the commits being merged
 will be appended to the specified message.
 +
-The 'git fmt-merge-msg' command can be
-used to give a good default for automated 'git merge'
+The `git fmt-merge-msg` command can be
+used to give a good default for automated `git merge`
 invocations. The automated message can include the branch description.
 
 --into-name <branch>::
@@ -104,14 +104,14 @@
 	present, apply it to the worktree.
 +
 If there were uncommitted worktree changes present when the merge
-started, 'git merge --abort' will in some cases be unable to
+started, `git merge --abort` will in some cases be unable to
 reconstruct these changes. It is therefore recommended to always
-commit or stash your changes before running 'git merge'.
+commit or stash your changes before running `git merge`.
 +
-'git merge --abort' is equivalent to 'git reset --merge' when
+`git merge --abort` is equivalent to `git reset --merge` when
 `MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in
-which case 'git merge --abort' applies the stash entry to the worktree
-whereas 'git reset --merge' will save the stashed changes in the stash
+which case `git merge --abort` applies the stash entry to the worktree
+whereas `git reset --merge` will save the stashed changes in the stash
 list.
 
 --quit::
@@ -120,8 +120,8 @@
 	stash entry will be saved to the stash list.
 
 --continue::
-	After a 'git merge' stops due to conflicts you can conclude the
-	merge by running 'git merge --continue' (see "HOW TO RESOLVE
+	After a `git merge` stops due to conflicts you can conclude the
+	merge by running `git merge --continue` (see "HOW TO RESOLVE
 	CONFLICTS" section below).
 
 <commit>...::
@@ -144,25 +144,25 @@
 Before applying outside changes, you should get your own work in
 good shape and committed locally, so it will not be clobbered if
 there are conflicts.  See also linkgit:git-stash[1].
-'git pull' and 'git merge' will stop without doing anything when
-local uncommitted changes overlap with files that 'git pull'/'git
-merge' may need to update.
+`git pull` and `git merge` will stop without doing anything when
+local uncommitted changes overlap with files that `git pull`/`git
+merge` may need to update.
 
 To avoid recording unrelated changes in the merge commit,
-'git pull' and 'git merge' will also abort if there are any changes
+`git pull` and `git merge` will also abort if there are any changes
 registered in the index relative to the `HEAD` commit.  (Special
 narrow exceptions to this rule may exist depending on which merge
 strategy is in use, but generally, the index must match HEAD.)
 
-If all named commits are already ancestors of `HEAD`, 'git merge'
+If all named commits are already ancestors of `HEAD`, `git merge`
 will exit early with the message "Already up to date."
 
 FAST-FORWARD MERGE
 ------------------
 
 Often the current branch head is an ancestor of the named commit.
-This is the most common case especially when invoked from 'git
-pull': you are tracking an upstream repository, you have committed
+This is the most common case especially when invoked from `git
+pull`: you are tracking an upstream repository, you have committed
 no local changes, and now you want to update to a newer upstream
 revision.  In this case, a new commit is not needed to store the
 combined history; instead, the `HEAD` (along with the index) is
@@ -194,9 +194,13 @@
    versions: stage 1 stores the version from the common ancestor,
    stage 2 from `HEAD`, and stage 3 from `MERGE_HEAD` (you
    can inspect the stages with `git ls-files -u`).  The working
-   tree files contain the result of the "merge" program; i.e. 3-way
+   tree files contain the result of the merge operation; i.e. 3-way
    merge results with familiar conflict markers `<<<` `===` `>>>`.
-5. No other changes are made.  In particular, the local
+5. A ref named `AUTO_MERGE` is written, pointing to a tree
+   corresponding to the current content of the working tree (including
+   conflict markers for textual conflicts).  Note that this ref is only
+   written when the 'ort' merge strategy is used (the default).
+6. No other changes are made.  In particular, the local
    modifications you had before you started merge will stay the
    same and the index entries for them stay as they were,
    i.e. matching `HEAD`.
@@ -265,7 +269,7 @@
 side wants to say it is hard and you'd prefer to go shopping, while the
 other side wants to claim it is easy.
 
-An alternative style can be used by setting the "merge.conflictStyle"
+An alternative style can be used by setting the `merge.conflictStyle`
 configuration variable to either "diff3" or "zdiff3".  In "diff3"
 style, the above conflict may look like this:
 
@@ -324,19 +328,20 @@
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
-   'git add' them to the index.  Use 'git commit' or
-   'git merge --continue' to seal the deal. The latter command
+   `git add` them to the index.  Use `git commit` or
+   `git merge --continue` to seal the deal. The latter command
    checks whether there is a (interrupted) merge in progress
-   before calling 'git commit'.
+   before calling `git commit`.
 
 You can work through the conflict with a number of tools:
 
  * Use a mergetool.  `git mergetool` to launch a graphical
-   mergetool which will work you through the merge.
+   mergetool which will work through the merge with you.
 
  * Look at the diffs.  `git diff` will show a three-way diff,
    highlighting changes from both the `HEAD` and `MERGE_HEAD`
-   versions.
+   versions. `git diff AUTO_MERGE` will show what changes you've
+   made so far to resolve textual conflicts.
 
  * Look at the diffs from each branch. `git log --merge -p <path>`
    will show diffs first for the `HEAD` version and then the
@@ -387,7 +392,7 @@
 
 branch.<name>.mergeOptions::
 	Sets default options for merging into branch <name>. The syntax and
-	supported options are the same as those of 'git merge', but option
+	supported options are the same as those of `git merge`, but option
 	values containing whitespace characters are currently not supported.
 
 include::includes/cmd-config-section-rest.txt[]
diff --git a/Documentation/git-mergetool--lib.txt b/Documentation/git-mergetool--lib.txt
index 3e8f59a..0726b56 100644
--- a/Documentation/git-mergetool--lib.txt
+++ b/Documentation/git-mergetool--lib.txt
@@ -28,22 +28,22 @@
 FUNCTIONS
 ---------
 get_merge_tool::
-	returns a merge tool. the return code is 1 if we returned a guessed
+	Returns a merge tool. The return code is 1 if we returned a guessed
 	merge tool, else 0. '$GIT_MERGETOOL_GUI' may be set to 'true' to
 	search for the appropriate guitool.
 
 get_merge_tool_cmd::
-	returns the custom command for a merge tool.
+	Returns the custom command for a merge tool.
 
 get_merge_tool_path::
-	returns the custom path for a merge tool.
+	Returns the custom path for a merge tool.
 
 initialize_merge_tool::
-	bring merge tool specific functions into scope so they can be used or
+	Brings merge tool specific functions into scope so they can be used or
 	overridden.
 
 run_merge_tool::
-	launches a merge tool given the tool name and a true/false
+	Launches a merge tool given the tool name and a true/false
 	flag to indicate whether a merge base is present.
 	'$MERGED', '$LOCAL', '$REMOTE', and '$BASE' must be defined
 	for use by the merge tool.
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index c44e205..b9e20c5 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -17,7 +17,7 @@
 merge conflicts.  It is typically run after 'git merge'.
 
 If one or more <file> parameters are given, the merge tool program will
-be run to resolve differences on each file (skipping those without
+be run to resolve differences in each file (skipping those without
 conflicts).  Specifying a directory will include all unresolved files in
 that path.  If no <file> names are specified, 'git mergetool' will run
 the merge tool program on every file with merge conflicts.
@@ -49,7 +49,7 @@
 +
 When 'git mergetool' is invoked with this tool (either through the
 `-t` or `--tool` option or the `merge.tool` configuration
-variable) the configured command line will be invoked with `$BASE`
+variable), the configured command line will be invoked with `$BASE`
 set to the name of a temporary file containing the common base for
 the merge, if available; `$LOCAL` set to the name of a temporary
 file containing the contents of the file on the current branch;
@@ -81,16 +81,17 @@
 
 -g::
 --gui::
-	When 'git-mergetool' is invoked with the `-g` or `--gui` option
+	When 'git-mergetool' is invoked with the `-g` or `--gui` option,
 	the default merge tool will be read from the configured
 	`merge.guitool` variable instead of `merge.tool`. If
 	`merge.guitool` is not set, we will fallback to the tool
-	configured under `merge.tool`.
+	configured under `merge.tool`. This may be autoselected using
+	the configuration variable `mergetool.guiDefault`.
 
 --no-gui::
-	This overrides a previous `-g` or `--gui` setting and reads the
-	default merge tool will be read from the configured `merge.tool`
-	variable.
+	This overrides a previous `-g` or `--gui` setting or
+	`mergetool.guiDefault` configuration and reads the default merge
+	tool from the configured `merge.tool` variable.
 
 -O<orderfile>::
 	Process files in the order specified in the
@@ -114,7 +115,7 @@
 `git mergetool` session has completed.
 
 Setting the `mergetool.keepBackup` configuration variable to `false`
-causes `git mergetool` to automatically remove the backup as files
+causes `git mergetool` to automatically remove the backup files as files
 are successfully merged.
 
 BACKEND SPECIFIC HINTS
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index 466a697..006d759 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -14,7 +14,7 @@
 DESCRIPTION
 -----------
 
-Reads a tag contents on standard input and creates a tag object. The
+Reads a tag's contents on standard input and creates a tag object. The
 output is the new tag's <object> identifier.
 
 This command is mostly equivalent to linkgit:git-hash-object[1]
@@ -27,13 +27,13 @@
 The difference is that mktag will die before writing the tag if the
 tag doesn't pass a linkgit:git-fsck[1] check.
 
-The "fsck" check done mktag is stricter than what linkgit:git-fsck[1]
+The "fsck" check done by mktag is stricter than what linkgit:git-fsck[1]
 would run by default in that all `fsck.<msg-id>` messages are promoted
 from warnings to errors (so e.g. a missing "tagger" line is an error).
 
 Extra headers in the object are also an error under mktag, but ignored
 by linkgit:git-fsck[1]. This extra check can be turned off by setting
-the appropriate `fsck.<msg-id>` varible:
+the appropriate `fsck.<msg-id>` variable:
 
     git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers
 
@@ -56,7 +56,7 @@
   tagger <tagger>
 
 followed by some 'optional' free-form message (some tags created
-by older Git may not have `tagger` line).  The message, when it
+by older Git may not have a `tagger` line).  The message, when it
 exists, is separated by a blank line from the header.  The
 message part may contain a signature that Git itself doesn't
 care about, but that can be verified with gpg.
diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt
index 76b44f4..383f09d 100644
--- a/Documentation/git-mktree.txt
+++ b/Documentation/git-mktree.txt
@@ -25,13 +25,13 @@
 
 --missing::
 	Allow missing objects.  The default behaviour (without this option)
-	is to verify that each tree entry's sha1 identifies an existing
+	is to verify that each tree entry's hash identifies an existing
 	object.  This option has no effect on the treatment of gitlink entries
 	(aka "submodules") which are always allowed to be missing.
 
 --batch::
 	Allow building of more than one tree object before exiting.  Each
-	tree is separated by a single blank line. The final new-line is
+	tree is separated by a single blank line. The final newline is
 	optional.  Note - if the `-z` option is used, lines are terminated
 	with NUL.
 
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index fb0220f..dc1bf61 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -13,10 +13,10 @@
 
 DESCRIPTION
 -----------
-Move or rename a file, directory or symlink.
+Move or rename a file, directory, or symlink.
 
  git mv [-v] [-f] [-n] [-k] <source> <destination>
- git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
+ git mv [-v] [-f] [-n] [-k] <source> ... <destination-directory>
 
 In the first form, it renames <source>, which must exist and be either
 a file, symlink or directory, to <destination>.
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index ec8a27c..d4f1c4d 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git name-rev' [--tags] [--refs=<pattern>]
-	       ( --all | --stdin | <commit-ish>... )
+	       ( --all | --annotate-stdin | <commit-ish>... )
 
 DESCRIPTION
 -----------
@@ -26,7 +26,7 @@
 
 --refs=<pattern>::
 	Only use refs whose names match a given shell pattern.  The pattern
-	can be one of branch name, tag name or fully qualified ref name. If
+	can be a branch name, a tag name, or a fully qualified ref name. If
 	given multiple times, use refs whose names match any of the given shell
 	patterns. Use `--no-refs` to clear any previous ref patterns given.
 
@@ -46,7 +46,8 @@
 	Transform stdin by substituting all the 40-character SHA-1
 	hexes (say $hex) with "$hex ($rev_name)".  When used with
 	--name-only, substitute with "$rev_name", omitting $hex
-	altogether.
+	altogether. This option was called `--stdin` in older versions
+	of Git.
 +
 For example:
 +
@@ -70,10 +71,6 @@
 while its tree object is 70d105cc79e63b81cfdcb08a15297c23e60b07ad
 -----------
 
---stdin::
-	This option is deprecated in favor of 'git name-rev --annotate-stdin'.
-	They are functionally equivalent.
-
 --name-only::
 	Instead of printing both the SHA-1 and the name, print only
 	the name.  If given with --tags the usual tag prefix of
@@ -107,7 +104,7 @@
 Another nice thing you can do is:
 
 ------------
-% git log | git name-rev --stdin
+% git log | git name-rev --annotate-stdin
 ------------
 
 GIT
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index efbc10f..c9221a6 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -9,10 +9,10 @@
 --------
 [verse]
 'git notes' [list [<object>]]
-'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' add [-f] [--allow-empty] [--[no-]separator | --separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
 'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
-'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' edit [--allow-empty] [<object>]
+'git notes' append [--allow-empty] [--[no-]separator | --separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' edit [--allow-empty] [<object>] [--[no-]stripspace]
 'git notes' show [<object>]
 'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
 'git notes' merge --commit [-v | -q]
@@ -56,7 +56,7 @@
 list::
 	List the notes object for a given object. If no object is
 	given, show a list of all note objects and the objects they
-	annotate (in the format "<note object> <annotated object>").
+	annotate (in the format "<note-object> <annotated-object>").
 	This is the default subcommand if no subcommand is given.
 
 add::
@@ -65,7 +65,9 @@
 	However, if you're using `add` interactively (using an editor
 	to supply the notes contents), then - instead of aborting -
 	the existing notes will be opened in the editor (like the `edit`
-	subcommand).
+	subcommand). If you specify multiple `-m` and `-F`, a blank
+	line will be inserted between the messages. Use the `--separator`
+	option to insert other delimiters.
 
 copy::
 	Copy the notes for the first object onto the second object (defaults to
@@ -85,8 +87,12 @@
 the command can read the input given to the `post-rewrite` hook.)
 
 append::
-	Append to the notes of an existing object (defaults to HEAD).
-	Creates a new notes object if needed.
+	Append new message(s) given by `-m` or `-F` options to an
+	existing note, or add them as a new note if one does not
+	exist, for the object (defaults to HEAD).  When appending to
+	an existing note, a blank line is added before each new
+	message as an inter-paragraph separator.  The separator can
+	be customized with the `--separator` option.
 
 edit::
 	Edit the notes for a given object (defaults to HEAD).
@@ -136,6 +142,7 @@
 	are concatenated as separate paragraphs.
 	Lines starting with `#` and empty lines other than a
 	single line between paragraphs will be stripped out.
+	If you wish to keep them verbatim, use `--no-stripspace`.
 
 -F <file>::
 --file=<file>::
@@ -143,12 +150,16 @@
 	read the note message from the standard input.
 	Lines starting with `#` and empty lines other than a
 	single line between paragraphs will be stripped out.
+	If you wish to keep them verbatim, use `--no-stripspace`.
 
 -C <object>::
 --reuse-message=<object>::
 	Take the given blob object (for example, another note) as the
 	note message. (Use `git notes copy <object>` instead to
-	copy notes between objects.)
+	copy notes between objects.).  By default, message will be
+	copied verbatim, but if you wish to strip out the lines
+	starting with `#` and empty lines other than a single line
+	between paragraphs, use with`--stripspace` option.
 
 -c <object>::
 --reedit-message=<object>::
@@ -159,6 +170,19 @@
 	Allow an empty note object to be stored. The default behavior is
 	to automatically remove empty notes.
 
+--[no-]separator, --separator=<paragraph-break>::
+	Specify a string used as a custom inter-paragraph separator
+	(a newline is added at the end as needed). If `--no-separator`, no
+	separators will be added between paragraphs.  Defaults to a blank
+	line.
+
+--[no-]stripspace::
+	Strip leading and trailing whitespace from the note message.
+	Also strip out empty lines other than a single line between
+	paragraphs. Lines starting with `#` will be stripped out
+	in non-editor cases like `-m`, `-F` and `-C`, but not in
+	editor case like `git notes edit`, `-c`, etc.
+
 --ref <ref>::
 	Manipulate the notes tree in <ref>.  This overrides
 	`GIT_NOTES_REF` and the "core.notesRef" configuration.  The ref
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index a9995a9..e32404c 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -116,9 +116,7 @@
 +
 Incompatible with `--unpack-unreachable`, `--keep-unreachable`,
 `--pack-loose-unreachable`, `--stdin-packs`, as well as any other
-options which imply `--revs`. Also incompatible with `--max-pack-size`;
-when this option is set, the maximum pack size is not inferred from
-`pack.packSizeLimit`.
+options which imply `--revs`.
 
 --cruft-expiration=<approxidate>::
 	If specified, objects are eliminated from the cruft pack if they
@@ -298,8 +296,8 @@
 	nevertheless.
 
 --filter=<filter-spec>::
-	Requires `--stdout`.  Omits certain objects (usually blobs) from
-	the resulting packfile.  See linkgit:git-rev-list[1] for valid
+	Omits certain objects (usually blobs) from the resulting
+	packfile.  See linkgit:git-rev-list[1] for valid
 	`<filter-spec>` forms.
 
 --no-filter::
diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt
index 99ef138..13c3eb5 100644
--- a/Documentation/git-pack-redundant.txt
+++ b/Documentation/git-pack-redundant.txt
@@ -11,6 +11,20 @@
 [verse]
 'git pack-redundant' [--verbose] [--alt-odb] (--all | <pack-filename>...)
 
+WARNING
+-------
+`git pack-redundant` has been deprecated and is scheduled for removal in
+a future version of Git. Because it can only remove entire duplicate
+packs and not individual duplicate objects, it is generally not a useful
+tool for reducing repository size. You are better off using `git gc` to
+do so, which will put objects into a new pack, removing duplicates.
+
+Running `pack-redundant` without the `--i-still-use-this` flag will fail
+in this release. If you believe you have a use case for which
+`pack-redundant` is better suited and oppose this removal, please
+contact the Git mailing list at git@vger.kernel.org. More information
+about the list is available at https://git-scm.com/community.
+
 DESCRIPTION
 -----------
 This program computes which packs in your repository
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
index 154081f..2dcabaf 100644
--- a/Documentation/git-pack-refs.txt
+++ b/Documentation/git-pack-refs.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git pack-refs' [--all] [--no-prune]
+'git pack-refs' [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]
 
 DESCRIPTION
 -----------
@@ -51,14 +51,50 @@
 packed, and leaves other refs
 alone.  This is because branches are expected to be actively
 developed and packing their tips does not help performance.
-This option causes branch tips to be packed as well.  Useful for
-a repository with many branches of historical interests.
+This option causes all refs to be packed as well, with the exception
+of hidden refs, broken refs, and symbolic refs. Useful for a repository
+with many branches of historical interests.
 
 --no-prune::
 
 The command usually removes loose refs under `$GIT_DIR/refs`
 hierarchy after packing them.  This option tells it not to.
 
+--auto::
+
+Pack refs as needed depending on the current state of the ref database. The
+behavior depends on the ref format used by the repository and may change in the
+future.
++
+	- "files": No special handling for `--auto` has been implemented.
++
+	- "reftable": Tables are compacted such that they form a geometric
+	  sequence. For two tables N and N+1, where N+1 is newer, this
+	  maintains the property that N is at least twice as big as N+1. Only
+	  tables that violate this property are compacted.
+
+--include <pattern>::
+
+Pack refs based on a `glob(7)` pattern. Repetitions of this option
+accumulate inclusion patterns. If a ref is both included in `--include` and
+`--exclude`, `--exclude` takes precedence. Using `--include` will preclude all
+tags from being included by default. Symbolic refs and broken refs will never
+be packed. When used with `--all`, it will be a noop. Use `--no-include` to clear
+and reset the list of patterns.
+
+--exclude <pattern>::
+
+Do not pack refs matching the given `glob(7)` pattern. Repetitions of this option
+accumulate exclusion patterns. Use `--no-exclude` to clear and reset the list of
+patterns. If a ref is already packed, including it with `--exclude` will not
+unpack it.
+
+When used with `--all`, pack only loose refs which do not match any of
+the provided `--exclude` patterns.
+
+When used with `--include`, refs provided to `--include`, minus refs that are
+provided to `--exclude` will be packed.
+
 
 BUGS
 ----
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index 844d6f8..db742dc 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -15,7 +15,7 @@
 DESCRIPTION
 -----------
 This program searches the `$GIT_OBJECT_DIRECTORY` for all objects that currently
-exist in a pack file as well as the independent object directories.
+exist in a pack file as well as in the independent object directories.
 
 All such extra objects are removed.
 
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 03552dd..9a45571 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -18,7 +18,7 @@
 'git prune'. See the section "NOTES", below.
 
 This runs 'git fsck --unreachable' using all the refs
-available in `refs/`, optionally with additional set of
+available in `refs/`, optionally with an additional set of
 objects specified on the command line, and prunes all unpacked
 objects unreachable from any of these head objects from the object database.
 In addition, it
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 0e14f8b..b2ae496 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -87,7 +87,7 @@
 --verbose::
 	Pass --verbose to git-fetch and git-merge.
 
---[no-]recurse-submodules[=yes|on-demand|no]::
+--[no-]recurse-submodules[=(yes|on-demand|no)]::
 	This option controls if new commits of populated submodules should
 	be fetched, and if the working trees of active submodules should be
 	updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
@@ -105,7 +105,7 @@
 include::merge-options.txt[]
 
 -r::
---rebase[=false|true|merges|interactive]::
+--rebase[=(false|true|merges|interactive)]::
 	When true, rebase the current branch on top of the upstream
 	branch after fetching. If there is a remote-tracking branch
 	corresponding to the upstream branch and the upstream branch
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 5bb1d5a..9b7cfbc 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,8 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
-	   [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
+'git push' [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
+	   [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose]
 	   [-u | --set-upstream] [-o <string> | --push-option=<string>]
 	   [--[no-]signed|--signed=(true|false|if-asked)]
 	   [--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]]
@@ -37,7 +37,7 @@
 and if it is not found, honors `push.default` configuration to decide
 what to push (See linkgit:git-config[1] for the meaning of `push.default`).
 
-When neither the command-line nor the configuration specify what to
+When neither the command-line nor the configuration specifies what to
 push, the default behavior is used, which corresponds to the `simple`
 value for `push.default`: the current branch is pushed to the
 corresponding upstream branch, but as a safety measure, the push is
@@ -48,7 +48,7 @@
 OPTIONS[[OPTIONS]]
 ------------------
 <repository>::
-	The "remote" repository that is destination of a push
+	The "remote" repository that is the destination of a push
 	operation.  This parameter can be either a URL
 	(see the section <<URLS,GIT URLS>> below) or the name
 	of a remote (see the section <<REMOTES,REMOTES>> below).
@@ -147,6 +147,7 @@
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
 
 --all::
+--branches::
 	Push all branches (i.e. refs under `refs/heads/`); cannot be
 	used with other <refspec>.
 
diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt
index 70562dc..40e02d9 100644
--- a/Documentation/git-quiltimport.txt
+++ b/Documentation/git-quiltimport.txt
@@ -38,14 +38,14 @@
 	a patch.  At the time of this writing only missing author
 	information is warned about.
 
---author Author Name <Author Email>::
+--author 'Author Name <Author Email>'::
 	The author name and email address to use when no author
 	information can be found in the patch description.
 
 --patches <dir>::
 	The directory to find the quilt patches.
 +
-The default for the patch directory is patches
+The default for the patch directory is 'patches'
 or the value of the `$QUILT_PATCHES` environment
 variable.
 
diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt
index 0b39371..fbdbe0b 100644
--- a/Documentation/git-range-diff.txt
+++ b/Documentation/git-range-diff.txt
@@ -70,7 +70,7 @@
 	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 why this is
+	See the ``Algorithm`` section below for an explanation of why this is
 	needed.
 
 --left-only::
@@ -166,7 +166,7 @@
 
 In this example, there are 3 old and 3 new commits, where the developer
 removed the 3rd, added a new one before the first two, and modified the
-commit message of the 2nd commit as well its diff.
+commit message of the 2nd commit as well as its diff.
 
 When the output goes to a terminal, it is color-coded by default, just
 like regular `git diff`'s output. In addition, the first line (adding a
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index b097074..1c48c28 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -25,15 +25,15 @@
 flag.  When used with `-m`, the `-u` flag causes it to also update
 the files in the work tree with the result of the merge.
 
-Trivial merges are done by 'git read-tree' itself.  Only conflicting paths
-will be in unmerged state when 'git read-tree' returns.
+Only trivial merges are done by 'git read-tree' itself.  Only conflicting paths
+will be in an unmerged state when 'git read-tree' returns.
 
 OPTIONS
 -------
 -m::
 	Perform a merge, not just a read.  The command will
 	refuse to run if your index file has unmerged entries,
-	indicating that you have not finished previous merge you
+	indicating that you have not finished a previous merge you
 	started.
 
 --reset::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bc..74df345 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -12,7 +12,7 @@
 	[--onto <newbase> | --keep-base] [<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
 	--root [<branch>]
-'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
+'git rebase' (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
 
 DESCRIPTION
 -----------
@@ -289,17 +289,25 @@
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty={drop,keep,ask}::
+--empty=(drop|keep|stop)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
-	upstream changes).  With drop (the default), commits that
-	become empty are dropped.  With keep, such commits are kept.
-	With ask (implied by `--interactive`), the rebase will halt when
-	an empty commit is applied allowing you to choose whether to
-	drop it, edit files more, or just commit the empty changes.
-	Other options, like `--exec`, will use the default of drop unless
-	`-i`/`--interactive` is explicitly specified.
+	upstream changes):
++
+--
+`drop`;;
+	The commit will be dropped. This is the default behavior.
+`keep`;;
+	The commit will be kept. This option is implied when `--exec` is
+	specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified. `ask` is a deprecated synonym of `stop`.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
@@ -523,26 +531,31 @@
 +
 The commit list format can be changed by setting the configuration option
 rebase.instructionFormat.  A customized instruction format will automatically
-have the long commit hash prepended to the format.
+have the commit hash prepended to the format.
 +
 See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
 	By default, a rebase will simply drop merge commits from the todo
 	list, and put the rebased commits into a single, linear branch.
 	With `--rebase-merges`, the rebase will instead try to preserve
 	the branching structure within the commits that are to be rebased,
 	by recreating the merge commits. Any resolved merge conflicts or
 	manual amendments in these merge commits will have to be
-	resolved/re-applied manually.
+	resolved/re-applied manually. `--no-rebase-merges` can be used to
+	countermand both the `rebase.rebaseMerges` config option and a previous
+	`--rebase-merges`.
 +
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
 +
 It is currently only possible to recreate the merge commits using the
 `ort` merge strategy; different merge strategies can be used only via
@@ -584,21 +597,27 @@
 
 --autosquash::
 --no-autosquash::
-	When the commit log message begins with "squash! ..." or "fixup! ..."
-	or "amend! ...", and there is already a commit in the todo list that
-	matches the same `...`, automatically modify the todo list of
-	`rebase -i`, so that the commit marked for squashing comes right after
-	the commit to be modified, and change the action of the moved commit
-	from `pick` to `squash` or `fixup` or `fixup -C` respectively. A commit
-	matches the `...` if the commit subject matches, or if the `...` refers
-	to the commit's hash. As a fall-back, partial matches of the commit
-	subject work, too. The recommended way to create fixup/amend/squash
-	commits is by using the `--fixup`, `--fixup=amend:` or `--fixup=reword:`
-	and `--squash` options respectively of linkgit:git-commit[1].
+	Automatically squash commits with specially formatted messages into
+	previous commits being rebased.  If a commit message starts with
+	"squash! ", "fixup! " or "amend! ", the remainder of the subject line
+	is taken as a commit specifier, which matches a previous commit if it
+	matches the subject line or the hash of that commit.  If no commit
+	matches fully, matches of the specifier with the start of commit
+	subjects are considered.
 +
-If the `--autosquash` option is enabled by default using the
-configuration variable `rebase.autoSquash`, this option can be
-used to override and disable this setting.
+In the rebase todo list, the actions of squash, fixup and amend commits are
+changed from `pick` to `squash`, `fixup` or `fixup -C`, respectively, and they
+are moved right after the commit they modify.  The `--interactive` option can
+be used to review and edit the todo list before proceeding.
++
+The recommended way to create commits with squash markers is by using the
+`--squash`, `--fixup`, `--fixup=amend:` or `--fixup=reword:` options of
+linkgit:git-commit[1], which take the target commit as an argument and
+automatically fill in the subject line of the new commit from that.
++
+Setting configuration variable `rebase.autoSquash` to true enables
+auto-squashing by default for interactive rebase.  The `--no-autosquash`
+option can be used to override that setting.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -615,13 +634,16 @@
 	Automatically reschedule `exec` commands that failed. This only makes
 	sense in interactive mode (or when an `--exec` option was provided).
 +
-Even though this option applies once a rebase is started, it's set for
-the whole rebase at the start based on either the
-`rebase.rescheduleFailedExec` configuration (see linkgit:git-config[1]
-or "CONFIGURATION" below) or whether this option is
-provided. Otherwise an explicit `--no-reschedule-failed-exec` at the
-start would be overridden by the presence of
-`rebase.rescheduleFailedExec=true` configuration.
+This option applies once a rebase is started. It is preserved for the whole
+rebase based on, in order, the command line option provided to the initial `git
+rebase`, the `rebase.rescheduleFailedExec` configuration (see
+linkgit:git-config[1] or "CONFIGURATION" below), or it defaults to false.
++
+Recording this option for the whole rebase is a convenience feature. Otherwise
+an explicit `--no-reschedule-failed-exec` at the start would be overridden by
+the presence of a `rebase.rescheduleFailedExec=true` configuration when `git
+rebase --continue` is invoked. Currently, you cannot pass
+`--[no-]reschedule-failed-exec` to `git rebase --continue`.
 
 --update-refs::
 --no-update-refs::
@@ -690,7 +712,7 @@
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty={drop,keep,ask}` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
@@ -952,10 +974,9 @@
 non-0 status) to give you an opportunity to fix the problem. You can
 continue with `git rebase --continue`.
 
-The "exec" command launches the command in a shell (the one specified
-in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
-use shell features (like "cd", ">", ";" ...). The command is run from
-the root of the working tree.
+The "exec" command launches the command in a shell (the default one, usually
+/bin/sh), so you can use shell features (like "cd", ">", ";" ...). The command
+is run from the root of the working tree.
 
 ----------------------------------
 $ git rebase -i --exec "make test"
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index 65ff518..20aca92 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -18,10 +18,10 @@
 
 This command is usually not invoked directly by the end user.
 The UI for the protocol is on the 'git send-pack' side, and the
-program pair is meant to be used to push updates to remote
+program pair is meant to be used to push updates to a remote
 repository.  For pull operations, see linkgit:git-fetch-pack[1].
 
-The command allows for creation and fast-forwarding of sha1 refs
+The command allows for the creation and fast-forwarding of sha1 refs
 (heads/tags) on the remote end (strictly speaking, it is the
 local end 'git-receive-pack' runs, but to the user who is sitting at
 the send-pack end, it is updating the remote.  Confused?)
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index ec64cbf..a929c52 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -10,6 +10,7 @@
 --------
 [verse]
 'git reflog' [show] [<log-options>] [<ref>]
+'git reflog list'
 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
 	[--rewrite] [--updateref] [--stale-fix]
 	[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
@@ -39,6 +40,8 @@
 `git reflog show` is an alias for `git log -g --abbrev-commit
 --pretty=oneline`; see linkgit:git-log[1] for more information.
 
+The "list" subcommand lists all refs which have a corresponding reflog.
+
 The "expire" subcommand prunes older reflog entries. Entries older
 than `expire` time, or entries older than `expire-unreachable` time
 and not reachable from the current tip, are removed from the reflog.
diff --git a/Documentation/git-remote-ext.txt b/Documentation/git-remote-ext.txt
index 88ea7e1..b33ee3c 100644
--- a/Documentation/git-remote-ext.txt
+++ b/Documentation/git-remote-ext.txt
@@ -44,15 +44,15 @@
 	This argument will not be passed to '<command>'. Instead, it
 	will cause the helper to start by sending git:// service requests to
 	the remote side with the service field set to an appropriate value and
-	the repository field set to rest of the argument. Default is not to send
+	the repository field set to the rest of the argument. Default is not to send
 	such a request.
 +
-This is useful if remote side is git:// server accessed over
+This is useful if the remote side is git:// server accessed over
 some tunnel.
 
 '%V' (must be first characters in argument)::
 	This argument will not be passed to '<command>'. Instead it sets
-	the vhost field in the git:// service request (to rest of the argument).
+	the vhost field in the git:// service request (to the rest of the argument).
 	Default is not to send vhost in such request (if sent).
 
 ENVIRONMENT VARIABLES
@@ -82,12 +82,12 @@
 
 "ext::ssh -i /home/foo/.ssh/somekey user&#64;host.example %S 'foo/repo'"::
 	Like host.example:foo/repo, but use /home/foo/.ssh/somekey as
-	keypair and user as user on remote side. This avoids needing to
+	keypair and user as the user on the remote side. This avoids the need to
 	edit .ssh/config.
 
 "ext::socat -t3600 - ABSTRACT-CONNECT:/git-server %G/somerepo"::
 	Represents repository with path /somerepo accessible over
-	git protocol at abstract namespace address /git-server.
+	git protocol at the abstract namespace address /git-server.
 
 "ext::git-server-alias foo %G/repo"::
 	Represents a repository with path /repo accessed using the
diff --git a/Documentation/git-remote-fd.txt b/Documentation/git-remote-fd.txt
index 0451ceb..1dd2648 100644
--- a/Documentation/git-remote-fd.txt
+++ b/Documentation/git-remote-fd.txt
@@ -13,19 +13,19 @@
 -----------
 This helper uses specified file descriptors to connect to a remote Git server.
 This is not meant for end users but for programs and scripts calling git
-fetch, push or archive.
+fetch, push, or archive.
 
 If only <infd> is given, it is assumed to be a bidirectional socket connected
-to remote Git server (git-upload-pack, git-receive-pack or
+to a remote Git server (git-upload-pack, git-receive-pack, or
 git-upload-archive). If both <infd> and <outfd> are given, they are assumed
 to be pipes connected to a remote Git server (<infd> being the inbound pipe
-and <outfd> being the outbound pipe.
+and <outfd> being the outbound pipe).
 
 It is assumed that any handshaking procedures have already been completed
 (such as sending service request for git://) before this helper is started.
 
 <anything> can be any string. It is ignored. It is meant for providing
-information to user in the URL in case that URL is displayed in some
+information to the user in the URL in case that URL is displayed in some
 context.
 
 ENVIRONMENT VARIABLES
@@ -45,7 +45,7 @@
 `git push fd::7,8 master (as URL)`::
 	Push master, using file descriptor #7 to read data from
 	git-receive-pack and file descriptor #8 to write data to
-	same service.
+	the same service.
 
 `git push fd::7,8/bar master`::
 	Same as above.
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 1dec314..932a5c3 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -35,7 +35,7 @@
 -v::
 --verbose::
 	Be a little more verbose and show remote url after name.
-	For promisor remotes, also show which filter (`blob:none` etc.)
+	For promisor remotes, also show which filters (`blob:none` etc.)
 	are configured.
 	NOTE: This must be placed between `remote` and subcommand.
 
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 4017157..c902512 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -74,6 +74,17 @@
 	immediately instead of waiting for the next `git gc` invocation.
 	Only useful with `--cruft -d`.
 
+--max-cruft-size=<n>::
+	Repack cruft objects into packs as large as `<n>` bytes before
+	creating new packs. As long as there are enough cruft packs
+	smaller than `<n>`, repacking will cause a new cruft pack to
+	be created containing objects from any combined cruft packs,
+	along with any new unreachable objects. Cruft packs larger than
+	`<n>` will not be modified. When the new cruft pack is larger
+	than `<n>` bytes, it will be split into multiple packs, all of
+	which are guaranteed to be at most `<n>` bytes in size. Only
+	useful with `--cruft -d`.
+
 --expire-to=<dir>::
 	Write a cruft pack containing pruned objects (if any) to the
 	directory `<dir>`. This option is useful for keeping a copy of
@@ -143,6 +154,29 @@
 	a larger and slower repository; see the discussion in
 	`pack.packSizeLimit`.
 
+--filter=<filter-spec>::
+	Remove objects matching the filter specification from the
+	resulting packfile and put them into a separate packfile. Note
+	that objects used in the working directory are not filtered
+	out. So for the split to fully work, it's best to perform it
+	in a bare repo and to use the `-a` and `-d` options along with
+	this option.  Also `--no-write-bitmap-index` (or the
+	`repack.writebitmaps` config option set to `false`) should be
+	used otherwise writing bitmap index will fail, as it supposes
+	a single packfile containing all the objects. See
+	linkgit:git-rev-list[1] for valid `<filter-spec>` forms.
+
+--filter-to=<dir>::
+	Write the pack containing filtered out objects to the
+	directory `<dir>`. Only useful with `--filter`. This can be
+	used for putting the pack on a separate object directory that
+	is accessed through the Git alternates mechanism. **WARNING:**
+	If the packfile containing the filtered out objects is not
+	accessible, the repo can become corrupt as it might not be
+	possible to access the objects in that packfile. See the
+	`objects` and `objects/info/alternates` sections of
+	linkgit:gitrepository-layout[5].
+
 -b::
 --write-bitmap-index::
 	Write a reachability bitmap index as part of the repack. This
@@ -165,7 +199,7 @@
 	Exclude the given pack from repacking. This is the equivalent
 	of having `.keep` file on the pack. `<pack-name>` is the
 	pack file name without leading directory (e.g. `pack-123.pack`).
-	The option could be specified multiple times to keep multiple
+	The option can be specified multiple times to keep multiple
 	packs.
 
 --unpack-unreachable=<when>::
@@ -186,7 +220,7 @@
 	Pass the `--delta-islands` option to `git-pack-objects`, see
 	linkgit:git-pack-objects[1].
 
--g=<factor>::
+-g<factor>::
 --geometric=<factor>::
 	Arrange resulting pack structure so that each successive pack
 	contains at least `<factor>` times the number of objects as the
@@ -203,11 +237,8 @@
 packs determined to need to be combined in order to restore a geometric
 progression.
 +
-When `--unpacked` is specified, loose objects are implicitly included in
-this "roll-up", without respect to their reachability. This is subject
-to change in the future. This option (implying a drastically different
-repack mode) is not guaranteed to work with all other combinations of
-option to `git repack`.
+Loose objects are implicitly included in this "roll-up", without respect to
+their reachability. This is subject to change in the future.
 +
 When writing a multi-pack bitmap, `git repack` selects the largest resulting
 pack as the preferred pack for object selection by the MIDX (see
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
index f271d75..0a65460 100644
--- a/Documentation/git-replace.txt
+++ b/Documentation/git-replace.txt
@@ -35,7 +35,7 @@
 except those doing reachability traversal (prune, pack transfer and
 fsck).
 
-It is possible to disable use of replacement references for any
+It is possible to disable the use of replacement references for any
 command using the `--no-replace-objects` option just after 'git'.
 
 For example if commit 'foo' has been replaced by commit 'bar':
@@ -111,14 +111,14 @@
 FORMATS
 -------
 
-The following format are available:
+The following formats are available:
 
 * 'short':
-	<replaced sha1>
+	<replaced-sha1>
 * 'medium':
-	<replaced sha1> -> <replacement sha1>
+	<replaced-sha1> -> <replacement-sha1>
 * 'long':
-	<replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>)
+	<replaced-sha1> (<replaced-type>) -> <replacement-sha1> (<replacement-type>)
 
 CREATING REPLACEMENT OBJECTS
 ----------------------------
diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
new file mode 100644
index 0000000..f6c269c
--- /dev/null
+++ b/Documentation/git-replay.txt
@@ -0,0 +1,127 @@
+git-replay(1)
+=============
+
+NAME
+----
+git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos too
+
+
+SYNOPSIS
+--------
+[verse]
+(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) <revision-range>...
+
+DESCRIPTION
+-----------
+
+Takes ranges of commits and replays them onto a new location. Leaves
+the working tree and the index untouched, and updates no references.
+The output of this command is meant to be used as input to
+`git update-ref --stdin`, which would update the relevant branches
+(see the OUTPUT section below).
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+
+--onto <newbase>::
+	Starting point at which to create the new commits.  May be any
+	valid commit, and not just an existing branch name.
++
+When `--onto` is specified, the update-ref command(s) in the output will
+update the branch(es) in the revision range to point at the new
+commits, similar to the way how `git rebase --update-refs` updates
+multiple branches in the affected range.
+
+--advance <branch>::
+	Starting point at which to create the new commits; must be a
+	branch name.
++
+When `--advance` is specified, the update-ref command(s) in the output
+will update the branch passed as an argument to `--advance` to point at
+the new commits (in other words, this mimics a cherry-pick operation).
+
+<revision-range>::
+	Range of commits to replay. More than one <revision-range> can
+	be passed, but in `--advance <branch>` mode, they should have
+	a single tip, so that it's clear where <branch> should point
+	to. See "Specifying Ranges" in linkgit:git-rev-parse and the
+	"Commit Limiting" options below.
+
+include::rev-list-options.txt[]
+
+OUTPUT
+------
+
+When there are no conflicts, the output of this command is usable as
+input to `git update-ref --stdin`.  It is of the form:
+
+	update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+	update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+	update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+
+where the number of refs updated depends on the arguments passed and
+the shape of the history being replayed.  When using `--advance`, the
+number of refs updated is always one, but for `--onto`, it can be one
+or more (rebasing multiple branches simultaneously is supported).
+
+EXIT STATUS
+-----------
+
+For a successful, non-conflicted replay, the exit status is 0.  When
+the replay has conflicts, the exit status is 1.  If the replay is not
+able to complete (or start) due to some kind of error, the exit status
+is something other than 0 or 1.
+
+EXAMPLES
+--------
+
+To simply rebase `mybranch` onto `target`:
+
+------------
+$ git replay --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
+update refs/heads/target ${NEW_target_HASH} ${OLD_target_HASH}
+------------
+
+Note that the first two examples replay the exact same commits and on
+top of the exact same new base, they only differ in that the first
+provides instructions to make mybranch point at the new commits and
+the second provides instructions to make target point at them.
+
+What if you have a stack of branches, one depending upon another, and
+you'd really like to rebase the whole set?
+
+------------
+$ git replay --contained --onto origin/main origin/main..tipbranch
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
+------------
+
+When calling `git replay`, one does not need to specify a range of
+commits to replay using the syntax `A..B`; any range expression will
+do:
+
+------------
+$ git replay --onto origin/main ^base branch1 branch2 branch3
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+------------
+
+This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
+all commits they have since `base`, playing them on top of
+`origin/main`. These three branches may have commits on top of `base`
+that they have in common, but that does not need to be the case.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-request-pull.txt b/Documentation/git-request-pull.txt
index fa5a426..15dcbb6 100644
--- a/Documentation/git-request-pull.txt
+++ b/Documentation/git-request-pull.txt
@@ -16,7 +16,7 @@
 Generate a request asking your upstream project to pull changes into
 their tree.  The request, printed to the standard output,
 begins with the branch description, summarizes
-the changes and indicates from where they can be pulled.
+the changes, and indicates from where they can be pulled.
 
 The upstream project is expected to have the commit named by
 `<start>` and the output asks it to integrate the changes you made
@@ -50,7 +50,7 @@
 --------
 
 Imagine that you built your work on your `master` branch on top of
-the `v1.0` release, and want it to be integrated to the project.
+the `v1.0` release, and want it to be integrated into the project.
 First you push that change to your public repository for others to
 see:
 
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index 5964810..975825b 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -78,6 +78,8 @@
 --theirs::
 	When restoring files in the working tree from the index, use
 	stage #2 ('ours') or #3 ('theirs') for unmerged paths.
+	This option cannot be used when checking out paths from a
+	tree-ish (i.e. with the `--source` option).
 +
 Note that during `git rebase` and `git pull --rebase`, 'ours' and
 'theirs' may appear swapped. See the explanation of the same options
@@ -87,6 +89,8 @@
 --merge::
 	When restoring files on the working tree from the index,
 	recreate the conflicted merge in the unmerged paths.
+	This option cannot be used when checking out paths from a
+	tree-ish (i.e. with the `--source` option).
 
 --conflict=<style>::
 	The same as `--merge` option above, but changes the way the
@@ -101,7 +105,7 @@
 	specified. Unmerged paths on the working tree are left alone.
 
 --ignore-skip-worktree-bits::
-	In sparse checkout mode, by default is to only update entries
+	In sparse checkout mode, the default is to only update entries
 	matched by `<pathspec>` and sparse patterns in
 	$GIT_DIR/info/sparse-checkout. This option ignores the sparse
 	patterns and unconditionally restores any files in
@@ -195,7 +199,7 @@
 $ git restore --staged hello.c
 ------------
 
-or you can restore both the index and the working tree (this the same
+or you can restore both the index and the working tree (this is the same
 as using linkgit:git-checkout[1])
 
 ------------
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 51029a2..2e05c4b 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -17,9 +17,9 @@
 :git-rev-list: 1
 include::rev-list-description.txt[]
 
-'rev-list' is a very essential Git command, since it
+'rev-list' is an essential Git command, since it
 provides the ability to build and traverse commit ancestry graphs. For
-this reason, it has a lot of different options that enables it to be
+this reason, it has a lot of different options that enable it to be
 used by commands as different as 'git bisect' and
 'git repack'.
 
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index bcd8069..f9d5a35 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -9,12 +9,12 @@
 SYNOPSIS
 --------
 [verse]
-'git rev-parse' [<options>] <args>...
+'git rev-parse' [<options>] <arg>...
 
 DESCRIPTION
 -----------
 
-Many Git porcelainish commands take mixture of flags
+Many Git porcelainish commands take a mixture of flags
 (i.e. parameters that begin with a dash '-') and parameters
 meant for the underlying 'git rev-list' command they use internally
 and flags and parameters for the other commands they use
@@ -36,7 +36,7 @@
 --sq-quote::
 	Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
 	section below). In contrast to the `--sq` option below, this
-	mode does only quoting. Nothing else is done to command input.
+	mode only does quoting. Nothing else is done to command input.
 
 Options for --parseopt
 ~~~~~~~~~~~~~~~~~~~~~~
@@ -130,7 +130,7 @@
 	'git diff-{asterisk}'). In contrast to the `--sq-quote` option,
 	the command input is still interpreted as usual.
 
---short[=length]::
+--short[=<length>]::
 	Same as `--verify` but shortens the object name to a unique
 	prefix with at least `length` characters. The minimum length
 	is 4, the default is the effective value of the `core.abbrev`
@@ -156,18 +156,30 @@
 	are not refs (i.e. branch or tag names; or more
 	explicitly disambiguating "heads/master" form, when you
 	want to name the "master" branch when there is an
-	unfortunately named tag "master"), and show them as full
+	unfortunately named tag "master"), and shows them as full
 	refnames (e.g. "refs/heads/master").
 
+--output-object-format=(sha1|sha256|storage)::
+
+	Allow oids to be input from any object format that the current
+	repository supports.
+
+	Specifying "sha1" translates if necessary and returns a sha1 oid.
+
+	Specifying "sha256" translates if necessary and returns a sha256 oid.
+
+	Specifying "storage" translates if necessary and returns an oid in
+	encoded in the storage hash algorithm.
+
 Options for Objects
 ~~~~~~~~~~~~~~~~~~~
 
 --all::
 	Show all refs found in `refs/`.
 
---branches[=pattern]::
---tags[=pattern]::
---remotes[=pattern]::
+--branches[=<pattern>]::
+--tags[=<pattern>]::
+--remotes[=<pattern>]::
 	Show all branches, tags, or remote-tracking branches,
 	respectively (i.e., refs found in `refs/heads`,
 	`refs/tags`, or `refs/remotes`, respectively).
@@ -176,7 +188,7 @@
 shown.  If the pattern does not contain a globbing character (`?`,
 `*`, or `[`), it is turned into a prefix match by appending `/*`.
 
---glob=pattern::
+--glob=<pattern>::
 	Show all refs matching the shell glob pattern `pattern`. If
 	the pattern does not start with `refs/`, this is automatically
 	prepended.  If the pattern does not contain a globbing
@@ -197,10 +209,11 @@
 or `--all`. If a trailing '/{asterisk}' is intended, it must be given
 explicitly.
 
---exclude-hidden=[receive|uploadpack]::
-	Do not include refs that would be hidden by `git-receive-pack` or
-	`git-upload-pack` by consulting the appropriate `receive.hideRefs` or
-	`uploadpack.hideRefs` configuration along with `transfer.hideRefs` (see
+--exclude-hidden=(fetch|receive|uploadpack)::
+	Do not include refs that would be hidden by `git-fetch`,
+	`git-receive-pack` or `git-upload-pack` by consulting the appropriate
+	`fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs`
+	configuration along with `transfer.hideRefs` (see
 	linkgit:git-config[1]). This option affects the next pseudo-ref option
 	`--all` or `--glob` and is cleared after processing them.
 
@@ -306,21 +319,24 @@
 	input, multiple algorithms may be printed, space-separated.
 	If not specified, the default is "storage".
 
+--show-ref-format::
+	Show the reference storage format used for the repository.
+
 
 Other Options
 ~~~~~~~~~~~~~
 
---since=datestring::
---after=datestring::
+--since=<datestring>::
+--after=<datestring>::
 	Parse the date string, and output the corresponding
 	--max-age= parameter for 'git rev-list'.
 
---until=datestring::
---before=datestring::
+--until=<datestring>::
+--before=<datestring>::
 	Parse the date string, and output the corresponding
 	--min-age= parameter for 'git rev-list'.
 
-<args>...::
+<arg>...::
 	Flags and parameters to be parsed.
 
 
@@ -382,7 +398,7 @@
 	dash to separate words in a multi-word argument hint.
 
 The remainder of the line, after stripping the spaces, is used
-as the help associated to the option.
+as the help associated with the option.
 
 Blank lines are ignored, and lines that don't match this specification are used
 as option group headers (start the line with a space to create such
@@ -397,7 +413,7 @@
 
 some-command does foo and bar!
 --
-h,help    show the help
+h,help!   show the help
 
 foo       some nifty option --foo
 bar=      some cool option --bar with an argument
@@ -423,10 +439,10 @@
     some-command does foo and bar!
 
     -h, --help            show the help
-    --foo                 some nifty option --foo
-    --bar ...             some cool option --bar with an argument
-    --baz <arg>           another cool option --baz with a named argument
-    --qux[=<path>]        qux may take a path argument but has meaning by itself
+    --[no-]foo            some nifty option --foo
+    --[no-]bar ...        some cool option --bar with an argument
+    --[no-]baz <arg>      another cool option --baz with a named argument
+    --[no-]qux[=<path>]   qux may take a path argument but has meaning by itself
 
 An option group Header
     -C[...]               option C with an optional argument
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index d2e10d3..568925d 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -116,7 +116,7 @@
 
 --reference::
 	Instead of starting the body of the log message with "This
-	reverts <full object name of the commit being reverted>.",
+	reverts <full-object-name-of-the-commit-being-reverted>.",
 	refer to the commit using "--pretty=reference" format
 	(cf. linkgit:git-log[1]).  The `revert.reference`
 	configuration variable can be used to enable this option by
@@ -142,6 +142,16 @@
 	changes. The revert only modifies the working tree and the
 	index.
 
+DISCUSSION
+----------
+
+While git creates a basic commit message automatically, it is
+_strongly_ recommended to explain why the original commit is being
+reverted.
+In addition, repeatedly reverting reverts will result in increasingly
+unwieldy subject lines, for example 'Reapply "Reapply "<original-subject>""'.
+Please consider rewording these to be shorter and more unique.
+
 CONFIGURATION
 -------------
 
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 81bc23f..363a269 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -163,7 +163,7 @@
 
 A submodule is considered up to date when the HEAD is the same as
 recorded in the index, no tracked files are modified and no untracked
-files that aren't ignored are present in the submodules work tree.
+files that aren't ignored are present in the submodule's work tree.
 Ignored files are deemed expendable and won't stop a submodule's work
 tree from being removed.
 
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 765b2df..c5d664f 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -9,8 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-'git send-email' [<options>] <file|directory>...
-'git send-email' [<options>] <format-patch options>
+'git send-email' [<options>] (<file>|<directory>)...
+'git send-email' [<options>] <format-patch-options>
 'git send-email' --dump-aliases
 
 
@@ -68,11 +68,12 @@
 	Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1])
 	to edit an introductory message for the patch series.
 +
-When `--compose` is used, git send-email will use the From, Subject, and
-In-Reply-To headers specified in the message. If the body of the message
-(what you type after the headers and a blank line) only contains blank
-(or Git: prefixed) lines, the summary won't be sent, but From, Subject,
-and In-Reply-To headers will be used unless they are removed.
+When `--compose` is used, git send-email will use the From, To, Cc, Bcc,
+Subject, Reply-To, and In-Reply-To headers specified in the message. If
+the body of the message (what you type after the headers and a blank
+line) only contains blank (or Git: prefixed) lines, the summary won't be
+sent, but the headers mentioned above will be used unless they are
+removed.
 +
 Missing From or In-Reply-To headers will be prompted for.
 +
@@ -93,7 +94,7 @@
 
 --in-reply-to=<identifier>::
 	Make the first mail (or all the mails with `--no-thread`) appear as a
-	reply to the given Message-Id, which avoids breaking threads to
+	reply to the given Message-ID, which avoids breaking threads to
 	provide a new patch series.
 	The second and subsequent emails will be sent as replies according to
 	the `--[no-]chain-reply-to` setting.
@@ -137,7 +138,7 @@
 
 --compose-encoding=<encoding>::
 	Specify encoding of compose message. Default is the value of the
-	'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
+	'sendemail.composeEncoding'; if that is unspecified, UTF-8 is assumed.
 
 --transfer-encoding=(7bit|8bit|quoted-printable|base64|auto)::
 	Specify the transfer encoding to be used to send the message over SMTP.
@@ -173,7 +174,7 @@
 	Specify a command to run to send the email. The command should
 	be sendmail-like; specifically, it must support the `-i` option.
 	The command will be executed in the shell if necessary.  Default
-	is the value of `sendemail.sendmailcmd`.  If unspecified, and if
+	is the value of `sendemail.sendmailCmd`.  If unspecified, and if
 	--smtp-server is also unspecified, git-send-email will search
 	for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH.
 
@@ -268,7 +269,7 @@
 	certificates concatenated together: see verify(1) -CAfile and
 	-CApath for more information on these). Set it to an empty string
 	to disable certificate verification. Defaults to the value of the
-	`sendemail.smtpsslcertpath` configuration variable, if set, or the
+	`sendemail.smtpSSLCertPath` configuration variable, if set, or the
 	backing SSL library's compiled-in default otherwise (which should
 	be the best choice on most platforms).
 
@@ -277,7 +278,7 @@
 	if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),
 	then authentication is not attempted.
 
---smtp-debug=0|1::
+--smtp-debug=(0|1)::
 	Enable (1) or disable (0) debug output. If enabled, SMTP
 	commands and replies will be printed. Useful to debug TLS
 	connection and authentication problems.
@@ -300,7 +301,9 @@
 Automating
 ~~~~~~~~~~
 
---no-[to|cc|bcc]::
+--no-to::
+--no-cc::
+--no-bcc::
 	Clears any list of "To:", "Cc:", "Bcc:" addresses previously
 	set via config.
 
@@ -312,7 +315,7 @@
 	Specify a command to execute once per patch file which
 	should generate patch file specific "To:" entries.
 	Output of this command must be single email address per line.
-	Default is the value of 'sendemail.tocmd' configuration value.
+	Default is the value of 'sendemail.toCmd' configuration value.
 
 --cc-cmd=<command>::
 	Specify a command to execute once per patch file which
@@ -320,6 +323,17 @@
 	Output of this command must be single email address per line.
 	Default is the value of `sendemail.ccCmd` configuration value.
 
+--header-cmd=<command>::
+	Specify a command that is executed once per outgoing message
+	and output RFC 2822 style header lines to be inserted into
+	them. When the `sendemail.headerCmd` configuration variable is
+	set, its value is always used. When --header-cmd is provided
+	at the command line, its value takes precedence over the
+	`sendemail.headerCmd` configuration variable.
+
+--no-header-cmd::
+	Disable any header command in use.
+
 --[no-]chain-reply-to::
 	If this is set, each email will be sent as a reply to the previous
 	email sent.  If disabled with "--no-chain-reply-to", all emails after
@@ -336,19 +350,19 @@
 
 --[no-]signed-off-by-cc::
 	If this is set, add emails found in the `Signed-off-by` trailer or Cc: lines to the
-	cc list. Default is the value of `sendemail.signedoffbycc` configuration
+	cc list. Default is the value of `sendemail.signedOffByCc` configuration
 	value; if that is unspecified, default to --signed-off-by-cc.
 
 --[no-]cc-cover::
 	If this is set, emails found in Cc: headers in the first patch of
 	the series (typically the cover letter) are added to the cc list
-	for each email set. Default is the value of 'sendemail.cccover'
+	for each email set. Default is the value of 'sendemail.ccCover'
 	configuration value; if that is unspecified, default to --no-cc-cover.
 
 --[no-]to-cover::
 	If this is set, emails found in To: headers in the first patch of
 	the series (typically the cover letter) are added to the to list
-	for each email set. Default is the value of 'sendemail.tocover'
+	for each email set. Default is the value of 'sendemail.toCover'
 	configuration value; if that is unspecified, default to --no-to-cover.
 
 --suppress-cc=<category>::
@@ -372,7 +386,7 @@
 - 'all' will suppress all auto cc values.
 --
 +
-Default is the value of `sendemail.suppresscc` configuration value; if
+Default is the value of `sendemail.suppressCc` configuration value; if
 that is unspecified, default to 'self' if --suppress-from is
 specified, as well as 'body' if --no-signed-off-cc is specified.
 
@@ -442,7 +456,7 @@
 			998 characters unless a suitable transfer encoding
 			('auto', 'base64', or 'quoted-printable') is used;
 			this is due to SMTP limits as described by
-			http://www.ietf.org/rfc/rfc5322.txt.
+			https://www.ietf.org/rfc/rfc5322.txt.
 --
 +
 Default is the value of `sendemail.validate`; if this is not set,
@@ -457,9 +471,9 @@
 
 --dump-aliases::
 	Instead of the normal operation, dump the shorthand alias names from
-	the configured alias file(s), one per line in alphabetical order. Note,
-	this only includes the alias name and not its expanded email addresses.
-	See 'sendemail.aliasesfile' for more information about aliases.
+	the configured alias file(s), one per line in alphabetical order. Note
+	that this only includes the alias name and not its expanded email addresses.
+	See 'sendemail.aliasesFile' for more information about aliases.
 
 
 CONFIGURATION
@@ -484,14 +498,10 @@
 	smtpServerPort = 587
 ----
 
-If you have multi-factor authentication set up on your Gmail account, you will
-need to generate an app-specific password for use with 'git send-email'. Visit
+If you have multi-factor authentication set up on your Gmail account, you can
+generate an app-specific password for use with 'git send-email'. Visit
 https://security.google.com/settings/security/apppasswords to create it.
 
-If you do not have multi-factor authentication set up on your Gmail account,
-you will need to allow less secure app access. Visit
-https://myaccount.google.com/lesssecureapps to enable it.
-
 Once your commits are ready to be sent to the mailing list, run the
 following commands:
 
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 595b002..b9e73f2 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -55,7 +55,7 @@
 --force::
 	Usually, the command refuses to update a remote ref that
 	is not an ancestor of the local ref used to overwrite it.
-	This flag disables the check.  What this means is that
+	This flag disables the check.  This means that
 	the remote repository can lose commits; use it with
 	care.
 
@@ -106,7 +106,7 @@
 There are three ways to specify which refs to update on the
 remote end.
 
-With `--all` flag, all refs that exist locally are transferred to
+With the `--all` flag, all refs that exist locally are transferred to
 the remote side.  You cannot specify any '<ref>' if you use
 this flag.
 
@@ -115,9 +115,9 @@
 
 When one or more '<ref>' are specified explicitly (whether on the
 command line or via `--stdin`), it can be either a
-single pattern, or a pair of such pattern separated by a colon
+single pattern, or a pair of such patterns separated by a colon
 ":" (this means that a ref name cannot have a colon in it).  A
-single pattern '<name>' is just a shorthand for '<name>:<name>'.
+single pattern '<name>' is just shorthand for '<name>:<name>'.
 
 Each pattern pair consists of the source side (before the colon)
 and the destination side (after the colon).  The ref to be
@@ -130,7 +130,7 @@
  - It is an error if <src> does not match exactly one of the
    local refs.
 
- - It is an error if <dst> matches more than one remote refs.
+ - It is an error if <dst> matches more than one remote ref.
 
  - If <dst> does not match any remote ref, either
 
@@ -143,9 +143,9 @@
 
 Without `--force`, the <src> ref is stored at the remote only if
 <dst> does not exist, or <dst> is a proper subset (i.e. an
-ancestor) of <src>.  This check, known as "fast-forward check",
-is performed in order to avoid accidentally overwriting the
-remote ref and lose other peoples' commits from there.
+ancestor) of <src>.  This check, known as the "fast-forward check",
+is performed to avoid accidentally overwriting the
+remote ref and losing other people's commits from there.
 
 With `--force`, the fast-forward check is disabled for all refs.
 
diff --git a/Documentation/git-sh-setup.txt b/Documentation/git-sh-setup.txt
index 8632612..bdaf6e5 100644
--- a/Documentation/git-sh-setup.txt
+++ b/Documentation/git-sh-setup.txt
@@ -22,7 +22,7 @@
 the normal Git directories and a few helper shell functions.
 
 Before sourcing it, your script should set up a few variables;
-`USAGE` (and `LONG_USAGE`, if any) is used to define message
+`USAGE` (and `LONG_USAGE`, if any) is used to define the message
 given by `usage()` shell function.  `SUBDIRECTORY_OK` can be set
 if the script can run from a subdirectory of the working tree
 (some commands do not).
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 71f608b..c771c89 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -50,7 +50,7 @@
 
 --current::
 	With this option, the command includes the current
-	branch to the list of revs to be shown when it is not
+	branch in the list of revs to be shown when it is not
 	given on the command line.
 
 --topo-order::
@@ -74,8 +74,7 @@
 	that is the common ancestor of all the branches.  This
 	flag tells the command to go <n> more common commits
 	beyond that.  When <n> is negative, display only the
-	<reference>s given, without showing the commit ancestry
-	tree.
+	<ref>s given, without showing the commit ancestry tree.
 
 --list::
 	Synonym to `--more=-1`
@@ -88,8 +87,8 @@
 	the case of three or more commits.
 
 --independent::
-	Among the <reference>s given, display only the ones that
-	cannot be reached from any other <reference>.
+	Among the <ref>s given, display only the ones that cannot be
+	reached from any other <ref>.
 
 --no-name::
 	Do not show naming strings for each commit.
@@ -126,25 +125,26 @@
 	default to color output.
 	Same as `--color=never`.
 
-Note that --more, --list, --independent and --merge-base options
+Note that --more, --list, --independent, and --merge-base options
 are mutually exclusive.
 
 
 OUTPUT
 ------
-Given N <references>, the first N lines are the one-line
-description from their commit message.  The branch head that is
-pointed at by $GIT_DIR/HEAD is prefixed with an asterisk `*`
-character while other heads are prefixed with a `!` character.
 
-Following these N lines, one-line log for each commit is
+Given N <ref>s, the first N lines are the one-line description from
+their commit message. The branch head that is pointed at by
+$GIT_DIR/HEAD is prefixed with an asterisk `*` character while other
+heads are prefixed with a `!` character.
+
+Following these N lines, a one-line log for each commit is
 displayed, indented N places.  If a commit is on the I-th
 branch, the I-th indentation character shows a `+` sign;
 otherwise it shows a space.  Merge commits are denoted by
 a `-` sign.  Each commit shows a short name that
 can be used as an extended SHA-1 to name that commit.
 
-The following example shows three branches, "master", "fixes"
+The following example shows three branches, "master", "fixes",
 and "mhf":
 
 ------------------------------------------------
@@ -154,7 +154,7 @@
   ! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
 ---
   + [mhf] Allow "+remote:local" refspec to cause --force when fetching.
-  + [mhf~1] Use git-octopus when pulling more than one heads.
+  + [mhf~1] Use git-octopus when pulling more than one head.
  +  [fixes] Introduce "reset type" flag to "git reset"
   + [mhf~2] "git fetch --force".
   + [mhf~3] Use .git/remote/origin, not .git/branches/origin.
@@ -197,7 +197,7 @@
 
 shows 10 reflog entries going back from the tip as of 1 hour ago.
 Without `--list`, the output also shows how these tips are
-topologically related with each other.
+topologically related to each other.
 
 CONFIGURATION
 -------------
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index d1d56f6..ba75747 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -8,10 +8,14 @@
 SYNOPSIS
 --------
 [verse]
-'git show-ref' [-q | --quiet] [--verify] [--head] [-d | --dereference]
+'git show-ref' [--head] [-d | --dereference]
 	     [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]
 	     [--heads] [--] [<pattern>...]
+'git show-ref' --verify [-q | --quiet] [-d | --dereference]
+	     [-s | --hash[=<n>]] [--abbrev[=<n>]]
+	     [--] [<ref>...]
 'git show-ref' --exclude-existing[=<pattern>]
+'git show-ref' --exists <ref>
 
 DESCRIPTION
 -----------
@@ -23,10 +27,14 @@
 
 By default, shows the tags, heads, and remote refs.
 
-The --exclude-existing form is a filter that does the inverse. It reads
+The `--exclude-existing` form is a filter that does the inverse. It reads
 refs from stdin, one ref per line, and shows those that don't exist in
 the local repository.
 
+The `--exists` form can be used to check for the existence of a single
+references. This form does not verify whether the reference resolves to an
+actual object.
+
 Use of this utility is encouraged in favor of directly accessing files under
 the `.git` directory.
 
@@ -47,14 +55,14 @@
 -d::
 --dereference::
 
-	Dereference tags into object IDs as well. They will be shown with "{caret}{}"
+	Dereference tags into object IDs as well. They will be shown with `^{}`
 	appended.
 
 -s::
 --hash[=<n>]::
 
-	Only show the SHA-1 hash, not the reference name. When combined with
-	--dereference the dereferenced tag will still be shown after the SHA-1.
+	Only show the OID, not the reference name. When combined with
+	`--dereference`, the dereferenced tag will still be shown after the OID.
 
 --verify::
 
@@ -62,6 +70,12 @@
 	Aside from returning an error code of 1, it will also print an error
 	message if `--quiet` was not specified.
 
+--exists::
+
+	Check whether the given reference exists. Returns an exit code of 0 if
+	it does, 2 if it is missing, and 1 in case looking up the reference
+	failed with an error other than the reference being missing.
+
 --abbrev[=<n>]::
 
 	Abbreviate the object name.  When using `--hash`, you do
@@ -70,15 +84,15 @@
 -q::
 --quiet::
 
-	Do not print any results to stdout. When combined with `--verify` this
-	can be used to silently check if a reference exists.
+	Do not print any results to stdout. Can be used with `--verify` to
+	silently check if a reference exists.
 
 --exclude-existing[=<pattern>]::
 
-	Make 'git show-ref' act as a filter that reads refs from stdin of the
-	form "`^(?:<anything>\s)?<refname>(?:\^{})?$`"
+	Make `git show-ref` act as a filter that reads refs from stdin of the
+	form `^(?:<anything>\s)?<refname>(?:\^{})?$`
 	and performs the following actions on each:
-	(1) strip "{caret}{}" at the end of line if any;
+	(1) strip `^{}` at the end of line if any;
 	(2) ignore if pattern is provided and does not head-match refname;
 	(3) warn if refname is not a well-formed refname and skip;
 	(4) ignore if refname is a ref that exists in the local repository;
@@ -96,7 +110,13 @@
 OUTPUT
 ------
 
-The output is in the format: '<SHA-1 ID>' '<space>' '<reference name>'.
+The output is in the format:
+
+------------
+<oid> SP <ref> LF
+------------
+
+For example,
 
 -----------------------------------------------------------------------------
 $ git show-ref --head --dereference
@@ -110,7 +130,13 @@
 ...
 -----------------------------------------------------------------------------
 
-When using --hash (and not --dereference) the output format is: '<SHA-1 ID>'
+When using `--hash` (and not `--dereference`), the output is in the format:
+
+------------
+<oid> LF
+------------
+
+For example,
 
 -----------------------------------------------------------------------------
 $ git show-ref --heads --hash
@@ -132,7 +158,7 @@
 -----------------------------------------------------------------------------
 
 This will show "refs/heads/master" but also "refs/remote/other-repo/master",
-if such references exists.
+if such references exist.
 
 When using the `--verify` flag, the command requires an exact path:
 
@@ -142,10 +168,10 @@
 
 will only match the exact branch called "master".
 
-If nothing matches, 'git show-ref' will return an error code of 1,
+If nothing matches, `git show-ref` will return an error code of 1,
 and in the case of verification, it will show an error message.
 
-For scripting, you can ask it to be quiet with the "--quiet" flag, which
+For scripting, you can ask it to be quiet with the `--quiet` flag, which
 allows you to do things like
 
 -----------------------------------------------------------------------------
@@ -157,11 +183,11 @@
 actually want to show any results, and we want to use the full refname for it
 in order to not trigger the problem with ambiguous partial matches).
 
-To show only tags, or only proper branch heads, use "--tags" and/or "--heads"
+To show only tags, or only proper branch heads, use `--tags` and/or `--heads`
 respectively (using both means that it shows tags and heads, but not other
 random references under the refs/ subdirectory).
 
-To do automatic tag object dereferencing, use the "-d" or "--dereference"
+To do automatic tag object dereferencing, use the `-d` or `--dereference`
 flag, so you can do
 
 -----------------------------------------------------------------------------
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index 2b1bc72..5eb6743 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -26,7 +26,7 @@
 
 For plain blobs, it shows the plain contents.
 
-The command takes options applicable to the 'git diff-tree' command to
+Some options that 'git log' command understands can be used to
 control how the changes the commit introduces are shown.
 
 This manual page describes only the most frequently used options.
@@ -61,7 +61,7 @@
 --------
 
 `git show v1.0.0`::
-	Shows the tag `v1.0.0`, along with the object the tags
+	Shows the tag `v1.0.0`, along with the object the tag
 	points at.
 
 `git show v1.0.0^{tree}`::
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index 68392d2..529a8ed 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git sparse-checkout' (init | list | set | add | reapply | disable) [<options>]
+'git sparse-checkout' (init | list | set | add | reapply | disable | check-rules) [<options>]
 
 
 DESCRIPTION
@@ -135,6 +135,29 @@
 the disable command, so the easy restore of calling a plain `init`
 decreased in utility.
 
+'check-rules'::
+	Check whether sparsity rules match one or more paths.
++
+By default `check-rules` reads a list of paths from stdin and outputs only
+the ones that match the current sparsity rules. The input is expected to consist
+of one path per line, matching the output of `git ls-tree --name-only` including
+that pathnames that begin with a double quote (") are interpreted as C-style
+quoted strings.
++
+When called with the `--rules-file <file>` flag the input files are matched
+against the sparse checkout rules found in `<file>` instead of the current ones.
+The rules in the files are expected to be in the same form as accepted by `git
+sparse-checkout set --stdin` (in particular, they must be newline-delimited).
++
+By default, the rules passed to the `--rules-file` option are interpreted as
+cone mode directories. To pass non-cone mode patterns with `--rules-file`,
+combine the option with the `--no-cone` option.
++
+When called with the `-z` flag, the format of the paths input on stdin as well
+as the output paths are \0 terminated and not quoted. Note that this does not
+apply to the format of the rules passed with the `--rules-file` option.
+
+
 EXAMPLES
 --------
 `git sparse-checkout set MY/DIR1 SUB/DIR2`::
@@ -263,7 +286,7 @@
     problem above?  Also, if it suggests paths, what if the user has a
     file or directory that begins with either a '!' or '#' or has a '*',
     '\', '?', '[', or ']' in its name?  And if it suggests paths, will
-    it complete "/pro" to "/proc" (in the root filesytem) rather than to
+    it complete "/pro" to "/proc" (in the root filesystem) rather than to
     "/progress.txt" in the current directory?  (Note that users are
     likely to want to start paths with a leading '/' in non-cone mode,
     for the same reason that .gitignore files often have one.)
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index f4bb611..06fb7f1 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -366,7 +366,7 @@
 # ... hack hack hack ...
 $ git add --patch foo           # add unrelated changes to the index
 $ git stash push --staged       # save these changes to the stash
-# ... hack hack hack, finish curent changes ...
+# ... hack hack hack, finish current changes ...
 $ git commit -m 'Massive'       # commit fully tested changes
 $ git switch fixup-branch       # switch to another branch
 $ git stash pop                 # to finish work on the saved changes
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index a051b1e..9a37688 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -79,6 +79,8 @@
 `git update-index --untracked-cache` and `git update-index
 --split-index`), Otherwise you can use `no` to have `git status`
 return more quickly without showing untracked files.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
 
 The default can be changed using the status.showUntrackedFiles
 configuration variable documented in linkgit:git-config[1].
@@ -245,11 +247,12 @@
 ....
 
 Submodules have more state and instead report
-		M    the submodule has a different HEAD than
-		     recorded in the index
-		m    the submodule has modified content
-		?    the submodule has untracked files
-since modified content or untracked files in a submodule cannot be added
+
+* 'M' = the submodule has a different HEAD than recorded in the index
+* 'm' = the submodule has modified content
+* '?' = the submodule has untracked files
+
+This is since modified content or untracked files in a submodule cannot be added
 via `git add` in the superproject to prepare a commit.
 
 'm' and '?' are applied recursively. For example if a nested submodule
@@ -308,7 +311,7 @@
 ------------------------------------------------------------
 # branch.oid <commit> | (initial)        Current commit.
 # branch.head <branch> | (detached)      Current branch.
-# branch.upstream <upstream_branch>      If upstream is set.
+# branch.upstream <upstream-branch>      If upstream is set.
 # branch.ab +<ahead> -<behind>           If upstream is set and
 					 the commit is present.
 ------------------------------------------------------------
@@ -471,7 +474,7 @@
 results, so it could be faster on subsequent runs.
 
 * The `--untracked-files=no` flag or the
-	`status.showUntrackedfiles=false` config (see above for both):
+	`status.showUntrackedFiles=no` config (see above for both):
 	indicate that `git status` should not report untracked
 	files. This is the fastest option. `git status` will not list
 	the untracked files, so you need to be careful to remember if
@@ -501,7 +504,7 @@
 	usually worth the additional size.
 
 * `core.untrackedCache=true` and `core.fsmonitor=true` or
-	`core.fsmonitor=<hook_command_pathname>` (see
+	`core.fsmonitor=<hook-command-pathname>` (see
 	linkgit:git-update-index[1]): enable both the untracked cache
 	and FSMonitor features and only search directories that have
 	been modified since the previous `git status` command.  This
diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt
index 2438f76..a293327 100644
--- a/Documentation/git-stripspace.txt
+++ b/Documentation/git-stripspace.txt
@@ -29,7 +29,7 @@
 In the case where the input consists entirely of whitespace characters, no
 output will be produced.
 
-*NOTE*: This is intended for cleaning metadata, prefer the `--whitespace=fix`
+*NOTE*: This is intended for cleaning metadata. Prefer the `--whitespace=fix`
 mode of linkgit:git-apply[1] for correcting whitespace of patches or files in
 the repository.
 
@@ -37,11 +37,11 @@
 -------
 -s::
 --strip-comments::
-	Skip and remove all lines starting with comment character (default '#').
+	Skip and remove all lines starting with a comment character (default '#').
 
 -c::
 --comment-lines::
-	Prepend comment character and blank to each line. Lines will automatically
+	Prepend the comment character and a blank space to each line. Lines will automatically
 	be terminated with a newline. On empty lines, only the comment character
 	will be prepended.
 
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 4d3ab6b..ca0347a 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -95,7 +95,7 @@
 init [--] [<path>...]::
 	Initialize the submodules recorded in the index (which were
 	added and committed elsewhere) by setting `submodule.$name.url`
-	in .git/config. It uses the same setting from `.gitmodules` as
+	in `.git/config`, using the same setting from `.gitmodules` as
 	a template. If the URL is relative, it will be resolved using
 	the default remote. If there is no default remote, the current
 	repository will be assumed to be upstream.
@@ -105,9 +105,12 @@
 configured to be active will be initialized, otherwise all submodules are
 initialized.
 +
-When present, it will also copy the value of `submodule.$name.update`.
-This command does not alter existing information in .git/config.
-You can then customize the submodule clone URLs in .git/config
+It will also copy the value of `submodule.$name.update`, if present in
+the `.gitmodules` file, to `.git/config`, but (1) this command does not
+alter existing information in `.git/config`, and (2) `submodule.$name.update`
+that is set to a custom command is *not* copied for security reasons.
++
+You can then customize the submodule clone URLs in `.git/config`
 for your local setup and proceed to `git submodule update`;
 you can also just use `git submodule update --init` without
 the explicit 'init' step if you do not intend to customize
@@ -133,7 +136,7 @@
 that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal
 options.
 
-update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter spec>] [--] [<path>...]::
+update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter-spec>] [--] [<path>...]::
 +
 --
 Update the registered submodules to match what the superproject
@@ -143,6 +146,8 @@
 on command line options and the value of `submodule.<name>.update`
 configuration variable. The command line option takes precedence over
 the configuration variable. If neither is given, a 'checkout' is performed.
+(note: what is in `.gitmodules` file is irrelevant at this point;
+see `git submodule init` above for how `.gitmodules` is used).
 The 'update' procedures supported both from the command line as well as
 through the `submodule.<name>.update` configuration are:
 
@@ -160,16 +165,18 @@
 	merge;; the commit recorded in the superproject will be merged
 	    into the current branch in the submodule.
 
-The following 'update' procedures are only available via the
-`submodule.<name>.update` configuration variable:
+The following update procedures have additional limitations:
 
-	custom command;; arbitrary shell command that takes a single
-	    argument (the sha1 of the commit recorded in the
-	    superproject) is executed. When `submodule.<name>.update`
-	    is set to '!command', the remainder after the exclamation mark
-	    is the custom command.
+	custom command;; mechanism for running arbitrary commands with the
+	    commit ID as an argument. Specifically, if the
+	    `submodule.<name>.update` configuration variable is set to
+	    `!custom command`, the object name of the commit recorded in the
+	    superproject for the submodule is appended to the `custom command`
+	    string and executed. Note that this mechanism is not supported in
+	    the `.gitmodules` file or on the command line.
 
-	none;; the submodule is not updated.
+	none;; the submodule is not updated. This update procedure is not
+	    allowed on the command line.
 
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in `.gitmodules`, you can automatically initialize the
@@ -178,7 +185,7 @@
 If `--recursive` is specified, this command will recurse into the
 registered submodules, and update any nested submodules within.
 
-If `--filter <filter spec>` is specified, the given partial clone filter will be
+If `--filter <filter-spec>` is specified, the given partial clone filter will be
 applied to the submodule. See linkgit:git-rev-list[1] for details on filter
 specifications.
 --
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 4e92308..43c68c2 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -37,12 +37,12 @@
 	argument.  Normally this command initializes the current
 	directory.
 
--T<trunk_subdir>;;
---trunk=<trunk_subdir>;;
--t<tags_subdir>;;
---tags=<tags_subdir>;;
--b<branches_subdir>;;
---branches=<branches_subdir>;;
+-T<trunk-subdir>;;
+--trunk=<trunk-subdir>;;
+-t<tags-subdir>;;
+--tags=<tags-subdir>;;
+-b<branches-subdir>;;
+--branches=<branches-subdir>;;
 -s;;
 --stdlayout;;
 	These are optional command-line options for init.  Each of
@@ -726,9 +726,9 @@
 	when tracking a single URL.  The 'log' and 'dcommit' commands
 	no longer require this switch as an argument.
 
--R<remote name>::
---svn-remote <remote name>::
-	Specify the [svn-remote "<remote name>"] section to use,
+-R<remote-name>::
+--svn-remote <remote-name>::
+	Specify the [svn-remote "<remote-name>"] section to use,
 	this allows SVN multiple repositories to be tracked.
 	Default: "svn"
 
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
index c60fc9c..f38e4c8 100644
--- a/Documentation/git-switch.txt
+++ b/Documentation/git-switch.txt
@@ -59,13 +59,18 @@
 -c <new-branch>::
 --create <new-branch>::
 	Create a new branch named `<new-branch>` starting at
-	`<start-point>` before switching to the branch. This is a
-	convenient shortcut for:
+	`<start-point>` before switching to the branch. This is the
+	transactional equivalent of
 +
 ------------
 $ git branch <new-branch>
 $ git switch <new-branch>
 ------------
++
+that is to say, the branch is not reset/created unless "git switch" is
+successful (e.g., when the branch is in use in another worktree, not
+just the current branch stays the same, but the branch is not reset to
+the start-point, either).
 
 -C <new-branch>::
 --force-create <new-branch>::
@@ -171,7 +176,7 @@
 	`branch.autoSetupMerge` configuration variable is true.
 
 --orphan <new-branch>::
-	Create a new 'orphan' branch, named `<new-branch>`. All
+	Create a new unborn branch, named `<new-branch>`. All
 	tracked files are removed.
 
 --ignore-other-worktrees::
diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 102c83e..761b154 100644
--- a/Documentation/git-symbolic-ref.txt
+++ b/Documentation/git-symbolic-ref.txt
@@ -27,7 +27,7 @@
 
 A symbolic ref is a regular file that stores a string that
 begins with `ref: refs/`.  For example, your `.git/HEAD` is
-a regular file whose contents is `ref: refs/heads/master`.
+a regular file whose content is `ref: refs/heads/master`.
 
 OPTIONS
 -------
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fdc72b5..5fe519c 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -131,6 +131,10 @@
 --ignore-case::
 	Sorting and filtering tags are case insensitive.
 
+--omit-empty::
+	Do not print a newline after formatted refs where the format expands
+	to the empty string.
+
 --column[=<options>]::
 --no-column::
 	Display tag listing in columns. See configuration variable
@@ -220,7 +224,7 @@
 
 -------------------------------------
 [user]
-    signingKey = <gpg-key_id>
+    signingKey = <gpg-key-id>
 -------------------------------------
 
 `pager.tag` is only respected when listing tags, i.e., when `-l` is
@@ -377,6 +381,16 @@
 
 include::date-formats.txt[]
 
+FILES
+-----
+
+`$GIT_DIR/TAG_EDITMSG`::
+	This file contains the message of an in-progress annotated
+	tag. If `git tag` exits due to an error before creating an
+	annotated tag then the tag message that has been provided by the
+	user in an editor session will be available in this file, but
+	may be overwritten by the next invocation of `git tag`.
+
 NOTES
 -----
 
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index f4bb9c5..8c47890 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -49,7 +49,7 @@
 --remove::
 	If a specified file is in the index but is missing then it's
 	removed.
-	Default behavior is to ignore removed file.
+	Default behavior is to ignore removed files.
 
 --refresh::
 	Looks at the current index and checks to see if merges or
@@ -95,7 +95,7 @@
 	the index.  If you want to change the working tree file,
 	you need to unset the bit to tell Git.  This is
 	sometimes helpful when working with a big project on a
-	filesystem that has very slow lstat(2) system call
+	filesystem that has a very slow lstat(2) system call
 	(e.g. cifs).
 +
 Git will fail (gracefully) in case it needs to modify this file
@@ -108,7 +108,7 @@
 	without regard to the "assume unchanged" setting.
 
 --[no-]skip-worktree::
-	When one of these flags is specified, the object name recorded
+	When one of these flags is specified, the object names recorded
 	for the paths are not updated. Instead, these options
 	set and unset the "skip-worktree" bit for the paths. See
 	section "Skip-worktree bit" below for more information.
@@ -119,7 +119,7 @@
 	the `--remove` option was specified.
 
 --[no-]fsmonitor-valid::
-	When one of these flags is specified, the object name recorded
+	When one of these flags is specified, the object names recorded
 	for the paths are not updated. Instead, these options
 	set and unset the "fsmonitor valid" bit for the paths. See
 	section "File System Monitor" below for more information.
@@ -127,7 +127,7 @@
 -g::
 --again::
 	Runs 'git update-index' itself on the paths whose index
-	entries are different from those from the `HEAD` commit.
+	entries are different from those of the `HEAD` commit.
 
 --unresolve::
 	Restores the 'unmerged' or 'needs updating' state of a
@@ -151,24 +151,30 @@
 	automatically removed with warning messages.
 
 --stdin::
-	Instead of taking list of paths from the command line,
-	read list of paths from the standard input.  Paths are
+	Instead of taking a list of paths from the command line,
+	read a list of paths from the standard input.  Paths are
 	separated by LF (i.e. one path per line) by default.
 
 --verbose::
-        Report what is being added and removed from index.
+	Report what is being added and removed from the index.
 
 --index-version <n>::
 	Write the resulting index out in the named on-disk format version.
-	Supported versions are 2, 3 and 4. The current default version is 2
+	Supported versions are 2, 3, and 4. The current default version is 2
 	or 3, depending on whether extra features are used, such as
-	`git add -N`.
+	`git add -N`.  With `--verbose`, also report the version the index
+	file uses before and after this command.
 +
 Version 4 performs a simple pathname compression that reduces index
 size by 30%-50% on large repositories, which results in faster load
-time. Version 4 is relatively young (first released in 1.8.0 in
-October 2012). Other Git implementations such as JGit and libgit2
-may not support it yet.
+time.  Git supports it since version 1.8.0, released in October 2012,
+and support for it was added to libgit2 in 2016 and to JGit in 2020.
+Older versions of this manual page called it "relatively young", but
+it should be considered mature technology these days.
+
+--show-index-version::
+	Report the index format version used by the on-disk index file.
+	See `--index-version` above.
 
 -z::
 	Only meaningful with `--stdin` or `--index-info`; paths are
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 48b6683..374a2eb 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -8,21 +8,21 @@
 SYNOPSIS
 --------
 [verse]
-'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<oldvalue>] | [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
+'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<old-oid>] | [--create-reflog] <ref> <new-oid> [<old-oid>] | --stdin [-z])
 
 DESCRIPTION
 -----------
-Given two arguments, stores the <newvalue> in the <ref>, possibly
+Given two arguments, stores the <new-oid> in the <ref>, possibly
 dereferencing the symbolic refs.  E.g. `git update-ref HEAD
-<newvalue>` updates the current branch head to the new object.
+<new-oid>` updates the current branch head to the new object.
 
-Given three arguments, stores the <newvalue> in the <ref>,
+Given three arguments, stores the <new-oid> in the <ref>,
 possibly dereferencing the symbolic refs, after verifying that
-the current value of the <ref> matches <oldvalue>.
-E.g. `git update-ref refs/heads/master <newvalue> <oldvalue>`
-updates the master branch head to <newvalue> only if its current
-value is <oldvalue>.  You can specify 40 "0" or an empty string
-as <oldvalue> to make sure that the ref you are creating does
+the current value of the <ref> matches <old-oid>.
+E.g. `git update-ref refs/heads/master <new-oid> <old-oid>`
+updates the master branch head to <new-oid> only if its current
+value is <old-oid>.  You can specify 40 "0" or an empty string
+as <old-oid> to make sure that the ref you are creating does
 not exist.
 
 It also allows a "ref" file to be a symbolic pointer to another
@@ -56,15 +56,15 @@
 archive by creating a symlink tree).
 
 With `-d` flag, it deletes the named <ref> after verifying it
-still contains <oldvalue>.
+still contains <old-oid>.
 
 With `--stdin`, update-ref reads instructions from standard input and
 performs all modifications together.  Specify commands of the form:
 
-	update SP <ref> SP <newvalue> [SP <oldvalue>] LF
-	create SP <ref> SP <newvalue> LF
-	delete SP <ref> [SP <oldvalue>] LF
-	verify SP <ref> [SP <oldvalue>] LF
+	update SP <ref> SP <new-oid> [SP <old-oid>] LF
+	create SP <ref> SP <new-oid> LF
+	delete SP <ref> [SP <old-oid>] LF
+	verify SP <ref> [SP <old-oid>] LF
 	option SP <opt> LF
 	start LF
 	prepare LF
@@ -82,10 +82,10 @@
 Alternatively, use `-z` to specify in NUL-terminated format, without
 quoting:
 
-	update SP <ref> NUL <newvalue> NUL [<oldvalue>] NUL
-	create SP <ref> NUL <newvalue> NUL
-	delete SP <ref> NUL [<oldvalue>] NUL
-	verify SP <ref> NUL [<oldvalue>] NUL
+	update SP <ref> NUL <new-oid> NUL [<old-oid>] NUL
+	create SP <ref> NUL <new-oid> NUL
+	delete SP <ref> NUL [<old-oid>] NUL
+	verify SP <ref> NUL [<old-oid>] NUL
 	option SP <opt> NUL
 	start NUL
 	prepare NUL
@@ -100,25 +100,25 @@
 repeated <ref> produce an error.  Command meanings are:
 
 update::
-	Set <ref> to <newvalue> after verifying <oldvalue>, if given.
-	Specify a zero <newvalue> to ensure the ref does not exist
-	after the update and/or a zero <oldvalue> to make sure the
+	Set <ref> to <new-oid> after verifying <old-oid>, if given.
+	Specify a zero <new-oid> to ensure the ref does not exist
+	after the update and/or a zero <old-oid> to make sure the
 	ref does not exist before the update.
 
 create::
-	Create <ref> with <newvalue> after verifying it does not
-	exist.  The given <newvalue> may not be zero.
+	Create <ref> with <new-oid> after verifying it does not
+	exist.  The given <new-oid> may not be zero.
 
 delete::
-	Delete <ref> after verifying it exists with <oldvalue>, if
-	given.  If given, <oldvalue> may not be zero.
+	Delete <ref> after verifying it exists with <old-oid>, if
+	given.  If given, <old-oid> may not be zero.
 
 verify::
-	Verify <ref> against <oldvalue> but do not change it.  If
-	<oldvalue> is zero or missing, the ref must not exist.
+	Verify <ref> against <old-oid> but do not change it.  If
+	<old-oid> is zero or missing, the ref must not exist.
 
 option::
-	Modify behavior of the next command naming a <ref>.
+	Modify the behavior of the next command naming a <ref>.
 	The only valid option is `no-deref` to avoid dereferencing
 	a symbolic ref.
 
@@ -141,7 +141,7 @@
 	Abort the transaction, releasing all locks if the transaction is in
 	prepared state.
 
-If all <ref>s can be locked with matching <oldvalue>s
+If all <ref>s can be locked with matching <old-oid>s
 simultaneously, all modifications are performed.  Otherwise, no
 modifications are performed.  Note that while each individual
 <ref> is updated or deleted atomically, a concurrent reader may
@@ -161,7 +161,7 @@
 
 Where "oldsha1" is the 40 character hexadecimal value previously
 stored in <ref>, "newsha1" is the 40 character hexadecimal value of
-<newvalue> and "committer" is the committer's name, email address
+<new-oid> and "committer" is the committer's name, email address
 and date in the standard Git committer ident format.
 
 Optionally with -m:
diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt
index 17e429d..6bc9b50 100644
--- a/Documentation/git-update-server-info.txt
+++ b/Documentation/git-update-server-info.txt
@@ -23,13 +23,13 @@
 -------
 -f::
 --force::
-	update the info files from scratch.
+	Update the info files from scratch.
 
 OUTPUT
 ------
 
 Currently the command updates the following files.  Please see
-linkgit:gitrepository-layout[5] for description of
+linkgit:gitrepository-layout[5] for a description of
 what they are for:
 
 * objects/info/packs
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index b656b47..7ad60bc 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -26,7 +26,7 @@
 -------
 
 --[no-]strict::
-	Do not try <directory>/.git/ if <directory> is no Git directory.
+	Do not try <directory>/.git/ if <directory> is not a Git directory.
 
 --timeout=<n>::
 	Interrupt transfer after <n> seconds of inactivity.
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index f40202b..0680568 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -19,7 +19,7 @@
 OPTIONS
 -------
 -l::
-	Cause the logical variables to be listed. In addition, all the
+	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`.)
@@ -71,6 +71,29 @@
 GIT_DEFAULT_BRANCH::
     The name of the first branch created in newly initialized repositories.
 
+GIT_SHELL_PATH::
+    The path of the binary providing the POSIX shell for commands which use the shell.
+
+GIT_ATTR_SYSTEM::
+    The path to the system linkgit:gitattributes[5] file, if one is enabled.
+
+GIT_ATTR_GLOBAL::
+    The path to the global (per-user) linkgit:gitattributes[5] file.
+
+GIT_CONFIG_SYSTEM::
+    The path to the system configuration file, if one is enabled.
+
+GIT_CONFIG_GLOBAL::
+    The path to the global (per-user) configuration files, if any.
+
+Most path values contain only one value. However, some can contain multiple
+values, which are separated by newlines, and are listed in order from highest to
+lowest priority.  Callers should be prepared for any such path value to contain
+multiple items.
+
+Note that paths are printed even if they do not exist, but not if they are
+disabled by other environment variables.
+
 SEE ALSO
 --------
 linkgit:git-commit-tree[1]
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index b8720dc..d7e8869 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -15,7 +15,7 @@
 DESCRIPTION
 -----------
 Reads given idx file for packed Git archive created with the
-'git pack-objects' command and verifies idx file and the
+'git pack-objects' command and verifies the idx file and the
 corresponding pack file.
 
 OPTIONS
@@ -25,13 +25,13 @@
 
 -v::
 --verbose::
-	After verifying the pack, show list of objects contained
+	After verifying the pack, show the list of objects contained
 	in the pack and a histogram of delta chain length.
 
 -s::
 --stat-only::
 	Do not verify the pack contents; only show the histogram of delta
-	chain length.  With `--verbose`, list of objects is also shown.
+	chain length.  With `--verbose`, the list of objects is also shown.
 
 \--::
 	Do not interpret any more arguments as options.
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index 8b63ceb..8e55e0b 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-whatchanged - Show logs with difference each commit introduces
+git-whatchanged - Show logs with differences each commit introduces
 
 
 SYNOPSIS
@@ -18,11 +18,11 @@
 
 New users are encouraged to use linkgit:git-log[1] instead.  The
 `whatchanged` command is essentially the same as linkgit:git-log[1]
-but defaults to show the raw format diff output and to skip merges.
+but defaults to showing the raw format diff output and skipping merges.
 
-The command is kept primarily for historical reasons; fingers of
+The command is primarily kept for historical reasons; fingers of
 many people who learned Git long before `git log` was invented by
-reading Linux kernel mailing list are trained to type it.
+reading the Linux kernel mailing list are trained to type it.
 
 
 Examples
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 063d6ee..2a240f5 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]]
-		   [-b <new-branch>] <path> [<commit-ish>]
+		   [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
 'git worktree list' [-v | --porcelain [-z]]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree move' <worktree> <new-path>
@@ -95,6 +95,16 @@
 `-b <branch>` was given.  If `<branch>` does exist, it will be checked out
 in the new worktree, if it's not checked out anywhere else, otherwise the
 command will refuse to create the worktree (unless `--force` is used).
++
+If `<commit-ish>` is omitted, neither `--detach`, or `--orphan` is
+used, and there are no valid local branches (or remote branches if
+`--guess-remote` is specified) then, as a convenience, the new worktree is
+associated with a new unborn branch named `<branch>` (after
+`$(basename <path>)` if neither `-b` or `-B` is used) as if `--orphan` was
+passed to the command. In the event the repository has a remote and
+`--guess-remote` is used, but no remote or local branches exist, then the
+command fails with a warning reminding the user to fetch from their remote
+first (or override by using `-f/--force`).
 
 list::
 
@@ -222,6 +232,10 @@
 	With `prune`, do not remove anything; just report what it would
 	remove.
 
+--orphan::
+	With `add`, make the new worktree and index empty, associating
+	the worktree with a new unborn branch named `<new-branch>`.
+
 --porcelain::
 	With `list`, output in an easy-to-parse format for scripts.
 	This format will remain stable across Git versions and regardless of user
@@ -272,7 +286,8 @@
 In general, all pseudo refs are per-worktree and all refs starting with
 `refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
 under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-however: refs inside `refs/bisect` and `refs/worktree` are not shared.
+however: refs inside `refs/bisect`, `refs/worktree` and `refs/rewritten` are
+not shared.
 
 Refs that are per-worktree can still be accessed from another worktree via
 two special paths, `main-worktree` and `worktrees`. The former gives
@@ -349,8 +364,8 @@
 `/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
 rev-parse --git-path refs/heads/master` uses
 `$GIT_COMMON_DIR` and returns `/path/main/.git/refs/heads/master`,
-since refs are shared across all worktrees, except `refs/bisect` and
-`refs/worktree`.
+since refs are shared across all worktrees, except `refs/bisect`,
+`refs/worktree` and `refs/rewritten`.
 
 See linkgit:gitrepository-layout[5] for more information. The rule of
 thumb is do not make any assumption about whether a path belongs to
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 74973d3..7a1b112 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -96,9 +96,9 @@
 	to avoid ambiguity with `<name>` containing one.
 +
 This is useful for cases where you want to pass transitory
-configuration options to git, but are doing so on OS's where
-other processes might be able to read your cmdline
-(e.g. `/proc/self/cmdline`), but not your environ
+configuration options to git, but are doing so on operating systems
+where other processes might be able to read your command line
+(e.g. `/proc/self/cmdline`), but not your environment
 (e.g. `/proc/self/environ`). That behavior is the default on
 Linux, but may not be on your system.
 +
@@ -174,8 +174,17 @@
 	directory.
 
 --no-replace-objects::
-	Do not use replacement refs to replace Git objects. See
-	linkgit:git-replace[1] for more information.
+	Do not use replacement refs to replace Git objects.
+	This is equivalent to exporting the `GIT_NO_REPLACE_OBJECTS`
+	environment variable with any value.
+	See linkgit:git-replace[1] for more information.
+
+--no-lazy-fetch::
+	Do not fetch missing objects from the promisor remote on
+	demand.  Useful together with `git cat-file -e <object>` to
+	see if the object is locally available.
+	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
+	environment variable to `1`.
 
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
@@ -202,7 +211,7 @@
 	Do not perform optional operations that require locks. This is
 	equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
 
---list-cmds=group[,group...]::
+--list-cmds=<group>[,<group>...]::
 	List commands by group. This is an internal/experimental
 	option and may change or be removed in the future. Supported
 	groups are: builtins, parseopt (builtin commands that use
@@ -212,6 +221,11 @@
 	nohelpers (exclude helper commands), alias and config
 	(retrieve command list from config variable completion.commands)
 
+--attr-source=<tree-ish>::
+	Read gitattributes from <tree-ish> instead of the worktree. See
+	linkgit:gitattributes[5]. This is equivalent to setting the
+	`GIT_ATTR_SOURCE` environment variable.
+
 GIT COMMANDS
 ------------
 
@@ -546,10 +560,15 @@
 
 `GIT_DEFAULT_HASH`::
 	If this variable is set, the default hash algorithm for new
-	repositories will be set to this value. This value is currently
-	ignored when cloning; the setting of the remote repository
-	is used instead. The default is "sha1". THIS VARIABLE IS
-	EXPERIMENTAL! See `--object-format` in linkgit:git-init[1].
+	repositories will be set to this value. This value is
+	ignored when cloning and the setting of the remote repository
+	is always used. The default is "sha1".
+	See `--object-format` in linkgit:git-init[1].
+
+`GIT_DEFAULT_REF_FORMAT`::
+	If this variable is set, the default reference backend format for new
+	repositories will be set to this value. The default is "files".
+	See `--ref-format` in linkgit:git-init[1].
 
 Git Commits
 ~~~~~~~~~~~
@@ -686,6 +705,9 @@
 	tells Git not to verify the SSL certificate when fetching or
 	pushing over HTTPS.
 
+`GIT_ATTR_SOURCE`::
+	Sets the treeish that gitattributes will be read from.
+
 `GIT_ASKPASS`::
 	If this environment variable is set, then Git commands which need to
 	acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
@@ -716,13 +738,12 @@
 	waiting for someone with sufficient permissions to fix it.
 
 `GIT_FLUSH`::
-// NEEDSWORK: make it into a usual Boolean environment variable
-	If this environment variable is set to "1", then commands such
+	If this Boolean environment variable is set to true, then commands such
 	as 'git blame' (in incremental mode), 'git rev-list', 'git log',
 	'git check-attr' and 'git check-ignore' will
 	force a flush of the output stream after each record have been
 	flushed. If this
-	variable is set to "0", the output of these commands will be done
+	variable is set to false, the output of these commands will be done
 	using completely buffered I/O.   If this environment variable is
 	not set, Git will choose buffered or record-oriented flushing
 	based on whether stdout appears to be redirected to a file or not.
@@ -830,7 +851,7 @@
 collisions).
 +
 In addition, if the variable is set to
-`af_unix:[<socket_type>:]<absolute-pathname>`, Git will try
+`af_unix:[<socket-type>:]<absolute-pathname>`, Git will try
 to open the path as a Unix Domain Socket.  The socket type
 can be either `stream` or `dgram`.
 +
@@ -860,6 +881,10 @@
 	header and packfile URIs. Set this Boolean environment variable to false to prevent this
 	redaction.
 
+`GIT_NO_REPLACE_OBJECTS`::
+	Setting and exporting this environment variable tells Git to
+	ignore replacement refs and do not replace Git objects.
+
 `GIT_LITERAL_PATHSPECS`::
 	Setting this Boolean environment variable to true will cause Git to treat all
 	pathspecs literally, rather than as glob patterns. For example,
@@ -881,6 +906,11 @@
 	Setting this Boolean environment variable to true will cause Git to treat all
 	pathspecs as case-insensitive.
 
+`GIT_NO_LAZY_FETCH`::
+	Setting this Boolean environment variable to true tells Git
+	not to lazily fetch missing objects from the promisor remote
+	on demand.
+
 `GIT_REFLOG_ACTION`::
 	When a ref is updated, reflog entries are created to keep
 	track of the reason why the ref was updated (which is
@@ -903,6 +933,16 @@
 	should not normally need to set this to `0`, but it may be
 	useful when trying to salvage data from a corrupted repository.
 
+`GIT_COMMIT_GRAPH_PARANOIA`::
+	When loading a commit object from the commit-graph, Git performs an
+	existence check on the object in the object database. This is done to
+	avoid issues with stale commit-graphs that contain references to
+	already-deleted commits, but comes with a performance penalty.
++
+The default is "false", which disables the aforementioned behavior.
+Setting this to "true" enables the existence check so that stale commits
+will never be returned from the commit-graph at the cost of performance.
+
 `GIT_ALLOW_PROTOCOL`::
 	If set to a colon-separated list of protocols, behave as if
 	`protocol.allow` is set to `never`, and each of the listed
@@ -920,7 +960,7 @@
 `GIT_PROTOCOL`::
 	For internal use only.  Used in handshaking the wire protocol.
 	Contains a colon ':' separated list of keys with optional values
-	'key[=value]'.  Presence of unknown keys and values must be
+	'<key>[=<value>]'.  Presence of unknown keys and values must be
 	ignored.
 +
 Note that servers may need to be configured to allow this variable to
@@ -1007,10 +1047,11 @@
 efficiency may later be compressed together into "pack files".
 
 Named pointers called refs mark interesting points in history.  A ref
-may contain the SHA-1 name of an object or the name of another ref.  Refs
-with names beginning `ref/head/` contain the SHA-1 name of the most
+may contain the SHA-1 name of an object or the name of another ref (the
+latter is called a "symbolic ref").
+Refs with names beginning `refs/head/` contain the SHA-1 name of the most
 recent commit (or "head") of a branch under development.  SHA-1 names of
-tags of interest are stored under `ref/tags/`.  A special ref named
+tags of interest are stored under `refs/tags/`.  A symbolic ref named
 `HEAD` contains the name of the currently checked-out branch.
 
 The index file is initialized with a list of all paths and, for each
@@ -1053,7 +1094,7 @@
 -------
 Git was started by Linus Torvalds, and is currently maintained by Junio
 C Hamano. Numerous contributions have come from the Git mailing list
-<git@vger.kernel.org>.  http://www.openhub.net/p/git/contributors/summary
+<git@vger.kernel.org>.  https://openhub.net/p/git/contributors/summary
 gives you a more complete list of contributors.
 
 If you have a clone of git.git itself, the
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 39bfbca..4338d02 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -100,6 +100,21 @@
 the name of the attribute prefixed with an exclamation point `!`.
 
 
+RESERVED BUILTIN_* ATTRIBUTES
+-----------------------------
+
+builtin_* is a reserved namespace for builtin attribute values. Any
+user defined attributes under this namespace will be ignored and
+trigger a warning.
+
+`builtin_objectmode`
+~~~~~~~~~~~~~~~~~~~~
+This attribute is for filtering files by their file bit modes (40000,
+120000, 160000, 100755, 100644). e.g. ':(attr:builtin_objectmode=160000)'.
+You may also check these values with `git check-attr builtin_objectmode -- <file>`.
+If the object is not in the index `git check-attr --cached` will return unspecified.
+
+
 EFFECTS
 -------
 
@@ -120,20 +135,19 @@
 `text`
 ^^^^^^
 
-This attribute enables and controls end-of-line normalization.  When a
-text file is normalized, its line endings are converted to LF in the
-repository.  To control what line ending style is used in the working
-directory, use the `eol` attribute for a single file and the
-`core.eol` configuration variable for all text files.
-Note that setting `core.autocrlf` to `true` or `input` overrides
-`core.eol` (see the definitions of those options in
-linkgit:git-config[1]).
+This attribute marks the path as a text file, which enables end-of-line
+conversion: When a matching file is added to the index, the file's line
+endings are normalized to LF in the index.  Conversely, when the file is
+copied from the index to the working directory, its line endings may be
+converted from LF to CRLF depending on the `eol` attribute, the Git
+config, and the platform (see explanation of `eol` below).
 
 Set::
 
 	Setting the `text` attribute on a path enables end-of-line
-	normalization and marks the path as a text file.  End-of-line
-	conversion takes place without guessing the content type.
+	conversion on checkin and checkout as described above.  Line endings
+	are normalized to LF in the index every time the file is checked in,
+	even if the file was previously added to Git with CRLF line endings.
 
 Unset::
 
@@ -142,10 +156,11 @@
 
 Set to string value "auto"::
 
-	When `text` is set to "auto", the path is marked for automatic
-	end-of-line conversion.  If Git decides that the content is
-	text, its line endings are converted to LF on checkin.
-	When the file has been committed with CRLF, no conversion is done.
+	When `text` is set to "auto", Git decides by itself whether the file
+	is text or binary.  If it is text and the file was not already in
+	Git with CRLF endings, line endings are converted on checkin and
+	checkout as described above.  Otherwise, no conversion is done on
+	checkin or checkout.
 
 Unspecified::
 
@@ -159,26 +174,29 @@
 `eol`
 ^^^^^
 
-This attribute sets a specific line-ending style to be used in the
-working directory.  This attribute has effect only if the `text`
-attribute is set or unspecified, or if it is set to `auto`, the file is
-detected as text, and it is stored with LF endings in the index.  Note
-that setting this attribute on paths which are in the index with CRLF
-line endings may make the paths to be considered dirty unless
-`text=auto` is set. Adding the path to the index again will normalize
-the line endings in the index.
+This attribute marks a path to use a specific line-ending style in the
+working tree when it is checked out.  It has effect only if `text` or
+`text=auto` is set (see above), but specifying `eol` automatically sets
+`text` if `text` was left unspecified.
 
 Set to string value "crlf"::
 
-	This setting forces Git to normalize line endings for this
-	file on checkin and convert them to CRLF when the file is
-	checked out.
+	This setting converts the file's line endings in the working
+	directory to CRLF when the file is checked out.
 
 Set to string value "lf"::
 
-	This setting forces Git to normalize line endings to LF on
-	checkin and prevents conversion to CRLF when the file is
-	checked out.
+	This setting uses the same line endings in the working directory as
+	in the index when the file is checked out.
+
+Unspecified::
+
+	If the `eol` attribute is unspecified for a file, its line endings
+	in the working directory are determined by the `core.autocrlf` or
+	`core.eol` configuration variable (see the definitions of those
+	options in linkgit:git-config[1]).  If `text` is set but neither of
+	those variables is, the default is `eol=crlf` on Windows and
+	`eol=lf` on all other platforms.
 
 Backwards compatibility with `crlf` attribute
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1119,17 +1137,20 @@
 name.
 
 The `merge.*.driver` variable's value is used to construct a
-command to run to merge ancestor's version (`%O`), current
+command to run to common ancestor's version (`%O`), current
 version (`%A`) and the other branches' version (`%B`).  These
 three tokens are replaced with the names of temporary files that
 hold the contents of these versions when the command line is
-built. Additionally, %L will be replaced with the conflict marker
+built. Additionally, `%L` will be replaced with the conflict marker
 size (see below).
 
 The merge driver is expected to leave the result of the merge in
 the file named with `%A` by overwriting it, and exit with zero
 status if it managed to merge them cleanly, or non-zero if there
-were conflicts.
+were conflicts.  When the driver crashes (e.g. killed by SEGV),
+it is expected to exit with non-zero status that are higher than
+128, and in such a case, the merge results in a failure (which is
+different from producing a conflict).
 
 The `merge.*.recursive` variable specifies what other merge
 driver to use when the merge driver is called for an internal
@@ -1138,15 +1159,16 @@
 internal merge and the final merge.
 
 The merge driver can learn the pathname in which the merged result
-will be stored via placeholder `%P`.
-
+will be stored via placeholder `%P`. The conflict labels to be used
+for the common ancestor, local head and other head can be passed by
+using '%S', '%X' and '%Y` respectively.
 
 `conflict-marker-size`
 ^^^^^^^^^^^^^^^^^^^^^^
 
 This attribute controls the length of conflict markers left in
-the work tree file during a conflicted merge.  Only setting to
-the value to a positive integer has any meaningful effect.
+the work tree file during a conflicted merge.  Only a positive
+integer has a meaningful effect.
 
 For example, this line in `.gitattributes` can be used to tell the merge
 machinery to leave much longer (instead of the usual 7-character-long)
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 1819a5a..7c70932 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -23,10 +23,10 @@
     A subcommand may take dashed options (which may take their own
     arguments, e.g. "--max-parents 2") and arguments.  You SHOULD
     give dashed options first and then arguments.  Some commands may
-    accept dashed options after you have already gave non-option
+    accept dashed options after you have already given non-option
     arguments (which may make the command ambiguous), but you should
     not rely on it (because eventually we may find a way to fix
-    these ambiguity by enforcing the "options then args" rule).
+    these ambiguities by enforcing the "options then args" rule).
 
  * Revisions come first and then paths.
    E.g. in `git diff v1.0 v2.0 arch/x86 include/asm-x86`,
@@ -37,12 +37,12 @@
    they can be disambiguated by placing `--` between them.
    E.g. `git diff -- HEAD` is, "I have a file called HEAD in my work
    tree.  Please show changes between the version I staged in the index
-   and what I have in the work tree for that file", not "show difference
+   and what I have in the work tree for that file", not "show the difference
    between the HEAD commit and the work tree as a whole".  You can say
    `git diff HEAD --` to ask for the latter.
 
  * Without disambiguating `--`, Git makes a reasonable guess, but errors
-   out and asking you to disambiguate when ambiguous.  E.g. if you have a
+   out and asks you to disambiguate when ambiguous.  E.g. if you have a
    file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
    you have to say either `git diff HEAD --` or `git diff -- HEAD` to
    disambiguate.
@@ -81,9 +81,6 @@
 Here are the rules regarding the "flags" that you should follow when you are
 scripting Git:
 
- * It's preferred to use the non-dashed form of Git commands, which means that
-   you should prefer `git foo` to `git-foo`.
-
  * Splitting short options to separate words (prefer `git foo -a -b`
    to `git foo -ab`, the latter may not even work).
 
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index c0b9525..2122aeb 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -1089,7 +1089,7 @@
 like this:
 
 ------------------------------------------------
-$ git config remote.linus.url http://www.kernel.org/pub/scm/git/git.git/
+$ git config remote.linus.url https://git.kernel.org/pub/scm/git/git.git/
 ------------------------------------------------
 
 and use the "linus" keyword with 'git pull' instead of the full URL.
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index 100f045..71dd197 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -104,6 +104,17 @@
 $ git config --global credential.helper foo
 -------------------------------------------
 
+=== Available helpers
+
+The community maintains a comprehensive list of Git credential helpers at
+https://git-scm.com/doc/credential-helpers.
+
+=== OAuth
+
+An alternative to inputting passwords or personal access tokens is to use an
+OAuth credential helper. Initial authentication opens a browser window to the
+host. Subsequent authentication happens in the background. Many popular Git
+hosts support OAuth.
 
 CREDENTIAL CONTEXTS
 -------------------
@@ -260,7 +271,7 @@
 
 `erase`::
 
-	Remove a matching credential, if any, from the helper's storage.
+	Remove matching credentials, if any, from the helper's storage.
 
 The details of the credential will be provided on the helper's stdin
 stream. The exact format is the same as the input/output format of the
diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt
index 0d57f86..642c512 100644
--- a/Documentation/gitdiffcore.txt
+++ b/Documentation/gitdiffcore.txt
@@ -173,7 +173,7 @@
 detection are off, rename detection adds a preliminary step that first
 checks if files are moved across directories while keeping their
 filename the same.  If there is a file added to a directory whose
-contents is sufficiently similar to a file with the same name that got
+contents are sufficiently similar to a file with the same name that got
 deleted from a different directory, it will mark them as renames and
 exclude them from the later quadratic step (the one that pairwise
 compares all unmatched files to find the "best" matches, determined by
@@ -213,7 +213,7 @@
 only 10 lines from a 100-line document, even if you added 910
 new lines to make a new 1000-line document, you did not do a
 complete rewrite.  diffcore-break breaks such a case in order to
-help diffcore-rename to consider such filepairs as candidate of
+help diffcore-rename to consider such filepairs as a candidate of
 rename/copy detection, but if filepairs broken that way were not
 matched with other filepairs to create rename/copy, then this
 transformation merges them back into the original
@@ -230,13 +230,13 @@
 
 * -B/60 (the same as above, since diffcore-break defaults to 50%).
 
-Note that earlier implementation left a broken pair as a separate
-creation and deletion patches.  This was an unnecessary hack and
+Note that earlier implementation left a broken pair as separate
+creation and deletion patches.  This was an unnecessary hack, and
 the latest implementation always merges all the broken pairs
 back into modifications, but the resulting patch output is
 formatted differently for easier review in case of such
-a complete rewrite by showing the entire contents of old version
-prefixed with '-', followed by the entire contents of new
+a complete rewrite by showing the entire contents of the old version
+prefixed with '-', followed by the entire contents of the new
 version prefixed with '+'.
 
 
@@ -245,25 +245,25 @@
 
 This transformation limits the set of filepairs to those that change
 specified strings between the preimage and the postimage in a certain
-way.  -S<block of text> and -G<regular expression> options are used to
+way.  -S<block-of-text> and -G<regular-expression> options are used to
 specify different ways these strings are sought.
 
-"-S<block of text>" detects filepairs whose preimage and postimage
+"-S<block-of-text>" detects filepairs whose preimage and postimage
 have different number of occurrences of the specified block of text.
 By definition, it will not detect in-file moves.  Also, when a
 changeset moves a file wholesale without affecting the interesting
 string, diffcore-rename kicks in as usual, and `-S` omits the filepair
 (since the number of occurrences of that string didn't change in that
 rename-detected filepair).  When used with `--pickaxe-regex`, treat
-the <block of text> as an extended POSIX regular expression to match,
+the <block-of-text> as an extended POSIX regular expression to match,
 instead of a literal string.
 
-"-G<regular expression>" (mnemonic: grep) detects filepairs whose
+"-G<regular-expression>" (mnemonic: grep) detects filepairs whose
 textual diff has an added or a deleted line that matches the given
 regular expression.  This means that it will detect in-file (or what
 rename-detection considers the same file) moves, which is noise.  The
 implementation runs diff twice and greps, and this can be quite
-expensive.  To speed things up binary files without textconv filters
+expensive.  To speed things up, binary files without textconv filters
 will be ignored.
 
 When `-S` or `-G` are used without `--pickaxe-all`, only filepairs
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index faba2ef..6cfdd0e 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -14,7 +14,7 @@
 -----------
 
 Git users can broadly be grouped into four categories for the purposes of
-describing here a small set of useful command for everyday Git.
+describing here a small set of useful commands for everyday Git.
 
 *	<<STANDALONE,Individual Developer (Standalone)>> commands are essential
 	for anybody who makes a commit, even for somebody who works alone.
@@ -229,7 +229,7 @@
   git am -3 -k`
 
 An alternate participant submission mechanism is using the
-`git request-pull` or pull-request mechanisms (e.g as used on
+`git request-pull` or pull-request mechanisms (e.g. as used on
 GitHub (www.github.com) to notify your upstream of your
 contribution.
 
diff --git a/Documentation/gitformat-bundle.txt b/Documentation/gitformat-bundle.txt
index 00e0a20..1b75cf7 100644
--- a/Documentation/gitformat-bundle.txt
+++ b/Documentation/gitformat-bundle.txt
@@ -67,7 +67,7 @@
 * "Capabilities", which are only in the v3 format, indicate functionality that
 	the bundle requires to be read properly.
 
-* "Prerequisites" lists the objects that are NOT included in the bundle and the
+* "Prerequisites" list the objects that are NOT included in the bundle and the
   reader of the bundle MUST already have, in order to use the data in the
   bundle. The objects stored in the bundle may refer to prerequisite objects and
   anything reachable from them (e.g. a tree object in the bundle can reference
@@ -86,10 +86,10 @@
 This is a comment and it has no specific meaning. The writer of the bundle MAY
 put any string here. The reader of the bundle MUST ignore the comment.
 
-Note on the shallow clone and a Git bundle
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Note on shallow clones and Git bundles
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Note that the prerequisites does not represent a shallow-clone boundary. The
+Note that the prerequisites do not represent a shallow-clone boundary. The
 semantics of the prerequisites and the shallow-clone boundaries are different,
 and the Git bundle v2 format cannot represent a shallow clone repository.
 
diff --git a/Documentation/gitformat-chunk.txt b/Documentation/gitformat-chunk.txt
index 57202ed..3315df6 100644
--- a/Documentation/gitformat-chunk.txt
+++ b/Documentation/gitformat-chunk.txt
@@ -42,7 +42,7 @@
 Each integer is stored in network-byte order.
 
 The chunk identifier `ID[i]` is a label for the data stored within this
-fill from `OFFSET[i]` (inclusive) to `OFFSET[i+1]` (exclusive). Thus, the
+file from `OFFSET[i]` (inclusive) to `OFFSET[i+1]` (exclusive). Thus, the
 size of the `i`th chunk is equal to the difference between `OFFSET[i+1]`
 and `OFFSET[i]`. This requires that the chunk data appears contiguously
 in the same order as the table of contents.
@@ -67,7 +67,7 @@
 information so the file format is identifiable before the chunk-based
 format begins.
 
-Then, call `add_chunk()` for each chunk that is intended for write. This
+Then, call `add_chunk()` for each chunk that is intended for writing. This
 populates the `chunkfile` with information about the order and size of
 each chunk to write. Provide a `chunk_write_fn` function pointer to
 perform the write of the chunk data upon request.
diff --git a/Documentation/gitformat-index.txt b/Documentation/gitformat-index.txt
index 0773e5c..145cace 100644
--- a/Documentation/gitformat-index.txt
+++ b/Documentation/gitformat-index.txt
@@ -386,8 +386,8 @@
 	long, "REUC" extension that is M-bytes long, followed by "EOIE",
 	then the hash would be:
 
-	Hash("TREE" + <binary representation of N> +
-		"REUC" + <binary representation of M>)
+	Hash("TREE" + <binary-representation-of-N> +
+		"REUC" + <binary-representation-of-M>)
 
 == Index Entry Offset Table
 
diff --git a/Documentation/gitformat-pack.txt b/Documentation/gitformat-pack.txt
index e06af02..d6ae229 100644
--- a/Documentation/gitformat-pack.txt
+++ b/Documentation/gitformat-pack.txt
@@ -17,8 +17,8 @@
 DESCRIPTION
 -----------
 
-The Git pack format is now Git stores most of its primary repository
-data. Over the lietime af a repository loose objects (if any) and
+The Git pack format is how Git stores most of its primary repository
+data. Over the lifetime of a repository, loose objects (if any) and
 smaller packs are consolidated into larger pack(s). See
 linkgit:git-gc[1] and linkgit:git-pack-objects[1].
 
@@ -48,7 +48,7 @@
      Observation: we cannot have more than 4G versions ;-) and
      more than 4G objects in a pack.
 
-   - The header is followed by number of object entries, each of
+   - The header is followed by a number of object entries, each of
      which looks like this:
 
      (undeltified representation)
@@ -62,7 +62,7 @@
 	 is an OBJ_OFS_DELTA object
      compressed delta data
 
-     Observation: length of each object is encoded in a variable
+     Observation: the length of each object is encoded in a variable
      length format and is not constrained to 32-bit or anything.
 
   - The trailer records a pack checksum of all of the above.
@@ -117,7 +117,7 @@
 from the base object. If the base object is deltified, it must be
 converted to canonical form first. Each instruction appends more and
 more data to the target object until it's complete. There are two
-supported instructions so far: one for copy a byte range from the
+supported instructions so far: one for copying a byte range from the
 source object and one for inserting new data embedded in the
 instruction itself.
 
@@ -137,7 +137,7 @@
 
 All offset and size bytes are optional. This is to reduce the
 instruction size when encoding small offsets or sizes. The first seven
-bits in the first octet determines which of the next seven octets is
+bits in the first octet determine which of the next seven octets is
 present. If bit zero is set, offset1 is present. If bit one is set
 offset2 is present and so on.
 
@@ -161,9 +161,9 @@
   | 0xxxxxxx |    data    |
   +----------+============+
 
-This is the instruction to construct target object without the base
+This is the instruction to construct the target object without the base
 object. The following data is appended to the target object. The first
-seven bits of the first octet determines the size of data in
+seven bits of the first octet determine the size of data in
 bytes. The size must be non-zero.
 
 ==== Reserved instruction
@@ -294,7 +294,7 @@
 
   - The same trailer as a v1 pack file:
 
-    A copy of the pack checksum at the end of
+    A copy of the pack checksum at the end of the
     corresponding packfile.
 
     Index checksum of all of the above.
@@ -390,10 +390,20 @@
 CHUNK DATA:
 
 	Packfile Names (ID: {'P', 'N', 'A', 'M'})
-	    Stores the packfile names as concatenated, null-terminated strings.
-	    Packfiles must be listed in lexicographic order for fast lookups by
-	    name. This is the only chunk not guaranteed to be a multiple of four
-	    bytes in length, so should be the last chunk for alignment reasons.
+	    Store the names of packfiles as a sequence of NUL-terminated
+	    strings. There is no extra padding between the filenames,
+	    and they are listed in lexicographic order. The chunk itself
+	    is padded at the end with between 0 and 3 NUL bytes to make the
+	    chunk size a multiple of 4 bytes.
+
+	Bitmapped Packfiles (ID: {'B', 'T', 'M', 'P'})
+	    Stores a table of two 4-byte unsigned integers in network order.
+	    Each table entry corresponds to a single pack (in the order that
+	    they appear above in the `PNAM` chunk). The values for each table
+	    entry are as follows:
+	    - The first bit position (in pseudo-pack order, see below) to
+	      contain an object from that pack.
+	    - The number of bits whose objects are selected from that pack.
 
 	OID Fanout (ID: {'O', 'I', 'D', 'F'})
 	    The ith entry, F[i], stores the number of OIDs with first
@@ -508,6 +518,73 @@
 The MIDX's reverse index is stored in the optional 'RIDX' chunk within
 the MIDX itself.
 
+=== `BTMP` chunk
+
+The Bitmapped Packfiles (`BTMP`) chunk encodes additional information
+about the objects in the multi-pack index's reachability bitmap. Recall
+that objects from the MIDX are arranged in "pseudo-pack" order (see
+above) for reachability bitmaps.
+
+From the example above, suppose we have packs "a", "b", and "c", with
+10, 15, and 20 objects, respectively. In pseudo-pack order, those would
+be arranged as follows:
+
+    |a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|
+
+When working with single-pack bitmaps (or, equivalently, multi-pack
+reachability bitmaps with a preferred pack), linkgit:git-pack-objects[1]
+performs ``verbatim'' reuse, attempting to reuse chunks of the bitmapped
+or preferred packfile instead of adding objects to the packing list.
+
+When a chunk of bytes is reused from an existing pack, any objects
+contained therein do not need to be added to the packing list, saving
+memory and CPU time. But a chunk from an existing packfile can only be
+reused when the following conditions are met:
+
+  - The chunk contains only objects which were requested by the caller
+    (i.e. does not contain any objects which the caller didn't ask for
+    explicitly or implicitly).
+
+  - All objects stored in non-thin packs as offset- or reference-deltas
+    also include their base object in the resulting pack.
+
+The `BTMP` chunk encodes the necessary information in order to implement
+multi-pack reuse over a set of packfiles as described above.
+Specifically, the `BTMP` chunk encodes three pieces of information (all
+32-bit unsigned integers in network byte-order) for each packfile `p`
+that is stored in the MIDX, as follows:
+
+`bitmap_pos`:: The first bit position (in pseudo-pack order) in the
+  multi-pack index's reachability bitmap occupied by an object from `p`.
+
+`bitmap_nr`:: The number of bit positions (including the one at
+  `bitmap_pos`) that encode objects from that pack `p`.
+
+For example, the `BTMP` chunk corresponding to the above example (with
+packs ``a'', ``b'', and ``c'') would look like:
+
+[cols="1,2,2"]
+|===
+| |`bitmap_pos` |`bitmap_nr`
+
+|packfile ``a''
+|`0`
+|`10`
+
+|packfile ``b''
+|`10`
+|`15`
+
+|packfile ``c''
+|`25`
+|`20`
+|===
+
+With this information in place, we can treat each packfile as
+individually reusable in the same fashion as verbatim pack reuse is
+performed on individual packs prior to the implementation of the `BTMP`
+chunk.
+
 == cruft packs
 
 The cruft packs feature offer an alternative to Git's traditional mechanism of
@@ -588,51 +665,17 @@
 It is linkgit:git-gc[1] that is typically responsible for removing expired
 unreachable objects.
 
-=== Caution for mixed-version environments
-
-Repositories that have cruft packs in them will continue to work with any older
-version of Git. Note, however, that previous versions of Git which do not
-understand the `.mtimes` file will use the cruft pack's mtime as the mtime for
-all of the objects in it. In other words, do not expect older (pre-cruft pack)
-versions of Git to interpret or even read the contents of the `.mtimes` file.
-
-Note that having mixed versions of Git GC-ing the same repository can lead to
-unreachable objects never being completely pruned. This can happen under the
-following circumstances:
-
-  - An older version of Git running GC explodes the contents of an existing
-    cruft pack loose, using the cruft pack's mtime.
-  - A newer version running GC collects those loose objects into a cruft pack,
-    where the .mtime file reflects the loose object's actual mtimes, but the
-    cruft pack mtime is "now".
-
-Repeating this process will lead to unreachable objects not getting pruned as a
-result of repeatedly resetting the objects' mtimes to the present time.
-
-If you are GC-ing repositories in a mixed version environment, consider omitting
-the `--cruft` option when using linkgit:git-repack[1] and linkgit:git-gc[1], and
-leaving the `gc.cruftPacks` configuration unset until all writers understand
-cruft packs.
-
 === Alternatives
 
 Notable alternatives to this design include:
 
-  - The location of the per-object mtime data, and
-  - Storing unreachable objects in multiple cruft packs.
+  - The location of the per-object mtime data.
 
 On the location of mtime data, a new auxiliary file tied to the pack was chosen
 to avoid complicating the `.idx` format. If the `.idx` format were ever to gain
 support for optional chunks of data, it may make sense to consolidate the
 `.mtimes` format into the `.idx` itself.
 
-Storing unreachable objects among multiple cruft packs (e.g., creating a new
-cruft pack during each repacking operation including only unreachable objects
-which aren't already stored in an earlier cruft pack) is significantly more
-complicated to construct, and so aren't pursued here. The obvious drawback to
-the current implementation is that the entire cruft pack must be re-written from
-scratch.
-
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 6290860..ee9b92c 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -80,7 +80,7 @@
 committed after applying the patch.
 
 It can be used to inspect the current working tree and refuse to
-make a commit if it does not pass certain test.
+make a commit if it does not pass certain tests.
 
 The default 'pre-applypatch' hook, when enabled, runs the
 'pre-commit' hook, if the latter is enabled.
@@ -157,7 +157,7 @@
 The purpose of the hook is to edit the message file in place, and
 it is not suppressed by the `--no-verify` option.  A non-zero exit
 means a failure of the hook and aborts the commit.  It should not
-be used as replacement for pre-commit hook.
+be used as a replacement for the pre-commit hook.
 
 The sample `prepare-commit-msg` hook that comes with Git removes the
 help message found in the commented portion of the commit template.
@@ -243,7 +243,7 @@
 Information about what is to be pushed is provided on the hook's standard
 input with lines of the form:
 
-  <local ref> SP <local object name> SP <remote ref> SP <remote object name> LF
+  <local-ref> SP <local-object-name> SP <remote-ref> SP <remote-object-name> LF
 
 For instance, if the command +git push origin master:foreign+ were run the
 hook would receive a line like the following:
@@ -251,9 +251,9 @@
   refs/heads/master 67890 refs/heads/foreign 12345
 
 although the full object name would be supplied.  If the foreign ref does not
-yet exist the `<remote object name>` will be the all-zeroes object name.  If a
-ref is to be deleted, the `<local ref>` will be supplied as `(delete)` and the
-`<local object name>` will be the all-zeroes object name.  If the local commit
+yet exist the `<remote-object-name>` will be the all-zeroes object name.  If a
+ref is to be deleted, the `<local-ref>` will be supplied as `(delete)` and the
+`<local-object-name>` will be the all-zeroes object name.  If the local commit
 was specified by something other than a name which could be expanded (such as
 `HEAD~`, or an object name) it will be supplied as it was originally given.
 
@@ -275,12 +275,12 @@
 arguments, but for each ref to be updated it receives on standard
 input a line of the format:
 
-  <old-value> SP <new-value> SP <ref-name> LF
+  <old-oid> SP <new-oid> SP <ref-name> LF
 
-where `<old-value>` is the old object name stored in the ref,
-`<new-value>` is the new object name to be stored in the ref and
+where `<old-oid>` is the old object name stored in the ref,
+`<new-oid>` is the new object name to be stored in the ref and
 `<ref-name>` is the full name of the ref.
-When creating a new ref, `<old-value>` is the all-zeroes object name.
+When creating a new ref, `<old-oid>` is the all-zeroes object name.
 
 If the hook exits with non-zero status, none of the refs will be
 updated. If the hook exits with zero, updating of individual refs can
@@ -345,7 +345,7 @@
 
 The default 'update' hook, when enabled--and with
 `hooks.allowunannotated` config option unset or set to false--prevents
-unannotated tags to be pushed.
+unannotated tags from being pushed.
 
 [[proc-receive]]
 proc-receive
@@ -379,12 +379,12 @@
     S: ... ...
     S: flush-pkt
 
-    # Receive result from the hook.
+    # Receive results from the hook.
     # OK, run this command successfully.
     H: PKT-LINE(ok <ref>)
     # NO, I reject it.
     H: PKT-LINE(ng <ref> <reason>)
-    # Fall through, let 'receive-pack' to execute it.
+    # Fall through, let 'receive-pack' execute it.
     H: PKT-LINE(ok <ref>)
     H: PKT-LINE(option fall-through)
     # OK, but has an alternate reference.  The alternate reference name
@@ -503,13 +503,13 @@
 For each reference update that was added to the transaction, the hook
 receives on standard input a line of the format:
 
-  <old-value> SP <new-value> SP <ref-name> LF
+  <old-oid> SP <new-oid> SP <ref-name> LF
 
-where `<old-value>` is the old object name passed into the reference
-transaction, `<new-value>` is the new object name to be stored in the
+where `<old-oid>` is the old object name passed into the reference
+transaction, `<new-oid>` is the new object name to be stored in the
 ref and `<ref-name>` is the full name of the ref. When force updating
 the reference regardless of its current value or when the reference is
-to be created anew, `<old-value>` is the all-zeroes object name. To
+to be created anew, `<old-oid>` is the all-zeroes object name. To
 distinguish these cases, you can inspect the current value of
 `<ref-name>` via `git rev-parse`.
 
@@ -595,10 +595,51 @@
 sendemail-validate
 ~~~~~~~~~~~~~~~~~~
 
-This hook is invoked by linkgit:git-send-email[1].  It takes a single parameter,
-the name of the file that holds the e-mail to be sent.  Exiting with a
-non-zero status causes `git send-email` to abort before sending any
-e-mails.
+This hook is invoked by linkgit:git-send-email[1].
+
+It takes these command line arguments. They are,
+1. the name of the file which holds the contents of the email to be sent.
+2. The name of the file which holds the SMTP headers of the email.
+
+The SMTP headers are passed in the exact same way as they are passed to the
+user's Mail Transport Agent (MTA). In effect, the email given to the user's
+MTA, is the contents of $2 followed by the contents of $1.
+
+An example of a few common headers is shown below. Take notice of the
+capitalization and multi-line tab structure.
+
+  From: Example <from@example.com>
+  To: to@example.com
+  Cc: cc@example.com,
+	  A <author@example.com>,
+	  One <one@example.com>,
+	  two@example.com
+  Subject: PATCH-STRING
+
+Exiting with a non-zero status causes `git send-email` to abort
+before sending any e-mails.
+
+The following environment variables are set when executing the hook.
+
+`GIT_SENDEMAIL_FILE_COUNTER`::
+	A 1-based counter incremented by one for every file holding an e-mail
+	to be sent (excluding any FIFOs). This counter does not follow the
+	patch series counter scheme. It will always start at 1 and will end at
+	GIT_SENDEMAIL_FILE_TOTAL.
+
+`GIT_SENDEMAIL_FILE_TOTAL`::
+	The total number of files that will be sent (excluding any FIFOs). This
+	counter does not follow the patch series counter scheme. It will always
+	be equal to the number of files being sent, whether there is a cover
+	letter or not.
+
+These variables may for instance be used to validate patch series.
+
+The sample `sendemail-validate` hook that comes with Git checks that all sent
+patches (excluding the cover letter) can be applied on top of the upstream
+repository default branch without conflicts. Some placeholders are left for
+additional validation steps to be performed after all patches of a given series
+have been applied.
 
 fsmonitor-watchman
 ~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index f2738b1..5e0964e 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -88,7 +88,7 @@
    Put a backslash ("`\`") in front of the first "`!`" for patterns
    that begin with a literal "`!`", for example, "`\!important!.txt`".
 
- - The slash '/' is used as the directory separator. Separators may
+ - The slash "`/`" is used as the directory separator. Separators may
    occur at the beginning, middle or end of the `.gitignore` search pattern.
 
  - If there is a separator at the beginning or middle (or both) of the
@@ -146,7 +146,9 @@
 not tracked by Git remain untracked.
 
 To stop tracking a file that is currently tracked, use
-'git rm --cached'.
+'git rm --cached' to remove the file from the index. The filename
+can then be added to the `.gitignore` file to stop the file from
+being reintroduced in later commits.
 
 Git does not follow symbolic links when accessing a `.gitignore` file in
 the working tree. This keeps behavior consistent when the file is
@@ -172,10 +174,10 @@
    is not relevant  if there is already a middle slash in
    the pattern.
 
- - The pattern "foo/*", matches "foo/test.json"
-   (a regular file), "foo/bar" (a directory), but it does not match
-   "foo/bar/hello.c" (a regular file), as the asterisk in the
-   pattern does not match "bar/hello.c" which has a slash in it.
+ - The pattern `foo/*`, matches `foo/test.json`
+   (a regular file), `foo/bar` (a directory), but it does not match
+   `foo/bar/hello.c` (a regular file), as the asterisk in the
+   pattern does not match `bar/hello.c` which has a slash in it.
 
 --------------------------------------------------------------
     $ git status
diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt
index d50e9ed..35b3996 100644
--- a/Documentation/gitk.txt
+++ b/Documentation/gitk.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'gitk' [<options>] [<revision range>] [--] [<path>...]
+'gitk' [<options>] [<revision-range>] [--] [<path>...]
 
 DESCRIPTION
 -----------
@@ -26,7 +26,7 @@
 gitk-specific options.
 
 gitk generally only understands options with arguments in the
-'sticked' form (see linkgit:gitcli[7]) due to limitations in the
+'stuck' form (see linkgit:gitcli[7]) due to limitations in the
 command-line parser.
 
 rev-list options and arguments
@@ -124,7 +124,7 @@
 	range to show.  The command is expected to print on its
 	standard output a list of additional revisions to be shown,
 	one per line.  Use this instead of explicitly specifying a
-	'<revision range>' if the set of commits to show may vary
+	'<revision-range>' if the set of commits to show may vary
 	between refreshes.
 
 --select-commit=<ref>::
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index dcee09b..d9bec8b 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -43,9 +43,9 @@
 	command in the superproject. This is only used by `git
 	submodule init` to initialize the configuration variable of
 	the same name. Allowed values here are 'checkout', 'rebase',
-	'merge' or 'none'. See description of 'update' command in
-	linkgit:git-submodule[1] for their meaning. For security
-	reasons, the '!command' form is not accepted here.
+	'merge' or 'none', but not '!command' (for security reasons).
+	See the description of the 'update' command in
+	linkgit:git-submodule[1] for more details.
 
 submodule.<name>.branch::
 	A remote branch name for tracking updates in the upstream submodule.
diff --git a/Documentation/gitprotocol-capabilities.txt b/Documentation/gitprotocol-capabilities.txt
index 0fb5ea0..2cf7735 100644
--- a/Documentation/gitprotocol-capabilities.txt
+++ b/Documentation/gitprotocol-capabilities.txt
@@ -30,7 +30,7 @@
 did not say it supports.
 
 Server MUST diagnose and abort if capabilities it does not understand
-was sent.  Server MUST NOT ignore capabilities that client requested
+were sent.  Server MUST NOT ignore capabilities that client requested
 and server advertised.  As a consequence of these rules, server MUST
 NOT advertise capabilities it does not understand.
 
@@ -61,8 +61,8 @@
 Without multi_ack, a client sends have lines in --date-order until
 the server has found a common base.  That means the client will send
 have lines that are already known by the server to be common, because
-they overlap in time with another branch that the server hasn't found
-a common base on yet.
+they overlap in time with another branch on which the server hasn't found
+a common base yet.
 
 For example suppose the client has commits in caps that the server
 doesn't and the server has commits in lower case that the client
@@ -88,7 +88,7 @@
 
 multi_ack_detailed
 ------------------
-This is an extension of multi_ack that permits client to better
+This is an extension of multi_ack that permits the client to better
 understand the server's in-memory state. See linkgit:gitprotocol-pack[5],
 section "Packfile Negotiation" for more information.
 
@@ -135,7 +135,7 @@
 side-band, side-band-64k
 ------------------------
 
-This capability means that server can send, and client understand multiplexed
+This capability means that the server can send, and the client can understand, multiplexed
 progress reports and error info interleaved with the packfile itself.
 
 These two options are mutually exclusive. A modern client always
@@ -163,14 +163,14 @@
 same deal, you have up to 65519 bytes of data and 1 byte for the stream
 code.
 
-The client MUST send only maximum of one of "side-band" and "side-
-band-64k".  Server MUST diagnose it as an error if client requests
+The client MUST send only one of "side-band" and "side-
+band-64k".  The server MUST diagnose it as an error if client requests
 both.
 
 ofs-delta
 ---------
 
-Server can send, and client understand PACKv2 with delta referring to
+The server can send, and the client can understand, PACKv2 with delta referring to
 its base by position in pack rather than by an obj-id.  That is, they can
 send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
 
@@ -252,7 +252,7 @@
 no-progress
 -----------
 
-The client was started with "git clone -q" or something, and doesn't
+The client was started with "git clone -q" or something similar, and doesn't
 want that side band 2.  Basically the client just says "I do not
 wish to receive stream 2 on sideband, so do not send it to me, and if
 you did, I will drop it on the floor anyway".  However, the sideband
@@ -273,7 +273,7 @@
 data, whether or not a server had advertised objects in the
 refs/tags/* namespace.
 
-Servers MUST pack the tags if their referrant is packed and the client
+Servers MUST pack the tags if their referent is packed and the client
 has requested include-tags.
 
 Clients MUST be prepared for the case where a server has ignored
@@ -378,7 +378,7 @@
 or partial fetch and request that the server omit various objects
 from the packfile.
 
-session-id=<session id>
+session-id=<session-id>
 -----------------------
 
 The server may advertise a session ID that can be used to identify this process
diff --git a/Documentation/gitprotocol-common.txt b/Documentation/gitprotocol-common.txt
index 1486651..cdc9d6e 100644
--- a/Documentation/gitprotocol-common.txt
+++ b/Documentation/gitprotocol-common.txt
@@ -13,7 +13,7 @@
 DESCRIPTION
 -----------
 
-This document sets defines things common to various over-the-wire
+This document defines things common to various over-the-wire
 protocols and file formats used in Git.
 
 ABNF Notation
diff --git a/Documentation/gitprotocol-http.txt b/Documentation/gitprotocol-http.txt
index ccc13f0..ec40a55 100644
--- a/Documentation/gitprotocol-http.txt
+++ b/Documentation/gitprotocol-http.txt
@@ -42,7 +42,7 @@
 by appending additional path components onto the end of the user
 supplied `$GIT_URL` string.
 
-An example of a dumb client requesting for a loose object:
+An example of a dumb client requesting a loose object:
 
   $GIT_URL:     http://example.com:8080/git/repo.git
   URL request:  http://example.com:8080/git/repo.git/objects/d0/49f6c27a2244e12041955e262a404c7faba355
@@ -379,7 +379,7 @@
 C: Build an empty set, `common`, to hold the objects that are later
    determined to be on both ends.
 
-C: Build a set, `want`, of the objects from `advertised` the client
+C: Build a set, `want`, of the objects from `advertised` that the client
    wants to fetch, based on what it saw during ref discovery.
 
 C: Start a queue, `c_pending`, ordered by commit time (popping newest
@@ -391,14 +391,14 @@
 
 C: Send one `$GIT_URL/git-upload-pack` request:
 
-   C: 0032want <want #1>...............................
-   C: 0032want <want #2>...............................
+   C: 0032want <want-#1>...............................
+   C: 0032want <want-#2>...............................
    ....
-   C: 0032have <common #1>.............................
-   C: 0032have <common #2>.............................
+   C: 0032have <common-#1>.............................
+   C: 0032have <common-#2>.............................
    ....
-   C: 0032have <have #1>...............................
-   C: 0032have <have #2>...............................
+   C: 0032have <have-#1>...............................
+   C: 0032have <have-#2>...............................
    ....
    C: 0000
 
@@ -423,7 +423,7 @@
 negotiated through the `object-format` capability (default SHA-1).
 
 The `have` list is created by popping the first 32 commits
-from `c_pending`.  Less can be supplied if `c_pending` empties.
+from `c_pending`.  Fewer can be supplied if `c_pending` empties.
 
 If the client has sent 256 "have" commits and has not yet
 received one of those back from `s_common`, or the client has
@@ -512,7 +512,7 @@
 the id obtained through ref discovery as old_id.
 
   update_request  =  command_list
-		     "PACK" <binary data>
+		     "PACK" <binary-data>
 
   command_list    =  PKT-LINE(command NUL cap_list LF)
 		     *(command_pkt)
@@ -529,8 +529,8 @@
 REFERENCES
 ----------
 
-http://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
-http://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
+https://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
+https://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
 
 SEE ALSO
 --------
diff --git a/Documentation/gitprotocol-pack.txt b/Documentation/gitprotocol-pack.txt
index dd4108b..837b691 100644
--- a/Documentation/gitprotocol-pack.txt
+++ b/Documentation/gitprotocol-pack.txt
@@ -30,7 +30,7 @@
 ---------------
 
 The descriptions below build on the pkt-line format described in
-linkgit:gitprotocol-common[5]. When the grammar indicate `PKT-LINE(...)`, unless
+linkgit:gitprotocol-common[5]. When the grammar indicates `PKT-LINE(...)`, unless
 otherwise noted the usual pkt-line LF rules apply: the sender SHOULD
 include a LF, but the receiver MUST NOT complain if it is not present.
 
@@ -137,7 +137,7 @@
 		    v
     ssh user@example.com "git-upload-pack '/project.git'"
 
-In a "user@host:path" format URI, its relative to the user's home
+In a "user@host:path" format URI, it's relative to the user's home
 directory, because the Git client will run:
 
      git clone user@example.com:project.git
@@ -325,7 +325,7 @@
 
 If the client has requested a positive depth, the server will compute
 the set of commits which are no deeper than the desired depth. The set
-of commits start at the client's wants.
+of commits starts at the client's wants.
 
 The server writes 'shallow' lines for each
 commit whose parents will not be sent as a result. The server writes
diff --git a/Documentation/gitprotocol-v2.txt b/Documentation/gitprotocol-v2.txt
index acb97ad..414bc62 100644
--- a/Documentation/gitprotocol-v2.txt
+++ b/Documentation/gitprotocol-v2.txt
@@ -29,7 +29,7 @@
     semantics the http remote helper can simply act as a proxy
 
 In protocol v2 communication is command oriented.  When first contacting a
-server a list of capabilities will advertised.  Some of these capabilities
+server a list of capabilities will be advertised.  Some of these capabilities
 will be commands which a client can request be executed.  Once a command
 has completed, a client can reuse the connection and request that other
 commands be executed.
@@ -199,7 +199,7 @@
 
 Additional features not supported in the base command will be advertised
 as the value of the command in the capability advertisement in the form
-of a space separated list of features: "<command>=<feature 1> <feature 2>"
+of a space separated list of features: "<command>=<feature-1> <feature-2>"
 
 ls-refs takes in the following arguments:
 
@@ -245,7 +245,7 @@
 
 Additional features not supported in the base command will be advertised
 as the value of the command in the capability advertisement in the form
-of a space separated list of features: "<command>=<feature 1> <feature 2>"
+of a space separated list of features: "<command>=<feature-1> <feature-2>"
 
 A `fetch` request can take the following arguments:
 
@@ -346,7 +346,8 @@
     want-ref <ref>
 	Indicates to the server that the client wants to retrieve a
 	particular ref, where <ref> is the full name of a ref on the
-	server.
+	server.  It is a protocol error to send want-ref for the
+	same ref more than once.
 
 If the 'sideband-all' feature is advertised, the following argument can be
 included in the client's request:
@@ -361,9 +362,10 @@
 If the 'packfile-uris' feature is advertised, the following argument
 can be included in the client's request as well as the potential
 addition of the 'packfile-uris' section in the server's response as
-explained below.
+explained below. Note that at most one `packfile-uris` line can be sent
+to the server.
 
-    packfile-uris <comma-separated list of protocols>
+    packfile-uris <comma-separated-list-of-protocols>
 	Indicates to the server that the client is willing to receive
 	URIs of any of the given protocols in place of objects in the
 	sent packfile. Before performing the connectivity check, the
@@ -534,7 +536,7 @@
 only handle SHA-1.  If the client would like to use a hash algorithm other than
 SHA-1, it should specify its object-format string.
 
-session-id=<session id>
+session-id=<session-id>
 ~~~~~~~~~~~~~~~~~~~~~~~
 
 The server may advertise a session ID that can be used to identify this process
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index ed8da42..d0be008 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -479,14 +479,14 @@
 'option depth' <depth>::
 	Deepens the history of a shallow repository.
 
-'option deepen-since <timestamp>::
+'option deepen-since' <timestamp>::
 	Deepens the history of a shallow repository based on time.
 
-'option deepen-not <ref>::
+'option deepen-not' <ref>::
 	Deepens the history of a shallow repository excluding ref.
 	Multiple options add up.
 
-'option deepen-relative {'true'|'false'}::
+'option deepen-relative' {'true'|'false'}::
 	Deepens the history of a shallow repository relative to
 	current boundary. Only valid when used with "option depth".
 
@@ -526,7 +526,7 @@
 'option pushcert' {'true'|'false'}::
 	GPG sign pushes.
 
-'option push-option <string>::
+'option push-option' <string>::
 	Transmit <string> as a push option. As the push option
 	must not contain LF or NUL characters, the string is not encoded.
 
@@ -542,13 +542,10 @@
 	transaction.  If successful, all refs will be updated, or none will.  If the
 	remote side does not support this capability, the push will fail.
 
-'option object-format' {'true'|algorithm}::
-	If 'true', indicate that the caller wants hash algorithm information
+'option object-format true'::
+	Indicate that the caller wants hash algorithm information
 	to be passed back from the remote.  This mode is used when fetching
 	refs.
-+
-If set to an algorithm, indicate that the caller wants to interact with
-the remote side using that algorithm.
 
 SEE ALSO
 --------
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 1a2ef4c..949cd8a 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -23,7 +23,9 @@
 
 *Note*: Also you can have a plain text file `.git` at the root of
 your working tree, containing `gitdir: <path>` to point at the real
-directory that has the repository.  This mechanism is often used for
+directory that has the repository.
+This mechanism is called a 'gitfile' and is usually managed via the
+`git submodule` and `git worktree` commands. It is often used for
 a working tree of a submodule checkout, to allow you in the
 containing superproject to `git checkout` a branch that does not
 have the submodule.  The `checkout` has to remove the entire
diff --git a/Documentation/gitsubmodules.txt b/Documentation/gitsubmodules.txt
index 941858a..f7b5a25 100644
--- a/Documentation/gitsubmodules.txt
+++ b/Documentation/gitsubmodules.txt
@@ -78,7 +78,7 @@
 
  * The command line for those commands that support taking submodules
    as part of their pathspecs. Most commands have a boolean flag
-   `--recurse-submodules` which specify whether to recurse into submodules.
+   `--recurse-submodules` which specifies whether to recurse into submodules.
    Examples are `grep` and `checkout`.
    Some commands take enums, such as `fetch` and `push`, where you can
    specify how submodules are affected.
@@ -151,7 +151,7 @@
 is not affected. This can be undone using `git submodule init`.
 
  * Deleted submodule: A submodule can be deleted by running
-`git rm <submodule path> && git commit`. This can be undone
+`git rm <submodule-path> && git commit`. This can be undone
 using `git revert`.
 +
 The deletion removes the superproject's tracking data, which are
@@ -192,7 +192,7 @@
   [submodule "baz"]
     url = https://example.org/baz
 
-In the above config only the submodule 'bar' and 'baz' are active,
+In the above config only the submodules 'bar' and 'baz' are active,
 'bar' due to (1) and 'baz' due to (3). 'foo' is inactive because
 (1) takes precedence over (3)
 
@@ -229,7 +229,7 @@
   git submodule add <URL> <path>
 
   # Occasionally update the submodule to a new version:
-  git -C <path> checkout <new version>
+  git -C <path> checkout <new-version>
   git add <path>
   git commit -m "update submodule to new version"
 
@@ -274,7 +274,7 @@
 into submodules. The `init` and `update` subcommands of `git submodule`
 will maintain submodules checked out and at an appropriate revision in
 your working tree. Alternatively you can set `submodule.recurse` to have
-`checkout` recursing into submodules (note that `submodule.recurse` also
+`checkout` recurse into submodules (note that `submodule.recurse` also
 affects other Git commands, see linkgit:git-config[1] for a complete list).
 
 
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 0e0b863..4759408 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -49,7 +49,7 @@
 Importing a new project
 -----------------------
 
-Assume you have a tarball project.tar.gz with your initial work.  You
+Assume you have a tarball `project.tar.gz` with your initial work.  You
 can place it under Git revision control as follows.
 
 ------------------------------------------------
@@ -65,10 +65,10 @@
 ------------------------------------------------
 
 You've now initialized the working directory--you may notice a new
-directory created, named ".git".
+directory created, named `.git`.
 
 Next, tell Git to take a snapshot of the contents of all files under the
-current directory (note the '.'), with 'git add':
+current directory (note the `.`), with `git add`:
 
 ------------------------------------------------
 $ git add .
@@ -76,7 +76,7 @@
 
 This snapshot is now stored in a temporary staging area which Git calls
 the "index".  You can permanently store the contents of the index in the
-repository with 'git commit':
+repository with `git commit`:
 
 ------------------------------------------------
 $ git commit
@@ -95,21 +95,20 @@
 ------------------------------------------------
 
 You are now ready to commit.  You can see what is about to be committed
-using 'git diff' with the --cached option:
+using `git diff` with the `--cached` option:
 
 ------------------------------------------------
 $ git diff --cached
 ------------------------------------------------
 
-(Without --cached, 'git diff' will show you any changes that
+(Without `--cached`, `git diff` will show you any changes that
 you've made but not yet added to the index.)  You can also get a brief
-summary of the situation with 'git status':
+summary of the situation with `git status`:
 
 ------------------------------------------------
 $ git status
 On branch master
 Changes to be committed:
-Your branch is up to date with 'origin/master'.
   (use "git restore --staged <file>..." to unstage)
 
 	modified:   file1
@@ -128,7 +127,7 @@
 This will again prompt you for a message describing the change, and then
 record a new version of the project.
 
-Alternatively, instead of running 'git add' beforehand, you can use
+Alternatively, instead of running `git add` beforehand, you can use
 
 ------------------------------------------------
 $ git commit -a
@@ -138,10 +137,10 @@
 them to the index, and commit, all in one step.
 
 A note on commit messages: Though not required, it's a good idea to
-begin the commit message with a single short (less than 50 character)
-line summarizing the change, followed by a blank line and then a more
-thorough description. The text up to the first blank line in a commit
-message is treated as the commit title, and that title is used
+begin the commit message with a single short (no more than 50
+characters) line summarizing the change, followed by a blank line and
+then a more thorough description. The text up to the first blank line in
+a commit message is treated as the commit title, and that title is used
 throughout Git.  For example, linkgit:git-format-patch[1] turns a
 commit into email, and it uses the title on the Subject line and the
 rest of the commit in the body.
@@ -151,7 +150,7 @@
 
 Many revision control systems provide an `add` command that tells the
 system to start tracking changes to a new file.  Git's `add` command
-does something simpler and more powerful: 'git add' is used both for new
+does something simpler and more powerful: `git add` is used both for new
 and newly modified files, and in both cases it takes a snapshot of the
 given files and stages that content in the index, ready for inclusion in
 the next commit.
@@ -182,7 +181,7 @@
 -----------------
 
 A single Git repository can maintain multiple branches of
-development.  To create a new branch named "experimental", use
+development.  To create a new branch named `experimental`, use
 
 ------------------------------------------------
 $ git branch experimental
@@ -201,8 +200,8 @@
 * master
 ------------------------------------------------
 
-The "experimental" branch is the one you just created, and the
-"master" branch is a default branch that was created for you
+The `experimental` branch is the one you just created, and the
+`master` branch is a default branch that was created for you
 automatically.  The asterisk marks the branch you are currently on;
 type
 
@@ -210,8 +209,8 @@
 $ git switch experimental
 ------------------------------------------------
 
-to switch to the experimental branch.  Now edit a file, commit the
-change, and switch back to the master branch:
+to switch to the `experimental` branch.  Now edit a file, commit the
+change, and switch back to the `master` branch:
 
 ------------------------------------------------
 (edit file)
@@ -220,9 +219,9 @@
 ------------------------------------------------
 
 Check that the change you made is no longer visible, since it was
-made on the experimental branch and you're back on the master branch.
+made on the `experimental` branch and you're back on the `master` branch.
 
-You can make a different change on the master branch:
+You can make a different change on the `master` branch:
 
 ------------------------------------------------
 (edit file)
@@ -230,7 +229,7 @@
 ------------------------------------------------
 
 at this point the two branches have diverged, with different changes
-made in each.  To merge the changes made in experimental into master, run
+made in each.  To merge the changes made in `experimental` into `master`, run
 
 ------------------------------------------------
 $ git merge experimental
@@ -258,16 +257,16 @@
 
 will show a nice graphical representation of the resulting history.
 
-At this point you could delete the experimental branch with
+At this point you could delete the `experimental` branch with
 
 ------------------------------------------------
 $ git branch -d experimental
 ------------------------------------------------
 
-This command ensures that the changes in the experimental branch are
+This command ensures that the changes in the `experimental` branch are
 already in the current branch.
 
-If you develop on a branch crazy-idea, then regret it, you can always
+If you develop on a branch `crazy-idea`, then regret it, you can always
 delete the branch with
 
 -------------------------------------
@@ -281,7 +280,7 @@
 ---------------------------
 
 Suppose that Alice has started a new project with a Git repository in
-/home/alice/project, and that Bob, who has a home directory on the
+`/home/alice/project`, and that Bob, who has a home directory on the
 same machine, wants to contribute.
 
 Bob begins with:
@@ -290,7 +289,7 @@
 bob$ git clone /home/alice/project myrepo
 ------------------------------------------------
 
-This creates a new directory "myrepo" containing a clone of Alice's
+This creates a new directory `myrepo` containing a clone of Alice's
 repository.  The clone is on an equal footing with the original
 project, possessing its own copy of the original project's history.
 
@@ -303,31 +302,31 @@
 ------------------------------------------------
 
 When he's ready, he tells Alice to pull changes from the repository
-at /home/bob/myrepo.  She does this with:
+at `/home/bob/myrepo`.  She does this with:
 
 ------------------------------------------------
 alice$ cd /home/alice/project
 alice$ git pull /home/bob/myrepo master
 ------------------------------------------------
 
-This merges the changes from Bob's "master" branch into Alice's
+This merges the changes from Bob's `master` branch into Alice's
 current branch.  If Alice has made her own changes in the meantime,
 then she may need to manually fix any conflicts.
 
-The "pull" command thus performs two operations: it fetches changes
+The `pull` command thus performs two operations: it fetches changes
 from a remote branch, then merges them into the current branch.
 
 Note that in general, Alice would want her local changes committed before
-initiating this "pull".  If Bob's work conflicts with what Alice did since
+initiating this `pull`.  If Bob's work conflicts with what Alice did since
 their histories forked, Alice will use her working tree and the index to
 resolve conflicts, and existing local changes will interfere with the
 conflict resolution process (Git will still perform the fetch but will
 refuse to merge -- Alice will have to get rid of her local changes in
 some way and pull again when this happens).
 
-Alice can peek at what Bob did without merging first, using the "fetch"
+Alice can peek at what Bob did without merging first, using the `fetch`
 command; this allows Alice to inspect what Bob did, using a special
-symbol "FETCH_HEAD", in order to determine if he has anything worth
+symbol `FETCH_HEAD`, in order to determine if he has anything worth
 pulling, like this:
 
 ------------------------------------------------
@@ -336,10 +335,10 @@
 ------------------------------------------------
 
 This operation is safe even if Alice has uncommitted local changes.
-The range notation "HEAD..FETCH_HEAD" means "show everything that is reachable
-from the FETCH_HEAD but exclude anything that is reachable from HEAD".
-Alice already knows everything that leads to her current state (HEAD),
-and reviews what Bob has in his state (FETCH_HEAD) that she has not
+The range notation `HEAD..FETCH_HEAD` means "show everything that is reachable
+from the `FETCH_HEAD` but exclude anything that is reachable from `HEAD`".
+Alice already knows everything that leads to her current state (`HEAD`),
+and reviews what Bob has in his state (`FETCH_HEAD`) that she has not
 seen with this command.
 
 If Alice wants to visualize what Bob did since their histories forked
@@ -349,7 +348,7 @@
 $ gitk HEAD..FETCH_HEAD
 ------------------------------------------------
 
-This uses the same two-dot range notation we saw earlier with 'git log'.
+This uses the same two-dot range notation we saw earlier with `git log`.
 
 Alice may want to view what both of them did since they forked.
 She can use three-dot form instead of the two-dot form:
@@ -361,13 +360,13 @@
 This means "show everything that is reachable from either one, but
 exclude anything that is reachable from both of them".
 
-Please note that these range notation can be used with both gitk
-and "git log".
+Please note that these range notation can be used with both `gitk`
+and `git log`.
 
 After inspecting what Bob did, if there is nothing urgent, Alice may
 decide to continue working without pulling from Bob.  If Bob's history
 does have something Alice would immediately need, Alice may choose to
-stash her work-in-progress first, do a "pull", and then finally unstash
+stash her work-in-progress first, do a `pull`, and then finally unstash
 her work-in-progress on top of the resulting history.
 
 When you are working in a small closely knit group, it is not
@@ -379,8 +378,8 @@
 alice$ git remote add bob /home/bob/myrepo
 ------------------------------------------------
 
-With this, Alice can perform the first part of the "pull" operation
-alone using the 'git fetch' command without merging them with her own
+With this, Alice can perform the first part of the `pull` operation
+alone using the `git fetch` command without merging them with her own
 branch, using:
 
 -------------------------------------
@@ -388,7 +387,7 @@
 -------------------------------------
 
 Unlike the longhand form, when Alice fetches from Bob using a
-remote repository shorthand set up with 'git remote', what was
+remote repository shorthand set up with `git remote`, what was
 fetched is stored in a remote-tracking branch, in this case
 `bob/master`.  So after this:
 
@@ -397,10 +396,10 @@
 -------------------------------------
 
 shows a list of all the changes that Bob made since he branched from
-Alice's master branch.
+Alice's `master` branch.
 
 After examining those changes, Alice
-could merge the changes into her master branch:
+could merge the changes into her `master` branch:
 
 -------------------------------------
 alice$ git merge bob/master
@@ -432,12 +431,12 @@
 /home/alice/project
 -------------------------------------
 
-(The complete configuration created by 'git clone' is visible using
+(The complete configuration created by `git clone` is visible using
 `git config -l`, 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
-name "origin/master":
+Git also keeps a pristine copy of Alice's `master` branch under the
+name `origin/master`:
 
 -------------------------------------
 bob$ git branch -r
@@ -462,8 +461,8 @@
 -----------------
 
 Git history is represented as a series of interrelated commits.  We
-have already seen that the 'git log' command can list those commits.
-Note that first line of each git log entry also gives a name for the
+have already seen that the `git log` command can list those commits.
+Note that first line of each `git log` entry also gives a name for the
 commit:
 
 -------------------------------------
@@ -475,7 +474,7 @@
     merge-base: Clarify the comments on post processing.
 -------------------------------------
 
-We can give this name to 'git show' to see the details about this
+We can give this name to `git show` to see the details about this
 commit.
 
 -------------------------------------
@@ -514,7 +513,7 @@
 $ git tag v2.5 1b2e1d63ff
 -------------------------------------
 
-you can refer to 1b2e1d63ff by the name "v2.5".  If you intend to
+you can refer to `1b2e1d63ff` by the name `v2.5`.  If you intend to
 share this name with other people (for example, to identify a release
 version), you should create a "tag" object, and perhaps sign it; see
 linkgit:git-tag[1] for details.
@@ -533,22 +532,22 @@
 Be careful with that last command: in addition to losing any changes
 in the working directory, it will also remove all later commits from
 this branch.  If this branch is the only branch containing those
-commits, they will be lost.  Also, don't use 'git reset' on a
+commits, they will be lost.  Also, don't use `git reset` on a
 publicly-visible branch that other developers pull from, as it will
 force needless merges on other developers to clean up the history.
-If you need to undo changes that you have pushed, use 'git revert'
+If you need to undo changes that you have pushed, use `git revert`
 instead.
 
-The 'git grep' command can search for strings in any version of your
+The `git grep` command can search for strings in any version of your
 project, so
 
 -------------------------------------
 $ git grep "hello" v2.5
 -------------------------------------
 
-searches for all occurrences of "hello" in v2.5.
+searches for all occurrences of "hello" in `v2.5`.
 
-If you leave out the commit name, 'git grep' will search any of the
+If you leave out the commit name, `git grep` will search any of the
 files it manages in your current directory.  So
 
 -------------------------------------
@@ -558,7 +557,7 @@
 is a quick way to search just the files that are tracked by Git.
 
 Many Git commands also take sets of commits, which can be specified
-in a number of ways.  Here are some examples with 'git log':
+in a number of ways.  Here are some examples with `git log`:
 
 -------------------------------------
 $ git log v2.5..v2.6            # commits between v2.5 and v2.6
@@ -568,16 +567,16 @@
 				# Makefile
 -------------------------------------
 
-You can also give 'git log' a "range" of commits where the first is not
+You can also give `git log` a "range" of commits where the first is not
 necessarily an ancestor of the second; for example, if the tips of
-the branches "stable" and "master" diverged from a common
+the branches `stable` and `master` diverged from a common
 commit some time ago, then
 
 -------------------------------------
 $ git log stable..master
 -------------------------------------
 
-will list commits made in the master branch but not in the
+will list commits made in the `master` branch but not in the
 stable branch, while
 
 -------------------------------------
@@ -585,15 +584,15 @@
 -------------------------------------
 
 will show the list of commits made on the stable branch but not
-the master branch.
+the `master` branch.
 
-The 'git log' command has a weakness: it must present commits in a
+The `git log` command has a weakness: it must present commits in a
 list.  When the history has lines of development that diverged and
-then merged back together, the order in which 'git log' presents
+then merged back together, the order in which `git log` presents
 those commits is meaningless.
 
 Most projects with multiple contributors (such as the Linux kernel,
-or Git itself) have frequent merges, and 'gitk' does a better job of
+or Git itself) have frequent merges, and `gitk` does a better job of
 visualizing their history.  For example,
 
 -------------------------------------
@@ -601,7 +600,7 @@
 -------------------------------------
 
 allows you to browse any commits from the last 2 weeks of commits
-that modified files under the "drivers" directory.  (Note: you can
+that modified files under the `drivers` directory.  (Note: you can
 adjust gitk's fonts by holding down the control key while pressing
 "-" or "+".)
 
@@ -613,7 +612,7 @@
 $ git diff v2.5:Makefile HEAD:Makefile.in
 -------------------------------------
 
-You can also use 'git show' to see any such file:
+You can also use `git show` to see any such file:
 
 -------------------------------------
 $ git show v2.5:Makefile
@@ -649,7 +648,7 @@
 
   * linkgit:git-bisect[1]: When there is a regression in your
     project, one way to track down the bug is by searching through
-    the history to find the exact commit that's to blame.  Git bisect
+    the history to find the exact commit that's to blame.  `git bisect`
     can help you perform a binary search for that commit.  It is
     smart enough to perform a close-to-optimal search even in the
     case of complex non-linear history with lots of merged branches.
diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt
index 34b1d6e..8598358 100644
--- a/Documentation/gitweb.conf.txt
+++ b/Documentation/gitweb.conf.txt
@@ -53,7 +53,7 @@
    `/etc/gitweb-common.conf`),
 
  * either per-instance configuration file (defaults to 'gitweb_config.perl'
-   in the same directory as the installed gitweb), or if it does not exists
+   in the same directory as the installed gitweb), or if it does not exist
    then fallback system-wide configuration file (defaults to `/etc/gitweb.conf`).
 
 Values obtained in later configuration files override values obtained earlier
@@ -242,7 +242,7 @@
 
 $highlight_bin::
 	Path to the highlight executable to use (it must be the one from
-	http://www.andre-simon.de[] due to assumptions about parameters and output).
+	http://andre-simon.de/zip/download.php[] due to assumptions about parameters and output).
 	By default set to 'highlight'; set it to full path to highlight
 	executable if it is not installed on your web server's PATH.
 	Note that 'highlight' feature must be set for gitweb to actually
@@ -343,7 +343,7 @@
 	Label for the "home link" at the top of all pages, leading to `$home_link`
 	(usually the main gitweb page, which contains the projects list).  It is
 	used as the first component of gitweb's "breadcrumb trail":
-	`<home link> / <project> / <action>`.  Can be set at build time using
+	`<home-link> / <project> / <action>`.  Can be set at build time using
 	the `GITWEB_HOME_LINK_STR` variable.  By default it is set to "projects",
 	as this link leads to the list of projects.  Another popular choice is to
 	set it to the name of site.  Note that it is treated as raw HTML so it
@@ -604,9 +604,9 @@
 Each `%feature` hash element is a hash reference and has the following
 structure:
 ----------------------------------------------------------------------
-"<feature_name>" => {
-	"sub" => <feature-sub (subroutine)>,
-	"override" => <allow-override (boolean)>,
+"<feature-name>" => {
+	"sub" => <feature-sub-(subroutine)>,
+	"override" => <allow-override-(boolean)>,
 	"default" => [ <options>... ]
 },
 ----------------------------------------------------------------------
@@ -614,7 +614,7 @@
 features the structure of appropriate `%feature` hash element has a simpler
 form:
 ----------------------------------------------------------------------
-"<feature_name>" => {
+"<feature-name>" => {
 	"override" => 0,
 	"default" => [ <options>... ]
 },
@@ -820,7 +820,7 @@
 (\'h' gitweb parameter) and `%b` to the current hash base
 (\'hb' gitweb parameter); `%%` expands to \'%'.
 +
-For example, at the time this page was written, the http://repo.or.cz[]
+For example, at the time this page was written, the https://repo.or.cz[]
 Git hosting site set it to the following to enable graphical log
 (using the third party tool *git-browser*):
 +
diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt
index 7cee9d3..56d24a3 100644
--- a/Documentation/gitweb.txt
+++ b/Documentation/gitweb.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 To get started with gitweb, run linkgit:git-instaweb[1] from a Git repository.
-This would configure and start your web server, and run web browser pointing to
+This will configure and start your web server, and run a web browser pointing to
 gitweb.
 
 
@@ -20,15 +20,15 @@
 * Browsing every revision of the repository.
 * Viewing the contents of files in the repository at any revision.
 * Viewing the revision log of branches, history of files and directories,
-  see what was changed when, by who.
+  seeing what was changed, when, and by whom.
 * Viewing the blame/annotation details of any file (if enabled).
 * Generating RSS and Atom feeds of commits, for any branch.
   The feeds are auto-discoverable in modern web browsers.
-* Viewing everything that was changed in a revision, and step through
+* Viewing everything that was changed in a revision, and stepping through
   revisions one at a time, viewing the history of the repository.
-* Finding commits which commit messages matches given search term.
+* Finding commits whose commit messages match a given search term.
 
-See http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
+See https://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
 browsed using gitweb itself.
 
 
@@ -41,9 +41,9 @@
 Repositories
 ~~~~~~~~~~~~
 Gitweb can show information from one or more Git repositories.  These
-repositories have to be all on local filesystem, and have to share common
+repositories have to be all on local filesystem, and have to share a common
 repository root, i.e. be all under a single parent repository (but see also
-"Advanced web server setup" section, "Webserver configuration with multiple
+the "Advanced web server setup" section, "Webserver configuration with multiple
 projects' root" subsection).
 
 -----------------------------------------------------------------------
@@ -51,7 +51,7 @@
 -----------------------------------------------------------------------
 
 The default value for `$projectroot` is `/pub/git`.  You can change it during
-building gitweb via `GITWEB_PROJECTROOT` build configuration variable.
+building gitweb via the `GITWEB_PROJECTROOT` build configuration variable.
 
 By default all Git repositories under `$projectroot` are visible and available
 to gitweb.  The list of projects is generated by default by scanning the
@@ -66,7 +66,7 @@
 
 Projects list file format
 ~~~~~~~~~~~~~~~~~~~~~~~~~
-Instead of having gitweb find repositories by scanning filesystem
+Instead of having gitweb find repositories by scanning the filesystem
 starting from $projectroot, you can provide a pre-generated list of
 visible projects by setting `$projects_list` to point to a plain text
 file with a list of projects (with some additional info).
@@ -305,7 +305,7 @@
 looks like this:
 
 -----------------------------------------------------------------------
-.../gitweb.cgi/<repo>/<action>/<revision_from>:/<path_from>..<revision_to>:/<path_to>?<arguments>
+.../gitweb.cgi/<repo>/<action>/<revision-from>:/<path-from>..<revision-to>:/<path-to>?<arguments>
 -----------------------------------------------------------------------
 
 
@@ -503,7 +503,7 @@
 
 The above configuration expects your public repositories to live under
 `/pub/git` and will serve them as `http://git.domain.org/dir-under-pub-git`,
-both as clonable Git URL and as browseable gitweb interface.  If you then
+both as clonable Git URL and as browsable gitweb interface.  If you then
 start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
 then you can even use the `git://` URL with exactly the same path.
 
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 5a53726..d71b199 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -98,9 +98,8 @@
 	revision.
 
 [[def_commit-ish]]commit-ish (also committish)::
-	A <<def_commit_object,commit object>> or an
-	<<def_object,object>> that can be recursively dereferenced to
-	a commit object.
+	A <<def_commit_object,commit object>> or an <<def_object,object>> that
+	can be recursively <<def_dereference,dereferenced>> to a commit object.
 	The following are all commit-ishes:
 	a commit object,
 	a <<def_tag_object,tag object>> that points to a commit
@@ -125,6 +124,25 @@
 	dangling object has no references to it from any
 	reference or <<def_object,object>> in the <<def_repository,repository>>.
 
+[[def_dereference]]dereference::
+	Referring to a <<def_symref,symbolic ref>>: the action of accessing the
+	<<def_ref,reference>> pointed at by a symbolic ref. Recursive
+	dereferencing involves repeating the aforementioned process on the
+	resulting ref until a non-symbolic reference is found.
++
+Referring to a <<def_tag_object,tag object>>: the action of accessing the
+<<def_object,object>> a tag points at. Tags are recursively dereferenced by
+repeating the operation on the result object until the result has either a
+specified <<def_object_type,object type>> (where applicable) or any non-"tag"
+object type. A synonym for "recursive dereference" in the context of tags is
+"<<def_peel,peel>>".
++
+Referring to a <<def_commit_object,commit object>>: the action of accessing
+the commit's tree object. Commits cannot be dereferenced recursively.
++
+Unless otherwise specified, "dereferencing" as it used in the context of Git
+commands or protocols is implicitly recursive.
+
 [[def_detached_HEAD]]detached HEAD::
 	Normally the <<def_HEAD,HEAD>> stores the name of a
 	<<def_branch,branch>>, and commands that operate on the
@@ -184,9 +202,11 @@
 [[def_gitfile]]gitfile::
 	A plain file `.git` at the root of a working tree that
 	points at the directory that is the real repository.
+	For proper use see linkgit:git-worktree[1] or linkgit:git-submodule[1].
+	For syntax see linkgit:gitrepository-layout[5].
 
 [[def_grafts]]grafts::
-	Grafts enables two otherwise different lines of development to be joined
+	Grafts enable two otherwise different lines of development to be joined
 	together by recording fake ancestry information for commits. This way
 	you can make Git pretend the set of <<def_parent,parents>> a <<def_commit,commit>> has
 	is different from what was recorded when the commit was
@@ -294,6 +314,12 @@
 [[def_octopus]]octopus::
 	To <<def_merge,merge>> more than two <<def_branch,branches>>.
 
+[[def_orphan]]orphan::
+	The act of getting on a <<def_branch,branch>> that does not
+	exist yet (i.e., an <<def_unborn,unborn>> branch).  After
+	such an operation, the commit first created becomes a commit
+	without a parent, starting a new history.
+
 [[def_origin]]origin::
 	The default upstream <<def_repository,repository>>. Most projects have
 	at least one upstream project which they track. By default
@@ -444,6 +470,10 @@
 	of the logical predecessor(s) in the line of development, i.e. its
 	parents.
 
+[[def_peel]]peel::
+	The action of recursively <<def_dereference,dereferencing>> a
+	<<def_tag_object,tag object>>.
+
 [[def_pickaxe]]pickaxe::
 	The term <<def_pickaxe,pickaxe>> refers to an option to the diffcore
 	routines that help select changes that add or delete a given text
@@ -608,6 +638,20 @@
 	An <<def_object,object>> used to temporarily store the contents of a
 	<<def_dirty,dirty>> working directory and the index for future reuse.
 
+[[def_special_ref]]special ref::
+	A ref that has different semantics than normal refs. These refs can be
+	accessed via normal Git commands but may not behave the same as a
+	normal ref in some cases.
++
+The following special refs are known to Git:
+
+ - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+   may refer to multiple object IDs. Each object ID is annotated with metadata
+   indicating where it was fetched from and its fetch status.
+
+ - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
+   conflicts. It contains all commit IDs which are being merged.
+
 [[def_submodule]]submodule::
 	A <<def_repository,repository>> that holds the history of a
 	separate project inside another repository (the latter of
@@ -620,12 +664,11 @@
 	copies of) commit objects of the contained submodules.
 
 [[def_symref]]symref::
-	Symbolic reference: instead of containing the <<def_SHA1,SHA-1>>
-	id itself, it is of the format 'ref: refs/some/thing' and when
-	referenced, it recursively dereferences to this reference.
-	'<<def_HEAD,HEAD>>' is a prime example of a symref. Symbolic
-	references are manipulated with the linkgit:git-symbolic-ref[1]
-	command.
+	Symbolic reference: instead of containing the <<def_SHA1,SHA-1>> id
+	itself, it is of the format 'ref: refs/some/thing' and when referenced,
+	it recursively <<def_dereference,dereferences>> to this reference.
+	'<<def_HEAD,HEAD>>' is a prime example of a symref. Symbolic references
+	are manipulated with the linkgit:git-symbolic-ref[1] command.
 
 [[def_tag]]tag::
 	A <<def_ref,ref>> under `refs/tags/` namespace that points to an
@@ -661,11 +704,11 @@
 	<<def_tree,tree>> is equivalent to a <<def_directory,directory>>.
 
 [[def_tree-ish]]tree-ish (also treeish)::
-	A <<def_tree_object,tree object>> or an <<def_object,object>>
-	that can be recursively dereferenced to a tree object.
-	Dereferencing a <<def_commit_object,commit object>> yields the
-	tree object corresponding to the <<def_revision,revision>>'s
-	top <<def_directory,directory>>.
+	A <<def_tree_object,tree object>> or an <<def_object,object>> that can
+	be recursively <<def_dereference,dereferenced>> to a tree object.
+	Dereferencing a <<def_commit_object,commit object>> yields the tree
+	object corresponding to the <<def_revision,revision>>'s top
+	<<def_directory,directory>>.
 	The following are all tree-ishes:
 	a <<def_commit-ish,commit-ish>>,
 	a tree object,
@@ -674,6 +717,18 @@
 	object,
 	etc.
 
+[[def_unborn]]unborn::
+	The <<def_HEAD,HEAD>> can point at a <<def_branch,branch>>
+	that does not yet exist and that does not have any commit on
+	it yet, and such a branch is called an unborn branch.  The
+	most typical way users encounter an unborn branch is by
+	creating a repository anew without cloning from elsewhere.
+	The HEAD would point at the 'main' (or 'master', depending
+	on your configuration) branch that is yet to be born.  Also
+	some operations can get you on an unborn branch with their
+	<<def_orphan,orphan>> option.
+
+
 [[def_unmerged_index]]unmerged index::
 	An <<def_index,index>> which contains unmerged
 	<<def_index_entry,index entries>>.
diff --git a/Documentation/howto/coordinate-embargoed-releases.txt b/Documentation/howto/coordinate-embargoed-releases.txt
index e653775..b9cb95e 100644
--- a/Documentation/howto/coordinate-embargoed-releases.txt
+++ b/Documentation/howto/coordinate-embargoed-releases.txt
@@ -145,7 +145,7 @@
 
 The first step is to https://github.com/git/git/security/advisories/new[open
 an advisory]. Technically, this is not necessary. However, it is the most
-convenient way to obtain the CVE number and it give us a private repository
+convenient way to obtain the CVE number and it gives us a private repository
 associated with it that can be used to collaborate on a fix.
 
 Notifying the Linux distributions
diff --git a/Documentation/howto/keep-canonical-history-correct.txt b/Documentation/howto/keep-canonical-history-correct.txt
index 35d48ef..5f800fd 100644
--- a/Documentation/howto/keep-canonical-history-correct.txt
+++ b/Documentation/howto/keep-canonical-history-correct.txt
@@ -213,4 +213,4 @@
 		 B0--B1---------B2
 ------------
 
-See also http://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
+See also https://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt
index d07c6d4..013014b 100644
--- a/Documentation/howto/maintain-git.txt
+++ b/Documentation/howto/maintain-git.txt
@@ -104,7 +104,7 @@
    files in mbox format).
 
  - Write his own patches to address issues raised on the list but
-   nobody has stepped up solving.  Send it out just like other
+   nobody has stepped up to solve.  Send it out just like other
    contributors do, and pick them up just like patches from other
    contributors (see above).
 
@@ -411,13 +411,13 @@
 
 A merge of two topics may not textually conflict but still have
 conflict at the semantic level. A classic example is for one topic
-to rename an variable and all its uses, while another topic adds a
+to rename a variable and all its uses, while another topic adds a
 new use of the variable under its old name. When these two topics
 are merged together, the reference to the variable newly added by
 the latter topic will still use the old name in the result.
 
 The Meta/Reintegrate script that is used by redo-jch and redo-seen
-scripts implements a crude but usable way to work this issue around.
+scripts implements a crude but usable way to work around this issue.
 When the script merges branch $X, it checks if "refs/merge-fix/$X"
 exists, and if so, the effect of it is squashed into the result of
 the mechanical merge.  In other words,
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
index 151ee84..4e727de 100644
--- a/Documentation/howto/update-hook-example.txt
+++ b/Documentation/howto/update-hook-example.txt
@@ -100,7 +100,7 @@
 
 if test -f "$allowed_users_file"
 then
-  rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
+  rc=$(grep -Ev '^(#|$)' $allowed_users_file |
     while read heads user_patterns
     do
       # does this rule apply to us?
@@ -138,7 +138,7 @@
 
 if test -f "$allowed_groups_file"
 then
-  rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
+  rc=$(grep -Ev '^(#|$)' $allowed_groups_file |
     while read heads group_patterns
     do
       # does this rule apply to us?
diff --git a/Documentation/howto/use-git-daemon.txt b/Documentation/howto/use-git-daemon.txt
index 7af2e52..2cad9b3 100644
--- a/Documentation/howto/use-git-daemon.txt
+++ b/Documentation/howto/use-git-daemon.txt
@@ -4,7 +4,7 @@
 =====================
 
 Git can be run in inetd mode and in stand alone mode. But all you want is
-let a coworker pull from you, and therefore need to set up a Git server
+to let a coworker pull from you, and therefore need to set up a Git server
 real quick, right?
 
 Note that git-daemon is not really chatty at the moment, especially when
diff --git a/Documentation/howto/using-merge-subtree.txt b/Documentation/howto/using-merge-subtree.txt
index a499a94..3bd581a 100644
--- a/Documentation/howto/using-merge-subtree.txt
+++ b/Documentation/howto/using-merge-subtree.txt
@@ -11,7 +11,7 @@
 How to use the subtree merge strategy
 =====================================
 
-There are situations where you want to include contents in your project
+There are situations where you want to include content in your project
 from an independently developed project. You can just pull from the
 other project as long as there are no conflicting paths.
 
diff --git a/Documentation/i18n.txt b/Documentation/i18n.txt
index 6c6baee..3a866af 100644
--- a/Documentation/i18n.txt
+++ b/Documentation/i18n.txt
@@ -34,7 +34,7 @@
 does not forbid it.  However, there are a few things to keep in
 mind.
 
-. 'git commit' and 'git commit-tree' issues
+. 'git commit' and 'git commit-tree' issue
   a warning if the commit log message given to it does not look
   like a valid UTF-8 string, unless you explicitly say your
   project uses a legacy encoding.  The way to say this is to
@@ -46,7 +46,7 @@
 ------------
 +
 Commit objects created with the above setting record the value
-of `i18n.commitEncoding` in its `encoding` header.  This is to
+of `i18n.commitEncoding` in their `encoding` header.  This is to
 help other people who look at them later.  Lack of this header
 implies that the commit log message is encoded in UTF-8.
 
diff --git a/Documentation/manpage-base-url.xsl.in b/Documentation/manpage-base-url.xsl.in
deleted file mode 100644
index e800904..0000000
--- a/Documentation/manpage-base-url.xsl.in
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- manpage-base-url.xsl:
-     special settings for manpages rendered from newer docbook -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-		version="1.0">
-
-<!-- set a base URL for relative links -->
-<xsl:param name="man.base.url.for.relative.links"
-	>@@MAN_BASE_URL@@</xsl:param>
-
-</xsl:stylesheet>
diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl
index a9c7ec6..beb5ff8 100644
--- a/Documentation/manpage-normal.xsl
+++ b/Documentation/manpage-normal.xsl
@@ -8,19 +8,7 @@
 <xsl:param name="man.output.quietly" select="1"/>
 <xsl:param name="refentry.meta.get.quietly" select="1"/>
 
-<!-- convert asciidoc callouts to man page format -->
-<xsl:template match="co">
-	<xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
-	<xsl:text>.sp&#10;</xsl:text>
-	<xsl:apply-templates/>
-	<xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
-	<xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
-	<xsl:apply-templates/>
-	<xsl:text>.br&#10;</xsl:text>
-</xsl:template>
+<!-- unset maximum length of title -->
+<xsl:param name="man.th.title.max.length"/>
 
 </xsl:stylesheet>
diff --git a/Documentation/manpage-quote-apos.xsl b/Documentation/manpage-quote-apos.xsl
deleted file mode 100644
index aeb8839..0000000
--- a/Documentation/manpage-quote-apos.xsl
+++ /dev/null
@@ -1,16 +0,0 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-		version="1.0">
-
-<!-- work around newer groff/man setups using a prettier apostrophe
-     that unfortunately does not quote anything when cut&pasting
-     examples to the shell -->
-<xsl:template name="escape.apostrophe">
-  <xsl:param name="content"/>
-  <xsl:call-template name="string.subst">
-    <xsl:with-param name="string" select="$content"/>
-    <xsl:with-param name="target">'</xsl:with-param>
-    <xsl:with-param name="replacement">\(aq</xsl:with-param>
-  </xsl:call-template>
-</xsl:template>
-
-</xsl:stylesheet>
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index d8f7cd7..3eaefc4 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -191,7 +191,7 @@
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
-	begins, record it in the special ref `MERGE_AUTOSTASH`
+	begins, record it in the ref `MERGE_AUTOSTASH`
 	and apply it after the operation ends.  This means
 	that you can run the operation on a dirty worktree.  However, use
 	with care: the final stash application after a successful
diff --git a/Documentation/mergetools/vimdiff.txt b/Documentation/mergetools/vimdiff.txt
index 2d631e9..befa86d 100644
--- a/Documentation/mergetools/vimdiff.txt
+++ b/Documentation/mergetools/vimdiff.txt
@@ -32,10 +32,10 @@
   - `+` is used to "open a new tab"
   - `,` is used to "open a new vertical split"
   - `/` is used to "open a new horizontal split"
-  - `@` is used to indicate which is the file containing the final version after
+  - `@` is used to indicate the file containing the final version after
     solving the conflicts. If not present, `MERGED` will be used by default.
 
-The precedence of the operators is this one (you can use parentheses to change
+The precedence of the operators is as follows (you can use parentheses to change
 it):
 
     `@` > `+` > `/` > `,`
@@ -162,7 +162,7 @@
 |       REMOTE        |                     |
 ---------------------------------------------
 ....
-Note how in the third tab definition we need to use parenthesis to make `,`
+Note how in the third tab definition we need to use parentheses to make `,`
 have precedence over `/`.
 --
 
@@ -177,7 +177,8 @@
 
 When using these variants, in order to specify a custom layout you will have to
 set configuration variables `mergetool.gvimdiff.layout` and
-`mergetool.nvimdiff.layout` instead of `mergetool.vimdiff.layout`
+`mergetool.nvimdiff.layout` instead of `mergetool.vimdiff.layout` (though the
+latter will be used as fallback if the variant-specific one is not set).
 
 In addition, for backwards compatibility with previous Git versions, you can
 also append `1`, `2` or `3` to either `vimdiff` or any of the variants (ex:
diff --git a/Documentation/object-format-disclaimer.txt b/Documentation/object-format-disclaimer.txt
index 4cb106f..e561e66 100644
--- a/Documentation/object-format-disclaimer.txt
+++ b/Documentation/object-format-disclaimer.txt
@@ -1,6 +1,9 @@
-THIS OPTION IS EXPERIMENTAL! SHA-256 support is experimental and still
-in an early stage.  A SHA-256 repository will in general not be able to
-share work with "regular" SHA-1 repositories.  It should be assumed
-that, e.g., Git internal file formats in relation to SHA-256
-repositories may change in backwards-incompatible ways.  Only use
-`--object-format=sha256` for testing purposes.
+Note: At present, there is no interoperability between SHA-256
+repositories and SHA-1 repositories.
+
+Historically, we warned that SHA-256 repositories may later need
+backward incompatible changes when we introduce such interoperability
+features. Today, we only expect compatible changes. Furthermore, if such
+changes prove to be necessary, it can be expected that SHA-256 repositories
+created with today's Git will be usable by future versions of Git
+without data loss.
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 3b71334..8ee940b 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -122,7 +122,9 @@
 - Placeholders that expand to a single literal character:
 '%n':: newline
 '%%':: a raw '%'
-'%x00':: print a byte from a hex code
+'%x00':: '%x' followed by two hexadecimal digits is replaced with a
+	 byte with the hexadecimal digits' value (we will call this
+	 "literal formatting code" in the rest of this document).
 
 - Placeholders that affect formatting of later placeholders:
 '%Cred':: switch color to red
@@ -222,13 +224,30 @@
 	linkgit:git-rev-list[1])
 '%d':: ref names, like the --decorate option of linkgit:git-log[1]
 '%D':: ref names without the " (", ")" wrapping.
-'%(describe[:options])':: human-readable name, like
-			  linkgit:git-describe[1]; empty string for
-			  undescribable commits.  The `describe` string
-			  may be followed by a colon and zero or more
-			  comma-separated options.  Descriptions can be
-			  inconsistent when tags are added or removed at
-			  the same time.
+'%(decorate[:<options>])'::
+ref names with custom decorations. The `decorate` string may be followed by a
+colon and zero or more comma-separated options. Option values may contain
+literal formatting codes. These must be used for commas (`%x2C`) and closing
+parentheses (`%x29`), due to their role in the option syntax.
++
+** 'prefix=<value>': Shown before the list of ref names.  Defaults to "{nbsp}`(`".
+** 'suffix=<value>': Shown after the list of ref names.  Defaults to "`)`".
+** 'separator=<value>': Shown between ref names.  Defaults to "`,`{nbsp}".
+** 'pointer=<value>': Shown between HEAD and the branch it points to, if any.
+		      Defaults to "{nbsp}`->`{nbsp}".
+** 'tag=<value>': Shown before tag names. Defaults to "`tag:`{nbsp}".
+
++
+For example, to produce decorations with no wrapping
+or tag annotations, and spaces as separators:
++
+`%(decorate:prefix=,suffix=,tag=,separator= )`
+
+'%(describe[:<options>])'::
+human-readable name, like linkgit:git-describe[1]; empty string for
+undescribable commits.  The `describe` string may be followed by a colon and
+zero or more comma-separated options.  Descriptions can be inconsistent when
+tags are added or removed at the same time.
 +
 ** 'tags[=<bool-value>]': Instead of only considering annotated tags,
    consider lightweight tags as well.
@@ -281,13 +300,11 @@
 '%gE':: reflog identity email (respecting .mailmap, see
 	linkgit:git-shortlog[1] or linkgit:git-blame[1])
 '%gs':: reflog subject
-'%(trailers[:options])':: display the trailers of the body as
-			  interpreted by
-			  linkgit:git-interpret-trailers[1]. The
-			  `trailers` string may be followed by a colon
-			  and zero or more comma-separated options.
-			  If any option is provided multiple times the
-			  last occurrence wins.
+'%(trailers[:<options>])'::
+display the trailers of the body as interpreted by
+linkgit:git-interpret-trailers[1]. The `trailers` string may be followed by
+a colon and zero or more comma-separated options. If any option is provided
+multiple times, the last occurrence wins.
 +
 ** 'key=<key>': only show trailers with specified <key>. Matching is done
    case-insensitively and trailing colon is optional. If option is
@@ -299,9 +316,8 @@
    `Reviewed-by`.
 ** 'only[=<bool>]': select whether non-trailer lines from the trailer
    block should be included.
-** 'separator=<sep>': specify a separator inserted between trailer
-   lines. When this option is not given each trailer line is
-   terminated with a line feed character. The string <sep> may contain
+** 'separator=<sep>': specify the separator inserted between trailer
+   lines. Defaults to a line feed character. The string <sep> may contain
    the literal formatting codes described above. To use comma as
    separator one must use `%x2C` as it would otherwise be parsed as
    next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
@@ -312,10 +328,9 @@
    `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
 ** 'keyonly[=<bool>]': only show the key part of the trailer.
 ** 'valueonly[=<bool>]': only show the value part of the trailer.
-** 'key_value_separator=<sep>': specify a separator inserted between
-   trailer lines. When this option is not given each trailer key-value
-   pair is separated by ": ". Otherwise it shares the same semantics
-   as 'separator=<sep>' above.
+** 'key_value_separator=<sep>': specify the separator inserted between
+   the key and value of each trailer. Defaults to ": ". Otherwise it
+   shares the same semantics as 'separator=<sep>' above.
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index dc685be..23888cd 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -48,7 +48,7 @@
 --expand-tabs::
 --no-expand-tabs::
 	Perform a tab expansion (replace each tab with enough spaces
-	to fill to the next display column that is multiple of '<n>')
+	to fill to the next display column that is a multiple of '<n>')
 	in the log message before showing it in the output.
 	`--expand-tabs` is a short-hand for `--expand-tabs=8`, and
 	`--no-expand-tabs` is a short-hand for `--expand-tabs=0`,
@@ -73,7 +73,7 @@
 With an optional '<ref>' argument, use the ref to find the notes
 to display.  The ref can specify the full refname when it begins
 with `refs/notes/`; when it begins with `notes/`, `refs/` and otherwise
-`refs/notes/` is prefixed to form a full name of the ref.
+`refs/notes/` is prefixed to form the full name of the ref.
 +
 Multiple --notes options can be combined to control which notes are
 being displayed. Examples: "--notes=foo" will show only notes from
@@ -87,6 +87,10 @@
 	"--notes --notes=foo --no-notes --notes=bar" will only show notes
 	from "refs/notes/bar".
 
+--show-notes-by-default::
+	Show the default notes unless options for displaying specific
+	notes are given.
+
 --show-notes[=<ref>]::
 --[no-]standard-notes::
 	These options are deprecated. Use the above --notes/--no-notes
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 95a7390..c718f79 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -71,7 +71,7 @@
 Unlike when pushing with linkgit:git-push[1], any updates outside of
 `refs/{tags,heads}/*` will be accepted without `+` in the refspec (or
 `--force`), whether that's swapping e.g. a tree object for a blob, or
-a commit for another commit that's doesn't have the previous commit as
+a commit for another commit that doesn't have the previous commit as
 an ancestor etc.
 +
 Unlike when pushing with linkgit:git-push[1], there is no
@@ -80,7 +80,7 @@
 +
 As with pushing with linkgit:git-push[1], all of the rules described
 above about what's not allowed as an update can be overridden by
-adding an the optional leading `+` to a refspec (or using `--force`
+adding an optional leading `+` to a refspec (or using the `--force`
 command line option). The only exception to this is that no amount of
 forcing will make the `refs/heads/*` namespace accept a non-commit
 object.
@@ -88,7 +88,7 @@
 [NOTE]
 When the remote branch you want to fetch is known to
 be rewound and rebased regularly, it is expected that
-its new tip will not be descendant of its previous tip
+its new tip will not be a descendant of its previous tip
 (as stored in your remote-tracking branch the last time
 you fetched).  You would want
 to use the `+` sign to indicate non-fast-forward updates
diff --git a/Documentation/ref-storage-format.txt b/Documentation/ref-storage-format.txt
new file mode 100644
index 0000000..14fff8a
--- /dev/null
+++ b/Documentation/ref-storage-format.txt
@@ -0,0 +1,3 @@
+* `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.
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 0d90d5b..00ccf68 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -56,7 +56,7 @@
 	error to use this option unless `--walk-reflogs` is in use.
 
 --grep=<pattern>::
-	Limit the commits output to ones with log message that
+	Limit the commits output to ones with a log message that
 	matches the specified pattern (regular expression).  With
 	more than one `--grep=<pattern>`, commits whose message
 	matches any of the given patterns are chosen (but see
@@ -72,7 +72,7 @@
 	instead of ones that match at least one.
 
 --invert-grep::
-	Limit the commits output to ones with log message that do not
+	Limit the commits output to ones with a log message that do not
 	match the pattern specified with `--grep=<pattern>`.
 
 -i::
@@ -151,6 +151,10 @@
 --not::
 	Reverses the meaning of the '{caret}' prefix (or lack thereof)
 	for all following revision specifiers, up to the next `--not`.
+	When used on the command line before --stdin, the revisions passed
+	through stdin will not be affected by it. Conversely, when passed
+	via standard input, the revisions passed on the command line will
+	not be affected by it.
 
 --all::
 	Pretend as if all the refs in `refs/`, along with `HEAD`, are
@@ -195,10 +199,11 @@
 or `--all`. If a trailing '/{asterisk}' is intended, it must be given
 explicitly.
 
---exclude-hidden=[receive|uploadpack]::
-	Do not include refs that would be hidden by `git-receive-pack` or
-	`git-upload-pack` by consulting the appropriate `receive.hideRefs` or
-	`uploadpack.hideRefs` configuration along with `transfer.hideRefs` (see
+--exclude-hidden=[fetch|receive|uploadpack]::
+	Do not include refs that would be hidden by `git-fetch`,
+	`git-receive-pack` or `git-upload-pack` by consulting the appropriate
+	`fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs`
+	configuration along with `transfer.hideRefs` (see
 	linkgit:git-config[1]). This option affects the next pseudo-ref option
 	`--all` or `--glob` and is cleared after processing them.
 
@@ -235,10 +240,13 @@
 endif::git-rev-list[]
 
 --stdin::
-	In addition to the '<commit>' listed on the command
-	line, read them from the standard input. If a `--` separator is
-	seen, stop reading commits and start reading paths to limit the
-	result.
+	In addition to getting arguments from the command line, read
+	them from standard input as well. This accepts commits and
+	pseudo-options like `--all` and `--glob=`. When a `--` separator
+	is seen, the following input is treated as paths and used to
+	limit the result. Flags like `--not` which are read via standard input
+	are only respected for arguments passed in the same way and will not
+	influence any subsequent command line arguments.
 
 ifdef::git-rev-list[]
 --quiet::
@@ -308,12 +316,12 @@
 With `--pretty` format other than `oneline` and `reference` (for obvious reasons),
 this causes the output to have two extra lines of information
 taken from the reflog.  The reflog designator in the output may be shown
-as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
-reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+as `ref@{<Nth>}` (where _<Nth>_ is the reverse-chronological index in the
+reflog) or as `ref@{<timestamp>}` (with the _<timestamp>_ for that entry),
 depending on a few rules:
 +
 --
-1. If the starting point is specified as `ref@{Nth}`, show the index
+1. If the starting point is specified as `ref@{<Nth>}`, show the index
    format.
 +
 2. If the starting point was specified as `ref@{now}`, show the
@@ -333,8 +341,11 @@
 Under `--pretty=reference`, this information will not be shown at all.
 
 --merge::
-	After a failed merge, show refs that touch files having a
-	conflict and don't exist on all heads to merge.
+	Show commits touching conflicted paths in the range `HEAD...<other>`,
+	where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
+	`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+	when the index has unmerged entries. This option can be used to show
+	relevant commits when resolving conflicts from a 3-way merge.
 
 --boundary::
 	Output excluded boundary commits. Boundary commits are
@@ -889,7 +900,7 @@
 	Print the object IDs of any object referenced by the listed
 	commits.  `--objects foo ^bar` thus means ``send me
 	all object IDs which I need to download if I have the commit
-	object _bar_ but not _foo_''.
+	object _bar_ but not _foo_''. See also `--object-names` below.
 
 --in-commit-order::
 	Print tree and blob ids in order of the commits. The tree
@@ -919,7 +930,12 @@
 
 --object-names::
 	Only useful with `--objects`; print the names of the object IDs
-	that are found. This is the default behavior.
+	that are found. This is the default behavior. Note that the
+	"name" of each object is ambiguous, and mostly intended as a
+	hint for packing objects. In particular: no distinction is made between
+	the names of tags, trees, and blobs; path names may be modified
+	to remove newlines; and if an object would appear multiple times
+	with different names, only one name is shown.
 
 --no-object-names::
 	Only useful with `--objects`; does not print the names of the object
@@ -934,10 +950,10 @@
 +
 The form '--filter=blob:none' omits all blobs.
 +
-The form '--filter=blob:limit=<n>[kmg]' omits blobs larger than n bytes
-or units.  n may be zero.  The suffixes k, m, and g can be used to name
-units in KiB, MiB, or GiB.  For example, 'blob:limit=1k' is the same
-as 'blob:limit=1024'.
+The form '--filter=blob:limit=<n>[kmg]' omits blobs of size at least n
+bytes or units.  n may be zero.  The suffixes k, m, and g can be used
+to name units in KiB, MiB, or GiB.  For example, 'blob:limit=1k'
+is the same as 'blob:limit=1024'.
 +
 The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects
 which are not of the requested type.
@@ -1006,6 +1022,10 @@
 +
 The form '--missing=print' is like 'allow-any', but will also print a
 list of the missing objects.  Object IDs are prefixed with a ``?'' character.
++
+If some tips passed to the traversal are missing, they will be
+considered as missing too, and the traversal will ignore them. In case
+we cannot get their Object ID though, an error will be raised.
 
 --exclude-promisor-objects::
 	(For internal use only.)  Prefilter object traversal at
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 9aa5805..6ea6c7c 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -30,10 +30,11 @@
   explicitly say 'heads/master' to tell Git which one you mean.
   When ambiguous, a '<refname>' is disambiguated by taking the
   first match in the following rules:
-
++
   . If '$GIT_DIR/<refname>' exists, that is what you mean (this is usually
-    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`
-    and `CHERRY_PICK_HEAD`);
+    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`,
+    `REBASE_HEAD`, `REVERT_HEAD`, `CHERRY_PICK_HEAD`, `BISECT_HEAD`
+    and `AUTO_MERGE`);
 
   . otherwise, 'refs/<refname>' if it exists;
 
@@ -44,19 +45,38 @@
   . otherwise, 'refs/remotes/<refname>' if it exists;
 
   . otherwise, 'refs/remotes/<refname>/HEAD' if it exists.
+
 +
-`HEAD` names the commit on which you based the changes in the working tree.
-`FETCH_HEAD` records the branch which you fetched from a remote repository
-with your last `git fetch` invocation.
-`ORIG_HEAD` is created by commands that move your `HEAD` in a drastic
-way (`git am`, `git merge`, `git rebase`, `git reset`),
-to record the position of the `HEAD` before their operation, so that
-you can easily change the tip of the branch back to the state before you ran
-them.
-`MERGE_HEAD` records the commit(s) which you are merging into your branch
-when you run `git merge`.
-`CHERRY_PICK_HEAD` records the commit which you are cherry-picking
-when you run `git cherry-pick`.
+  `HEAD`:::
+    names the commit on which you based the changes in the working tree.
+  `FETCH_HEAD`:::
+    records the branch which you fetched from a remote repository with
+    your last `git fetch` invocation.
+  `ORIG_HEAD`:::
+    is created by commands that move your `HEAD` in a drastic way (`git
+    am`, `git merge`, `git rebase`, `git reset`), to record the position
+    of the `HEAD` before their operation, so that you can easily change
+    the tip of the branch back to the state before you ran them.
+  `MERGE_HEAD`:::
+    records the commit(s) which you are merging into your branch when you
+    run `git merge`.
+  `REBASE_HEAD`:::
+    during a rebase, records the commit at which the operation is
+    currently stopped, either because of conflicts or an `edit` command in
+    an interactive rebase.
+  `REVERT_HEAD`:::
+    records the commit which you are reverting when you run `git revert`.
+  `CHERRY_PICK_HEAD`:::
+    records the commit which you are cherry-picking when you run `git
+    cherry-pick`.
+  `BISECT_HEAD`:::
+    records the current commit to be tested when you run `git bisect
+    --no-checkout`.
+  `AUTO_MERGE`:::
+    records a tree object corresponding to the state the
+    'ort' merge strategy wrote to the working tree when a merge operation
+    resulted in conflicts.
+
 +
 Note that any of the 'refs/*' cases above may come either from
 the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
diff --git a/Documentation/scalar.txt b/Documentation/scalar.txt
index f33436c..361f51a 100644
--- a/Documentation/scalar.txt
+++ b/Documentation/scalar.txt
@@ -8,7 +8,8 @@
 SYNOPSIS
 --------
 [verse]
-scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]
+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
+	[--[no-]src] <url> [<enlistment>]
 scalar list
 scalar register [<enlistment>]
 scalar unregister [<enlistment>]
@@ -80,6 +81,11 @@
 cloning. If the HEAD at the remote did not point at any branch when
 `--single-branch` clone was made, no remote-tracking branch is created.
 
+--[no-]src::
+	By default, `scalar clone` places the cloned repository within a
+	`<entlistment>/src` directory. Use `--no-src` to place the cloned
+	repository directly in the `<enlistment>` directory.
+
 --[no-]full-clone::
 	A sparse-checkout is initialized by default. This behavior can be
 	turned off via `--full-clone`.
diff --git a/Documentation/signoff-option.txt b/Documentation/signoff-option.txt
index 12aa233..d98758f 100644
--- a/Documentation/signoff-option.txt
+++ b/Documentation/signoff-option.txt
@@ -9,7 +9,7 @@
 	the committer has the rights to submit the work under the
 	project's license or agrees to some contributor representation,
 	such as a Developer Certificate of Origin.
-	(See http://developercertificate.org for the one used by the
+	(See https://developercertificate.org for the one used by the
 	Linux kernel and Git projects.)  Consult the documentation or
 	leadership of the project to which you're contributing to
 	understand how the signoffs are used in that project.
diff --git a/Documentation/technical/api-index-skel.txt b/Documentation/technical/api-index-skel.txt
index eda8c19..7780a76 100644
--- a/Documentation/technical/api-index-skel.txt
+++ b/Documentation/technical/api-index-skel.txt
@@ -1,7 +1,7 @@
 Git API Documents
 =================
 
-Git has grown a set of internal API over time.  This collection
+Git has grown a set of internal APIs over time.  This collection
 documents them.
 
 ////////////////////////////////////////////////////////////////
diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt
index 487d4d8..c2ba018 100644
--- a/Documentation/technical/api-merge.txt
+++ b/Documentation/technical/api-merge.txt
@@ -28,9 +28,9 @@
 
 * `struct ll_merge_options`
 
-Check ll-merge.h for details.
+Check merge-ll.h for details.
 
 Low-level (single file) merge
 -----------------------------
 
-Check ll-merge.h for details.
+Check merge-ll.h for details.
diff --git a/Documentation/technical/api-simple-ipc.txt b/Documentation/technical/api-simple-ipc.txt
index d44ada9..c4fb152 100644
--- a/Documentation/technical/api-simple-ipc.txt
+++ b/Documentation/technical/api-simple-ipc.txt
@@ -2,7 +2,7 @@
 ==============
 
 The Simple-IPC API is a collection of `ipc_` prefixed library routines
-and a basic communication protocol that allow an IPC-client process to
+and a basic communication protocol that allows an IPC-client process to
 send an application-specific IPC-request message to an IPC-server
 process and receive an application-specific IPC-response message.
 
@@ -20,12 +20,12 @@
 
 The IPC-client routines within a client application process connect
 to the IPC-server and send a request message and wait for a response.
-When received, the response is returned back the caller.
+When received, the response is returned back to the caller.
 
 For example, the `fsmonitor--daemon` feature will be built as a server
 application on top of the IPC-server library routines.  It will have
 threads watching for file system events and a thread pool waiting for
-client connections.  Clients, such as `git status` will request a list
+client connections.  Clients, such as `git status`, will request a list
 of file system events since a point in time and the server will
 respond with a list of changed files and directories.  The formats of
 the request and response are application-specific; the IPC-client and
@@ -37,7 +37,7 @@
 
 The Simple-IPC mechanism differs from the existing `sub-process.c`
 model (Documentation/technical/long-running-process-protocol.txt) and
-used by applications like Git-LFS.  In the LFS-style sub-process model
+used by applications like Git-LFS.  In the LFS-style sub-process model,
 the helper is started by the foreground process, communication happens
 via a pair of file descriptors bound to the stdin/stdout of the
 sub-process, the sub-process only serves the current foreground
@@ -102,4 +102,4 @@
 response, and disconnect.  It is a one round trip facility for
 querying the server.  The Simple-IPC routines hide the socket,
 named pipe, and thread pool details and allow the application
-layer to focus on the application at hand.
+layer to focus on the task at hand.
diff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt
index c2e652b..f5d2009 100644
--- a/Documentation/technical/bitmap-format.txt
+++ b/Documentation/technical/bitmap-format.txt
@@ -114,7 +114,7 @@
 
     * N entries with compressed bitmaps, one for each indexed commit
 +
-Where `N` is the total amount of entries in this bitmap index.
+Where `N` is the total number of entries in this bitmap index.
 Each entry contains the following:
 
 	** {empty}
@@ -126,7 +126,7 @@
 	** {empty}
 	1-byte XOR-offset: ::
 	    The xor offset used to compress this bitmap. For an entry
-	    in position `x`, a XOR offset of `y` means that the actual
+	    in position `x`, an XOR offset of `y` means that the actual
 	    bitmap representing this commit is composed by XORing the
 	    bitmap for this entry with the bitmap in entry `x-y` (i.e.
 	    the bitmap `y` entries before this one).
@@ -239,7 +239,7 @@
 
 For a `.bitmap` containing `nr_entries` reachability bitmaps, the table
 contains a list of `nr_entries` <commit_pos, offset, xor_row> triplets
-(sorted in the ascending order of `commit_pos`). The content of i'th
+(sorted in the ascending order of `commit_pos`). The content of the i'th
 triplet is -
 
 	* {empty}
diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt
index 86fed0d..2c26e95 100644
--- a/Documentation/technical/commit-graph.txt
+++ b/Documentation/technical/commit-graph.txt
@@ -136,7 +136,7 @@
 
 - Commit grafts and replace objects can change the shape of the commit
   history. The latter can also be enabled/disabled on the fly using
-  `--no-replace-objects`. This leads to difficultly storing both possible
+  `--no-replace-objects`. This leads to difficulty storing both possible
   interpretations of a commit id, especially when computing generation
   numbers. The commit-graph will not be read or written when
   replace-objects or grafts are present.
diff --git a/Documentation/technical/parallel-checkout.txt b/Documentation/technical/parallel-checkout.txt
index 47c9b61..b4a144e 100644
--- a/Documentation/technical/parallel-checkout.txt
+++ b/Documentation/technical/parallel-checkout.txt
@@ -63,7 +63,7 @@
 contention. A `perf` profiling indicated that around 20% of the runtime
 during a local Linux clone (on an SSD) was spent in locking functions.
 For this reason this approach was rejected in favor of using multiple
-child processes, which led to a better performance.
+child processes, which led to better performance.
 
 Multi-Process Solution
 ----------------------
@@ -126,7 +126,7 @@
 
 * W5: Writes the result to the file descriptor opened at W2.
 
-* W6: Calls `fstat()` or lstat()` on the just-written path, and sends
+* W6: Calls `fstat()` or `lstat()` on the just-written path, and sends
   the result back to the main process, together with the end status of
   the operation and the item's identification number.
 
@@ -148,7 +148,7 @@
 
 - First, it updates the in-memory index with the `lstat()` information
   sent by the workers. (This must be done first as this information
-  might me required in the following step.)
+  might be required in the following step.)
 
 - Then it writes the items which collided on disk (i.e. items marked
   with `PC_ITEM_COLLIDED`). More on this below.
@@ -185,7 +185,7 @@
 process must remove all files that prevent this entry from being written
 (before enqueueing it). This includes any non-directory file in the
 leading path of the entry. Later, when a worker gets assigned the entry,
-it looks again for the non-directories files and for an already existing
+it looks again for the non-directory files and for an already existing
 file at the entry's path. If any of these checks finds something, the
 worker knows that there was a path collision.
 
@@ -232,7 +232,7 @@
 Ineligible entries are checked out by the classic sequential codepath
 *before* spawning workers.
 
-Note: submodules's files are also eligible for parallel checkout (as
+Note: submodules' files are also eligible for parallel checkout (as
 long as they don't fall into any of the excluding categories mentioned
 above). But since each submodule is checked out in its own child
 process, we don't mix the superproject's and the submodules' files in
diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt
index 92fcee2..cd948b0 100644
--- a/Documentation/technical/partial-clone.txt
+++ b/Documentation/technical/partial-clone.txt
@@ -3,7 +3,7 @@
 
 The "Partial Clone" feature is a performance optimization for Git that
 allows Git to function without having a complete copy of the repository.
-The goal of this work is to allow Git better handle extremely large
+The goal of this work is to allow Git to better handle extremely large
 repositories.
 
 During clone and fetch operations, Git downloads the complete contents
@@ -256,7 +256,7 @@
 - Dynamic object fetching currently uses the existing pack protocol V0
   which means that each object is requested via fetch-pack.  The server
   will send a full set of info/refs when the connection is established.
-  If there are large number of refs, this may incur significant overhead.
+  If there are a large number of refs, this may incur significant overhead.
 
 
 Future Work
@@ -265,7 +265,7 @@
 - Improve the way to specify the order in which promisor remotes are
   tried.
 +
-For example this could allow to specify explicitly something like:
+For example this could allow specifying explicitly something like:
 "When fetching from this remote, I want to use these promisor remotes
 in this order, though, when pushing or fetching to that remote, I want
 to use those promisor remotes in that order."
@@ -322,7 +322,7 @@
 
 [a] expensive-to-modify list of missing objects:  Earlier in the design of
     partial clone we discussed the need for a single list of missing objects.
-    This would essentially be a sorted linear list of OIDs that the were
+    This would essentially be a sorted linear list of OIDs that were
     omitted by the server during a clone or subsequent fetches.
 
 This file would need to be loaded into memory on every object lookup.
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index ceda4bb..59bea66 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -11,7 +11,7 @@
 "virtual" in the sense that it does not necessarily have to, and
 often does not, match the files in the working tree.
 
-There are cases Git needs to examine the differences between the
+There are cases where Git needs to examine the differences between the
 virtual working tree state in the index and the files in the
 working tree.  The most obvious case is when the user asks `git
 diff` (or its low level implementation, `git diff-files`) or
@@ -165,9 +165,9 @@
 
 In order to avoid the above runtime penalty, post 1.4.2 Git used
 to have a code that made sure the index file
-got timestamp newer than the youngest files in the index when
-there are many young files with the same timestamp as the
-resulting index file would otherwise would have by waiting
+got a timestamp newer than the youngest files in the index when
+there were many young files with the same timestamp as the
+resulting index file otherwise would have by waiting
 before finishing writing the index file out.
 
 I suspected that in practice the situation where many paths in the
@@ -190,7 +190,7 @@
 however, the initial computation of all object names in the
 index takes more than one second, and the index file is written
 out after all that happens.  Therefore the timestamp of the
-index file will be more than one seconds later than the
+index file will be more than one second later than the
 youngest file in the working tree.  This means that in these
 cases there actually will not be any racily clean entry in
 the resulting index.
diff --git a/Documentation/technical/reftable.txt b/Documentation/technical/reftable.txt
index 6a67cc4..dd0b37c 100644
--- a/Documentation/technical/reftable.txt
+++ b/Documentation/technical/reftable.txt
@@ -46,7 +46,7 @@
 
 Storage in the file is organized into variable sized blocks. Prefix
 compression is used within a single block to reduce disk space. Block
-size and alignment is tunable by the writer.
+size and alignment are tunable by the writer.
 
 Performance
 ^^^^^^^^^^^
@@ -115,7 +115,7 @@
 Varint encoding is identical to the ofs-delta encoding method used
 within pack files.
 
-Decoder works such as:
+Decoder works as follows:
 
 ....
 val = buf[ptr] & 0x7f
@@ -175,7 +175,7 @@
 footer
 ....
 
-in a log-only file the first log block immediately follows the file
+In a log-only file, the first log block immediately follows the file
 header, without padding to block alignment.
 
 Block size
@@ -247,7 +247,7 @@
 ....
 
 The header is identical to `version_number=1`, with the 4-byte hash ID
-("sha1" for SHA1 and "s256" for SHA-256) append to the header.
+("sha1" for SHA1 and "s256" for SHA-256) appended to the header.
 
 For maximum backward compatibility, it is recommended to use version 1 when
 writing SHA1 reftables.
@@ -288,7 +288,7 @@
 `restart_count` to binary search between restarts before starting a
 linear scan.
 
-Exactly `restart_count` 3-byte `restart_offset` values precedes the
+Exactly `restart_count` 3-byte `restart_offset` values precede the
 `restart_count`. Offsets are relative to the start of the block and
 refer to the first byte of any `ref_record` whose name has not been
 prefix compressed. Entries in the `restart_offset` list must be sorted,
diff --git a/Documentation/technical/remembering-renames.txt b/Documentation/technical/remembering-renames.txt
index 1e34d91..73f4176 100644
--- a/Documentation/technical/remembering-renames.txt
+++ b/Documentation/technical/remembering-renames.txt
@@ -664,7 +664,7 @@
 renames for any files within a directory that was renamed, in which
 case we will not have been able to detect any rename for the directory
 itself.  In such a case, we do not know whether the directory was
-renamed; we want to be careful to avoid cacheing some kind of "this
+renamed; we want to be careful to avoid caching some kind of "this
 directory was not renamed" statement.  If we did, then a subsequent
 commit being rebased could add a file to the old directory, and the
 user would expect it to end up in the correct directory -- something
diff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt
index 8ef664b..4728142 100644
--- a/Documentation/technical/repository-version.txt
+++ b/Documentation/technical/repository-version.txt
@@ -96,7 +96,13 @@
 ==== `worktreeConfig`
 
 If set, by default "git config" reads from both "config" and
-"config.worktree" file from GIT_DIR in that order. In
+"config.worktree" files from GIT_DIR in that order. In
 multiple working directory mode, "config" file is shared while
 "config.worktree" is per-working directory (i.e., it's in
 GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. The valid values are
+`files` (loose references with a packed-refs file) and `reftable` (see
+Documentation/technical/reftable.txt).
diff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt
index be58f1b..580f233 100644
--- a/Documentation/technical/rerere.txt
+++ b/Documentation/technical/rerere.txt
@@ -60,7 +60,7 @@
     what AB and AC wanted to do.
 
 As branch AC2 refers to the same commit as AC, the above implies that
-this is also compatible what AB and AC2 wanted to do.
+this is also compatible with what AB and AC2 wanted to do.
 
 By extension, this means that rerere should recognize that the above
 conflicts are the same.  To do this, the labels on the conflict
@@ -76,7 +76,7 @@
 Sorting hunks
 ~~~~~~~~~~~~~
 
-As before, lets imagine that a common ancestor had a file with line A
+As before, let's imagine that a common ancestor had a file with line A
 its early part, and line X in its late part.  And then four branches
 are forked that do these things:
 
@@ -145,7 +145,7 @@
 Nested conflicts are handled very similarly to "simple" conflicts.
 Similar to simple conflicts, the conflict is first normalized by
 stripping the labels from conflict markers, stripping the common ancestor
-version, and the sorting the conflict hunks, both for the outer and the
+version, and sorting the conflict hunks, both for the outer and the
 inner conflict.  This is done recursively, so any number of nested
 conflicts can be handled.
 
diff --git a/Documentation/technical/unit-tests.txt b/Documentation/technical/unit-tests.txt
new file mode 100644
index 0000000..206037f
--- /dev/null
+++ b/Documentation/technical/unit-tests.txt
@@ -0,0 +1,240 @@
+= Unit Testing
+
+In our current testing environment, we spend a significant amount of effort
+crafting end-to-end tests for error conditions that could easily be captured by
+unit tests (or we simply forgo some hard-to-setup and rare error conditions).
+Unit tests additionally provide stability to the codebase and can simplify
+debugging through isolation. Writing unit tests in pure C, rather than with our
+current shell/test-tool helper setup, simplifies test setup, simplifies passing
+data around (no shell-isms required), and reduces testing runtime by not
+spawning a separate process for every test invocation.
+
+We believe that a large body of unit tests, living alongside the existing test
+suite, will improve code quality for the Git project.
+
+== Definitions
+
+For the purposes of this document, we'll use *test framework* to refer to
+projects that support writing test cases and running tests within the context
+of a single executable. *Test harness* will refer to projects that manage
+running multiple executables (each of which may contain multiple test cases) and
+aggregating their results.
+
+In reality, these terms are not strictly defined, and many of the projects
+discussed below contain features from both categories.
+
+For now, we will evaluate projects solely on their framework features. Since we
+are relying on having TAP output (see below), we can assume that any framework
+can be made to work with a harness that we can choose later.
+
+
+== Summary
+
+We believe the best way forward is to implement a custom TAP framework for the
+Git project. We use a version of the framework originally proposed in
+https://lore.kernel.org/git/c902a166-98ce-afba-93f2-ea6027557176@gmail.com/[1].
+
+See the <<framework-selection,Framework Selection>> section below for the
+rationale behind this decision.
+
+
+== Choosing a test harness
+
+During upstream discussion, it was occasionally noted that `prove` provides many
+convenient features, such as scheduling slower tests first, or re-running
+previously failed tests.
+
+While we already support the use of `prove` as a test harness for the shell
+tests, it is not strictly required. The t/Makefile allows running shell tests
+directly (though with interleaved output if parallelism is enabled). Git
+developers who wish to use `prove` as a more advanced harness can do so by
+setting DEFAULT_TEST_TARGET=prove in their config.mak.
+
+We will follow a similar approach for unit tests: by default the test
+executables will be run directly from the t/Makefile, but `prove` can be
+configured with DEFAULT_UNIT_TEST_TARGET=prove.
+
+
+[[framework-selection]]
+== Framework selection
+
+There are a variety of features we can use to rank the candidate frameworks, and
+those features have different priorities:
+
+* Critical features: we probably won't consider a framework without these
+** Can we legally / easily use the project?
+*** <<license,License>>
+*** <<vendorable-or-ubiquitous,Vendorable or ubiquitous>>
+*** <<maintainable-extensible,Maintainable / extensible>>
+*** <<major-platform-support,Major platform support>>
+** Does the project support our bare-minimum needs?
+*** <<tap-support,TAP support>>
+*** <<diagnostic-output,Diagnostic output>>
+*** <<runtime-skippable-tests,Runtime-skippable tests>>
+* Nice-to-have features:
+** <<parallel-execution,Parallel execution>>
+** <<mock-support,Mock support>>
+** <<signal-error-handling,Signal & error-handling>>
+* Tie-breaker stats
+** <<project-kloc,Project KLOC>>
+** <<adoption,Adoption>>
+
+[[license]]
+=== License
+
+We must be able to legally use the framework in connection with Git. As Git is
+licensed only under GPLv2, we must eliminate any LGPLv3, GPLv3, or Apache 2.0
+projects.
+
+[[vendorable-or-ubiquitous]]
+=== Vendorable or ubiquitous
+
+We want to avoid forcing Git developers to install new tools just to run unit
+tests. Any prospective frameworks and harnesses must either be vendorable
+(meaning, we can copy their source directly into Git's repository), or so
+ubiquitous that it is reasonable to expect that most developers will have the
+tools installed already.
+
+[[maintainable-extensible]]
+=== Maintainable / extensible
+
+It is unlikely that any pre-existing project perfectly fits our needs, so any
+project we select will need to be actively maintained and open to accepting
+changes. Alternatively, assuming we are vendoring the source into our repo, it
+must be simple enough that Git developers can feel comfortable making changes as
+needed to our version.
+
+In the comparison table below, "True" means that the framework seems to have
+active developers, that it is simple enough that Git developers can make changes
+to it, and that the project seems open to accepting external contributions (or
+that it is vendorable). "Partial" means that at least one of the above
+conditions holds.
+
+[[major-platform-support]]
+=== Major platform support
+
+At a bare minimum, unit-testing must work on Linux, MacOS, and Windows.
+
+In the comparison table below, "True" means that it works on all three major
+platforms with no issues. "Partial" means that there may be annoyances on one or
+more platforms, but it is still usable in principle.
+
+[[tap-support]]
+=== TAP support
+
+The https://testanything.org/[Test Anything Protocol] is a text-based interface
+that allows tests to communicate with a test harness. It is already used by
+Git's integration test suite. Supporting TAP output is a mandatory feature for
+any prospective test framework.
+
+In the comparison table below, "True" means this is natively supported.
+"Partial" means TAP output must be generated by post-processing the native
+output.
+
+Frameworks that do not have at least Partial support will not be evaluated
+further.
+
+[[diagnostic-output]]
+=== Diagnostic output
+
+When a test case fails, the framework must generate enough diagnostic output to
+help developers find the appropriate test case in source code in order to debug
+the failure.
+
+[[runtime-skippable-tests]]
+=== Runtime-skippable tests
+
+Test authors may wish to skip certain test cases based on runtime circumstances,
+so the framework should support this.
+
+[[parallel-execution]]
+=== Parallel execution
+
+Ideally, we will build up a significant collection of unit test cases, most
+likely split across multiple executables. It will be necessary to run these
+tests in parallel to enable fast develop-test-debug cycles.
+
+In the comparison table below, "True" means that individual test cases within a
+single test executable can be run in parallel. We assume that executable-level
+parallelism can be handled by the test harness.
+
+[[mock-support]]
+=== Mock support
+
+Unit test authors may wish to test code that interacts with objects that may be
+inconvenient to handle in a test (e.g. interacting with a network service).
+Mocking allows test authors to provide a fake implementation of these objects
+for more convenient tests.
+
+[[signal-error-handling]]
+=== Signal & error handling
+
+The test framework should fail gracefully when test cases are themselves buggy
+or when they are interrupted by signals during runtime.
+
+[[project-kloc]]
+=== Project KLOC
+
+The size of the project, in thousands of lines of code as measured by
+https://dwheeler.com/sloccount/[sloccount] (rounded up to the next multiple of
+1,000). As a tie-breaker, we probably prefer a project with fewer LOC.
+
+[[adoption]]
+=== Adoption
+
+As a tie-breaker, we prefer a more widely-used project. We use the number of
+GitHub / GitLab stars to estimate this.
+
+
+=== Comparison
+
+:true: [lime-background]#True#
+:false: [red-background]#False#
+:partial: [yellow-background]#Partial#
+
+:gpl: [lime-background]#GPL v2#
+:isc: [lime-background]#ISC#
+:mit: [lime-background]#MIT#
+:expat: [lime-background]#Expat#
+:lgpl: [lime-background]#LGPL v2.1#
+
+:custom-impl: https://lore.kernel.org/git/c902a166-98ce-afba-93f2-ea6027557176@gmail.com/[Custom Git impl.]
+:greatest: https://github.com/silentbicycle/greatest[Greatest]
+:criterion: https://github.com/Snaipe/Criterion[Criterion]
+:c-tap: https://github.com/rra/c-tap-harness/[C TAP]
+:check: https://libcheck.github.io/check/[Check]
+
+[format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"]
+|=====
+Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiquitous>>","<<maintainable-extensible,Maintainable / extensible>>","<<major-platform-support,Major platform support>>","<<tap-support,TAP support>>","<<diagnostic-output,Diagnostic output>>","<<runtime--skippable-tests,Runtime- skippable tests>>","<<parallel-execution,Parallel execution>>","<<mock-support,Mock support>>","<<signal-error-handling,Signal & error handling>>","<<project-kloc,Project KLOC>>","<<adoption,Adoption>>"
+{custom-impl},{gpl},{true},{true},{true},{true},{true},{true},{false},{false},{false},1,0
+{greatest},{isc},{true},{partial},{true},{partial},{true},{true},{false},{false},{false},3,1400
+{criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800
+{c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33
+{check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973
+|=====
+
+=== Additional framework candidates
+
+Several suggested frameworks have been eliminated from consideration:
+
+* Incompatible licenses:
+** https://github.com/zorgnax/libtap[libtap] (LGPL v3)
+** https://cmocka.org/[cmocka] (Apache 2.0)
+* Missing source: https://www.kindahl.net/mytap/doc/index.html[MyTap]
+* No TAP support:
+** https://nemequ.github.io/munit/[µnit]
+** https://github.com/google/cmockery[cmockery]
+** https://github.com/lpabon/cmockery2[cmockery2]
+** https://github.com/ThrowTheSwitch/Unity[Unity]
+** https://github.com/siu/minunit[minunit]
+** https://cunit.sourceforge.net/[CUnit]
+
+
+== Milestones
+
+* Add useful tests of library-like code
+* Integrate with
+  https://lore.kernel.org/git/20230502211454.1673000-1-calvinwan@google.com/[stdlib
+  work]
+* Run alongside regular `make test` target
diff --git a/Documentation/trace2-target-values.txt b/Documentation/trace2-target-values.txt
index 3985b6d..06f1953 100644
--- a/Documentation/trace2-target-values.txt
+++ b/Documentation/trace2-target-values.txt
@@ -5,7 +5,7 @@
 * `<absolute-pathname>` - Writes to the file in append mode. If the target
 already exists and is a directory, the traces will be written to files (one
 per process) underneath the given directory.
-* `af_unix:[<socket_type>:]<absolute-pathname>` - Write to a
+* `af_unix:[<socket-type>:]<absolute-pathname>` - Write to a
 Unix DomainSocket (on platforms that support them).  Socket
 type can be either `stream` or `dgram`; if omitted Git will
 try both.
diff --git a/Documentation/urls-remotes.txt b/Documentation/urls-remotes.txt
index e410912..bf17012 100644
--- a/Documentation/urls-remotes.txt
+++ b/Documentation/urls-remotes.txt
@@ -33,9 +33,9 @@
 ------------
 
 The `<pushurl>` is used for pushes only. It is optional and defaults
-to `<URL>`. Pushing to a remote affects all defined pushurls or to all
+to `<URL>`. Pushing to a remote affects all defined pushurls or all
 defined urls if no pushurls are defined. Fetch, however, will only
-fetch from the first defined url if muliple urls are defined.
+fetch from the first defined url if multiple urls are defined.
 
 Named file in `$GIT_DIR/remotes`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -48,7 +48,7 @@
 following format:
 
 ------------
-	URL: one of the above URL format
+	URL: one of the above URL formats
 	Push: <refspec>
 	Pull: <refspec>
 
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 1c229d7..7cec85a 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -6,23 +6,23 @@
 Depending on the transport protocol, some of this information may be
 absent.
 
-Git supports ssh, git, http, and https protocols (in addition, ftp,
+Git supports ssh, git, http, and https protocols (in addition, ftp
 and ftps can be used for fetching, but this is inefficient and
-deprecated; do not use it).
+deprecated; do not use them).
 
 The native transport (i.e. git:// URL) does no authentication and
 should be used with caution on unsecured networks.
 
 The following syntaxes may be used with them:
 
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}:__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++http++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++ftp++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
 
 An alternative scp-like syntax may also be used with the ssh protocol:
 
-- {startsb}user@{endsb}host.xz:path/to/repo.git/
+- {startsb}__<user>__++@++{endsb}__<host>__++:/++__<path-to-git-repo>__
 
 This syntax is only recognized if there are no slashes before the
 first colon. This helps differentiate a local path that contains a
@@ -30,40 +30,40 @@
 absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
 url.
 
-The ssh and git protocols additionally support ~username expansion:
+The ssh and git protocols additionally support ++~++__<username>__ expansion:
 
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- {startsb}__<user>__++@++{endsb}__<host>__++:~++__<user>__++/++__<path-to-git-repo>__
 
 For local repositories, also supported by Git natively, the following
 syntaxes may be used:
 
-- /path/to/repo.git/
-- \file:///path/to/repo.git/
+- `/path/to/repo.git/`
+- ++file:///path/to/repo.git/++
 
 ifndef::git-clone[]
 These two syntaxes are mostly equivalent, except when cloning, when
-the former implies --local option. See linkgit:git-clone[1] for
+the former implies `--local` option. See linkgit:git-clone[1] for
 details.
 endif::git-clone[]
 
 ifdef::git-clone[]
 These two syntaxes are mostly equivalent, except the former implies
---local option.
+`--local` option.
 endif::git-clone[]
 
-'git clone', 'git fetch' and 'git pull', but not 'git push', will also
+`git clone`, `git fetch` and `git pull`, but not `git push`, will also
 accept a suitable bundle file. See linkgit:git-bundle[1].
 
 When Git doesn't know how to handle a certain transport protocol, it
-attempts to use the 'remote-<transport>' remote helper, if one
+attempts to use the `remote-`{empty}__<transport>__ remote helper, if one
 exists. To explicitly request a remote helper, the following syntax
 may be used:
 
-- <transport>::<address>
+- _<transport>_::__<address>__
 
-where <address> may be a path, a server and path, or an arbitrary
+where _<address>_ may be a path, a server and path, or an arbitrary
 URL-like string recognized by the specific remote helper being
 invoked. See linkgit:gitremote-helpers[7] for details.
 
@@ -72,10 +72,11 @@
 use will be rewritten into URLs that work), you can create a
 configuration section of the form:
 
-------------
-	[url "<actual url base>"]
-		insteadOf = <other url base>
-------------
+[verse]
+--
+	[url "__<actual-url-base>__"]
+		insteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
@@ -91,10 +92,11 @@
 If you want to rewrite URLs for push only, you can create a
 configuration section of the form:
 
-------------
-	[url "<actual url base>"]
-		pushInsteadOf = <other url base>
-------------
+[verse]
+--
+	[url "__<actual-url-base>__"]
+		pushInsteadOf = _<other-url-base>_
+--
 
 For example, with this:
 
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index dc9c6a6..90a4189 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1122,7 +1122,7 @@
 === Creating good commit messages
 
 Though not required, it's a good idea to begin the commit message
-with a single short (less than 50 character) line summarizing the
+with a single short (no more than 50 characters) line summarizing the
 change, followed by a blank line and then a more thorough
 description.  The text up to the first blank line in a commit
 message is treated as the commit title, and that title is used
@@ -1343,6 +1343,33 @@
 $ git diff --theirs file.txt	# same as the above.
 -------------------------------------------------
 
+When using the 'ort' merge strategy (the default), before updating the working
+tree with the result of the merge, Git writes a ref named AUTO_MERGE
+reflecting the state of the tree it is about to write. Conflicted paths with
+textual conflicts that could not be automatically merged are written to this
+tree with conflict markers, just as in the working tree. AUTO_MERGE can thus be
+used with linkgit:git-diff[1] to show the changes you've made so far to resolve
+conflicts. Using the same example as above, after resolving the conflict we
+get:
+
+-------------------------------------------------
+$ git diff AUTO_MERGE
+diff --git a/file.txt b/file.txt
+index cd10406..8bf5ae7 100644
+--- a/file.txt
++++ b/file.txt
+@@ -1,5 +1 @@
+-<<<<<<< HEAD:file.txt
+-Hello world
+-=======
+-Goodbye
+->>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
++Goodbye world
+-------------------------------------------------
+
+Notice that the diff shows we deleted the conflict markers and both versions of
+the content line, and wrote "Goodbye world" instead.
+
 The linkgit:git-log[1] and linkgit:gitk[1] commands also provide special help
 for merges:
 
@@ -4066,15 +4093,46 @@
 about the data in the object.  It's worth noting that the SHA-1 hash
 that is used to name the object is the hash of the original data
 plus this header, so `sha1sum` 'file' does not match the object name
-for 'file'.
+for 'file' (the earliest versions of Git hashed slightly differently
+but the conclusion is still the same).
+
+The following is a short example that demonstrates how these hashes
+can be generated manually:
+
+Let's assume a small text file with some simple content:
+
+-------------------------------------------------
+$ echo "Hello world" >hello.txt
+-------------------------------------------------
+
+We can now manually generate the hash Git would use for this file:
+
+- The object we want the hash for is of type "blob" and its size is
+  12 bytes.
+
+- Prepend the object header to the file content and feed this to
+  `sha1sum`:
+
+-------------------------------------------------
+$ { printf "blob 12\0"; cat hello.txt; } | sha1sum
+802992c4220de19a90767f3000a79a31b98d0df7  -
+-------------------------------------------------
+
+This manually constructed hash can be verified using `git hash-object`
+which of course hides the addition of the header:
+
+-------------------------------------------------
+$ git hash-object hello.txt
+802992c4220de19a90767f3000a79a31b98d0df7
+-------------------------------------------------
 
 As a result, the general consistency of an object can always be tested
 independently of the contents or the type of the object: all objects can
 be validated by verifying that (a) their hashes match the content of the
 file and (b) the object successfully inflates to a stream of bytes that
 forms a sequence of
-`<ascii type without space> + <space> + <ascii decimal size> +
-<byte\0> + <binary object data>`.
+`<ascii-type-without-space> + <space> + <ascii-decimal-size> +
+<byte\0> + <binary-object-data>`.
 
 The structured objects can further have their structure and
 connectivity to other objects verified. This is generally done with
@@ -4096,19 +4154,18 @@
 ----------------------------------------------------
 
 The initial revision lays the foundation for almost everything Git has
-today, but is small enough to read in one sitting.
+today (even though details may differ in a few places), but is small
+enough to read in one sitting.
 
 Note that terminology has changed since that revision.  For example, the
 README in that revision uses the word "changeset" to describe what we
 now call a <<def_commit_object,commit>>.
 
-Also, we do not call it "cache" any more, but rather "index"; however, the
-file is still called `cache.h`.  Remark: Not much reason to change it now,
-especially since there is no good single name for it anyway, because it is
-basically _the_ header file which is included by _all_ of Git's C sources.
+Also, we do not call it "cache" any more, but rather "index"; however,
+the file is still called `read-cache.h`.
 
 If you grasp the ideas in that initial commit, you should check out a
-more recent version and skim `cache.h`, `object.h` and `commit.h`.
+more recent version and skim `read-cache-ll.h`, `object.h` and `commit.h`.
 
 In the early days, Git (in the tradition of UNIX) was a bunch of programs
 which were extremely simple, and which you used in scripts, piping the
@@ -4119,11 +4176,11 @@
 and to avoid code duplication.
 
 By now, you know what the index is (and find the corresponding data
-structures in `cache.h`), and that there are just a couple of object types
-(blobs, trees, commits and tags) which inherit their common structure from
-`struct object`, which is their first member (and thus, you can cast e.g.
-`(struct object *)commit` to achieve the _same_ as `&commit->object`, i.e.
-get at the object name and flags).
+structures in `read-cache-ll.h`), and that there are just a couple of
+object types (blobs, trees, commits and tags) which inherit their
+common structure from `struct object`, which is their first member
+(and thus, you can cast e.g.  `(struct object *)commit` to achieve the
+_same_ as `&commit->object`, i.e.  get at the object name and flags).
 
 Now is a good point to take a break to let this information sink in.
 
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 71425bd..df788c7 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.40.1
+DEF_VER=v2.44.GIT
 
 LF='
 '
@@ -11,7 +11,7 @@
 if test -f version
 then
 	VN=$(cat version) || VN="$DEF_VER"
-elif test -d ${GIT_DIR:-.git} -o -f .git &&
+elif { test -d "${GIT_DIR:-.git}" || test -f .git; } &&
 	VN=$(git describe --match "v[0-9]*" HEAD 2>/dev/null) &&
 	case "$VN" in
 	*$LF*) (exit 1) ;;
diff --git a/INSTALL b/INSTALL
index 4b42288..2a46d04 100644
--- a/INSTALL
+++ b/INSTALL
@@ -119,12 +119,12 @@
 	- A POSIX-compliant shell is required to run some scripts needed
 	  for everyday use (e.g. "bisect", "request-pull").
 
-	- "Perl" version 5.8 or later is needed to use some of the
+	- "Perl" version 5.8.1 or later is needed to use some of the
 	  features (e.g. sending patches using "git send-email",
 	  interacting with svn repositories with "git svn").  If you can
 	  live without these, use NO_PERL.  Note that recent releases of
 	  Redhat/Fedora are reported to ship Perl binary package with some
-	  core modules stripped away (see http://lwn.net/Articles/477234/),
+	  core modules stripped away (see https://lwn.net/Articles/477234/),
 	  so you might need to install additional packages other than Perl
 	  itself, e.g. Digest::MD5, File::Spec, File::Temp, Net::Domain,
 	  Net::SMTP, and Time::HiRes.
@@ -139,7 +139,7 @@
 	  not need that functionality, use NO_CURL to build without
 	  it.
 
-	  Git requires version "7.19.5" or later of "libcurl" to build
+	  Git requires version "7.21.3" or later of "libcurl" to build
 	  without NO_CURL. This version requirement may be bumped in
 	  the future.
 
diff --git a/Makefile b/Makefile
index 50ee51f..44b281b 100644
--- a/Makefile
+++ b/Makefile
@@ -186,7 +186,7 @@
 # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 #
 # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
-# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
+# as the compiler can crash (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
@@ -207,10 +207,6 @@
 # Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
 # field that counts the on-disk footprint in 512-byte blocks.
 #
-# Define GNU_ROFF if your target system uses GNU groff.  This forces
-# apostrophes to be ASCII so that cut&pasting examples to the shell
-# will work.
-#
 # Define USE_ASCIIDOCTOR to use Asciidoctor instead of AsciiDoc to build the
 # documentation.
 #
@@ -686,6 +682,9 @@
 TEST_OBJS =
 TEST_PROGRAMS_NEED_X =
 THIRD_PARTY_SOURCES =
+UNIT_TEST_PROGRAMS =
+UNIT_TEST_DIR = t/unit-tests
+UNIT_TEST_BIN = $(UNIT_TEST_DIR)/bin
 
 # Having this variable in your environment would break pipelines because
 # you cause "cd" to echo its destination to stdout.  It can also take
@@ -753,7 +752,13 @@
 
 ETAGS_TARGET = TAGS
 
+# If you add a new fuzzer, please also make sure to run it in
+# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
+# runs in the future.
+FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
 FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
 .PHONY: fuzz-objs
@@ -762,7 +767,7 @@
 # Always build fuzz objects even if not testing, to prevent bit-rot.
 all:: $(FUZZ_OBJS)
 
-FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
 
 # Empty...
 EXTRA_PROGRAMS =
@@ -792,8 +797,8 @@
 TEST_BUILTINS_OBJS += test-config.o
 TEST_BUILTINS_OBJS += test-crontab.o
 TEST_BUILTINS_OBJS += test-csprng.o
-TEST_BUILTINS_OBJS += test-ctype.o
 TEST_BUILTINS_OBJS += test-date.o
+TEST_BUILTINS_OBJS += test-delete-gpgsig.o
 TEST_BUILTINS_OBJS += test-delta.o
 TEST_BUILTINS_OBJS += test-dir-iterator.o
 TEST_BUILTINS_OBJS += test-drop-caches.o
@@ -803,7 +808,7 @@
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-env-helper.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
-TEST_BUILTINS_OBJS += test-fast-rebase.o
+TEST_BUILTINS_OBJS += test-find-pack.o
 TEST_BUILTINS_OBJS += test-fsmonitor-client.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
@@ -812,7 +817,6 @@
 TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
 TEST_BUILTINS_OBJS += test-hexdump.o
-TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
 TEST_BUILTINS_OBJS += test-match-trees.o
@@ -829,7 +833,6 @@
 TEST_BUILTINS_OBJS += test-path-utils.o
 TEST_BUILTINS_OBJS += test-pcre2-config.o
 TEST_BUILTINS_OBJS += test-pkt-line.o
-TEST_BUILTINS_OBJS += test-prio-queue.o
 TEST_BUILTINS_OBJS += test-proc-receive.o
 TEST_BUILTINS_OBJS += test-progress.o
 TEST_BUILTINS_OBJS += test-reach.o
@@ -856,6 +859,7 @@
 TEST_BUILTINS_OBJS += test-submodule.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
+TEST_BUILTINS_OBJS += test-truncate.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
 TEST_BUILTINS_OBJS += test-userdiff.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
@@ -1036,6 +1040,7 @@
 LIB_OBJS += fsmonitor-ipc.o
 LIB_OBJS += fsmonitor-settings.o
 LIB_OBJS += gettext.o
+LIB_OBJS += git-zlib.o
 LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
@@ -1043,6 +1048,7 @@
 LIB_OBJS += hashmap.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
+LIB_OBJS += hex-ll.o
 LIB_OBJS += hook.o
 LIB_OBJS += ident.o
 LIB_OBJS += json-writer.o
@@ -1054,20 +1060,22 @@
 LIB_OBJS += list-objects-filter-options.o
 LIB_OBJS += list-objects-filter.o
 LIB_OBJS += list-objects.o
-LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += loose.o
 LIB_OBJS += ls-refs.o
 LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += mem-pool.o
 LIB_OBJS += merge-blobs.o
+LIB_OBJS += merge-ll.o
 LIB_OBJS += merge-ort.o
 LIB_OBJS += merge-ort-wrappers.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += merge.o
 LIB_OBJS += midx.o
+LIB_OBJS += midx-write.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += negotiator/default.o
 LIB_OBJS += negotiator/noop.o
@@ -1076,6 +1084,7 @@
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += notes.o
+LIB_OBJS += object-file-convert.o
 LIB_OBJS += object-file.o
 LIB_OBJS += object-name.o
 LIB_OBJS += object.o
@@ -1093,6 +1102,7 @@
 LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
 LIB_OBJS += parallel-checkout.o
+LIB_OBJS += parse.o
 LIB_OBJS += parse-options-cb.o
 LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
@@ -1121,6 +1131,7 @@
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
+LIB_OBJS += refs/reftable-backend.o
 LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
@@ -1145,6 +1156,7 @@
 LIB_OBJS += sparse-index.o
 LIB_OBJS += split-index.o
 LIB_OBJS += stable-qsort.o
+LIB_OBJS += statinfo.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
@@ -1196,7 +1208,6 @@
 LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
-LIB_OBJS += zlib.o
 
 BUILTIN_OBJS += builtin/add.o
 BUILTIN_OBJS += builtin/am.o
@@ -1290,6 +1301,7 @@
 BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
+BUILTIN_OBJS += builtin/replay.o
 BUILTIN_OBJS += builtin/rerere.o
 BUILTIN_OBJS += builtin/reset.o
 BUILTIN_OBJS += builtin/rev-list.o
@@ -1335,6 +1347,15 @@
 THIRD_PARTY_SOURCES += sha1collisiondetection/%
 THIRD_PARTY_SOURCES += sha1dc/%
 
+UNIT_TEST_PROGRAMS += t-basic
+UNIT_TEST_PROGRAMS += t-mem-pool
+UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGRAMS += t-prio-queue
+UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
+UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
+UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
+
 # xdiff and reftable libs may in turn depend on what is in libgit.a
 GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
 EXTLIBS =
@@ -1575,7 +1596,7 @@
 
 ifdef LIBPCREDIR
 	BASIC_CFLAGS += -I$(LIBPCREDIR)/include
-	EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
+	EXTLIBS += $(call libpath_template,$(LIBPCREDIR)/$(lib))
 endif
 
 ifdef HAVE_ALLOCA_H
@@ -1595,7 +1616,7 @@
 	ifdef CURLDIR
 		# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
 		CURL_CFLAGS = -I$(CURLDIR)/include
-		CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib)
+		CURL_LIBCURL = $(call libpath_template,$(CURLDIR)/$(lib))
 	else
 		CURL_CFLAGS =
 		CURL_LIBCURL =
@@ -1631,7 +1652,7 @@
 	ifndef NO_EXPAT
 		ifdef EXPATDIR
 			BASIC_CFLAGS += -I$(EXPATDIR)/include
-			EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
+			EXPAT_LIBEXPAT = $(call libpath_template,$(EXPATDIR)/$(lib)) -lexpat
 		else
 			EXPAT_LIBEXPAT = -lexpat
 		endif
@@ -1644,7 +1665,7 @@
 
 ifdef ZLIB_PATH
 	BASIC_CFLAGS += -I$(ZLIB_PATH)/include
-	EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
+	EXTLIBS += $(call libpath_template,$(ZLIB_PATH)/$(lib))
 endif
 EXTLIBS += -lz
 
@@ -1652,7 +1673,7 @@
 	OPENSSL_LIBSSL = -lssl
 	ifdef OPENSSLDIR
 		BASIC_CFLAGS += -I$(OPENSSLDIR)/include
-		OPENSSL_LINK = -L$(OPENSSLDIR)/$(lib) $(CC_LD_DYNPATH)$(OPENSSLDIR)/$(lib)
+		OPENSSL_LINK = $(call libpath_template,$(OPENSSLDIR)/$(lib))
 	else
 		OPENSSL_LINK =
 	endif
@@ -1679,7 +1700,7 @@
 	ifdef NEEDS_LIBICONV
 		ifdef ICONVDIR
 			BASIC_CFLAGS += -I$(ICONVDIR)/include
-			ICONV_LINK = -L$(ICONVDIR)/$(lib) $(CC_LD_DYNPATH)$(ICONVDIR)/$(lib)
+			ICONV_LINK = $(call libpath_template,$(ICONVDIR)/$(lib))
 		else
 			ICONV_LINK =
 		endif
@@ -1955,7 +1976,7 @@
 	BASIC_CFLAGS += \
 		-DSHA1DC_NO_STANDARD_INCLUDES \
 		-DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
-		-DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
+		-DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"git-compat-util.h\"" \
 		-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
 endif
 endif
@@ -2342,7 +2363,7 @@
 
 all:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
-	$(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) $(OTHER_PROGRAMS))), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
+	$(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) $(OTHER_PROGRAMS))), if test ! -d '$p' && test ! '$p' -ef '$p$X'; then $(RM) '$p'; fi;)
 endif
 
 all::
@@ -2676,6 +2697,7 @@
 OBJECTS += $(XDIFF_OBJS)
 OBJECTS += $(FUZZ_OBJS)
 OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
+OBJECTS += $(UNIT_TEST_OBJS)
 
 ifndef NO_CURL
 	OBJECTS += http.o http-walker.o remote-curl.o
@@ -2723,7 +2745,7 @@
 
 ifdef USE_COMPUTED_HEADER_DEPENDENCIES
 # Take advantage of gcc's on-the-fly dependency generation
-# See <http://gcc.gnu.org/gcc-3.0/features.html>.
+# See <https://gcc.gnu.org/gcc-3.0/features.html>.
 dep_files_present := $(wildcard $(dep_files))
 ifneq ($(dep_files_present),)
 include $(dep_files_present)
@@ -2746,8 +2768,8 @@
 	'-DBINDIR="$(bindir_relative_SQ)"' \
 	'-DFALLBACK_RUNTIME_PREFIX="$(prefix_SQ)"'
 
-builtin/init-db.sp builtin/init-db.s builtin/init-db.o: GIT-PREFIX
-builtin/init-db.sp builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
+setup.sp setup.s setup.o: GIT-PREFIX
+setup.sp setup.s setup.o: EXTRA_CPPFLAGS = \
 	-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
 
 config.sp config.s config.o: GIT-PREFIX
@@ -2782,6 +2804,13 @@
 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 $<
+
+headless-git$X: headless-git.o git.res GIT-LDFLAGS
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res
+
 git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
@@ -3171,7 +3200,7 @@
 
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
-all:: $(TEST_PROGRAMS) $(test_bindir_programs)
+all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS)
 
 bin-wrappers/%: wrap-for-bin.sh
 	$(call mkdir_p_parent_template)
@@ -3219,6 +3248,12 @@
 sparse: $(SP_OBJ)
 
 EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/%
+ifndef OPENSSL_SHA1
+	EXCEPT_HDRS += sha1/openssl.h
+endif
+ifndef OPENSSL_SHA256
+	EXCEPT_HDRS += sha256/openssl.h
+endif
 ifndef NETTLE_SHA256
 	EXCEPT_HDRS += sha256/nettle.h
 endif
@@ -3591,12 +3626,12 @@
 .PHONY: rpm
 
 ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),)
-OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll)
+OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll t/unit-tests/bin/*.dll)
 endif
 
 artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
 		GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
-		$(MOFILES)
+		$(UNIT_TEST_PROGS) $(MOFILES)
 	$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
 		SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
 	test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3651,10 +3686,11 @@
 	$(RM) contrib/coccinelle/*.cocci.patch
 
 clean: profile-clean coverage-clean cocciclean
-	$(RM) -r .build
+	$(RM) -r .build $(UNIT_TEST_BIN)
 	$(RM) po/git.pot po/git-core.pot
 	$(RM) git.res
 	$(RM) $(OBJECTS)
+	$(RM) headless-git.o
 	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
 	$(RM) $(TEST_PROGRAMS)
@@ -3683,6 +3719,7 @@
 	$(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
 ifdef MSVC
 	$(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS))
+	$(RM) headless-git.o.pdb
 	$(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS))
 	$(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS))
 	$(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS))
@@ -3823,15 +3860,26 @@
 #
 # make CC=clang CXX=clang++ \
 #      CFLAGS="-fsanitize=fuzzer-no-link,address" \
-#      LIB_FUZZING_ENGINE="-fsanitize=fuzzer" \
+#      LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
 #      fuzz-all
 #
-FUZZ_CXXFLAGS ?= $(CFLAGS)
+FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
 
 .PHONY: fuzz-all
 
-$(FUZZ_PROGRAMS): all
-	$(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
-		$(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
+	$(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
+		-Wl,--allow-multiple-definition \
+		$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
 
 fuzz-all: $(FUZZ_PROGRAMS)
+
+$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS
+	$(call mkdir_p_parent_template)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+		$(filter %.o,$^) $(filter %.a,$^) $(LIBS)
+
+.PHONY: build-unit-tests unit-tests
+build-unit-tests: $(UNIT_TEST_PROGS)
+unit-tests: $(UNIT_TEST_PROGS)
+	$(MAKE) -C t/ unit-tests
diff --git a/README.md b/README.md
index 7ce4f05..665ce5f 100644
--- a/README.md
+++ b/README.md
@@ -39,10 +39,10 @@
 string translations (localization l10) should see [po/README.md][]
 (a `po` file is a Portable Object file that holds the translations).
 
-To subscribe to the list, send an email with just "subscribe git" in
-the body to majordomo@vger.kernel.org (not the Git list). The mailing
+To subscribe to the list, send an email to <git+subscribe@vger.kernel.org>
+(see https://subspace.kernel.org/subscribing.html for details). The mailing
 list archives are available at <https://lore.kernel.org/git/>,
-<http://marc.info/?l=git> and other archival sites.
+<https://marc.info/?l=git> and other archival sites.
 
 Issues which are security relevant should be disclosed privately to
 the Git Security mailing list <git-security@googlegroups.com>.
diff --git a/RelNotes b/RelNotes
index 829f78a..ae70277 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.40.1.txt
\ No newline at end of file
+Documentation/RelNotes/2.45.0.txt
\ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 39e06b5..1202cde 100644
--- a/abspath.c
+++ b/abspath.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "strbuf.h"
 
 /*
  * Do not use this for inspecting *tracked* content.  When path is a
@@ -280,3 +282,46 @@
 #endif
 	return strbuf_detach(&path, NULL);
 }
+
+char *prefix_filename_except_for_dash(const char *pfx, const char *arg)
+{
+	if (!strcmp(arg, "-"))
+		return xstrdup(arg);
+	return prefix_filename(pfx, arg);
+}
+
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
+{
+	if (!*path)
+		die("The empty string is not a valid path");
+	if (!is_absolute_path(path)) {
+		struct stat cwd_stat, pwd_stat;
+		size_t orig_len = sb->len;
+		char *cwd = xgetcwd();
+		char *pwd = getenv("PWD");
+		if (pwd && strcmp(pwd, cwd) &&
+		    !stat(cwd, &cwd_stat) &&
+		    (cwd_stat.st_dev || cwd_stat.st_ino) &&
+		    !stat(pwd, &pwd_stat) &&
+		    pwd_stat.st_dev == cwd_stat.st_dev &&
+		    pwd_stat.st_ino == cwd_stat.st_ino)
+			strbuf_addstr(sb, pwd);
+		else
+			strbuf_addstr(sb, cwd);
+		if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
+			strbuf_addch(sb, '/');
+		free(cwd);
+	}
+	strbuf_addstr(sb, path);
+}
+
+void strbuf_add_real_path(struct strbuf *sb, const char *path)
+{
+	if (sb->len) {
+		struct strbuf resolved = STRBUF_INIT;
+		strbuf_realpath(&resolved, path, 1);
+		strbuf_addbuf(sb, &resolved);
+		strbuf_release(&resolved);
+	} else
+		strbuf_realpath(sb, path, 1);
+}
diff --git a/abspath.h b/abspath.h
new file mode 100644
index 0000000..4653080
--- /dev/null
+++ b/abspath.h
@@ -0,0 +1,54 @@
+#ifndef ABSPATH_H
+#define ABSPATH_H
+
+int is_directory(const char *);
+char *strbuf_realpath(struct strbuf *resolved, const char *path,
+		      int die_on_error);
+char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
+				int die_on_error);
+char *real_pathdup(const char *path, int die_on_error);
+const char *absolute_path(const char *path);
+char *absolute_pathdup(const char *path);
+
+/*
+ * Concatenate "prefix" (if len is non-zero) and "path", with no
+ * connecting characters (so "prefix" should end with a "/").
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ *
+ * The return value is always a newly allocated string (even if the
+ * prefix was empty).
+ */
+char *prefix_filename(const char *prefix, const char *path);
+
+/* Likewise, but path=="-" always yields "-" */
+char *prefix_filename_except_for_dash(const char *prefix, const char *path);
+
+static inline int is_absolute_path(const char *path)
+{
+	return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
+}
+
+/**
+ * Add a path to a buffer, converting a relative path to an
+ * absolute one in the process.  Symbolic links are not
+ * resolved.
+ */
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+
+/**
+ * Canonize `path` (make it absolute, resolve symlinks, remove extra
+ * slashes) and append it to `sb`.  Die with an informative error
+ * message if there is a problem.
+ *
+ * The directory part of `path` (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.
+ *
+ * Callers that don't mind links should use the more lightweight
+ * strbuf_add_absolute_path() instead.
+ */
+void strbuf_add_real_path(struct strbuf *sb, const char *path);
+
+#endif /* ABSPATH_H */
diff --git a/add-interactive.c b/add-interactive.c
index 00a0f6f..6bf87e7 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -1,8 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "add-interactive.h"
 #include "color.h"
 #include "config.h"
 #include "diffcore.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
+#include "repository.h"
 #include "revision.h"
 #include "refs.h"
 #include "string-list.h"
@@ -10,6 +16,7 @@
 #include "dir.h"
 #include "run-command.h"
 #include "prompt.h"
+#include "tree.h"
 
 static void init_color(struct repository *r, struct add_i_state *s,
 		       const char *section_and_slot, char *dst,
@@ -551,7 +558,7 @@
 		opt.def = is_initial ?
 			empty_tree_oid_hex() : oid_to_hex(&head_oid);
 
-		init_revisions(&rev, NULL);
+		repo_init_revisions(r, &rev, NULL);
 		setup_revisions(0, NULL, &rev, &opt);
 
 		rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
@@ -562,7 +569,7 @@
 			copy_pathspec(&rev.prune_data, ps);
 
 		if (s.mode == FROM_INDEX)
-			run_diff_index(&rev, 1);
+			run_diff_index(&rev, DIFF_INDEX_CACHED);
 		else {
 			rev.diffopt.flags.ignore_dirty_submodules = 1;
 			run_diff_files(&rev, 0);
@@ -1014,9 +1021,9 @@
 	return res;
 }
 
-static int run_help(struct add_i_state *s, const struct pathspec *unused_ps,
-		    struct prefix_item_list *unused_files,
-		    struct list_and_choose_options *unused_opts)
+static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED,
+		    struct prefix_item_list *files UNUSED,
+		    struct list_and_choose_options *opts UNUSED)
 {
 	color_fprintf_ln(stdout, s->help_color, "status        - %s",
 			 _("show paths with changes"));
@@ -1067,7 +1074,7 @@
 	const char *color, *reset;
 };
 
-static void print_command_item(int i, int selected,
+static void print_command_item(int i, int selected UNUSED,
 			       struct string_list_item *item,
 			       void *print_command_item_data)
 {
diff --git a/add-patch.c b/add-patch.c
index a86a92e..a06dd18 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -1,11 +1,17 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "add-interactive.h"
+#include "advice.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
+#include "read-cache-ll.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "strvec.h"
 #include "pathspec.h"
 #include "color.h"
-#include "diff.h"
 #include "compat/terminal.h"
 #include "prompt.h"
 
@@ -414,7 +420,7 @@
 		strvec_push(&args,
 			    /* could be on an unborn branch */
 			    !strcmp("HEAD", s->revision) &&
-			    get_oid("HEAD", &oid) ?
+			    repo_get_oid(the_repository, "HEAD", &oid) ?
 			    empty_tree_oid_hex() : s->revision);
 	}
 	color_arg_index = args.nr;
@@ -483,7 +489,8 @@
 		if (!eol)
 			eol = pend;
 
-		if (starts_with(p, "diff ")) {
+		if (starts_with(p, "diff ") ||
+		    starts_with(p, "* Unmerged path ")) {
 			complete_file(marker, hunk);
 			ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
 				   file_diff_alloc);
@@ -1098,24 +1105,26 @@
 	size_t i;
 
 	strbuf_reset(&s->buf);
-	strbuf_commented_addf(&s->buf, _("Manual hunk edit mode -- see bottom for "
-				      "a quick guide.\n"));
+	strbuf_commented_addf(&s->buf, comment_line_str,
+			      _("Manual hunk edit mode -- see bottom for "
+				"a quick guide.\n"));
 	render_hunk(s, hunk, 0, 0, &s->buf);
-	strbuf_commented_addf(&s->buf,
+	strbuf_commented_addf(&s->buf, comment_line_str,
 			      _("---\n"
 				"To remove '%c' lines, make them ' ' lines "
 				"(context).\n"
 				"To remove '%c' lines, delete them.\n"
-				"Lines starting with %c will be removed.\n"),
+				"Lines starting with %s will be removed.\n"),
 			      s->mode->is_reverse ? '+' : '-',
 			      s->mode->is_reverse ? '-' : '+',
-			      comment_line_char);
-	strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
+			      comment_line_str);
+	strbuf_commented_addf(&s->buf, comment_line_str, "%s",
+			      _(s->mode->edit_hunk_hint));
 	/*
 	 * TRANSLATORS: 'it' refers to the patch mentioned in the previous
 	 * messages.
 	 */
-	strbuf_commented_addf(&s->buf,
+	strbuf_commented_addf(&s->buf, comment_line_str,
 			      _("If it does not apply cleanly, you will be "
 				"given an opportunity to\n"
 				"edit again.  If all lines of the hunk are "
@@ -1130,7 +1139,7 @@
 	for (i = 0; i < s->buf.len; ) {
 		size_t next = find_next_line(&s->buf, i);
 
-		if (s->buf.buf[i] != comment_line_char)
+		if (!starts_with(s->buf.buf + i, comment_line_str))
 			strbuf_add(&s->plain, s->buf.buf + i, next - i);
 		i = next;
 	}
@@ -1379,13 +1388,14 @@
    "/ - 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\n"
    "? - print help\n");
 
 static int patch_update_file(struct add_p_state *s,
 			     struct file_diff *file_diff)
 {
 	size_t hunk_index = 0;
-	ssize_t i, undecided_previous, undecided_next;
+	ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
 	struct hunk *hunk;
 	char ch;
 	struct child_process cp = CHILD_PROCESS_INIT;
@@ -1438,8 +1448,11 @@
 
 		strbuf_reset(&s->buf);
 		if (file_diff->hunk_nr) {
-			render_hunk(s, hunk, 0, colored, &s->buf);
-			fputs(s->buf.buf, stdout);
+			if (rendered_hunk_index != hunk_index) {
+				render_hunk(s, hunk, 0, colored, &s->buf);
+				fputs(s->buf.buf, stdout);
+				rendered_hunk_index = hunk_index;
+			}
 
 			strbuf_reset(&s->buf);
 			if (undecided_previous >= 0) {
@@ -1471,6 +1484,7 @@
 				permitted |= ALLOW_EDIT;
 				strbuf_addstr(&s->buf, ",e");
 			}
+			strbuf_addstr(&s->buf, ",p");
 		}
 		if (file_diff->deleted)
 			prompt_mode_type = PROMPT_DELETION;
@@ -1635,13 +1649,15 @@
 			hunk_index = i;
 		} else if (s->answer.buf[0] == 's') {
 			size_t splittable_into = hunk->splittable_into;
-			if (!(permitted & ALLOW_SPLIT))
+			if (!(permitted & ALLOW_SPLIT)) {
 				err(s, _("Sorry, cannot split this hunk"));
-			else if (!split_hunk(s, file_diff,
-					     hunk - file_diff->hunk))
+			} else if (!split_hunk(s, file_diff,
+					     hunk - file_diff->hunk)) {
 				color_fprintf_ln(stdout, s->s.header_color,
 						 _("Split into %d hunks."),
 						 (int)splittable_into);
+				rendered_hunk_index = -1;
+			}
 		} else if (s->answer.buf[0] == 'e') {
 			if (!(permitted & ALLOW_EDIT))
 				err(s, _("Sorry, cannot edit this hunk"));
@@ -1649,6 +1665,8 @@
 				hunk->use = USE_HUNK;
 				goto soft_increment;
 			}
+		} else if (s->answer.buf[0] == 'p') {
+			rendered_hunk_index = -1;
 		} else {
 			const char *p = _(help_patch_remainder), *eol = p;
 
@@ -1720,14 +1738,6 @@
 	if (mode == ADD_P_STASH)
 		s.mode = &patch_mode_stash;
 	else if (mode == ADD_P_RESET) {
-		/*
-		 * NEEDSWORK: Instead of comparing to the literal "HEAD",
-		 * compare the commit objects instead so that other ways of
-		 * saying the same thing (such as "@") are also handled
-		 * appropriately.
-		 *
-		 * This applies to the cases below too.
-		 */
 		if (!revision || !strcmp(revision, "HEAD"))
 			s.mode = &patch_mode_reset_head;
 		else
diff --git a/advice.c b/advice.c
index fd18968..7511119 100644
--- a/advice.c
+++ b/advice.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
 #include "color.h"
+#include "gettext.h"
 #include "help.h"
 #include "string-list.h"
 
@@ -31,50 +33,59 @@
 	return "";
 }
 
+enum advice_level {
+	ADVICE_LEVEL_NONE = 0,
+	ADVICE_LEVEL_DISABLED,
+	ADVICE_LEVEL_ENABLED,
+};
+
 static struct {
 	const char *key;
-	int enabled;
+	enum advice_level level;
 } advice_setting[] = {
-	[ADVICE_ADD_EMBEDDED_REPO]			= { "addEmbeddedRepo", 1 },
-	[ADVICE_ADD_EMPTY_PATHSPEC]			= { "addEmptyPathspec", 1 },
-	[ADVICE_ADD_IGNORED_FILE]			= { "addIgnoredFile", 1 },
-	[ADVICE_AM_WORK_DIR] 				= { "amWorkDir", 1 },
-	[ADVICE_AMBIGUOUS_FETCH_REFSPEC]		= { "ambiguousFetchRefspec", 1 },
-	[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] 	= { "checkoutAmbiguousRemoteBranchName", 1 },
-	[ADVICE_COMMIT_BEFORE_MERGE]			= { "commitBeforeMerge", 1 },
-	[ADVICE_DETACHED_HEAD]				= { "detachedHead", 1 },
-	[ADVICE_SUGGEST_DETACHING_HEAD]			= { "suggestDetachingHead", 1 },
-	[ADVICE_FETCH_SHOW_FORCED_UPDATES]		= { "fetchShowForcedUpdates", 1 },
-	[ADVICE_GRAFT_FILE_DEPRECATED]			= { "graftFileDeprecated", 1 },
-	[ADVICE_IGNORED_HOOK]				= { "ignoredHook", 1 },
-	[ADVICE_IMPLICIT_IDENTITY]			= { "implicitIdentity", 1 },
-	[ADVICE_NESTED_TAG]				= { "nestedTag", 1 },
-	[ADVICE_OBJECT_NAME_WARNING]			= { "objectNameWarning", 1 },
-	[ADVICE_PUSH_ALREADY_EXISTS]			= { "pushAlreadyExists", 1 },
-	[ADVICE_PUSH_FETCH_FIRST]			= { "pushFetchFirst", 1 },
-	[ADVICE_PUSH_NEEDS_FORCE]			= { "pushNeedsForce", 1 },
-	[ADVICE_PUSH_REF_NEEDS_UPDATE]			= { "pushRefNeedsUpdate", 1 },
-
-	/* make this an alias for backward compatibility */
-	[ADVICE_PUSH_UPDATE_REJECTED_ALIAS]		= { "pushNonFastForward", 1 },
-
-	[ADVICE_PUSH_NON_FF_CURRENT]			= { "pushNonFFCurrent", 1 },
-	[ADVICE_PUSH_NON_FF_MATCHING]			= { "pushNonFFMatching", 1 },
-	[ADVICE_PUSH_UNQUALIFIED_REF_NAME]		= { "pushUnqualifiedRefName", 1 },
-	[ADVICE_PUSH_UPDATE_REJECTED]			= { "pushUpdateRejected", 1 },
-	[ADVICE_RESET_NO_REFRESH_WARNING]		= { "resetNoRefresh", 1 },
-	[ADVICE_RESOLVE_CONFLICT]			= { "resolveConflict", 1 },
-	[ADVICE_RM_HINTS]				= { "rmHints", 1 },
-	[ADVICE_SEQUENCER_IN_USE]			= { "sequencerInUse", 1 },
-	[ADVICE_SET_UPSTREAM_FAILURE]			= { "setUpstreamFailure", 1 },
-	[ADVICE_SKIPPED_CHERRY_PICKS]			= { "skippedCherryPicks", 1 },
-	[ADVICE_STATUS_AHEAD_BEHIND_WARNING]		= { "statusAheadBehindWarning", 1 },
-	[ADVICE_STATUS_HINTS]				= { "statusHints", 1 },
-	[ADVICE_STATUS_U_OPTION]			= { "statusUoption", 1 },
-	[ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
-	[ADVICE_SUBMODULES_NOT_UPDATED] 		= { "submodulesNotUpdated", 1 },
-	[ADVICE_UPDATE_SPARSE_PATH]			= { "updateSparsePath", 1 },
-	[ADVICE_WAITING_FOR_EDITOR]			= { "waitingForEditor", 1 },
+	[ADVICE_ADD_EMBEDDED_REPO]			= { "addEmbeddedRepo" },
+	[ADVICE_ADD_EMPTY_PATHSPEC]			= { "addEmptyPathspec" },
+	[ADVICE_ADD_IGNORED_FILE]			= { "addIgnoredFile" },
+	[ADVICE_AMBIGUOUS_FETCH_REFSPEC]		= { "ambiguousFetchRefspec" },
+	[ADVICE_AM_WORK_DIR] 				= { "amWorkDir" },
+	[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] 	= { "checkoutAmbiguousRemoteBranchName" },
+	[ADVICE_COMMIT_BEFORE_MERGE]			= { "commitBeforeMerge" },
+	[ADVICE_DETACHED_HEAD]				= { "detachedHead" },
+	[ADVICE_DIVERGING]				= { "diverging" },
+	[ADVICE_FETCH_SHOW_FORCED_UPDATES]		= { "fetchShowForcedUpdates" },
+	[ADVICE_FORCE_DELETE_BRANCH]			= { "forceDeleteBranch" },
+	[ADVICE_GRAFT_FILE_DEPRECATED]			= { "graftFileDeprecated" },
+	[ADVICE_IGNORED_HOOK]				= { "ignoredHook" },
+	[ADVICE_IMPLICIT_IDENTITY]			= { "implicitIdentity" },
+	[ADVICE_MERGE_CONFLICT]				= { "mergeConflict" },
+	[ADVICE_NESTED_TAG]				= { "nestedTag" },
+	[ADVICE_OBJECT_NAME_WARNING]			= { "objectNameWarning" },
+	[ADVICE_PUSH_ALREADY_EXISTS]			= { "pushAlreadyExists" },
+	[ADVICE_PUSH_FETCH_FIRST]			= { "pushFetchFirst" },
+	[ADVICE_PUSH_NEEDS_FORCE]			= { "pushNeedsForce" },
+	[ADVICE_PUSH_NON_FF_CURRENT]			= { "pushNonFFCurrent" },
+	[ADVICE_PUSH_NON_FF_MATCHING]			= { "pushNonFFMatching" },
+	[ADVICE_PUSH_REF_NEEDS_UPDATE]			= { "pushRefNeedsUpdate" },
+	[ADVICE_PUSH_UNQUALIFIED_REF_NAME]		= { "pushUnqualifiedRefName" },
+	[ADVICE_PUSH_UPDATE_REJECTED]			= { "pushUpdateRejected" },
+	[ADVICE_PUSH_UPDATE_REJECTED_ALIAS]		= { "pushNonFastForward" }, /* backwards compatibility */
+	[ADVICE_REF_SYNTAX]				= { "refSyntax" },
+	[ADVICE_RESET_NO_REFRESH_WARNING]		= { "resetNoRefresh" },
+	[ADVICE_RESOLVE_CONFLICT]			= { "resolveConflict" },
+	[ADVICE_RM_HINTS]				= { "rmHints" },
+	[ADVICE_SEQUENCER_IN_USE]			= { "sequencerInUse" },
+	[ADVICE_SET_UPSTREAM_FAILURE]			= { "setUpstreamFailure" },
+	[ADVICE_SKIPPED_CHERRY_PICKS]			= { "skippedCherryPicks" },
+	[ADVICE_STATUS_AHEAD_BEHIND_WARNING]		= { "statusAheadBehindWarning" },
+	[ADVICE_STATUS_HINTS]				= { "statusHints" },
+	[ADVICE_STATUS_U_OPTION]			= { "statusUoption" },
+	[ADVICE_SUBMODULES_NOT_UPDATED] 		= { "submodulesNotUpdated" },
+	[ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie" },
+	[ADVICE_SUBMODULE_MERGE_CONFLICT]               = { "submoduleMergeConflict" },
+	[ADVICE_SUGGEST_DETACHING_HEAD]			= { "suggestDetachingHead" },
+	[ADVICE_UPDATE_SPARSE_PATH]			= { "updateSparsePath" },
+	[ADVICE_WAITING_FOR_EDITOR]			= { "waitingForEditor" },
+	[ADVICE_WORKTREE_ADD_ORPHAN]			= { "worktreeAddOrphan" },
 };
 
 static const char turn_off_instructions[] =
@@ -94,8 +105,9 @@
 
 	for (cp = buf.buf; *cp; cp = np) {
 		np = strchrnul(cp, '\n');
-		fprintf(stderr,	_("%shint: %.*s%s\n"),
+		fprintf(stderr,	_("%shint:%s%.*s%s\n"),
 			advise_get_color(ADVICE_COLOR_HINT),
+			(np == cp) ? "" : " ",
 			(int)(np - cp), cp,
 			advise_get_color(ADVICE_COLOR_RESET));
 		if (*np)
@@ -114,13 +126,13 @@
 
 int advice_enabled(enum advice_type type)
 {
-	switch(type) {
-	case ADVICE_PUSH_UPDATE_REJECTED:
-		return advice_setting[ADVICE_PUSH_UPDATE_REJECTED].enabled &&
-		       advice_setting[ADVICE_PUSH_UPDATE_REJECTED_ALIAS].enabled;
-	default:
-		return advice_setting[type].enabled;
-	}
+	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
+
+	if (type == ADVICE_PUSH_UPDATE_REJECTED)
+		return enabled &&
+		       advice_enabled(ADVICE_PUSH_UPDATE_REJECTED_ALIAS);
+
+	return enabled;
 }
 
 void advise_if_enabled(enum advice_type type, const char *advice, ...)
@@ -131,7 +143,8 @@
 		return;
 
 	va_start(params, advice);
-	vadvise(advice, 1, advice_setting[type].key, params);
+	vadvise(advice, !advice_setting[type].level, advice_setting[type].key,
+		params);
 	va_end(params);
 }
 
@@ -160,7 +173,9 @@
 	for (i = 0; i < ARRAY_SIZE(advice_setting); i++) {
 		if (strcasecmp(k, advice_setting[i].key))
 			continue;
-		advice_setting[i].enabled = git_config_bool(var, value);
+		advice_setting[i].level = git_config_bool(var, value)
+					  ? ADVICE_LEVEL_ENABLED
+					  : ADVICE_LEVEL_DISABLED;
 		return 0;
 	}
 
@@ -187,9 +202,10 @@
 		error(_("Pulling is not possible because you have unmerged files."));
 	else if (!strcmp(me, "revert"))
 		error(_("Reverting is not possible because you have unmerged files."));
+	else if (!strcmp(me, "rebase"))
+		error(_("Rebasing is not possible because you have unmerged files."));
 	else
-		error(_("It is not possible to %s because you have unmerged files."),
-			me);
+		BUG("Unhandled conflict reason '%s'", me);
 
 	if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
 		/*
@@ -217,6 +233,14 @@
 
 void NORETURN die_ff_impossible(void)
 {
+	advise_if_enabled(ADVICE_DIVERGING,
+		_("Diverging branches can't be fast-forwarded, you need to either:\n"
+		"\n"
+		"\tgit merge --no-ff\n"
+		"\n"
+		"or:\n"
+		"\n"
+		"\tgit rebase\n"));
 	die(_("Not possible to fast-forward, aborting."));
 }
 
diff --git a/advice.h b/advice.h
index 07e0f76..c8d29f9 100644
--- a/advice.h
+++ b/advice.h
@@ -1,8 +1,6 @@
 #ifndef ADVICE_H
 #define ADVICE_H
 
-#include "git-compat-util.h"
-
 struct string_list;
 
 /*
@@ -12,20 +10,22 @@
  * Add the new config variable to Documentation/config/advice.txt.
  * Call advise_if_enabled to print your advice.
  */
- enum advice_type {
+enum advice_type {
 	ADVICE_ADD_EMBEDDED_REPO,
 	ADVICE_ADD_EMPTY_PATHSPEC,
 	ADVICE_ADD_IGNORED_FILE,
-	ADVICE_AM_WORK_DIR,
 	ADVICE_AMBIGUOUS_FETCH_REFSPEC,
+	ADVICE_AM_WORK_DIR,
 	ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
 	ADVICE_COMMIT_BEFORE_MERGE,
 	ADVICE_DETACHED_HEAD,
-	ADVICE_SUGGEST_DETACHING_HEAD,
+	ADVICE_DIVERGING,
 	ADVICE_FETCH_SHOW_FORCED_UPDATES,
+	ADVICE_FORCE_DELETE_BRANCH,
 	ADVICE_GRAFT_FILE_DEPRECATED,
 	ADVICE_IGNORED_HOOK,
 	ADVICE_IMPLICIT_IDENTITY,
+	ADVICE_MERGE_CONFLICT,
 	ADVICE_NESTED_TAG,
 	ADVICE_OBJECT_NAME_WARNING,
 	ADVICE_PUSH_ALREADY_EXISTS,
@@ -33,23 +33,27 @@
 	ADVICE_PUSH_NEEDS_FORCE,
 	ADVICE_PUSH_NON_FF_CURRENT,
 	ADVICE_PUSH_NON_FF_MATCHING,
-	ADVICE_PUSH_UNQUALIFIED_REF_NAME,
-	ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
-	ADVICE_PUSH_UPDATE_REJECTED,
 	ADVICE_PUSH_REF_NEEDS_UPDATE,
+	ADVICE_PUSH_UNQUALIFIED_REF_NAME,
+	ADVICE_PUSH_UPDATE_REJECTED,
+	ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
+	ADVICE_REF_SYNTAX,
 	ADVICE_RESET_NO_REFRESH_WARNING,
 	ADVICE_RESOLVE_CONFLICT,
 	ADVICE_RM_HINTS,
 	ADVICE_SEQUENCER_IN_USE,
 	ADVICE_SET_UPSTREAM_FAILURE,
+	ADVICE_SKIPPED_CHERRY_PICKS,
 	ADVICE_STATUS_AHEAD_BEHIND_WARNING,
 	ADVICE_STATUS_HINTS,
 	ADVICE_STATUS_U_OPTION,
-	ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
 	ADVICE_SUBMODULES_NOT_UPDATED,
+	ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
+	ADVICE_SUBMODULE_MERGE_CONFLICT,
+	ADVICE_SUGGEST_DETACHING_HEAD,
 	ADVICE_UPDATE_SPARSE_PATH,
 	ADVICE_WAITING_FOR_EDITOR,
-	ADVICE_SKIPPED_CHERRY_PICKS,
+	ADVICE_WORKTREE_ADD_ORPHAN,
 };
 
 int git_default_advice_config(const char *var, const char *value);
diff --git a/alias.c b/alias.c
index 00abde0..5a238f2 100644
--- a/alias.c
+++ b/alias.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "alias.h"
 #include "config.h"
+#include "gettext.h"
+#include "strbuf.h"
 #include "string-list.h"
 
 struct config_alias_data {
@@ -9,7 +11,8 @@
 	struct string_list *list;
 };
 
-static int config_alias_cb(const char *key, const char *value, void *d)
+static int config_alias_cb(const char *key, const char *value,
+			   const struct config_context *ctx UNUSED, void *d)
 {
 	struct config_alias_data *data = d;
 	const char *p;
@@ -44,6 +47,23 @@
 	read_early_config(config_alias_cb, &data);
 }
 
+void quote_cmdline(struct strbuf *buf, const char **argv)
+{
+	for (const char **argp = argv; *argp; argp++) {
+		if (argp != argv)
+			strbuf_addch(buf, ' ');
+		strbuf_addch(buf, '"');
+		for (const char *p = *argp; *p; p++) {
+			const char c = *p;
+
+			if (c == '"' || c =='\\')
+				strbuf_addch(buf, '\\');
+			strbuf_addch(buf, c);
+		}
+		strbuf_addch(buf, '"');
+	}
+}
+
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
 #define SPLIT_CMDLINE_ARGC_OVERFLOW 3
diff --git a/alias.h b/alias.h
index aef4843..43db736 100644
--- a/alias.h
+++ b/alias.h
@@ -1,9 +1,12 @@
 #ifndef ALIAS_H
 #define ALIAS_H
 
+struct strbuf;
 struct string_list;
 
 char *alias_lookup(const char *alias);
+/* Quote argv so buf can be parsed by split_cmdline() */
+void quote_cmdline(struct strbuf *buf, const char **argv);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/alloc.c b/alloc.c
index 27f697e..377e80f 100644
--- a/alloc.c
+++ b/alloc.c
@@ -8,11 +8,12 @@
  * up with maximal alignment because it doesn't know what the object alignment
  * for the new allocation is.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "blob.h"
 #include "tree.h"
 #include "commit.h"
+#include "repository.h"
 #include "tag.h"
 #include "alloc.h"
 
diff --git a/apply.c b/apply.c
index 9762157..34f2032 100644
--- a/apply.c
+++ b/apply.c
@@ -7,21 +7,34 @@
  *
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "base85.h"
 #include "config.h"
-#include "object-store.h"
-#include "blob.h"
+#include "object-store-ll.h"
 #include "delta.h"
 #include "diff.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "lockfile.h"
+#include "name-hash.h"
+#include "object-name.h"
+#include "object-file.h"
 #include "parse-options.h"
+#include "path.h"
 #include "quote.h"
+#include "read-cache.h"
 #include "rerere.h"
 #include "apply.h"
 #include "entry.h"
+#include "setup.h"
+#include "symlinks.h"
+#include "wildmatch.h"
+#include "ws.h"
 
 struct gitdiff_data {
 	struct strbuf *root;
@@ -64,7 +77,8 @@
 		return 0;
 	}
 	/*
-	 * Please update $__git_whitespacelist in git-completion.bash
+	 * Please update $__git_whitespacelist in git-completion.bash,
+	 * Documentation/git-apply.txt, and Documentation/git-am.txt
 	 * when you add new options.
 	 */
 	return error(_("unrecognized whitespace option '%s'"), option);
@@ -398,9 +412,10 @@
 
 static int read_patch_file(struct strbuf *sb, int fd)
 {
-	if (strbuf_read(sb, fd, 0) < 0 || sb->len >= MAX_APPLY_SIZE)
-		return error_errno("git apply: failed to read");
-
+	if (strbuf_read(sb, fd, 0) < 0)
+		return error_errno(_("failed to read patch"));
+	else if (sb->len >= MAX_APPLY_SIZE)
+		return error(_("patch too large"));
 	/*
 	 * Make sure that we have some slop in the buffer
 	 * so that we can do speculative "memcmp" etc, and
@@ -1277,8 +1292,15 @@
 				return NULL; /* no postimage name */
 			second = skip_tree_prefix(p_value, name + len + 1,
 						  line_len - (len + 1));
+			/*
+			 * If we are at the SP at the end of a directory,
+			 * skip_tree_prefix() may return NULL as that makes
+			 * it appears as if we have an absolute path.
+			 * Keep going to find another SP.
+			 */
 			if (!second)
-				return NULL;
+				continue;
+
 			/*
 			 * Does len bytes starting at "name" and "second"
 			 * (that are separated by one HT or SP we just
@@ -2205,7 +2227,8 @@
 		struct fragment *frag = p->fragments;
 
 		SWAP(p->new_name, p->old_name);
-		SWAP(p->new_mode, p->old_mode);
+		if (p->new_mode)
+			SWAP(p->new_mode, p->old_mode);
 		SWAP(p->is_new, p->is_delete);
 		SWAP(p->lines_added, p->lines_deleted);
 		SWAP(p->old_oid_prefix, p->new_oid_prefix);
@@ -3201,7 +3224,8 @@
 		unsigned long size;
 		char *result;
 
-		result = read_object_file(&oid, &type, &size);
+		result = repo_read_object_file(the_repository, &oid, &type,
+					       &size);
 		if (!result)
 			return error(_("the necessary postimage %s for "
 				       "'%s' cannot be read"),
@@ -3264,7 +3288,8 @@
 		unsigned long sz;
 		char *result;
 
-		result = read_object_file(oid, &type, &sz);
+		result = repo_read_object_file(the_repository, oid, &type,
+					       &sz);
 		if (!result)
 			return -1;
 		/* XXX read_sha1_file NUL-terminates */
@@ -3492,7 +3517,8 @@
 
 	clear_image(image);
 
-	image->buf = read_object_file(result_id, &type, &size);
+	image->buf = repo_read_object_file(the_repository, result_id, &type,
+					   &size);
 	if (!image->buf || type != OBJ_BLOB)
 		die("unable to read blob object %s", oid_to_hex(result_id));
 	image->len = size;
@@ -3610,7 +3636,7 @@
 	/* Preimage the patch was prepared for */
 	if (patch->is_new)
 		write_object_file("", 0, OBJ_BLOB, &pre_oid);
-	else if (get_oid(patch->old_oid_prefix, &pre_oid) ||
+	else if (repo_get_oid(the_repository, patch->old_oid_prefix, &pre_oid) ||
 		 read_blob_object(&buf, &pre_oid, patch->old_mode))
 		return error(_("repository lacks the necessary blob to perform 3-way merge."));
 
@@ -3760,8 +3786,17 @@
 		return error_errno("%s", old_name);
 	}
 
-	if (!state->cached && !previous)
-		st_mode = ce_mode_from_stat(*ce, st->st_mode);
+	if (!state->cached && !previous) {
+		if (*ce && !(*ce)->ce_mode)
+			BUG("ce_mode == 0 for path '%s'", old_name);
+
+		if (trust_executable_bit)
+			st_mode = ce_mode_from_stat(*ce, st->st_mode);
+		else if (*ce)
+			st_mode = (*ce)->ce_mode;
+		else
+			st_mode = patch->old_mode;
+	}
 
 	if (patch->is_new < 0)
 		patch->is_new = 0;
@@ -4127,7 +4162,7 @@
 			else
 				return error(_("sha1 information is lacking or "
 					       "useless for submodule %s"), name);
-		} else if (!get_oid_blob(patch->old_oid_prefix, &oid)) {
+		} else if (!repo_get_oid_blob(the_repository, patch->old_oid_prefix, &oid)) {
 			; /* ok */
 		} else if (!patch->lines_added && !patch->lines_deleted) {
 			/* mode-only change: update the current */
@@ -4413,6 +4448,7 @@
 			   const char *buf,
 			   unsigned long size)
 {
+	char *newpath = NULL;
 	int res;
 
 	if (state->cached)
@@ -4474,24 +4510,26 @@
 		unsigned int nr = getpid();
 
 		for (;;) {
-			char newpath[PATH_MAX];
-			mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
+			newpath = mkpathdup("%s~%u", path, nr);
 			res = try_create_file(state, newpath, mode, buf, size);
 			if (res < 0)
-				return -1;
+				goto out;
 			if (!res) {
 				if (!rename(newpath, path))
-					return 0;
+					goto out;
 				unlink_or_warn(newpath);
 				break;
 			}
 			if (errno != EEXIST)
 				break;
 			++nr;
+			FREE_AND_NULL(newpath);
 		}
 	}
-	return error_errno(_("unable to write file '%s' mode %o"),
-			   path, mode);
+	res = error_errno(_("unable to write file '%s' mode %o"), path, mode);
+out:
+	free(newpath);
+	return res;
 }
 
 static int add_conflicted_stages_file(struct apply_state *state,
@@ -4627,8 +4665,11 @@
 			return error_errno(_("cannot open %s"), namebuf);
 	}
 	rej = fdopen(fd, "w");
-	if (!rej)
-		return error_errno(_("cannot open %s"), namebuf);
+	if (!rej) {
+		error_errno(_("cannot open %s"), namebuf);
+		close(fd);
+		return -1;
+	}
 
 	/* Normal git tools never deal with .rej, so do not pretend
 	 * this is a git patch by saying --git or giving extended
diff --git a/apply.h b/apply.h
index b9f18ce..7cd38b1 100644
--- a/apply.h
+++ b/apply.h
@@ -1,7 +1,7 @@
 #ifndef APPLY_H
 #define APPLY_H
 
-#include "hash.h"
+#include "hash-ll.h"
 #include "lockfile.h"
 #include "string-list.h"
 #include "strmap.h"
diff --git a/archive-tar.c b/archive-tar.c
index f8fad294..8ae3012 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -1,13 +1,18 @@
 /*
  * Copyright (c) 2005, 2006 Rene Scharfe
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
 #include "tar.h"
 #include "archive.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
 #include "streaming.h"
 #include "run-command.h"
+#include "write-or-die.h"
 
 #define RECORDSIZE	(512)
 #define BLOCKSIZE	(RECORDSIZE * 20)
@@ -360,7 +365,7 @@
 	int i;
 	for (i = 0; i < nr_tar_filters; i++) {
 		struct archiver *ar = tar_filters[i];
-		if (!strncmp(ar->name, name, len) && !ar->name[len])
+		if (!xstrncmpz(ar->name, name, len))
 			return ar;
 	}
 	return NULL;
@@ -406,14 +411,15 @@
 	return 0;
 }
 
-static int git_tar_config(const char *var, const char *value, void *cb)
+static int git_tar_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "tar.umask")) {
 		if (value && !strcmp(value, "user")) {
 			tar_umask = umask(0);
 			umask(tar_umask);
 		} else {
-			tar_umask = git_config_int(var, value);
+			tar_umask = git_config_int(var, value, ctx->kvi);
 		}
 		return 0;
 	}
diff --git a/archive-zip.c b/archive-zip.c
index 0456f1e..fd1d3f8 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -1,13 +1,18 @@
 /*
  * Copyright (c) 2006 Rene Scharfe
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "archive.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
 #include "streaming.h"
 #include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
 #include "userdiff.h"
+#include "write-or-die.h"
 #include "xdiff-interface.h"
 #include "date.h"
 
@@ -613,6 +618,7 @@
 }
 
 static int archive_zip_config(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
 			      void *data UNUSED)
 {
 	return userdiff_config(var, value);
diff --git a/archive.c b/archive.c
index 9aeaf2b..5287fcd 100644
--- a/archive.c
+++ b/archive.c
@@ -1,14 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "path.h"
+#include "pretty.h"
+#include "setup.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit.h"
+#include "tree.h"
 #include "tree-walk.h"
 #include "attr.h"
 #include "archive.h"
 #include "parse-options.h"
 #include "unpack-trees.h"
-#include "dir.h"
 #include "quote.h"
 
 static char const * const archive_usage[] = {
@@ -59,7 +68,8 @@
 		strbuf_add(&fmt, b + 8, c - b - 8);
 
 		strbuf_add(buf, src, b - src);
-		format_commit_message(commit, fmt.buf, buf, ctx);
+		repo_format_commit_message(the_repository, commit, fmt.buf,
+					   buf, ctx);
 		len -= c + 1 - src;
 		src  = c + 1;
 	}
@@ -84,7 +94,7 @@
 			       (args->tree ? &args->tree->object.oid : NULL), oid);
 
 	path += args->baselen;
-	buffer = read_object_file(oid, type, sizep);
+	buffer = repo_read_object_file(the_repository, oid, type, sizep);
 	if (buffer && S_ISREG(mode)) {
 		struct strbuf buf = STRBUF_INIT;
 		size_t size = 0;
@@ -120,7 +130,7 @@
 	static struct attr_check *check;
 	if (!check)
 		check = attr_check_initl("export-ignore", "export-subst", NULL);
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	return check;
 }
 
@@ -166,6 +176,29 @@
 		args->convert = check_attr_export_subst(check);
 	}
 
+	if (args->prefix) {
+		static struct strbuf new_path = STRBUF_INIT;
+		static struct strbuf buf = STRBUF_INIT;
+		const char *rel;
+
+		rel = relative_path(path_without_prefix, args->prefix, &buf);
+
+		/*
+		 * We don't add an entry for the current working
+		 * directory when we are at the root; skip it also when
+		 * we're in a subdirectory or submodule.  Skip entries
+		 * higher up as well.
+		 */
+		if (!strcmp(rel, "./") || starts_with(rel, "../"))
+			return S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0;
+
+		/* rel can refer to path, so don't edit it in place */
+		strbuf_reset(&new_path);
+		strbuf_add(&new_path, args->base, args->baselen);
+		strbuf_addstr(&new_path, rel);
+		strbuf_swap(&path, &new_path);
+	}
+
 	if (args->verbose)
 		fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
 
@@ -306,7 +339,8 @@
 		opts.src_index = args->repo->index;
 		opts.dst_index = args->repo->index;
 		opts.fn = oneway_merge;
-		init_tree_desc(&t, args->tree->buffer, args->tree->size);
+		init_tree_desc(&t, &args->tree->object.oid,
+			       args->tree->buffer, args->tree->size);
 		if (unpack_trees(1, &t, &opts))
 			return -1;
 		git_attr_set_direction(GIT_ATTR_INDEX);
@@ -401,6 +435,27 @@
 	return ret;
 }
 
+static int reject_outside(const struct object_id *oid UNUSED,
+			  struct strbuf *base, const char *filename,
+			  unsigned mode, void *context)
+{
+	struct archiver_args *args = context;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	int ret = 0;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	strbuf_addbuf(&path, base);
+	strbuf_addstr(&path, filename);
+	if (starts_with(relative_path(path.buf, args->prefix, &buf), "../"))
+		ret = -1;
+	strbuf_release(&buf);
+	strbuf_release(&path);
+	return ret;
+}
+
 static int path_exists(struct archiver_args *args, const char *path)
 {
 	const char *paths[] = { path, NULL };
@@ -408,8 +463,13 @@
 	int ret;
 
 	ctx.args = args;
-	parse_pathspec(&ctx.pathspec, 0, 0, "", paths);
+	parse_pathspec(&ctx.pathspec, 0, PATHSPEC_PREFER_CWD,
+		       args->prefix, paths);
 	ctx.pathspec.recursive = 1;
+	if (args->prefix && read_tree(args->repo, args->tree, &ctx.pathspec,
+				      reject_outside, args))
+		die(_("pathspec '%s' matches files outside the "
+		      "current directory"), path);
 	ret = read_tree(args->repo, args->tree,
 			&ctx.pathspec,
 			reject_entry, &ctx);
@@ -425,9 +485,8 @@
 	 * Also if pathspec patterns are dependent, we're in big
 	 * trouble as we test each one separately
 	 */
-	parse_pathspec(&ar_args->pathspec, 0,
-		       PATHSPEC_PREFER_FULL,
-		       "", pathspec);
+	parse_pathspec(&ar_args->pathspec, 0, PATHSPEC_PREFER_CWD,
+		       ar_args->prefix, pathspec);
 	ar_args->pathspec.recursive = 1;
 	if (pathspec) {
 		while (*pathspec) {
@@ -439,8 +498,7 @@
 }
 
 static void parse_treeish_arg(const char **argv,
-		struct archiver_args *ar_args, const char *prefix,
-		int remote)
+			      struct archiver_args *ar_args, int remote)
 {
 	const char *name = argv[0];
 	const struct object_id *commit_oid;
@@ -455,13 +513,14 @@
 		const char *colon = strchrnul(name, ':');
 		int refnamelen = colon - name;
 
-		if (!dwim_ref(name, refnamelen, &oid, &ref, 0))
+		if (!repo_dwim_ref(the_repository, name, refnamelen, &oid, &ref, 0))
 			die(_("no such ref: %.*s"), refnamelen, name);
 	} else {
-		dwim_ref(name, strlen(name), &oid, &ref, 0);
+		repo_dwim_ref(the_repository, name, strlen(name), &oid, &ref,
+			      0);
 	}
 
-	if (get_oid(name, &oid))
+	if (repo_get_oid(the_repository, name, &oid))
 		die(_("not a valid object name: %s"), name);
 
 	commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1);
@@ -479,20 +538,6 @@
 	if (!tree)
 		die(_("not a tree object: %s"), oid_to_hex(&oid));
 
-	if (prefix) {
-		struct object_id tree_oid;
-		unsigned short mode;
-		int err;
-
-		err = get_tree_entry(ar_args->repo,
-				     &tree->object.oid,
-				     prefix, &tree_oid,
-				     &mode);
-		if (err || !S_ISDIR(mode))
-			die(_("current working directory is untracked"));
-
-		tree = parse_tree_indirect(&tree_oid);
-	}
 	ar_args->refname = ref;
 	ar_args->tree = tree;
 	ar_args->commit_oid = commit_oid;
@@ -641,6 +686,8 @@
 		base = "";
 
 	if (list) {
+		if (argc)
+			die(_("extra command line parameter '%s'"), *argv);
 		for (i = 0; i < nr_archivers; i++)
 			if (!is_remote || archivers[i]->flags & ARCHIVER_REMOTE)
 				printf("%s\n", archivers[i]->name);
@@ -710,7 +757,7 @@
 		setup_git_directory();
 	}
 
-	parse_treeish_arg(argv, &args, prefix, remote);
+	parse_treeish_arg(argv, &args, remote);
 	parse_pathspec_arg(argv + 1, &args);
 
 	rc = ar->write_archive(ar, &args);
diff --git a/archive.h b/archive.h
index 7178e2a..bbe65ba 100644
--- a/archive.h
+++ b/archive.h
@@ -1,8 +1,8 @@
 #ifndef ARCHIVE_H
 #define ARCHIVE_H
 
-#include "cache.h"
 #include "pathspec.h"
+#include "string-list.h"
 
 struct repository;
 struct pretty_print_context;
diff --git a/attr.c b/attr.c
index 1053dfc..679e422 100644
--- a/attr.c
+++ b/attr.c
@@ -6,16 +6,26 @@
  * an insanely large number of attributes.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
 #include "exec-cmd.h"
 #include "attr.h"
 #include "dir.h"
+#include "gettext.h"
+#include "path.h"
 #include "utf8.h"
 #include "quote.h"
+#include "read-cache-ll.h"
+#include "refs.h"
 #include "revision.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "setup.h"
 #include "thread-utils.h"
+#include "tree-walk.h"
+#include "object-name.h"
+
+const char *git_attr_tree;
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -174,6 +184,15 @@
 	}
 }
 
+/*
+ * Atribute name cannot begin with "builtin_" which
+ * is a reserved namespace for built in attributes values.
+ */
+static int attr_name_reserved(const char *name)
+{
+	return starts_with(name, "builtin_");
+}
+
 static int attr_name_valid(const char *name, size_t namelen)
 {
 	/*
@@ -306,7 +325,7 @@
 			cp++;
 			len--;
 		}
-		if (!attr_name_valid(cp, len)) {
+		if (!attr_name_valid(cp, len) || attr_name_reserved(cp)) {
 			report_invalid_attr(cp, len, src, lineno);
 			return NULL;
 		}
@@ -370,7 +389,7 @@
 		name += strlen(ATTRIBUTE_MACRO_PREFIX);
 		name += strspn(name, blank);
 		namelen = strcspn(name, blank);
-		if (!attr_name_valid(name, namelen)) {
+		if (!attr_name_valid(name, namelen) || attr_name_reserved(name)) {
 			report_invalid_attr(name, namelen, src, lineno);
 			goto fail_return;
 		}
@@ -800,35 +819,56 @@
 static struct attr_stack *read_attr_from_index(struct index_state *istate,
 					       const char *path, unsigned flags)
 {
+	struct attr_stack *stack = NULL;
 	char *buf;
 	unsigned long size;
+	int sparse_dir_pos = -1;
 
 	if (!istate)
 		return NULL;
 
 	/*
-	 * The .gitattributes file only applies to files within its
-	 * parent directory. In the case of cone-mode sparse-checkout,
-	 * the .gitattributes file is sparse if and only if all paths
-	 * within that directory are also sparse. Thus, don't load the
-	 * .gitattributes file since it will not matter.
-	 *
-	 * In the case of a sparse index, it is critical that we don't go
-	 * looking for a .gitattributes file, as doing so would cause the
-	 * index to expand.
+	 * When handling sparse-checkouts, .gitattributes files
+	 * may reside within a sparse directory. We distinguish
+	 * whether a path exists directly in the index or not by
+	 * evaluating if 'pos' is negative.
+	 * If 'pos' is negative, the path is not directly present
+	 * in the index and is likely within a sparse directory.
+	 * For paths not in the index, The absolute value of 'pos'
+	 * minus 1 gives us the position where the path would be
+	 * inserted in lexicographic order within the index.
+	 * We then subtract another 1 from this value
+	 * (sparse_dir_pos = -pos - 2) to find the position of the
+	 * last index entry which is lexicographically smaller than
+	 * the path. This would be the sparse directory containing
+	 * the path. By identifying the sparse directory containing
+	 * the path, we can correctly read the attributes specified
+	 * in the .gitattributes file from the tree object of the
+	 * sparse directory.
 	 */
-	if (!path_in_cone_mode_sparse_checkout(path, istate))
-		return NULL;
+	if (!path_in_cone_mode_sparse_checkout(path, istate)) {
+		int pos = index_name_pos_sparse(istate, path, strlen(path));
 
-	buf = read_blob_data_from_index(istate, path, &size);
-	if (!buf)
-		return NULL;
-	if (size >= ATTR_MAX_FILE_SIZE) {
-		warning(_("ignoring overly large gitattributes blob '%s'"), path);
-		return NULL;
+		if (pos < 0)
+			sparse_dir_pos = -pos - 2;
 	}
 
-	return read_attr_from_buf(buf, path, flags);
+	if (sparse_dir_pos >= 0 &&
+	    S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) &&
+	    !strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) {
+		const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]);
+		stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
+	} else {
+		buf = read_blob_data_from_index(istate, path, &size);
+		if (!buf)
+			return NULL;
+		if (size >= ATTR_MAX_FILE_SIZE) {
+			warning(_("ignoring overly large gitattributes blob '%s'"), path);
+			return NULL;
+		}
+		stack = read_attr_from_buf(buf, path, flags);
+	}
+	return stack;
 }
 
 static struct attr_stack *read_attr(struct index_state *istate,
@@ -864,7 +904,7 @@
 	return res;
 }
 
-static const char *git_etc_gitattributes(void)
+const char *git_attr_system_file(void)
 {
 	static const char *system_wide;
 	if (!system_wide)
@@ -872,7 +912,7 @@
 	return system_wide;
 }
 
-static const char *get_home_gitattributes(void)
+const char *git_attr_global_file(void)
 {
 	if (!git_attributes_file)
 		git_attributes_file = xdg_config_home("attributes");
@@ -880,7 +920,7 @@
 	return git_attributes_file;
 }
 
-static int git_attr_system(void)
+int git_attr_system_is_enabled(void)
 {
 	return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
 }
@@ -914,14 +954,14 @@
 	push_stack(stack, e, NULL, 0);
 
 	/* system-wide frame */
-	if (git_attr_system()) {
-		e = read_attr_from_file(git_etc_gitattributes(), flags);
+	if (git_attr_system_is_enabled()) {
+		e = read_attr_from_file(git_attr_system_file(), flags);
 		push_stack(stack, e, NULL, 0);
 	}
 
 	/* home directory */
-	if (get_home_gitattributes()) {
-		e = read_attr_from_file(get_home_gitattributes(), flags);
+	if (git_attr_global_file()) {
+		e = read_attr_from_file(git_attr_global_file(), flags);
 		push_stack(stack, e, NULL, 0);
 	}
 
@@ -1165,11 +1205,136 @@
 	fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
 
+static const char *default_attr_source_tree_object_name;
+static int ignore_bad_attr_tree;
+
+void set_git_attr_source(const char *tree_object_name)
+{
+	default_attr_source_tree_object_name = xstrdup(tree_object_name);
+}
+
+static void compute_default_attr_source(struct object_id *attr_source)
+{
+	if (!default_attr_source_tree_object_name)
+		default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
+
+	if (!default_attr_source_tree_object_name && git_attr_tree) {
+		default_attr_source_tree_object_name = git_attr_tree;
+		ignore_bad_attr_tree = 1;
+	}
+
+	if (!default_attr_source_tree_object_name &&
+	    startup_info->have_repository &&
+	    is_bare_repository()) {
+		default_attr_source_tree_object_name = "HEAD";
+		ignore_bad_attr_tree = 1;
+	}
+
+	if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
+		return;
+
+	if (repo_get_oid_treeish(the_repository,
+				 default_attr_source_tree_object_name,
+				 attr_source) && !ignore_bad_attr_tree)
+		die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+}
+
+static struct object_id *default_attr_source(void)
+{
+	static struct object_id attr_source;
+
+	if (is_null_oid(&attr_source))
+		compute_default_attr_source(&attr_source);
+	if (is_null_oid(&attr_source))
+		return NULL;
+	return &attr_source;
+}
+
+static const char *interned_mode_string(unsigned int mode)
+{
+	static struct {
+		unsigned int val;
+		char str[7];
+	} mode_string[] = {
+		{ .val = 0040000 },
+		{ .val = 0100644 },
+		{ .val = 0100755 },
+		{ .val = 0120000 },
+		{ .val = 0160000 },
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mode_string); i++) {
+		if (mode_string[i].val != mode)
+			continue;
+		if (!*mode_string[i].str)
+			snprintf(mode_string[i].str, sizeof(mode_string[i].str),
+				 "%06o", mode);
+		return mode_string[i].str;
+	}
+	BUG("Unsupported mode 0%o", mode);
+}
+
+static const char *builtin_object_mode_attr(struct index_state *istate, const char *path)
+{
+	unsigned int mode;
+
+	if (direction == GIT_ATTR_CHECKIN) {
+		struct object_id oid;
+		struct stat st;
+		if (lstat(path, &st))
+			die_errno(_("unable to stat '%s'"), path);
+		mode = canon_mode(st.st_mode);
+		if (S_ISDIR(mode)) {
+			/*
+			 *`path` is either a directory or it is a submodule,
+			 * in which case it is already indexed as submodule
+			 * or it does not exist in the index yet and we need to
+			 * check if we can resolve to a ref.
+			*/
+			int pos = index_name_pos(istate, path, strlen(path));
+			if (pos >= 0) {
+				 if (S_ISGITLINK(istate->cache[pos]->ce_mode))
+					 mode = istate->cache[pos]->ce_mode;
+			} else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
+				mode = S_IFGITLINK;
+			}
+		}
+	} else {
+		/*
+		 * For GIT_ATTR_CHECKOUT and GIT_ATTR_INDEX we only check
+		 * for mode in the index.
+		 */
+		int pos = index_name_pos(istate, path, strlen(path));
+		if (pos >= 0)
+			mode = istate->cache[pos]->ce_mode;
+		else
+			return ATTR__UNSET;
+	}
+
+	return interned_mode_string(mode);
+}
+
+
+static const char *compute_builtin_attr(struct index_state *istate,
+					  const char *path,
+					  const struct git_attr *attr) {
+	static const struct git_attr *object_mode_attr;
+
+	if (!object_mode_attr)
+		object_mode_attr = git_attr("builtin_objectmode");
+
+	if (attr == object_mode_attr)
+		return builtin_object_mode_attr(istate, path);
+	return ATTR__UNSET;
+}
+
 void git_check_attr(struct index_state *istate,
-		    const struct object_id *tree_oid, const char *path,
+		    const char *path,
 		    struct attr_check *check)
 {
 	int i;
+	const struct object_id *tree_oid = default_attr_source();
 
 	collect_some_attrs(istate, tree_oid, path, check);
 
@@ -1177,15 +1342,16 @@
 		unsigned int n = check->items[i].attr->attr_nr;
 		const char *value = check->all_attrs[n].value;
 		if (value == ATTR__UNKNOWN)
-			value = ATTR__UNSET;
+			value = compute_builtin_attr(istate, path, check->all_attrs[n].attr);
 		check->items[i].value = value;
 	}
 }
 
-void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
+void git_all_attrs(struct index_state *istate,
 		   const char *path, struct attr_check *check)
 {
 	int i;
+	const struct object_id *tree_oid = default_attr_source();
 
 	attr_check_reset(check);
 	collect_some_attrs(istate, tree_oid, path, check);
diff --git a/attr.h b/attr.h
index 9884ea2..127998a 100644
--- a/attr.h
+++ b/attr.h
@@ -45,7 +45,7 @@
  * const char *path;
  *
  * setup_check();
- * git_check_attr(&the_index, tree_oid, path, check);
+ * git_check_attr(&the_index, path, check);
  * ------------
  *
  * - Act on `.value` member of the result, left in `check->items[]`:
@@ -120,7 +120,6 @@
 #define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
 
 struct index_state;
-struct object_id;
 
 /**
  * An attribute is an opaque object that is identified by its name. Pass the
@@ -136,6 +135,12 @@
 struct attr_stack;
 
 /*
+ * The textual object name for the tree-ish used by git_check_attr()
+ * to read attributes from (instead of from the working tree).
+ */
+void set_git_attr_source(const char *);
+
+/*
  * Given a string, return the gitattribute object that
  * corresponds to it.
  */
@@ -203,14 +208,14 @@
 const char *git_attr_name(const struct git_attr *);
 
 void git_check_attr(struct index_state *istate,
-		    const struct object_id *tree_oid, const char *path,
+		    const char *path,
 		    struct attr_check *check);
 
 /*
  * Retrieve all attributes that apply to the specified path.
  * check holds the attributes and their values.
  */
-void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
+void git_all_attrs(struct index_state *istate,
 		   const char *path, struct attr_check *check);
 
 enum git_attr_direction {
@@ -222,4 +227,15 @@
 
 void attr_start(void);
 
+/* Return the system gitattributes file. */
+const char *git_attr_system_file(void);
+
+/* Return the global gitattributes file, if any. */
+const char *git_attr_global_file(void);
+
+/* Return whether the system gitattributes file is enabled and should be used. */
+int git_attr_system_is_enabled(void);
+
+extern const char *git_attr_tree;
+
 #endif /* ATTR_H */
diff --git a/banned.h b/banned.h
index 6ccf46b..44e76bd 100644
--- a/banned.h
+++ b/banned.h
@@ -18,6 +18,10 @@
 #define strncpy(x,y,n) BANNED(strncpy)
 #undef strncat
 #define strncat(x,y,n) BANNED(strncat)
+#undef strtok
+#define strtok(x,y) BANNED(strtok)
+#undef strtok_r
+#define strtok_r(x,y,z) BANNED(strtok_r)
 
 #undef sprintf
 #undef vsprintf
diff --git a/base85.c b/base85.c
index 5ca601e..bbacdca 100644
--- a/base85.c
+++ b/base85.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "base85.h"
 
 #undef DEBUG_85
 
diff --git a/base85.h b/base85.h
new file mode 100644
index 0000000..c835086
--- /dev/null
+++ b/base85.h
@@ -0,0 +1,7 @@
+#ifndef BASE85_H
+#define BASE85_H
+
+int decode_85(char *dst, const char *line, int linelen);
+void encode_85(char *buf, const unsigned char *data, int bytes);
+
+#endif /* BASE85_H */
diff --git a/bisect.c b/bisect.c
index ef5ee5a..60aae2f 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1,12 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
 #include "refs.h"
 #include "list-objects.h"
 #include "quote.h"
-#include "hash-lookup.h"
 #include "run-command.h"
 #include "log-tree.h"
 #include "bisect.h"
@@ -14,7 +16,9 @@
 #include "strvec.h"
 #include "commit-slab.h"
 #include "commit-reach.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "dir.h"
 
 static struct oid_array good_revs;
@@ -148,11 +152,15 @@
 		unsigned commit_flags = commit->object.flags;
 		enum object_type type;
 		unsigned long size;
-		char *buf = read_object_file(&commit->object.oid, &type,
-					     &size);
+		char *buf = repo_read_object_file(the_repository,
+						  &commit->object.oid, &type,
+						  &size);
 		const char *subject_start;
 		int subject_len;
 
+		if (!buf)
+			die(_("unable to read %s"), oid_to_hex(&commit->object.oid));
+
 		fprintf(stderr, "%c%c%c ",
 			(commit_flags & TREESAME) ? ' ' : 'T',
 			(commit_flags & UNINTERESTING) ? 'U' : ' ',
@@ -465,7 +473,6 @@
 }
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
-static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
@@ -701,26 +708,10 @@
 
 static int is_expected_rev(const struct object_id *oid)
 {
-	const char *filename = git_path_bisect_expected_rev();
-	struct stat st;
-	struct strbuf str = STRBUF_INIT;
-	FILE *fp;
-	int res = 0;
-
-	if (stat(filename, &st) || !S_ISREG(st.st_mode))
+	struct object_id expected_oid;
+	if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
 		return 0;
-
-	fp = fopen_or_warn(filename, "r");
-	if (!fp)
-		return 0;
-
-	if (strbuf_getline_lf(&str, fp) != EOF)
-		res = !strcmp(str.buf, oid_to_hex(oid));
-
-	strbuf_release(&str);
-	fclose(fp);
-
-	return res;
+	return oideq(oid, &expected_oid);
 }
 
 enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
@@ -751,7 +742,8 @@
 	}
 
 	commit = lookup_commit_reference(the_repository, bisect_rev);
-	format_commit_message(commit, "[%H] %s%n", &commit_msg, &pp);
+	repo_format_commit_message(the_repository, commit, "[%H] %s%n",
+				   &commit_msg, &pp);
 	fputs(commit_msg.buf, stdout);
 	strbuf_release(&commit_msg);
 
@@ -844,9 +836,11 @@
 static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
 {
 	enum bisect_error res = BISECT_OK;
-	struct commit_list *result;
+	struct commit_list *result = NULL;
 
-	result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
+	if (repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
+				      rev + 1, &result) < 0)
+		exit(128);
 
 	for (; result; result = result->next) {
 		const struct object_id *mb = &result->item->object.oid;
@@ -1177,10 +1171,10 @@
 	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
 	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
 	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
+	string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
 	result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
 	refs_for_removal.strdup_strings = 1;
 	string_list_clear(&refs_for_removal, 0);
-	unlink_or_warn(git_path_bisect_expected_rev());
 	unlink_or_warn(git_path_bisect_ancestors_ok());
 	unlink_or_warn(git_path_bisect_log());
 	unlink_or_warn(git_path_bisect_names());
diff --git a/blame.c b/blame.c
index 8bfeaa1..1a16d4e 100644
--- a/blame.c
+++ b/blame.c
@@ -1,11 +1,20 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "cache-tree.h"
 #include "mergesort.h"
+#include "commit.h"
+#include "convert.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "gettext.h"
+#include "hex.h"
+#include "path.h"
+#include "read-cache.h"
+#include "revision.h"
+#include "setup.h"
 #include "tag.h"
+#include "trace2.h"
 #include "blame.h"
 #include "alloc.h"
 #include "commit-slab.h"
@@ -176,12 +185,12 @@
 static struct commit *fake_working_tree_commit(struct repository *r,
 					       struct diff_options *opt,
 					       const char *path,
-					       const char *contents_from)
+					       const char *contents_from,
+					       struct object_id *oid)
 {
 	struct commit *commit;
 	struct blame_origin *origin;
 	struct commit_list **parent_tail, *parent;
-	struct object_id head_oid;
 	struct strbuf buf = STRBUF_INIT;
 	const char *ident;
 	time_t now;
@@ -197,17 +206,18 @@
 	commit->date = now;
 	parent_tail = &commit->parents;
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-		die("no such ref: HEAD");
-
-	parent_tail = append_parent(r, parent_tail, &head_oid);
+	parent_tail = append_parent(r, parent_tail, oid);
 	append_merge_parents(r, parent_tail);
 	verify_working_tree_path(r, commit, path);
 
 	origin = make_origin(commit, path);
 
-	ident = fmt_ident("Not Committed Yet", "not.committed.yet",
-			WANT_BLANK_IDENT, NULL, 0);
+	if (contents_from)
+		ident = fmt_ident("External file (--contents)", "external.file",
+				  WANT_BLANK_IDENT, NULL, 0);
+	else
+		ident = fmt_ident("Not Committed Yet", "not.committed.yet",
+				  WANT_BLANK_IDENT, NULL, 0);
 	strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
 	for (parent = commit->parents; parent; parent = parent->next)
 		strbuf_addf(&msg, "parent %s\n",
@@ -1028,8 +1038,9 @@
 				    &o->blob_oid, 1, &file->ptr, &file_size))
 			;
 		else
-			file->ptr = read_object_file(&o->blob_oid, &type,
-						     &file_size);
+			file->ptr = repo_read_object_file(the_repository,
+							  &o->blob_oid, &type,
+							  &file_size);
 		file->size = file_size;
 
 		if (!file->ptr)
@@ -2429,7 +2440,7 @@
 
 			if (sg_origin[i])
 				continue;
-			if (parse_commit(p))
+			if (repo_parse_commit(the_repository, p))
 				continue;
 			porigin = find(sb->repo, p, origin, sb->bloom_data);
 			if (!porigin)
@@ -2592,7 +2603,7 @@
 		 * so hold onto it in the meantime.
 		 */
 		blame_origin_incref(suspect);
-		parse_commit(commit);
+		repo_parse_commit(the_repository, commit);
 		if (sb->reverse ||
 		    (!(commit->object.flags & UNINTERESTING) &&
 		     !(revs->max_age != -1 && commit->date < revs->max_age)))
@@ -2771,22 +2782,39 @@
 		sb->commits.compare = compare_commits_by_reverse_commit_date;
 	}
 
-	if (sb->final && sb->contents_from)
-		die(_("cannot use --contents with final commit object name"));
-
 	if (sb->reverse && sb->revs->first_parent_only)
 		sb->revs->children.name = NULL;
 
-	if (!sb->final) {
+	if (sb->contents_from || !sb->final) {
+		struct object_id head_oid, *parent_oid;
+
 		/*
-		 * "--not A B -- path" without anything positive;
-		 * do not default to HEAD, but use the working tree
-		 * or "--contents".
+		 * Build a fake commit at the top of the history, when
+		 * (1) "git blame [^A] --path", i.e. with no positive end
+		 *     of the history range, in which case we build such
+		 *     a fake commit on top of the HEAD to blame in-tree
+		 *     modifications.
+		 * (2) "git blame --contents=file [A] -- path", with or
+		 *     without positive end of the history range but with
+		 *     --contents, in which case we pretend that there is
+		 *     a fake commit on top of the positive end (defaulting to
+		 *     HEAD) that has the given contents in the path.
 		 */
-		setup_work_tree();
+		if (sb->final) {
+			parent_oid = &sb->final->object.oid;
+		} else {
+			if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+				die("no such ref: HEAD");
+			parent_oid = &head_oid;
+		}
+
+		if (!sb->contents_from)
+			setup_work_tree();
+
 		sb->final = fake_working_tree_commit(sb->repo,
 						     &sb->revs->diffopt,
-						     sb->path, sb->contents_from);
+						     sb->path, sb->contents_from,
+						     parent_oid);
 		add_pending_object(sb->revs, &(sb->final->object), ":");
 	}
 
@@ -2838,8 +2866,10 @@
 				    &sb->final_buf_size))
 			;
 		else
-			sb->final_buf = read_object_file(&o->blob_oid, &type,
-							 &sb->final_buf_size);
+			sb->final_buf = repo_read_object_file(the_repository,
+							      &o->blob_oid,
+							      &type,
+							      &sb->final_buf_size);
 
 		if (!sb->final_buf)
 			die(_("cannot read blob %s for path %s"),
diff --git a/blame.h b/blame.h
index 38bde53..5b4e47d 100644
--- a/blame.h
+++ b/blame.h
@@ -1,12 +1,9 @@
 #ifndef BLAME_H
 #define BLAME_H
 
-#include "cache.h"
-#include "commit.h"
+#include "oidset.h"
 #include "xdiff-interface.h"
-#include "revision.h"
 #include "prio-queue.h"
-#include "diff.h"
 
 #define PICKAXE_BLAME_MOVE		01
 #define PICKAXE_BLAME_COPY		02
diff --git a/blob.c b/blob.c
index 8f83523..3fb2922 100644
--- a/blob.c
+++ b/blob.c
@@ -1,6 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "blob.h"
-#include "repository.h"
 #include "alloc.h"
 
 const char *blob_type = "blob";
diff --git a/bloom.c b/bloom.c
index d073052..e529f76 100644
--- a/bloom.c
+++ b/bloom.c
@@ -2,10 +2,10 @@
 #include "bloom.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "revision.h"
 #include "hashmap.h"
 #include "commit-graph.h"
 #include "commit.h"
+#include "commit-slab.h"
 
 define_commit_slab(bloom_filter_slab, struct bloom_filter);
 
@@ -28,6 +28,26 @@
 	return ((unsigned char)1) << (pos & (BITS_PER_WORD - 1));
 }
 
+static int check_bloom_offset(struct commit_graph *g, uint32_t pos,
+			      uint32_t offset)
+{
+	/*
+	 * Note that we allow offsets equal to the data size, which would set
+	 * our pointers at one past the end of the chunk memory. This is
+	 * necessary because the on-disk index points to the end of the
+	 * entries (so we can compute size by comparing adjacent ones). And
+	 * naturally the final entry's end is one-past-the-end of the chunk.
+	 */
+	if (offset <= g->chunk_bloom_data_size - BLOOMDATA_CHUNK_HEADER_SIZE)
+		return 0;
+
+	warning("ignoring out-of-range offset (%"PRIuMAX") for changed-path"
+		" filter at pos %"PRIuMAX" of %s (chunk size: %"PRIuMAX")",
+		(uintmax_t)offset, (uintmax_t)pos,
+		g->filename, (uintmax_t)g->chunk_bloom_data_size);
+	return -1;
+}
+
 static int load_bloom_filter_from_graph(struct commit_graph *g,
 					struct bloom_filter *filter,
 					uint32_t graph_pos)
@@ -50,6 +70,20 @@
 	else
 		start_index = 0;
 
+	if (check_bloom_offset(g, lex_pos, end_index) < 0 ||
+	    check_bloom_offset(g, lex_pos - 1, start_index) < 0)
+		return 0;
+
+	if (end_index < start_index) {
+		warning("ignoring decreasing changed-path index offsets"
+			" (%"PRIuMAX" > %"PRIuMAX") for positions"
+			" %"PRIuMAX" and %"PRIuMAX" of %s",
+			(uintmax_t)start_index, (uintmax_t)end_index,
+			(uintmax_t)(lex_pos-1), (uintmax_t)lex_pos,
+			g->filename);
+		return 0;
+	}
+
 	filter->len = end_index - start_index;
 	filter->data = (unsigned char *)(g->chunk_bloom_data +
 					sizeof(unsigned char) * start_index +
diff --git a/branch.c b/branch.c
index e5614b5..621019f 100644
--- a/branch.c
+++ b/branch.c
@@ -1,10 +1,16 @@
 #include "git-compat-util.h"
-#include "cache.h"
+#include "advice.h"
 #include "config.h"
 #include "branch.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "path.h"
 #include "refs.h"
 #include "refspec.h"
 #include "remote.h"
+#include "repository.h"
 #include "sequencer.h"
 #include "commit.h"
 #include "worktree.h"
@@ -32,7 +38,7 @@
 	if (!remote_find_tracking(remote, &tracking->spec)) {
 		switch (++tracking->matches) {
 		case 1:
-			string_list_append(tracking->srcs, tracking->spec.src);
+			string_list_append_nodup(tracking->srcs, tracking->spec.src);
 			tracking->remote = remote->name;
 			break;
 		case 2:
@@ -228,7 +234,7 @@
 		return -1;
 	}
 
-	tracking->remote = xstrdup(branch->remote_name);
+	tracking->remote = branch->remote_name;
 	for (i = 0; i < branch->merge_nr; i++)
 		string_list_append(tracking->srcs, branch->merge_name[i]);
 	return 0;
@@ -328,7 +334,7 @@
 		if (!skip_prefix(tracking.srcs->items[0].string,
 				 "refs/heads/", &tracked_branch) ||
 		    strcmp(tracked_branch, new_ref))
-			return;
+			goto cleanup;
 	}
 
 	if (tracking.srcs->nr < 1)
@@ -364,8 +370,12 @@
  */
 int validate_branchname(const char *name, struct strbuf *ref)
 {
-	if (strbuf_check_branch_ref(ref, name))
-		die(_("'%s' is not a valid branch name"), name);
+	if (strbuf_check_branch_ref(ref, name)) {
+		int code = die_message(_("'%s' is not a valid branch name"), name);
+		advise_if_enabled(ADVICE_REF_SYNTAX,
+				  _("See `man git check-ref-format`"));
+		exit(code);
+	}
 
 	return ref_exists(ref->buf);
 }
@@ -414,9 +424,9 @@
 		wt_status_state_free_buffers(&state);
 
 		if (wt_status_check_bisect(wt, &state) &&
-		    state.branch) {
+		    state.bisecting_from) {
 			struct strbuf ref = STRBUF_INIT;
-			strbuf_addf(&ref, "refs/heads/%s", state.branch);
+			strbuf_addf(&ref, "refs/heads/%s", state.bisecting_from);
 			old = strmap_put(&current_checked_out_branches,
 					 ref.buf,
 					 xstrdup(wt->path));
@@ -465,7 +475,7 @@
 
 	if ((path = branch_checked_out(ref->buf)))
 		die(_("cannot force update the branch '%s' "
-		      "checked out at '%s'"),
+		      "used by worktree at '%s'"),
 		    ref->buf + strlen("refs/heads/"), path);
 
 	return 1;
@@ -475,9 +485,12 @@
 {
 	char *tracking_branch = cb_data;
 	struct refspec_item query;
+	int res;
 	memset(&query, 0, sizeof(struct refspec_item));
 	query.dst = tracking_branch;
-	return !remote_find_tracking(remote, &query);
+	res = !remote_find_tracking(remote, &query);
+	free(query.src);
+	return res;
 }
 
 static int validate_remote_tracking_branch(char *ref)
@@ -531,7 +544,7 @@
 		explicit_tracking = 1;
 
 	real_ref = NULL;
-	if (get_oid_mb(start_name, &oid)) {
+	if (repo_get_oid_mb(r, start_name, &oid)) {
 		if (explicit_tracking) {
 			int code = die_message(_(upstream_missing), start_name);
 			advise_if_enabled(ADVICE_SET_UPSTREAM_FAILURE,
@@ -541,7 +554,8 @@
 		die(_("not a valid object name: '%s'"), start_name);
 	}
 
-	switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
+	switch (repo_dwim_ref(r, start_name, strlen(start_name), &oid,
+			      &real_ref, 0)) {
 	case 0:
 		/* Not branching from any existing branch */
 		if (explicit_tracking)
@@ -632,9 +646,10 @@
 			     const char *orig_ref, enum branch_track track,
 			     int quiet)
 {
-	char *real_orig_ref;
+	char *real_orig_ref = NULL;
 	dwim_branch_start(r, orig_ref, track, &real_orig_ref, NULL);
 	setup_tracking(new_ref, real_orig_ref, track, quiet);
+	free(real_orig_ref);
 }
 
 /**
@@ -772,7 +787,7 @@
 			    name);
 	}
 
-	create_branch(the_repository, name, start_commitish, force, 0, reflog, quiet,
+	create_branch(r, name, start_commitish, force, 0, reflog, quiet,
 		      BRANCH_TRACK_NEVER, dry_run);
 	if (dry_run)
 		return;
@@ -806,8 +821,9 @@
 	unlink(git_path_merge_rr(r));
 	unlink(git_path_merge_msg(r));
 	unlink(git_path_merge_mode(r));
-	unlink(git_path_auto_merge(r));
-	save_autostash(git_path_merge_autostash(r));
+	refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+			NULL, REF_NO_DEREF);
+	save_autostash_ref(r, "MERGE_AUTOSTASH");
 }
 
 void remove_branch_state(struct repository *r, int verbose)
@@ -820,40 +836,17 @@
 void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
 	struct worktree **worktrees = get_worktrees();
-	const struct worktree *wt;
 
-	wt = find_shared_symref(worktrees, "HEAD", branch);
-	if (wt && (!ignore_current_worktree || !wt->is_current)) {
-		skip_prefix(branch, "refs/heads/", &branch);
-		die(_("'%s' is already checked out at '%s'"), branch, wt->path);
+	for (int i = 0; worktrees[i]; i++) {
+		if (worktrees[i]->is_current && ignore_current_worktree)
+			continue;
+
+		if (is_shared_symref(worktrees[i], "HEAD", branch)) {
+			skip_prefix(branch, "refs/heads/", &branch);
+			die(_("'%s' is already used by worktree at '%s'"),
+				branch, worktrees[i]->path);
+		}
 	}
 
 	free_worktrees(worktrees);
 }
-
-int replace_each_worktree_head_symref(const char *oldref, const char *newref,
-				      const char *logmsg)
-{
-	int ret = 0;
-	struct worktree **worktrees = get_worktrees();
-	int i;
-
-	for (i = 0; worktrees[i]; i++) {
-		struct ref_store *refs;
-
-		if (worktrees[i]->is_detached)
-			continue;
-		if (!worktrees[i]->head_ref)
-			continue;
-		if (strcmp(oldref, worktrees[i]->head_ref))
-			continue;
-
-		refs = get_worktree_ref_store(worktrees[i]);
-		if (refs_create_symref(refs, "HEAD", newref, logmsg))
-			ret = error(_("HEAD of working tree %s is not updated"),
-				    worktrees[i]->path);
-	}
-
-	free_worktrees(worktrees);
-	return ret;
-}
diff --git a/branch.h b/branch.h
index ef56103..30c01ae 100644
--- a/branch.h
+++ b/branch.h
@@ -155,12 +155,4 @@
  */
 void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
-/*
- * Update all per-worktree HEADs pointing at the old ref to point the new ref.
- * This will be used when renaming a branch. Returns 0 if successful, non-zero
- * otherwise.
- */
-int replace_each_worktree_head_symref(const char *oldref, const char *newref,
-				      const char *logmsg);
-
 #endif
diff --git a/builtin.h b/builtin.h
index 46cc789..2828063 100644
--- a/builtin.h
+++ b/builtin.h
@@ -2,9 +2,6 @@
 #define BUILTIN_H
 
 #include "git-compat-util.h"
-#include "strbuf.h"
-#include "cache.h"
-#include "commit.h"
 
 /*
  * builtin API
@@ -107,6 +104,16 @@
 
 int is_builtin(const char *s);
 
+/*
+ * Builtins which do not use RUN_SETUP should never see
+ * a prefix that is not empty; use this to protect downstream
+ * code which is not prepared to call prefix_filename(), etc.
+ */
+#define BUG_ON_NON_EMPTY_PREFIX(prefix) do { \
+	if ((prefix)) \
+		BUG("unexpected prefix in builtin: %s", (prefix)); \
+} while (0)
+
 int cmd_add(int argc, const char **argv, const char *prefix);
 int cmd_am(int argc, const char **argv, const char *prefix);
 int cmd_annotate(int argc, const char **argv, const char *prefix);
@@ -204,6 +211,7 @@
 int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 int cmd_repack(int argc, const char **argv, const char *prefix);
+int cmd_replay(int argc, const char **argv, const char *prefix);
 int cmd_rerere(int argc, const char **argv, const char *prefix);
 int cmd_reset(int argc, const char **argv, const char *prefix);
 int cmd_restore(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 61dd386..ae723bc 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -4,18 +4,21 @@
  * Copyright (C) 2006 Linus Torvalds
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "advice.h"
+#include "config.h"
 #include "lockfile.h"
+#include "editor.h"
 #include "dir.h"
+#include "gettext.h"
 #include "pathspec.h"
-#include "exec-cmd.h"
-#include "cache-tree.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
 #include "diff.h"
-#include "diffcore.h"
+#include "read-cache.h"
+#include "repository.h"
 #include "revision.h"
 #include "bulk-checkin.h"
 #include "strvec.h"
@@ -33,11 +36,6 @@
 static int include_sparse;
 static const char *pathspec_from_file;
 
-struct update_callback_data {
-	int flags;
-	int add_errors;
-};
-
 static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
 {
 	int i, ret = 0;
@@ -66,95 +64,6 @@
 	return ret;
 }
 
-static int fix_unmerged_status(struct diff_filepair *p,
-			       struct update_callback_data *data)
-{
-	if (p->status != DIFF_STATUS_UNMERGED)
-		return p->status;
-	if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
-		/*
-		 * This is not an explicit add request, and the
-		 * path is missing from the working tree (deleted)
-		 */
-		return DIFF_STATUS_DELETED;
-	else
-		/*
-		 * Either an explicit add request, or path exists
-		 * in the working tree.  An attempt to explicitly
-		 * add a path that does not exist in the working tree
-		 * will be caught as an error by the caller immediately.
-		 */
-		return DIFF_STATUS_MODIFIED;
-}
-
-static void update_callback(struct diff_queue_struct *q,
-			    struct diff_options *opt UNUSED, void *cbdata)
-{
-	int i;
-	struct update_callback_data *data = cbdata;
-
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		const char *path = p->one->path;
-
-		if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
-			continue;
-
-		switch (fix_unmerged_status(p, data)) {
-		default:
-			die(_("unexpected diff status %c"), p->status);
-		case DIFF_STATUS_MODIFIED:
-		case DIFF_STATUS_TYPE_CHANGED:
-			if (add_file_to_index(&the_index, path,	data->flags)) {
-				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
-					die(_("updating files failed"));
-				data->add_errors++;
-			}
-			break;
-		case DIFF_STATUS_DELETED:
-			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
-				break;
-			if (!(data->flags & ADD_CACHE_PRETEND))
-				remove_file_from_index(&the_index, path);
-			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
-				printf(_("remove '%s'\n"), path);
-			break;
-		}
-	}
-}
-
-int add_files_to_cache(const char *prefix,
-		       const struct pathspec *pathspec, int flags)
-{
-	struct update_callback_data data;
-	struct rev_info rev;
-
-	memset(&data, 0, sizeof(data));
-	data.flags = flags;
-
-	repo_init_revisions(the_repository, &rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-	if (pathspec)
-		copy_pathspec(&rev.prune_data, pathspec);
-	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = update_callback;
-	rev.diffopt.format_callback_data = &data;
-	rev.diffopt.flags.override_submodule_config = 1;
-	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
-
-	/*
-	 * Use an ODB transaction to optimize adding multiple objects.
-	 * This function is invoked from commands other than 'add', which
-	 * may not have their own transaction active.
-	 */
-	begin_odb_transaction();
-	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	end_odb_transaction();
-
-	release_revisions(&rev);
-	return !!data.add_errors;
-}
-
 static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
 {
 	int i, retval = 0;
@@ -206,7 +115,7 @@
 	int i, ret = 0;
 	char *skip_worktree_seen = NULL;
 	struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
-	int flags = REFRESH_IGNORE_SKIP_WORKTREE |
+	unsigned int flags = REFRESH_IGNORE_SKIP_WORKTREE |
 		    (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
 
 	seen = xcalloc(pathspec->nr, 1);
@@ -270,7 +179,7 @@
 	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
 	if (repo_read_index(the_repository) < 0)
-		die(_("Could not read the index"));
+		die(_("could not read the index"));
 
 	repo_init_revisions(the_repository, &rev, prefix);
 	rev.diffopt.context = 7;
@@ -282,22 +191,21 @@
 	out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
 	rev.diffopt.file = xfdopen(out, "w");
 	rev.diffopt.close_file = 1;
-	if (run_diff_files(&rev, 0))
-		die(_("Could not write patch"));
+	run_diff_files(&rev, 0);
 
 	if (launch_editor(file, NULL, NULL))
 		die(_("editing patch failed"));
 
 	if (stat(file, &st))
-		die_errno(_("Could not stat '%s'"), file);
+		die_errno(_("could not stat '%s'"), file);
 	if (!st.st_size)
-		die(_("Empty patch. Aborted."));
+		die(_("empty patch. aborted"));
 
 	child.git_cmd = 1;
 	strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
 		     NULL);
 	if (run_command(&child))
-		die(_("Could not apply '%s'"), file);
+		die(_("could not apply '%s'"), file);
 
 	unlink(file);
 	free(file);
@@ -320,6 +228,8 @@
 
 static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
 {
+	BUG_ON_OPT_ARG(arg);
+
 	/* if we are told to ignore, we are not adding removals */
 	*(int *)opt->value = !unset ? 0 : 1;
 	return 0;
@@ -354,7 +264,8 @@
 	OPT_END(),
 };
 
-static int add_config(const char *var, const char *value, void *cb)
+static int add_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "add.ignoreerrors") ||
 	    !strcmp(var, "add.ignore-errors")) {
@@ -362,7 +273,10 @@
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char embedded_advice[] = N_(
@@ -396,9 +310,9 @@
 	strbuf_strip_suffix(&name, "/");
 
 	warning(_("adding embedded git repository: %s"), name.buf);
-	if (!adviced_on_embedded_repo &&
-	    advice_enabled(ADVICE_ADD_EMBEDDED_REPO)) {
-		advise(embedded_advice, name.buf, name.buf);
+	if (!adviced_on_embedded_repo) {
+		advise_if_enabled(ADVICE_ADD_EMBEDDED_REPO,
+				  embedded_advice, name.buf, name.buf);
 		adviced_on_embedded_repo = 1;
 	}
 
@@ -414,10 +328,8 @@
 		fprintf(stderr, _(ignore_error));
 		for (i = 0; i < dir->ignored_nr; i++)
 			fprintf(stderr, "%s\n", dir->ignored[i]->name);
-		if (advice_enabled(ADVICE_ADD_IGNORED_FILE))
-			advise(_("Use -f if you really want to add them.\n"
-				"Turn this message off by running\n"
-				"\"git config advice.addIgnoredFile false\""));
+		advise_if_enabled(ADVICE_ADD_IGNORED_FILE,
+				  _("Use -f if you really want to add them."));
 		exit_status = 1;
 	}
 
@@ -456,6 +368,7 @@
 	int add_new_files;
 	int require_pathspec;
 	char *seen = NULL;
+	char *ps_matched = NULL;
 	struct lock_file lock_file = LOCK_INIT;
 
 	git_config(add_config, NULL);
@@ -507,7 +420,7 @@
 	 * Check the "pathspec '%s' did not match any files" block
 	 * below before enabling new magic.
 	 */
-	parse_pathspec(&pathspec, PATHSPEC_ATTR,
+	parse_pathspec(&pathspec, 0,
 		       PATHSPEC_PREFER_FULL |
 		       PATHSPEC_SYMLINK_LEADING_PATH,
 		       prefix, argv);
@@ -516,7 +429,7 @@
 		if (pathspec.nr)
 			die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
-		parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
+		parse_pathspec_file(&pathspec, 0,
 				    PATHSPEC_PREFER_FULL |
 				    PATHSPEC_SYMLINK_LEADING_PATH,
 				    prefix, pathspec_from_file, pathspec_file_nul);
@@ -526,10 +439,8 @@
 
 	if (require_pathspec && pathspec.nr == 0) {
 		fprintf(stderr, _("Nothing specified, nothing added.\n"));
-		if (advice_enabled(ADVICE_ADD_EMPTY_PATHSPEC))
-			advise( _("Maybe you wanted to say 'git add .'?\n"
-				"Turn this message off by running\n"
-				"\"git config advice.addEmptyPathspec false\""));
+		advise_if_enabled(ADVICE_ADD_EMPTY_PATHSPEC,
+				  _("Maybe you wanted to say 'git add .'?"));
 		return 0;
 	}
 
@@ -587,7 +498,8 @@
 			       PATHSPEC_LITERAL |
 			       PATHSPEC_GLOB |
 			       PATHSPEC_ICASE |
-			       PATHSPEC_EXCLUDE);
+			       PATHSPEC_EXCLUDE |
+			       PATHSPEC_ATTR);
 
 		for (i = 0; i < pathspec.nr; i++) {
 			const char *path = pathspec.items[i].match;
@@ -634,10 +546,17 @@
 
 	begin_odb_transaction();
 
+	ps_matched = xcalloc(pathspec.nr, 1);
 	if (add_renormalize)
 		exit_status |= renormalize_tracked_files(&pathspec, flags);
 	else
-		exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+		exit_status |= add_files_to_cache(the_repository, prefix,
+						  &pathspec, ps_matched,
+						  include_sparse, flags);
+
+	if (take_worktree_changes && !add_renormalize && !ignore_add_errors &&
+	    report_path_error(ps_matched, &pathspec))
+		exit(128);
 
 	if (add_new_files)
 		exit_status |= add_files(&dir, flags);
@@ -649,8 +568,9 @@
 finish:
 	if (write_locked_index(&the_index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-		die(_("Unable to write new index file"));
+		die(_("unable to write new index file"));
 
+	free(ps_matched);
 	dir_clear(&dir);
 	clear_pathspec(&pathspec);
 	return exit_status;
diff --git a/builtin/am.c b/builtin/am.c
index e0848dd..022cae2 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -4,10 +4,14 @@
  * Based on git-am.sh by Junio C Hamano.
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
-#include "exec-cmd.h"
+#include "abspath.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "dir.h"
 #include "run-command.h"
@@ -19,20 +23,21 @@
 #include "refs.h"
 #include "commit.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "unpack-trees.h"
 #include "branch.h"
+#include "object-name.h"
+#include "preload-index.h"
 #include "sequencer.h"
 #include "revision.h"
 #include "merge-recursive.h"
 #include "log-tree.h"
 #include "notes-utils.h"
 #include "rerere.h"
-#include "prompt.h"
 #include "mailinfo.h"
 #include "apply.h"
 #include "string-list.h"
-#include "packfile.h"
+#include "pager.h"
+#include "path.h"
 #include "repository.h"
 #include "pretty.h"
 
@@ -83,9 +88,16 @@
 	SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
 };
 
-enum show_patch_type {
-	SHOW_PATCH_RAW = 0,
-	SHOW_PATCH_DIFF = 1,
+enum resume_type {
+	RESUME_FALSE = 0,
+	RESUME_APPLY,
+	RESUME_RESOLVED,
+	RESUME_SKIP,
+	RESUME_ABORT,
+	RESUME_QUIT,
+	RESUME_SHOW_PATCH_RAW,
+	RESUME_SHOW_PATCH_DIFF,
+	RESUME_ALLOW_EMPTY,
 };
 
 enum empty_action {
@@ -777,7 +789,7 @@
  * A split_mail_conv() callback that converts an StGit patch to an RFC2822
  * message suitable for parsing with git-mailinfo.
  */
-static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED)
 {
 	struct strbuf sb = STRBUF_INIT;
 	int subject_printed = 0;
@@ -860,7 +872,7 @@
  * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
  * message suitable for parsing with git-mailinfo.
  */
-static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED)
 {
 	struct strbuf sb = STRBUF_INIT;
 	int rc = 0;
@@ -1066,7 +1078,7 @@
 	else
 		write_state_text(state, "applying", "");
 
-	if (!get_oid("HEAD", &curr_head)) {
+	if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
 		write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
 		if (!state->rebasing)
 			update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
@@ -1109,7 +1121,7 @@
 	unlink(am_path(state, "original-commit"));
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
-	if (!get_oid("HEAD", &head))
+	if (!repo_get_oid(the_repository, "HEAD", &head))
 		write_state_text(state, "abort-safety", oid_to_hex(&head));
 	else
 		write_state_text(state, "abort-safety", "");
@@ -1138,19 +1150,23 @@
 static void NORETURN die_user_resolve(const struct am_state *state)
 {
 	if (state->resolvemsg) {
-		printf_ln("%s", state->resolvemsg);
+		advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", state->resolvemsg);
 	} else {
 		const char *cmdline = state->interactive ? "git am -i" : "git am";
+		struct strbuf sb = STRBUF_INIT;
 
-		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
-		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+		strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\".\n"), cmdline);
+		strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead.\n"), cmdline);
 
 		if (advice_enabled(ADVICE_AM_WORK_DIR) &&
 		    is_empty_or_missing_file(am_path(state, "patch")) &&
 		    !repo_index_has_changes(the_repository, NULL, NULL))
-			printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+			strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\".\n"), cmdline);
 
-		printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+		strbuf_addf(&sb, _("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+
+		advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", sb.buf);
+		strbuf_release(&sb);
 	}
 
 	exit(128);
@@ -1274,7 +1290,7 @@
 
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
-	strbuf_stripspace(&msg, 0);
+	strbuf_stripspace(&msg, NULL);
 
 	assert(!state->author_name);
 	state->author_name = strbuf_detach(&author_name, NULL);
@@ -1329,7 +1345,8 @@
 	size_t ident_len;
 	struct ident_split id;
 
-	buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+	buffer = repo_logmsg_reencode(the_repository, commit, NULL,
+				      get_commit_output_encoding());
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -1361,7 +1378,7 @@
 		die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
 	state->msg = xstrdup(msg + 2);
 	state->msg_len = strlen(state->msg);
-	unuse_commit_buffer(commit, buffer);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 /**
@@ -1402,9 +1419,9 @@
 	struct rev_info rev_info;
 	FILE *fp;
 
-	if (!get_oid("HEAD", &head)) {
+	if (!repo_get_oid(the_repository, "HEAD", &head)) {
 		struct commit *commit = lookup_commit_or_die(&head, "HEAD");
-		tree = get_commit_tree(commit);
+		tree = repo_get_commit_tree(the_repository, commit);
 	} else
 		tree = lookup_tree(the_repository,
 				   the_repository->hash_algo->empty_tree);
@@ -1420,7 +1437,7 @@
 	rev_info.diffopt.close_file = 1;
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
-	run_diff_index(&rev_info, 1);
+	run_diff_index(&rev_info, DIFF_INDEX_CACHED);
 	release_revisions(&rev_info);
 }
 
@@ -1556,7 +1573,7 @@
 	struct commit *result;
 	char *their_tree_name;
 
-	if (get_oid("HEAD", &our_tree) < 0)
+	if (repo_get_oid(the_repository, "HEAD", &our_tree) < 0)
 		oidcpy(&our_tree, the_hash_algo->empty_tree);
 
 	if (build_fake_ancestor(state, index_path))
@@ -1583,7 +1600,7 @@
 		rev_info.diffopt.filter |= diff_filter_bit('M');
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
-		run_diff_index(&rev_info, 1);
+		run_diff_index(&rev_info, DIFF_INDEX_CACHED);
 		release_revisions(&rev_info);
 	}
 
@@ -1646,7 +1663,7 @@
 	if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 
-	if (!get_oid_commit("HEAD", &parent)) {
+	if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
 		old_oid = &parent;
 		commit_list_insert(lookup_commit(the_repository, &parent),
 				   &parents);
@@ -1981,8 +1998,8 @@
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
 	opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
 	opts.fn = twoway_merge;
-	init_tree_desc(&t[0], head->buffer, head->size);
-	init_tree_desc(&t[1], remote->buffer, remote->size);
+	init_tree_desc(&t[0], &head->object.oid, head->buffer, head->size);
+	init_tree_desc(&t[1], &remote->object.oid, remote->buffer, remote->size);
 
 	if (unpack_trees(2, t, &opts)) {
 		rollback_lock_file(&lock_file);
@@ -2016,7 +2033,7 @@
 	opts.dst_index = &the_index;
 	opts.merge = 1;
 	opts.fn = oneway_merge;
-	init_tree_desc(&t[0], tree->buffer, tree->size);
+	init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
 
 	if (unpack_trees(1, t, &opts)) {
 		rollback_lock_file(&lock_file);
@@ -2088,7 +2105,7 @@
 
 	am_rerere_clear();
 
-	if (get_oid("HEAD", &head))
+	if (repo_get_oid(the_repository, "HEAD", &head))
 		oidcpy(&head, the_hash_algo->empty_tree);
 
 	if (clean_index(&head, &head))
@@ -2130,7 +2147,7 @@
 		oidclr(&abort_safety);
 	strbuf_release(&sb);
 
-	if (get_oid("HEAD", &head))
+	if (repo_get_oid(the_repository, "HEAD", &head))
 		oidclr(&head);
 
 	if (oideq(&head, &abort_safety))
@@ -2163,7 +2180,7 @@
 	if (!has_curr_head)
 		oidcpy(&curr_head, the_hash_algo->empty_tree);
 
-	has_orig_head = !get_oid("ORIG_HEAD", &orig_head);
+	has_orig_head = !repo_get_oid(the_repository, "ORIG_HEAD", &orig_head);
 	if (!has_orig_head)
 		oidcpy(&orig_head, the_hash_algo->empty_tree);
 
@@ -2181,7 +2198,7 @@
 	am_destroy(state);
 }
 
-static int show_patch(struct am_state *state, enum show_patch_type sub_mode)
+static int show_patch(struct am_state *state, enum resume_type resume_mode)
 {
 	struct strbuf sb = STRBUF_INIT;
 	const char *patch_path;
@@ -2196,11 +2213,11 @@
 		return run_command(&cmd);
 	}
 
-	switch (sub_mode) {
-	case SHOW_PATCH_RAW:
+	switch (resume_mode) {
+	case RESUME_SHOW_PATCH_RAW:
 		patch_path = am_path(state, msgnum(state));
 		break;
-	case SHOW_PATCH_DIFF:
+	case RESUME_SHOW_PATCH_DIFF:
 		patch_path = am_path(state, "patch");
 		break;
 	default:
@@ -2247,77 +2264,35 @@
 	return 0;
 }
 
-enum resume_type {
-	RESUME_FALSE = 0,
-	RESUME_APPLY,
-	RESUME_RESOLVED,
-	RESUME_SKIP,
-	RESUME_ABORT,
-	RESUME_QUIT,
-	RESUME_SHOW_PATCH,
-	RESUME_ALLOW_EMPTY,
-};
-
-struct resume_mode {
-	enum resume_type mode;
-	enum show_patch_type sub_mode;
-};
-
 static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset)
 {
 	int *opt_value = opt->value;
-	struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode);
 
+	BUG_ON_OPT_NEG(unset);
+
+	if (!arg)
+		*opt_value = opt->defval;
+	else if (!strcmp(arg, "raw"))
+		*opt_value = RESUME_SHOW_PATCH_RAW;
+	else if (!strcmp(arg, "diff"))
+		*opt_value = RESUME_SHOW_PATCH_DIFF;
 	/*
 	 * Please update $__git_showcurrentpatch in git-completion.bash
 	 * when you add new options
 	 */
-	const char *valid_modes[] = {
-		[SHOW_PATCH_DIFF] = "diff",
-		[SHOW_PATCH_RAW] = "raw"
-	};
-	int new_value = SHOW_PATCH_RAW;
-
-	BUG_ON_OPT_NEG(unset);
-
-	if (arg) {
-		for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) {
-			if (!strcmp(arg, valid_modes[new_value]))
-				break;
-		}
-		if (new_value >= ARRAY_SIZE(valid_modes))
-			return error(_("invalid value for '%s': '%s'"),
-				     "--show-current-patch", arg);
-	}
-
-	if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
-		return error(_("options '%s=%s' and '%s=%s' "
-					   "cannot be used together"),
-					 "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]);
-
-	resume->mode = RESUME_SHOW_PATCH;
-	resume->sub_mode = new_value;
+	else
+		return error(_("invalid value for '%s': '%s'"),
+			     "--show-current-patch", arg);
 	return 0;
 }
 
-static int git_am_config(const char *k, const char *v, void *cb UNUSED)
-{
-	int status;
-
-	status = git_gpg_config(k, v, NULL);
-	if (status)
-		return status;
-
-	return git_default_config(k, v, NULL);
-}
-
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
 	struct am_state state;
 	int binary = -1;
 	int keep_cr = -1;
 	int patch_format = PATCH_FORMAT_UNKNOWN;
-	struct resume_mode resume = { .mode = RESUME_FALSE };
+	enum resume_type resume_mode = RESUME_FALSE;
 	int in_progress;
 	int ret = 0;
 
@@ -2348,12 +2323,9 @@
 			N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
 		OPT_BOOL('m', "message-id", &state.message_id,
 			N_("pass -m flag to git-mailinfo")),
-		OPT_SET_INT_F(0, "keep-cr", &keep_cr,
-			N_("pass --keep-cr flag to git-mailsplit for mbox format"),
-			1, PARSE_OPT_NONEG),
-		OPT_SET_INT_F(0, "no-keep-cr", &keep_cr,
-			N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
-			0, PARSE_OPT_NONEG),
+		OPT_SET_INT(0, "keep-cr", &keep_cr,
+			    N_("pass --keep-cr flag to git-mailsplit for mbox format"),
+			    1),
 		OPT_BOOL('c', "scissors", &state.scissors,
 			N_("strip everything before a scissors line")),
 		OPT_CALLBACK_F(0, "quoted-cr", &state.quoted_cr, N_("action"),
@@ -2391,27 +2363,27 @@
 			PARSE_OPT_NOARG),
 		OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
 			N_("override error message when patch failure occurs")),
-		OPT_CMDMODE(0, "continue", &resume.mode,
+		OPT_CMDMODE(0, "continue", &resume_mode,
 			N_("continue applying patches after resolving a conflict"),
 			RESUME_RESOLVED),
-		OPT_CMDMODE('r', "resolved", &resume.mode,
+		OPT_CMDMODE('r', "resolved", &resume_mode,
 			N_("synonyms for --continue"),
 			RESUME_RESOLVED),
-		OPT_CMDMODE(0, "skip", &resume.mode,
+		OPT_CMDMODE(0, "skip", &resume_mode,
 			N_("skip the current patch"),
 			RESUME_SKIP),
-		OPT_CMDMODE(0, "abort", &resume.mode,
+		OPT_CMDMODE(0, "abort", &resume_mode,
 			N_("restore the original branch and abort the patching operation"),
 			RESUME_ABORT),
-		OPT_CMDMODE(0, "quit", &resume.mode,
+		OPT_CMDMODE(0, "quit", &resume_mode,
 			N_("abort the patching operation but keep HEAD where it is"),
 			RESUME_QUIT),
-		{ OPTION_CALLBACK, 0, "show-current-patch", &resume.mode,
+		{ OPTION_CALLBACK, 0, "show-current-patch", &resume_mode,
 		  "(diff|raw)",
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
-		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
-		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+		  parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW },
+		OPT_CMDMODE(0, "allow-empty", &resume_mode,
 			N_("record the empty patch as an empty commit"),
 			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
@@ -2423,7 +2395,7 @@
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-		OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "(stop|drop|keep)",
 		  N_("how to handle empty patches"),
 		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
@@ -2434,7 +2406,7 @@
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(usage, options);
 
-	git_config(git_am_config, NULL);
+	git_config(git_default_config, NULL);
 
 	am_state_init(&state);
 
@@ -2466,12 +2438,12 @@
 		 *    intend to feed us a patch but wanted to continue
 		 *    unattended.
 		 */
-		if (argc || (resume.mode == RESUME_FALSE && !isatty(0)))
+		if (argc || (resume_mode == RESUME_FALSE && !isatty(0)))
 			die(_("previous rebase directory %s still exists but mbox given."),
 				state.dir);
 
-		if (resume.mode == RESUME_FALSE)
-			resume.mode = RESUME_APPLY;
+		if (resume_mode == RESUME_FALSE)
+			resume_mode = RESUME_APPLY;
 
 		if (state.signoff == SIGNOFF_EXPLICIT)
 			am_append_signoff(&state);
@@ -2485,7 +2457,7 @@
 		 * stray directories.
 		 */
 		if (file_exists(state.dir) && !state.rebasing) {
-			if (resume.mode == RESUME_ABORT || resume.mode == RESUME_QUIT) {
+			if (resume_mode == RESUME_ABORT || resume_mode == RESUME_QUIT) {
 				am_destroy(&state);
 				am_state_release(&state);
 				return 0;
@@ -2496,7 +2468,7 @@
 				state.dir);
 		}
 
-		if (resume.mode)
+		if (resume_mode)
 			die(_("Resolve operation not in progress, we are not resuming."));
 
 		for (i = 0; i < argc; i++) {
@@ -2514,7 +2486,7 @@
 		strvec_clear(&paths);
 	}
 
-	switch (resume.mode) {
+	switch (resume_mode) {
 	case RESUME_FALSE:
 		am_run(&state, 0);
 		break;
@@ -2523,7 +2495,7 @@
 		break;
 	case RESUME_RESOLVED:
 	case RESUME_ALLOW_EMPTY:
-		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
+		am_resolve(&state, resume_mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
@@ -2535,8 +2507,9 @@
 		am_rerere_clear();
 		am_destroy(&state);
 		break;
-	case RESUME_SHOW_PATCH:
-		ret = show_patch(&state, resume.sub_mode);
+	case RESUME_SHOW_PATCH_RAW:
+	case RESUME_SHOW_PATCH_DIFF:
+		ret = show_patch(&state, resume_mode);
 		break;
 	default:
 		BUG("invalid resume value");
diff --git a/builtin/apply.c b/builtin/apply.c
index 555219d..861a019 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,6 +1,6 @@
-#include "cache.h"
 #include "builtin.h"
-#include "parse-options.h"
+#include "gettext.h"
+#include "repository.h"
 #include "apply.h"
 
 static const char * const apply_usage[] = {
diff --git a/builtin/archive.c b/builtin/archive.c
index f094390..15ee1ec 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -2,13 +2,13 @@
  * Copyright (c) 2006 Franck Bui-Huu
  * Copyright (c) 2006 Rene Scharfe
  */
-#include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "gettext.h"
 #include "transport.h"
 #include "parse-options.h"
 #include "pkt-line.h"
-#include "sideband.h"
+#include "repository.h"
 
 static void create_output_file(const char *output_file)
 {
@@ -81,7 +81,7 @@
 int cmd_archive(int argc, const char **argv, const char *prefix)
 {
 	const char *exec = "git-upload-archive";
-	const char *output = NULL;
+	char *output = NULL;
 	const char *remote = NULL;
 	struct option local_opts[] = {
 		OPT_FILENAME('o', "output", &output,
@@ -106,5 +106,6 @@
 
 	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
 
+	UNLEAK(output);
 	return write_archive(argc, argv, prefix, the_repository, output, 0);
 }
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 7301740..f69c3f7 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -1,17 +1,21 @@
 #include "builtin.h"
-#include "cache.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "bisect.h"
 #include "refs.h"
-#include "dir.h"
 #include "strvec.h"
 #include "run-command.h"
+#include "oid-array.h"
+#include "path.h"
 #include "prompt.h"
 #include "quote.h"
 #include "revision.h"
 
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
-static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
@@ -20,7 +24,7 @@
 static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 
 #define BUILTIN_GIT_BISECT_START_USAGE \
-	N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \
+	N_("git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]" \
 	   "    [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
 	   "    [<pathspec>...]")
 #define BUILTIN_GIT_BISECT_STATE_USAGE \
@@ -40,7 +44,7 @@
 #define BUILTIN_GIT_BISECT_LOG_USAGE \
 	"git bisect log"
 #define BUILTIN_GIT_BISECT_RUN_USAGE \
-	N_("git bisect run <cmd>...")
+	N_("git bisect run <cmd> [<arg>...]")
 
 static const char * const git_bisect_usage[] = {
 	BUILTIN_GIT_BISECT_START_USAGE,
@@ -227,24 +231,24 @@
 	struct strbuf branch = STRBUF_INIT;
 
 	if (!commit) {
-		if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) {
+		if (!strbuf_read_file(&branch, git_path_bisect_start(), 0))
 			printf(_("We are not bisecting.\n"));
-			return 0;
-		}
-		strbuf_rtrim(&branch);
+		else
+			strbuf_rtrim(&branch);
 	} else {
 		struct object_id oid;
 
-		if (get_oid_commit(commit, &oid))
+		if (repo_get_oid_commit(the_repository, commit, &oid))
 			return error(_("'%s' is not a valid commit"), commit);
 		strbuf_addstr(&branch, commit);
 	}
 
-	if (!ref_exists("BISECT_HEAD")) {
+	if (branch.len && !ref_exists("BISECT_HEAD")) {
 		struct child_process cmd = CHILD_PROCESS_INIT;
 
 		cmd.git_cmd = 1;
-		strvec_pushl(&cmd.args, "checkout", branch.buf, "--", NULL);
+		strvec_pushl(&cmd.args, "checkout", "--ignore-other-worktrees",
+				branch.buf, "--", NULL);
 		if (run_command(&cmd)) {
 			error(_("could not check out original"
 				" HEAD '%s'. Try 'git bisect"
@@ -265,7 +269,8 @@
 	struct strbuf commit_msg = STRBUF_INIT;
 	char *label = xstrfmt(fmt, state);
 
-	format_commit_message(commit, "%s", &commit_msg, &pp);
+	repo_format_commit_message(the_repository, commit, "%s", &commit_msg,
+				   &pp);
 
 	fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid),
 		commit_msg.buf);
@@ -292,7 +297,7 @@
 		goto finish;
 	}
 
-	if (get_oid(rev, &oid)) {
+	if (repo_get_oid(the_repository, rev, &oid)) {
 		res = error(_("couldn't get the oid of the rev '%s'"), rev);
 		goto finish;
 	}
@@ -567,7 +572,7 @@
 	 * sets up a revision walk.
 	 */
 	reset_revision_walk();
-	init_revisions(revs, NULL);
+	repo_init_revisions(the_repository, revs, NULL);
 	setup_revisions(0, NULL, revs, NULL);
 	for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
 	cb.object_flags = UNINTERESTING;
@@ -603,8 +608,8 @@
 
 	while ((commit = get_revision(&revs)) != NULL) {
 		strbuf_reset(&commit_name);
-		format_commit_message(commit, "%s",
-				      &commit_name, &pp);
+		repo_format_commit_message(the_repository, commit, "%s",
+					   &commit_name, &pp);
 		fprintf(fp, "# possible first %s commit: [%s] %s\n",
 			terms->term_bad, oid_to_hex(&commit->object.oid),
 			commit_name.buf);
@@ -633,7 +638,8 @@
 
 	read_ref(bad_ref, &oid);
 	commit = lookup_commit_reference_by_name(bad_ref);
-	format_commit_message(commit, "%s", &commit_name, &pp);
+	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",
 			    terms->term_bad, oid_to_hex(&commit->object.oid),
@@ -775,7 +781,7 @@
 	 */
 	head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
 	if (!head)
-		if (get_oid("HEAD", &head_oid))
+		if (repo_get_oid(the_repository, "HEAD", &head_oid))
 			return error(_("bad HEAD - I need a HEAD"));
 
 	/*
@@ -801,11 +807,11 @@
 		}
 	} else {
 		/* Get the rev from where we start. */
-		if (!get_oid(head, &head_oid) &&
+		if (!repo_get_oid(the_repository, head, &head_oid) &&
 		    !starts_with(head, "refs/heads/")) {
 			strbuf_reset(&start_head);
 			strbuf_addstr(&start_head, oid_to_hex(&head_oid));
-		} else if (!get_oid(head, &head_oid) &&
+		} else if (!repo_get_oid(the_repository, head, &head_oid) &&
 			   skip_prefix(head, "refs/heads/", &head)) {
 			strbuf_addstr(&start_head, head);
 		} else {
@@ -828,7 +834,7 @@
 		write_file(git_path_bisect_first_parent(), "\n");
 
 	if (no_checkout) {
-		if (get_oid(start_head.buf, &oid) < 0) {
+		if (repo_get_oid(the_repository, start_head.buf, &oid) < 0) {
 			res = error(_("invalid ref: '%s'"), start_head.buf);
 			goto finish;
 		}
@@ -912,7 +918,6 @@
 	const char *state;
 	int i, verify_expected = 1;
 	struct object_id oid, expected;
-	struct strbuf buf = STRBUF_INIT;
 	struct oid_array revs = OID_ARRAY_INIT;
 
 	if (!argc)
@@ -933,11 +938,12 @@
 
 	if (argc == 0) {
 		const char *head = "BISECT_HEAD";
-		enum get_oid_result res_head = get_oid(head, &oid);
+		enum get_oid_result res_head = repo_get_oid(the_repository,
+							    head, &oid);
 
 		if (res_head == MISSING_OBJECT) {
 			head = "HEAD";
-			res_head = get_oid(head, &oid);
+			res_head = repo_get_oid(the_repository, head, &oid);
 		}
 
 		if (res_head)
@@ -953,7 +959,7 @@
 	for (; argc; argc--, argv++) {
 		struct commit *commit;
 
-		if (get_oid(*argv, &oid)){
+		if (repo_get_oid(the_repository, *argv, &oid)){
 			error(_("Bad rev input: %s"), *argv);
 			oid_array_clear(&revs);
 			return BISECT_FAILED;
@@ -966,10 +972,8 @@
 		oid_array_append(&revs, &commit->object.oid);
 	}
 
-	if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
-	    get_oid_hex(buf.buf, &expected) < 0)
+	if (read_ref("BISECT_EXPECTED_REV", &expected))
 		verify_expected = 0; /* Ignore invalid file contents */
-	strbuf_release(&buf);
 
 	for (i = 0; i < revs.nr; i++) {
 		if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
@@ -978,7 +982,7 @@
 		}
 		if (verify_expected && !oideq(&revs.oid[i], &expected)) {
 			unlink_or_warn(git_path_bisect_ancestors_ok());
-			unlink_or_warn(git_path_bisect_expected_rev());
+			delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
 			verify_expected = 0;
 		}
 	}
@@ -1092,7 +1096,7 @@
 			struct rev_info revs;
 			struct commit *commit;
 
-			init_revisions(&revs, NULL);
+			repo_init_revisions(the_repository, &revs, NULL);
 			setup_revisions(2, argv + i - 1, &revs, NULL);
 
 			if (prepare_revision_walk(&revs))
diff --git a/builtin/blame.c b/builtin/blame.c
index 71f925e..db1f56d 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -5,10 +5,13 @@
  * See COPYING for licensing conditions
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "color.h"
 #include "builtin.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "commit.h"
 #include "diff.h"
@@ -22,12 +25,15 @@
 #include "userdiff.h"
 #include "line-range.h"
 #include "line-log.h"
-#include "dir.h"
 #include "progress.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
 #include "blame.h"
 #include "refs.h"
+#include "setup.h"
 #include "tag.h"
+#include "write-or-die.h"
 
 static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
 static char annotate_usage[] = N_("git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -199,13 +205,13 @@
 	const char *message;
 
 	encoding = get_log_output_encoding();
-	message = logmsg_reencode(commit, NULL, encoding);
+	message = repo_logmsg_reencode(the_repository, commit, NULL, encoding);
 	get_ac_line(message, "\nauthor ",
 		    &ret->author, &ret->author_mail,
 		    &ret->author_time, &ret->author_tz);
 
 	if (!detailed) {
-		unuse_commit_buffer(commit, message);
+		repo_unuse_commit_buffer(the_repository, commit, message);
 		return;
 	}
 
@@ -219,7 +225,7 @@
 	else
 		strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
 
-	unuse_commit_buffer(commit, message);
+	repo_unuse_commit_buffer(the_repository, commit, message);
 }
 
 /*
@@ -601,8 +607,9 @@
 
 static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
 {
-	const char *uniq = find_unique_abbrev(&suspect->commit->object.oid,
-					      auto_abbrev);
+	const char *uniq = repo_find_unique_abbrev(the_repository,
+						   &suspect->commit->object.oid,
+						   auto_abbrev);
 	int len = strlen(uniq);
 	if (auto_abbrev < len)
 		return len;
@@ -685,7 +692,8 @@
 	return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
-static int git_blame_config(const char *var, const char *value, void *cb)
+static int git_blame_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "blame.showroot")) {
 		show_root = git_config_bool(var, value);
@@ -739,6 +747,8 @@
 	}
 
 	if (!strcmp(var, "blame.coloring")) {
+		if (!value)
+			return config_error_nonbool(var);
 		if (!strcmp(value, "repeatedLines")) {
 			coloring_mode |= OUTPUT_COLOR_LINE;
 		} else if (!strcmp(value, "highlightRecent")) {
@@ -758,7 +768,7 @@
 	if (userdiff_config(var, value) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int blame_copy_callback(const struct option *option, const char *arg, int unset)
@@ -802,7 +812,7 @@
 {
 	struct object_id oid;
 
-	if (get_oid(name, &oid))
+	if (repo_get_oid(the_repository, name, &oid))
 		return 0;
 	return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
 }
@@ -845,7 +855,7 @@
 						    peel_to_commit_oid, sb);
 	}
 	for_each_string_list_item(i, ignore_rev_list) {
-		if (get_oid_committish(i->string, &oid) ||
+		if (repo_get_oid_committish(the_repository, i->string, &oid) ||
 		    peel_to_commit_oid(&oid, sb))
 			die(_("cannot find revision %s to ignore"), i->string);
 		oidset_insert(&sb->ignore_list, &oid);
diff --git a/builtin/branch.c b/builtin/branch.c
index f63fd45..dd3e3a7 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -5,24 +5,26 @@
  * Based on git-branch.sh by Junio C Hamano.
  */
 
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "color.h"
+#include "editor.h"
+#include "environment.h"
 #include "refs.h"
 #include "commit.h"
-#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "remote.h"
 #include "parse-options.h"
 #include "branch.h"
-#include "diff.h"
-#include "revision.h"
+#include "path.h"
 #include "string-list.h"
 #include "column.h"
 #include "utf8.h"
-#include "wt-status.h"
 #include "ref-filter.h"
 #include "worktree.h"
 #include "help.h"
+#include "advice.h"
 #include "commit-reach.h"
 
 static const char * const builtin_branch_usage[] = {
@@ -77,7 +79,8 @@
 
 define_list_config_array(color_branch_slots);
 
-static int git_branch_config(const char *var, const char *value, void *cb)
+static int git_branch_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -111,7 +114,10 @@
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
@@ -150,24 +156,31 @@
 	if (!reference_rev)
 		reference_rev = head_rev;
 
-	merged = reference_rev ? in_merge_bases(rev, reference_rev) : 0;
+	merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
+						     reference_rev) : 0;
+	if (merged < 0)
+		exit(128);
 
 	/*
 	 * After the safety valve is fully redefined to "check with
 	 * upstream, if any, otherwise with HEAD", we should just
-	 * return the result of the in_merge_bases() above without
+	 * return the result of the repo_in_merge_bases() above without
 	 * any of the following code, but during the transition period,
 	 * a gentle reminder is in order.
 	 */
-	if ((head_rev != reference_rev) &&
-	    (head_rev ? in_merge_bases(rev, head_rev) : 0) != merged) {
-		if (merged)
+	if (head_rev != reference_rev) {
+		int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0;
+		if (expect < 0)
+			exit(128);
+		if (expect == merged)
+			; /* okay */
+		else if (merged)
 			warning(_("deleting branch '%s' that has been merged to\n"
-				"         '%s', but not yet merged to HEAD."),
+				"         '%s', but not yet merged to HEAD"),
 				name, reference_name);
 		else
 			warning(_("not deleting branch '%s' that is not yet merged to\n"
-				"         '%s', even though it is merged to HEAD."),
+				"         '%s', even though it is merged to HEAD"),
 				name, reference_name);
 	}
 	free(reference_name_to_free);
@@ -180,13 +193,14 @@
 {
 	struct commit *rev = lookup_commit_reference(the_repository, oid);
 	if (!force && !rev) {
-		error(_("Couldn't look up commit object for '%s'"), refname);
+		error(_("couldn't look up commit object for '%s'"), refname);
 		return -1;
 	}
 	if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
-		error(_("The branch '%s' is not fully merged.\n"
-		      "If you are sure you want to delete it, "
-		      "run 'git branch -D %s'."), branchname, branchname);
+		error(_("the branch '%s' is not fully merged"), branchname);
+		advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
+				  _("If you are sure you want to delete it, "
+				  "run 'git branch -D %s'"), branchname);
 		return -1;
 	}
 	return 0;
@@ -197,7 +211,7 @@
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "branch.%s", branchname);
 	if (git_config_rename_section(buf.buf, NULL) < 0)
-		warning(_("Update of config-file failed"));
+		warning(_("update of config-file failed"));
 	strbuf_release(&buf);
 }
 
@@ -216,10 +230,11 @@
 	struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
 	struct string_list_item *item;
 	int branch_name_pos;
+	const char *fmt_remotes = "refs/remotes/%s";
 
 	switch (kinds) {
 	case FILTER_REFS_REMOTES:
-		fmt = "refs/remotes/%s";
+		fmt = fmt_remotes;
 		/* For subsequent UI messages */
 		remote_branch = 1;
 		allowed_interpret = INTERPRET_BRANCH_REMOTE;
@@ -249,8 +264,8 @@
 		if (kinds == FILTER_REFS_BRANCHES) {
 			const char *path;
 			if ((path = branch_checked_out(name))) {
-				error(_("Cannot delete branch '%s' "
-					"checked out at '%s'"),
+				error(_("cannot delete branch '%s' "
+					"used by worktree at '%s'"),
 				      bname.buf, path);
 				ret = 1;
 				continue;
@@ -263,9 +278,25 @@
 					| RESOLVE_REF_ALLOW_BAD_NAME,
 					&oid, &flags);
 		if (!target) {
-			error(remote_branch
-			      ? _("remote-tracking branch '%s' not found.")
-			      : _("branch '%s' not found."), bname.buf);
+			if (remote_branch) {
+				error(_("remote-tracking branch '%s' not found"), bname.buf);
+			} else {
+				char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
+				char *virtual_target = resolve_refdup(virtual_name,
+							RESOLVE_REF_READING
+							| RESOLVE_REF_NO_RECURSE
+							| RESOLVE_REF_ALLOW_BAD_NAME,
+							&oid, &flags);
+				FREE_AND_NULL(virtual_name);
+
+				if (virtual_target)
+					error(_("branch '%s' not found.\n"
+						"Did you forget --remote?"),
+						bname.buf);
+				else
+					error(_("branch '%s' not found"), bname.buf);
+				FREE_AND_NULL(virtual_target);
+			}
 			ret = 1;
 			continue;
 		}
@@ -280,7 +311,7 @@
 		item = string_list_append(&refs_to_delete, name);
 		item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
 				    : (flags & REF_ISSYMREF) ? target
-				    : find_unique_abbrev(&oid, DEFAULT_ABBREV));
+				    : repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
 
 	next:
 		free(target);
@@ -342,17 +373,8 @@
 	static struct strbuf buf = STRBUF_INIT;
 
 	strbuf_reset(&buf);
-	while (*s) {
-		const char *ep = strchrnul(s, '%');
-		if (s < ep)
-			strbuf_add(&buf, s, ep - s);
-		if (*ep == '%') {
-			strbuf_addstr(&buf, "%%");
-			s = ep + 1;
-		} else {
-			s = ep;
-		}
-	}
+	while (strbuf_expand_step(&buf, &s))
+		strbuf_addstr(&buf, "%%");
 	return buf.buf;
 }
 
@@ -420,8 +442,6 @@
 {
 	int i;
 	struct ref_array array;
-	struct strbuf out = STRBUF_INIT;
-	struct strbuf err = STRBUF_INIT;
 	int maxwidth = 0;
 	const char *remote_prefix = "";
 	char *to_free = NULL;
@@ -448,25 +468,30 @@
 	if (verify_ref_format(format))
 		die(_("unable to parse format string"));
 
+	filter_ahead_behind(the_repository, format, &array);
 	ref_array_sort(sorting, &array);
 
-	for (i = 0; i < array.nr; i++) {
-		strbuf_reset(&err);
-		strbuf_reset(&out);
-		if (format_ref_array_item(array.items[i], format, &out, &err))
-			die("%s", err.buf);
-		if (column_active(colopts)) {
-			assert(!filter->verbose && "--column and --verbose are incompatible");
-			 /* format to a string_list to let print_columns() do its job */
+	if (column_active(colopts)) {
+		struct strbuf out = STRBUF_INIT, err = STRBUF_INIT;
+
+		assert(!filter->verbose && "--column and --verbose are incompatible");
+
+		for (i = 0; i < array.nr; i++) {
+			strbuf_reset(&err);
+			strbuf_reset(&out);
+			if (format_ref_array_item(array.items[i], format, &out, &err))
+				die("%s", err.buf);
+
+			/* format to a string_list to let print_columns() do its job */
 			string_list_append(output, out.buf);
-		} else {
-			fwrite(out.buf, 1, out.len, stdout);
-			putchar('\n');
 		}
+
+		strbuf_release(&err);
+		strbuf_release(&out);
+	} else {
+		print_formatted_ref_array(&array, format);
 	}
 
-	strbuf_release(&err);
-	strbuf_release(&out);
 	ref_array_clear(&array);
 	free(to_free);
 }
@@ -486,9 +511,9 @@
 		die(_("HEAD (%s) points outside of refs/heads/"), refname);
 }
 
-static void reject_rebase_or_bisect_branch(const char *target)
+static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
+					   const char *target)
 {
-	struct worktree **worktrees = get_worktrees();
 	int i;
 
 	for (i = 0; worktrees[i]; i++) {
@@ -498,24 +523,57 @@
 			continue;
 
 		if (is_worktree_being_rebased(wt, target))
-			die(_("Branch %s is being rebased at %s"),
+			die(_("branch %s is being rebased at %s"),
 			    target, wt->path);
 
 		if (is_worktree_being_bisected(wt, target))
-			die(_("Branch %s is being bisected at %s"),
+			die(_("branch %s is being bisected at %s"),
 			    target, wt->path);
 	}
-
-	free_worktrees(worktrees);
 }
 
+/*
+ * Update all per-worktree HEADs pointing at the old ref to point the new ref.
+ * This will be used when renaming a branch. Returns 0 if successful, non-zero
+ * otherwise.
+ */
+static int replace_each_worktree_head_symref(struct worktree **worktrees,
+					     const char *oldref, const char *newref,
+					     const char *logmsg)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; worktrees[i]; i++) {
+		struct ref_store *refs;
+
+		if (worktrees[i]->is_detached)
+			continue;
+		if (!worktrees[i]->head_ref)
+			continue;
+		if (strcmp(oldref, worktrees[i]->head_ref))
+			continue;
+
+		refs = get_worktree_ref_store(worktrees[i]);
+		if (refs_create_symref(refs, "HEAD", newref, logmsg))
+			ret = error(_("HEAD of working tree %s is not updated"),
+				    worktrees[i]->path);
+	}
+
+	return ret;
+}
+
+#define IS_HEAD 1
+#define IS_ORPHAN 2
+
 static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
 {
 	struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
 	struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
 	const char *interpreted_oldname = NULL;
 	const char *interpreted_newname = NULL;
-	int recovery = 0;
+	int recovery = 0, oldref_usage = 0;
+	struct worktree **worktrees = get_worktrees();
 
 	if (strbuf_check_branch_ref(&oldref, oldname)) {
 		/*
@@ -524,15 +582,30 @@
 		 */
 		if (ref_exists(oldref.buf))
 			recovery = 1;
-		else
-			die(_("Invalid branch name: '%s'"), oldname);
+		else {
+			int code = die_message(_("invalid branch name: '%s'"), oldname);
+			advise_if_enabled(ADVICE_REF_SYNTAX,
+					  _("See `man git check-ref-format`"));
+			exit(code);
+		}
 	}
 
-	if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
-		if (copy && !strcmp(head, oldname))
-			die(_("No commit on branch '%s' yet."), oldname);
+	for (int i = 0; worktrees[i]; i++) {
+		struct worktree *wt = worktrees[i];
+
+		if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) {
+			oldref_usage |= IS_HEAD;
+			if (is_null_oid(&wt->head_oid))
+				oldref_usage |= IS_ORPHAN;
+			break;
+		}
+	}
+
+	if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+		if (oldref_usage & IS_HEAD)
+			die(_("no commit on branch '%s' yet"), oldname);
 		else
-			die(_("No branch named '%s'."), oldname);
+			die(_("no branch named '%s'"), oldname);
 	}
 
 	/*
@@ -544,7 +617,7 @@
 	else
 		validate_new_branchname(newname, &newref, force);
 
-	reject_rebase_or_bisect_branch(oldref.buf);
+	reject_rebase_or_bisect_branch(worktrees, oldref.buf);
 
 	if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
 	    !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
@@ -558,38 +631,39 @@
 		strbuf_addf(&logmsg, "Branch: renamed %s to %s",
 			    oldref.buf, newref.buf);
 
-	if (!copy &&
-	    (!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) &&
+	if (!copy && !(oldref_usage & IS_ORPHAN) &&
 	    rename_ref(oldref.buf, newref.buf, logmsg.buf))
-		die(_("Branch rename failed"));
+		die(_("branch rename failed"));
 	if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
-		die(_("Branch copy failed"));
+		die(_("branch copy failed"));
 
 	if (recovery) {
 		if (copy)
-			warning(_("Created a copy of a misnamed branch '%s'"),
+			warning(_("created a copy of a misnamed branch '%s'"),
 				interpreted_oldname);
 		else
-			warning(_("Renamed a misnamed branch '%s' away"),
+			warning(_("renamed a misnamed branch '%s' away"),
 				interpreted_oldname);
 	}
 
-	if (!copy &&
-	    replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
-		die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
+	if (!copy && (oldref_usage & IS_HEAD) &&
+	    replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
+					      logmsg.buf))
+		die(_("branch renamed to %s, but HEAD is not updated"), newname);
 
 	strbuf_release(&logmsg);
 
 	strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
 	strbuf_addf(&newsection, "branch.%s", interpreted_newname);
 	if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
-		die(_("Branch is renamed, but update of config-file failed"));
+		die(_("branch is renamed, but update of config-file failed"));
 	if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
-		die(_("Branch is copied, but update of config-file failed"));
+		die(_("branch is copied, but update of config-file failed"));
 	strbuf_release(&oldref);
 	strbuf_release(&newref);
 	strbuf_release(&oldsection);
 	strbuf_release(&newsection);
+	free_worktrees(worktrees);
 }
 
 static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
@@ -603,18 +677,18 @@
 	exists = !read_branch_desc(&buf, branch_name);
 	if (!buf.len || buf.buf[buf.len-1] != '\n')
 		strbuf_addch(&buf, '\n');
-	strbuf_commented_addf(&buf,
+	strbuf_commented_addf(&buf, comment_line_str,
 		    _("Please edit the description for the branch\n"
 		      "  %s\n"
-		      "Lines starting with '%c' will be stripped.\n"),
-		    branch_name, comment_line_char);
+		      "Lines starting with '%s' will be stripped.\n"),
+		    branch_name, comment_line_str);
 	write_file_buf(edit_description(), buf.buf, buf.len);
 	strbuf_reset(&buf);
 	if (launch_editor(edit_description(), &buf, NULL)) {
 		strbuf_release(&buf);
 		return -1;
 	}
-	strbuf_stripspace(&buf, 1);
+	strbuf_stripspace(&buf, comment_line_str);
 
 	strbuf_addf(&name, "branch.%s.description", branch_name);
 	if (buf.len || exists)
@@ -636,7 +710,7 @@
 	int reflog = 0, quiet = 0, icase = 0, force = 0,
 	    recurse_submodules_explicit = 0;
 	enum branch_track track;
-	struct ref_filter filter;
+	struct ref_filter filter = REF_FILTER_INIT;
 	static struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
@@ -655,8 +729,9 @@
 		OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
 		OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")),
 		OPT__COLOR(&branch_use_color, N_("use colored output")),
-		OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
-			FILTER_REFS_REMOTES),
+		OPT_SET_INT_F('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
+			      FILTER_REFS_REMOTES,
+			      PARSE_OPT_NONEG),
 		OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
 		OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
 		OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
@@ -664,12 +739,15 @@
 		OPT__ABBREV(&filter.abbrev),
 
 		OPT_GROUP(N_("Specific git-branch actions:")),
-		OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
-			FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES),
+		OPT_SET_INT_F('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
+			      FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES,
+			      PARSE_OPT_NONEG),
 		OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
 		OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
 		OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
 		OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+		OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
+			N_("do not output a newline after empty formatted refs")),
 		OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
 		OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
 		OPT_BOOL('l', "list", &list, N_("list branch names")),
@@ -692,20 +770,25 @@
 
 	setup_ref_filter_porcelain_msg();
 
-	memset(&filter, 0, sizeof(filter));
 	filter.kind = FILTER_REFS_BRANCHES;
 	filter.abbrev = -1;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_branch_usage, options);
 
+	/*
+	 * Try to set sort keys from config. If config does not set any,
+	 * fall back on default (refname) sorting.
+	 */
 	git_config(git_branch_config, &sorting_options);
+	if (!sorting_options.nr)
+		string_list_append(&sorting_options, "refname");
 
 	track = git_branch_track;
 
 	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
 	if (!head)
-		die(_("Failed to resolve HEAD as a valid ref."));
+		die(_("failed to resolve HEAD as a valid ref"));
 	if (!strcmp(head, "HEAD"))
 		filter.detached = 1;
 	else if (!skip_prefix(head, "refs/heads/", &head))
@@ -759,6 +842,8 @@
 	if (list)
 		setup_auto_pager("branch", 1);
 
+	UNLEAK(sorting_options);
+
 	if (delete) {
 		if (!argc)
 			die(_("branch name required"));
@@ -786,6 +871,7 @@
 		print_columns(&output, colopts, NULL);
 		string_list_clear(&output, 0);
 		ref_sorting_release(sorting);
+		ref_filter_clear(&filter);
 		return 0;
 	} else if (edit_description) {
 		const char *branch_name;
@@ -795,7 +881,7 @@
 
 		if (!argc) {
 			if (filter.detached)
-				die(_("Cannot give description to detached HEAD"));
+				die(_("cannot give description to detached HEAD"));
 			branch_name = head;
 		} else if (argc == 1) {
 			strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
@@ -806,9 +892,9 @@
 
 		strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
 		if (!ref_exists(branch_ref.buf))
-			error((!argc || !strcmp(head, branch_name))
-			      ? _("No commit on branch '%s' yet.")
-			      : _("No branch named '%s'."),
+			error((!argc || branch_checked_out(branch_ref.buf))
+			      ? _("no commit on branch '%s' yet")
+			      : _("no branch named '%s'"),
 			      branch_name);
 		else if (!edit_branch_description(branch_name))
 			ret = 0; /* happy */
@@ -821,8 +907,8 @@
 		if (!argc)
 			die(_("branch name required"));
 		else if ((argc == 1) && filter.detached)
-			die(copy? _("cannot copy the current branch while not on any.")
-				: _("cannot rename the current branch while not on any."));
+			die(copy? _("cannot copy the current branch while not on any")
+				: _("cannot rename the current branch while not on any"));
 		else if (argc == 1)
 			copy_or_rename_branch(head, argv[0], copy, copy + rename > 1);
 		else if (argc == 2)
@@ -845,14 +931,14 @@
 		if (!branch) {
 			if (!argc || !strcmp(argv[0], "HEAD"))
 				die(_("could not set upstream of HEAD to %s when "
-				      "it does not point to any branch."),
+				      "it does not point to any branch"),
 				    new_upstream);
 			die(_("no such branch '%s'"), argv[0]);
 		}
 
 		if (!ref_exists(branch->refname)) {
-			if (!argc || !strcmp(head, branch->name))
-				die(_("No commit on branch '%s' yet."), branch->name);
+			if (!argc || branch_checked_out(branch->refname))
+				die(_("no commit on branch '%s' yet"), branch->name);
 			die(_("branch '%s' does not exist"), branch->name);
 		}
 
@@ -875,12 +961,12 @@
 		if (!branch) {
 			if (!argc || !strcmp(argv[0], "HEAD"))
 				die(_("could not unset upstream of HEAD when "
-				      "it does not point to any branch."));
+				      "it does not point to any branch"));
 			die(_("no such branch '%s'"), argv[0]);
 		}
 
 		if (!branch_has_merge_config(branch))
-			die(_("Branch '%s' has no upstream information"), branch->name);
+			die(_("branch '%s' has no upstream information"), branch->name);
 
 		strbuf_reset(&buf);
 		strbuf_addf(&buf, "branch.%s.remote", branch->name);
@@ -894,11 +980,11 @@
 		const char *start_name = argc == 2 ? argv[1] : head;
 
 		if (filter.kind != FILTER_REFS_BRANCHES)
-			die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+			die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n"
 				  "Did you mean to use: -a|-r --list <pattern>?"));
 
 		if (track == BRANCH_TRACK_OVERRIDE)
-			die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
+			die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead"));
 
 		if (recurse_submodules) {
 			create_branches_recursively(the_repository, branch_name,
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 5bc254b..25f860a 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "editor.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "strbuf.h"
 #include "help.h"
@@ -6,7 +9,8 @@
 #include "hook.h"
 #include "hook-list.h"
 #include "diagnose.h"
-
+#include "object-file.h"
+#include "setup.h"
 
 static void get_system_info(struct strbuf *sys_info)
 {
@@ -60,7 +64,8 @@
 }
 
 static const char * const bugreport_usage[] = {
-	N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+	N_("git bugreport [(-o | --output-directory) <path>]\n"
+	   "              [(-s | --suffix) <format> | --no-suffix]\n"
 	   "              [--diagnose[=<mode>]]"),
 	NULL
 };
@@ -122,6 +127,11 @@
 	argc = parse_options(argc, argv, prefix, bugreport_options,
 			     bugreport_usage, 0);
 
+	if (argc) {
+		error(_("unknown argument `%s'"), argv[0]);
+		usage(bugreport_usage[0]);
+	}
+
 	/* Prepare the path to put the result */
 	prefixed_filename = prefix_filename(prefix,
 					    option_output ? option_output : "");
@@ -129,8 +139,11 @@
 	strbuf_complete(&report_path, '/');
 	output_path_len = report_path.len;
 
-	strbuf_addstr(&report_path, "git-bugreport-");
-	strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+	strbuf_addstr(&report_path, "git-bugreport");
+	if (option_suffix) {
+		strbuf_addch(&report_path, '-');
+		strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+	}
 	strbuf_addstr(&report_path, ".txt");
 
 	switch (safe_create_leading_directories(report_path.buf)) {
diff --git a/builtin/bundle.c b/builtin/bundle.c
index acceef6..3ad11dc 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -1,7 +1,11 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "setup.h"
 #include "strvec.h"
 #include "parse-options.h"
-#include "cache.h"
+#include "pkt-line.h"
+#include "repository.h"
 #include "bundle.h"
 
 /*
@@ -12,7 +16,7 @@
  */
 
 #define BUILTIN_BUNDLE_CREATE_USAGE \
-	N_("git bundle create [-q | --quiet | --progress | --all-progress] [--all-progress-implied]\n" \
+	N_("git bundle create [-q | --quiet | --progress]\n" \
 	   "                  [--version=<version>] <file> <git-rev-list-args>")
 #define BUILTIN_BUNDLE_VERIFY_USAGE \
 	N_("git bundle verify [-q | --quiet] <file>")
@@ -59,46 +63,41 @@
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 	if (!argc)
 		usage_msg_opt(_("need a <file> argument"), usagestr, options);
-	*bundle_file = prefix_filename(prefix, argv[0]);
+	*bundle_file = prefix_filename_except_for_dash(prefix, argv[0]);
 	return argc;
 }
 
 static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
-	int all_progress_implied = 0;
-	int progress = isatty(STDERR_FILENO);
-	struct strvec pack_opts;
+	struct strvec pack_opts = STRVEC_INIT;
 	int version = -1;
 	int ret;
 	struct option options[] = {
-		OPT_SET_INT('q', "quiet", &progress,
-			    N_("do not show progress meter"), 0),
-		OPT_SET_INT(0, "progress", &progress,
-			    N_("show progress meter"), 1),
-		OPT_SET_INT(0, "all-progress", &progress,
-			    N_("show progress meter during object writing phase"), 2),
-		OPT_BOOL(0, "all-progress-implied",
-			 &all_progress_implied,
-			 N_("similar to --all-progress when progress meter is shown")),
+		OPT_PASSTHRU_ARGV('q', "quiet", &pack_opts, NULL,
+				  N_("do not show progress meter"),
+				  PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV(0, "progress", &pack_opts, NULL,
+				  N_("show progress meter"),
+				  PARSE_OPT_NOARG),
+		OPT_PASSTHRU_ARGV(0, "all-progress", &pack_opts, NULL,
+				  N_("historical; same as --progress"),
+				  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
+		OPT_PASSTHRU_ARGV(0, "all-progress-implied", &pack_opts, NULL,
+				  N_("historical; does nothing"),
+				  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
 		OPT_INTEGER(0, "version", &version,
 			    N_("specify bundle format version")),
 		OPT_END()
 	};
 	char *bundle_file;
 
+	if (isatty(STDERR_FILENO))
+		strvec_push(&pack_opts, "--progress");
+	strvec_push(&pack_opts, "--all-progress-implied");
+
 	argc = parse_options_cmd_bundle(argc, argv, prefix,
 			builtin_bundle_create_usage, options, &bundle_file);
 	/* bundle internals use argv[1] as further parameters */
 
-	strvec_init(&pack_opts);
-	if (progress == 0)
-		strvec_push(&pack_opts, "--quiet");
-	else if (progress == 1)
-		strvec_push(&pack_opts, "--progress");
-	else if (progress == 2)
-		strvec_push(&pack_opts, "--all-progress");
-	if (progress && all_progress_implied)
-		strvec_push(&pack_opts, "--all-progress-implied");
-
 	if (!startup_info->have_repository)
 		die(_("Need a repository to create a bundle."));
 	ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version);
@@ -107,6 +106,23 @@
 	return ret;
 }
 
+/*
+ * Similar to read_bundle_header(), but handle "-" as stdin.
+ */
+static int open_bundle(const char *path, struct bundle_header *header,
+		       const char **name)
+{
+	if (!strcmp(path, "-")) {
+		if (name)
+			*name = "<stdin>";
+		return read_bundle_header_fd(0, header, "<stdin>");
+	}
+
+	if (name)
+		*name = path;
+	return read_bundle_header(path, header);
+}
+
 static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
 	struct bundle_header header = BUNDLE_HEADER_INIT;
 	int bundle_fd = -1;
@@ -118,12 +134,13 @@
 		OPT_END()
 	};
 	char *bundle_file;
+	const char *name;
 
 	argc = parse_options_cmd_bundle(argc, argv, prefix,
 			builtin_bundle_verify_usage, options, &bundle_file);
 	/* bundle internals use argv[1] as further parameters */
 
-	if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+	if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
 		ret = 1;
 		goto cleanup;
 	}
@@ -134,7 +151,7 @@
 		goto cleanup;
 	}
 
-	fprintf(stderr, _("%s is okay\n"), bundle_file);
+	fprintf(stderr, _("%s is okay\n"), name);
 	ret = 0;
 cleanup:
 	free(bundle_file);
@@ -155,7 +172,7 @@
 			builtin_bundle_list_heads_usage, options, &bundle_file);
 	/* bundle internals use argv[1] as further parameters */
 
-	if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+	if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
 		ret = 1;
 		goto cleanup;
 	}
@@ -185,7 +202,7 @@
 			builtin_bundle_unbundle_usage, options, &bundle_file);
 	/* bundle internals use argv[1] as further parameters */
 
-	if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) {
+	if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) {
 		ret = 1;
 		goto cleanup;
 	}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index cc17635..0c948f4 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -4,19 +4,26 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "convert.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "ident.h"
 #include "parse-options.h"
 #include "userdiff.h"
 #include "streaming.h"
-#include "tree-walk.h"
 #include "oid-array.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
 #include "promisor-remote.h"
 #include "mailmap.h"
+#include "write-or-die.h"
 
 enum batch_mode {
 	BATCH_MODE_CONTENTS,
@@ -32,7 +39,8 @@
 	int all_objects;
 	int unordered;
 	int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */
-	int nul_terminated;
+	char input_delim;
+	char output_delim;
 	const char *format;
 };
 
@@ -60,7 +68,7 @@
 {
 	enum object_type type;
 
-	*buf = read_object_file(oid, &type, size);
+	*buf = repo_read_object_file(the_repository, oid, &type, size);
 	if (!*buf)
 		return error(_("cannot read object %s '%s'"),
 			     oid_to_hex(oid), path);
@@ -98,7 +106,10 @@
 	struct object_info oi = OBJECT_INFO_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
-	unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE;
+	unsigned get_oid_flags =
+		GET_OID_RECORD_PATH |
+		GET_OID_ONLY_TO_DIE |
+		GET_OID_HASH_ANY;
 	const char *path = force_path;
 	const int opt_cw = (opt == 'c' || opt == 'w');
 	if (!path && opt_cw)
@@ -152,7 +163,7 @@
 		goto cleanup;
 
 	case 'e':
-		return !has_object_file(&oid);
+		return !repo_has_object_file(the_repository, &oid);
 
 	case 'w':
 
@@ -187,7 +198,8 @@
 			ret = stream_blob(&oid);
 			goto cleanup;
 		}
-		buf = read_object_file(&oid, &type, &size);
+		buf = repo_read_object_file(the_repository, &oid, &type,
+					    &size);
 		if (!buf)
 			die("Cannot read object %s", obj_name);
 
@@ -207,11 +219,18 @@
 		if (exp_type_id == OBJ_BLOB) {
 			struct object_id blob_oid;
 			if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) {
-				char *buffer = read_object_file(&oid, &type,
-								&size);
+				char *buffer = repo_read_object_file(the_repository,
+								     &oid,
+								     &type,
+								     &size);
 				const char *target;
+
+				if (!buffer)
+					die(_("unable to read %s"), oid_to_hex(&oid));
+
 				if (!skip_prefix(buffer, "object ", &target) ||
-				    get_oid_hex(target, &blob_oid))
+				    get_oid_hex_algop(target, &blob_oid,
+						      &hash_algos[oid.algo]))
 					die("%s not a valid tag", oid_to_hex(&oid));
 				free(buffer);
 			} else
@@ -295,11 +314,9 @@
 	return alen == slen && !memcmp(atom, s, alen);
 }
 
-static void expand_atom(struct strbuf *sb, const char *atom, int len,
-			void *vdata)
+static int expand_atom(struct strbuf *sb, const char *atom, int len,
+		       struct expand_data *data)
 {
-	struct expand_data *data = vdata;
-
 	if (is_atom("objectname", atom, len)) {
 		if (!data->mark_query)
 			strbuf_addstr(sb, oid_to_hex(&data->oid));
@@ -330,22 +347,24 @@
 			strbuf_addstr(sb,
 				      oid_to_hex(&data->delta_base_oid));
 	} else
-		die("unknown format element: %.*s", len, atom);
+		return 0;
+	return 1;
 }
 
-static size_t expand_format(struct strbuf *sb, const char *start, void *data)
+static void expand_format(struct strbuf *sb, const char *start,
+			  struct expand_data *data)
 {
-	const char *end;
+	while (strbuf_expand_step(sb, &start)) {
+		const char *end;
 
-	if (*start != '(')
-		return 0;
-	end = strchr(start + 1, ')');
-	if (!end)
-		die("format element '%s' does not end in ')'", start);
-
-	expand_atom(sb, start + 1, end - start - 1, data);
-
-	return end - start + 1;
+		if (skip_prefix(start, "%", &start) || *start != '(')
+			strbuf_addch(sb, '%');
+		else if ((end = strchr(start + 1, ')')) &&
+			 expand_atom(sb, start + 1, end - start - 1, data))
+			start = end + 1;
+		else
+			strbuf_expand_bad_format(start, "cat-file");
+	}
 }
 
 static void batch_write(struct batch_options *opt, const void *data, int len)
@@ -383,9 +402,10 @@
 				if (!textconv_object(the_repository,
 						     data->rest, 0100644, oid,
 						     1, &contents, &size))
-					contents = read_object_file(oid,
-								    &type,
-								    &size);
+					contents = repo_read_object_file(the_repository,
+									 oid,
+									 &type,
+									 &size);
 				if (!contents)
 					die("could not convert '%s' %s",
 					    oid_to_hex(oid), data->rest);
@@ -402,7 +422,10 @@
 		unsigned long size;
 		void *contents;
 
-		contents = read_object_file(oid, &type, &size);
+		contents = repo_read_object_file(the_repository, oid, &type,
+						 &size);
+		if (!contents)
+			die("object %s disappeared", oid_to_hex(oid));
 
 		if (use_mailmap) {
 			size_t s = size;
@@ -410,8 +433,6 @@
 			size = cast_size_t_to_ulong(s);
 		}
 
-		if (!contents)
-			die("object %s disappeared", oid_to_hex(oid));
 		if (type != data->type)
 			die("object %s changed type!?", oid_to_hex(oid));
 		if (data->info.sizep && size != data->size && !use_mailmap)
@@ -422,11 +443,12 @@
 	}
 }
 
-static void print_default_format(struct strbuf *scratch, struct expand_data *data)
+static void print_default_format(struct strbuf *scratch, struct expand_data *data,
+				 struct batch_options *opt)
 {
-	strbuf_addf(scratch, "%s %s %"PRIuMAX"\n", oid_to_hex(&data->oid),
+	strbuf_addf(scratch, "%s %s %"PRIuMAX"%c", oid_to_hex(&data->oid),
 		    type_name(data->type),
-		    (uintmax_t)data->size);
+		    (uintmax_t)data->size, opt->output_delim);
 }
 
 /*
@@ -455,8 +477,8 @@
 						       &data->oid, &data->info,
 						       OBJECT_INFO_LOOKUP_REPLACE);
 		if (ret < 0) {
-			printf("%s missing\n",
-			       obj_name ? obj_name : oid_to_hex(&data->oid));
+			printf("%s missing%c",
+			       obj_name ? obj_name : oid_to_hex(&data->oid), opt->output_delim);
 			fflush(stdout);
 			return;
 		}
@@ -467,6 +489,8 @@
 
 			buf = repo_read_object_file(the_repository, &data->oid, &data->type,
 						    &data->size);
+			if (!buf)
+				die(_("unable to read %s"), oid_to_hex(&data->oid));
 			buf = replace_idents_using_mailmap(buf, &s);
 			data->size = cast_size_t_to_ulong(s);
 
@@ -477,17 +501,17 @@
 	strbuf_reset(scratch);
 
 	if (!opt->format) {
-		print_default_format(scratch, data);
+		print_default_format(scratch, data, opt);
 	} else {
-		strbuf_expand(scratch, opt->format, expand_format, data);
-		strbuf_addch(scratch, '\n');
+		expand_format(scratch, opt->format, data);
+		strbuf_addch(scratch, opt->output_delim);
 	}
 
 	batch_write(opt, scratch->buf, scratch->len);
 
 	if (opt->batch_mode == BATCH_MODE_CONTENTS) {
 		print_object_or_die(opt, data);
-		batch_write(opt, "\n", 1);
+		batch_write(opt, &opt->output_delim, 1);
 	}
 }
 
@@ -497,7 +521,9 @@
 			     struct expand_data *data)
 {
 	struct object_context ctx;
-	int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
+	int flags =
+		GET_OID_HASH_ANY |
+		(opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0);
 	enum get_oid_result result;
 
 	result = get_oid_with_context(the_repository, obj_name,
@@ -505,22 +531,25 @@
 	if (result != FOUND) {
 		switch (result) {
 		case MISSING_OBJECT:
-			printf("%s missing\n", obj_name);
+			printf("%s missing%c", obj_name, opt->output_delim);
 			break;
 		case SHORT_NAME_AMBIGUOUS:
-			printf("%s ambiguous\n", obj_name);
+			printf("%s ambiguous%c", obj_name, opt->output_delim);
 			break;
 		case DANGLING_SYMLINK:
-			printf("dangling %"PRIuMAX"\n%s\n",
-			       (uintmax_t)strlen(obj_name), obj_name);
+			printf("dangling %"PRIuMAX"%c%s%c",
+			       (uintmax_t)strlen(obj_name),
+			       opt->output_delim, obj_name, opt->output_delim);
 			break;
 		case SYMLINK_LOOP:
-			printf("loop %"PRIuMAX"\n%s\n",
-			       (uintmax_t)strlen(obj_name), obj_name);
+			printf("loop %"PRIuMAX"%c%s%c",
+			       (uintmax_t)strlen(obj_name),
+			       opt->output_delim, obj_name, opt->output_delim);
 			break;
 		case NOT_DIR:
-			printf("notdir %"PRIuMAX"\n%s\n",
-			       (uintmax_t)strlen(obj_name), obj_name);
+			printf("notdir %"PRIuMAX"%c%s%c",
+			       (uintmax_t)strlen(obj_name),
+			       opt->output_delim, obj_name, opt->output_delim);
 			break;
 		default:
 			BUG("unknown get_sha1_with_context result %d\n",
@@ -532,9 +561,9 @@
 	}
 
 	if (ctx.mode == 0) {
-		printf("symlink %"PRIuMAX"\n%s\n",
+		printf("symlink %"PRIuMAX"%c%s%c",
 		       (uintmax_t)ctx.symlink_path.len,
-		       ctx.symlink_path.buf);
+		       opt->output_delim, ctx.symlink_path.buf, opt->output_delim);
 		fflush(stdout);
 		return;
 	}
@@ -559,7 +588,7 @@
 }
 
 static int collect_loose_object(const struct object_id *oid,
-				const char *path,
+				const char *path UNUSED,
 				void *data)
 {
 	oid_array_append(data, oid);
@@ -567,8 +596,8 @@
 }
 
 static int collect_packed_object(const struct object_id *oid,
-				 struct packed_git *pack,
-				 uint32_t pos,
+				 struct packed_git *pack UNUSED,
+				 uint32_t pos UNUSED,
 				 void *data)
 {
 	oid_array_append(data, oid);
@@ -591,7 +620,7 @@
 }
 
 static int batch_unordered_loose(const struct object_id *oid,
-				 const char *path,
+				 const char *path UNUSED,
 				 void *data)
 {
 	return batch_unordered_object(oid, NULL, 0, data);
@@ -679,20 +708,12 @@
 	struct queued_cmd *queued_cmd = NULL;
 	size_t alloc = 0, nr = 0;
 
-	while (1) {
-		int i, ret;
+	while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
+		int i;
 		const struct parse_cmd *cmd = NULL;
 		const char *p = NULL, *cmd_end;
 		struct queued_cmd call = {0};
 
-		if (opt->nul_terminated)
-			ret = strbuf_getline_nul(&input, stdin);
-		else
-			ret = strbuf_getline(&input, stdin);
-
-		if (ret)
-			break;
-
 		if (!input.len)
 			die(_("empty command in input"));
 		if (isspace(*input.buf))
@@ -762,9 +783,8 @@
 	 */
 	memset(&data, 0, sizeof(data));
 	data.mark_query = 1;
-	strbuf_expand(&output,
+	expand_format(&output,
 		      opt->format ? opt->format : DEFAULT_FORMAT,
-		      expand_format,
 		      &data);
 	data.mark_query = 0;
 	strbuf_release(&output);
@@ -787,10 +807,10 @@
 		if (!memcmp(&data.info, &empty, sizeof(empty)))
 			data.skip_object_info = 1;
 
-		if (has_promisor_remote())
+		if (repo_has_promisor_remote(the_repository))
 			warning("This repository uses promisor remotes. Some objects may not be loaded.");
 
-		read_replace_refs = 0;
+		disable_replace_refs();
 
 		cb.opt = opt;
 		cb.expand = &data;
@@ -836,16 +856,7 @@
 		goto cleanup;
 	}
 
-	while (1) {
-		int ret;
-		if (opt->nul_terminated)
-			ret = strbuf_getline_nul(&input, stdin);
-		else
-			ret = strbuf_getline(&input, stdin);
-
-		if (ret == EOF)
-			break;
-
+	while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
 		if (data.split_on_whitespace) {
 			/*
 			 * Split at first whitespace, tying off the beginning
@@ -870,12 +881,13 @@
 	return retval;
 }
 
-static int git_cat_file_config(const char *var, const char *value, void *cb)
+static int git_cat_file_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	if (userdiff_config(var, value) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int batch_option_callback(const struct option *opt,
@@ -914,16 +926,18 @@
 	const char *exp_type = NULL, *obj_name = NULL;
 	struct batch_options batch = {0};
 	int unknown_type = 0;
+	int input_nul_terminated = 0;
+	int nul_terminated = 0;
 
 	const char * const usage[] = {
 		N_("git cat-file <type> <object>"),
 		N_("git cat-file (-e | -p) <object>"),
 		N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
-		N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
-		   "             [--buffer] [--follow-symlinks] [--unordered]\n"
-		   "             [--textconv | --filters] [-z]"),
 		N_("git cat-file (--textconv | --filters)\n"
 		   "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
+		N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
+		   "             [--buffer] [--follow-symlinks] [--unordered]\n"
+		   "             [--textconv | --filters] [-Z]"),
 		NULL
 	};
 	const struct option options[] = {
@@ -950,7 +964,9 @@
 			N_("like --batch, but don't emit <contents>"),
 			PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
 			batch_option_callback),
-		OPT_BOOL('z', NULL, &batch.nul_terminated, N_("stdin is NUL-terminated")),
+		OPT_BOOL_F('z', NULL, &input_nul_terminated, N_("stdin is NUL-terminated"),
+			PARSE_OPT_HIDDEN),
+		OPT_BOOL('Z', NULL, &nul_terminated, N_("stdin and stdout is NUL-terminated")),
 		OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"),
 			N_("read commands from stdin"),
 			PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
@@ -1009,9 +1025,18 @@
 	else if (batch.all_objects)
 		usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
 			       "--batch-all-objects");
-	else if (batch.nul_terminated)
+	else if (input_nul_terminated)
 		usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
 			       "-z");
+	else if (nul_terminated)
+		usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+			       "-Z");
+
+	batch.input_delim = batch.output_delim = '\n';
+	if (input_nul_terminated)
+		batch.input_delim = '\0';
+	if (nul_terminated)
+		batch.input_delim = batch.output_delim = '\0';
 
 	/* Batch defaults */
 	if (batch.buffer_output < 0)
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index d7a40e6..c1da1d1 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,10 +1,15 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "attr.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "quote.h"
+#include "repository.h"
+#include "setup.h"
 #include "parse-options.h"
+#include "write-or-die.h"
 
 static int all_attrs;
 static int cached_attrs;
@@ -58,7 +63,7 @@
 }
 
 static void check_attr(const char *prefix, struct attr_check *check,
-		       const struct object_id *tree_oid, int collect_all,
+		       int collect_all,
 		       const char *file)
 
 {
@@ -66,9 +71,9 @@
 		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
 
 	if (collect_all) {
-		git_all_attrs(&the_index, tree_oid, full_path, check);
+		git_all_attrs(&the_index, full_path, check);
 	} else {
-		git_check_attr(&the_index, tree_oid, full_path, check);
+		git_check_attr(&the_index, full_path, check);
 	}
 	output_attr(check, file);
 
@@ -76,7 +81,7 @@
 }
 
 static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
-				   const struct object_id *tree_oid, int collect_all)
+				   int collect_all)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct strbuf unquoted = STRBUF_INIT;
@@ -90,7 +95,7 @@
 				die("line is badly quoted");
 			strbuf_swap(&buf, &unquoted);
 		}
-		check_attr(prefix, check, tree_oid, collect_all, buf.buf);
+		check_attr(prefix, check, collect_all, buf.buf);
 		maybe_flush_or_die(stdout, "attribute to stdout");
 	}
 	strbuf_release(&buf);
@@ -106,7 +111,6 @@
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
 	struct attr_check *check;
-	struct object_id *tree_oid = NULL;
 	struct object_id initialized_oid;
 	int cnt, i, doubledash, filei;
 
@@ -118,6 +122,9 @@
 	argc = parse_options(argc, argv, prefix, check_attr_options,
 			     check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
 
+	prepare_repo_settings(the_repository);
+	the_repository->settings.command_requires_full_index = 0;
+
 	if (repo_read_index(the_repository) < 0) {
 		die("invalid cache");
 	}
@@ -182,14 +189,14 @@
 	if (source) {
 		if (repo_get_oid_tree(the_repository, source, &initialized_oid))
 			die("%s: not a valid tree-ish source", source);
-		tree_oid = &initialized_oid;
+		set_git_attr_source(source);
 	}
 
 	if (stdin_paths)
-		check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
+		check_attr_stdin_paths(prefix, check, all_attrs);
 	else {
 		for (i = filei; i < argc; i++)
-			check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
+			check_attr(prefix, check, all_attrs, argv[i]);
 		maybe_flush_or_die(stdout, "attribute to stdout");
 	}
 
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index ab77606..906cd96 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,12 +1,14 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "gettext.h"
 #include "quote.h"
 #include "pathspec.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "submodule.h"
+#include "write-or-die.h"
 
 static int quiet, verbose, stdin_paths, show_non_matching, no_index;
 static const char * const check_ignore_usage[] = {
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index 7dc47e4..b8a05b8 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -1,8 +1,12 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "ident.h"
 #include "mailmap.h"
 #include "parse-options.h"
+#include "strbuf.h"
 #include "string-list.h"
+#include "write-or-die.h"
 
 static int use_stdin;
 static const char * const check_mailmap_usage[] = {
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index fd0e5f8..5eb6bdc 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -2,9 +2,9 @@
  * GIT - The information manager from hell
  */
 
-#include "cache.h"
-#include "refs.h"
 #include "builtin.h"
+#include "refs.h"
+#include "setup.h"
 #include "strbuf.h"
 
 static const char builtin_check_ref_format_usage[] =
@@ -60,6 +60,8 @@
 	char *to_free = NULL;
 	int ret = 1;
 
+	BUG_ON_NON_EMPTY_PREFIX(prefix);
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(builtin_check_ref_format_usage);
 
diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index ede7dc3..6b62b53 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -1,9 +1,11 @@
 #include "builtin.h"
 #include "config.h"
 #include "entry.h"
+#include "gettext.h"
 #include "parallel-checkout.h"
 #include "parse-options.h"
 #include "pkt-line.h"
+#include "read-cache-ll.h"
 
 static void packet_to_pc_item(const char *buffer, int len,
 			      struct parallel_checkout_item *pc_item)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index cf6fba9..2e086a2 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -7,19 +7,23 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
-#include "dir.h"
+#include "gettext.h"
 #include "lockfile.h"
 #include "quote.h"
+#include "repository.h"
 #include "cache-tree.h"
 #include "parse-options.h"
 #include "entry.h"
 #include "parallel-checkout.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "sparse-index.h"
 
 #define CHECKOUT_ALL 4
 static int nul_term_line;
 static int checkout_stage; /* default to checkout stage0 */
 static int ignore_skip_worktree; /* default to 0 */
-static int to_tempfile;
+static int to_tempfile = -1;
 static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
 
 static struct checkout state = CHECKOUT_INIT;
@@ -188,15 +192,16 @@
 static int option_parse_stage(const struct option *opt,
 			      const char *arg, int unset)
 {
+	int *stage = opt->value;
+
 	BUG_ON_OPT_NEG(unset);
 
 	if (!strcmp(arg, "all")) {
-		to_tempfile = 1;
-		checkout_stage = CHECKOUT_ALL;
+		*stage = CHECKOUT_ALL;
 	} else {
 		int ch = arg[0];
 		if ('1' <= ch && ch <= '3')
-			checkout_stage = arg[0] - '0';
+			*stage = arg[0] - '0';
 		else
 			die(_("stage should be between 1 and 3 or all"));
 	}
@@ -234,7 +239,7 @@
 			N_("write the content to temporary files")),
 		OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
 			N_("when creating files, prepend <string>")),
-		OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)",
+		OPT_CALLBACK_F(0, "stage", &checkout_stage, "(1|2|3|all)",
 			N_("copy out the files from named stage"),
 			PARSE_OPT_NONEG, option_parse_stage),
 		OPT_END()
@@ -264,6 +269,12 @@
 		state.base_dir = "";
 	state.base_dir_len = strlen(state.base_dir);
 
+	if (to_tempfile < 0)
+		to_tempfile = (checkout_stage == CHECKOUT_ALL);
+	if (!to_tempfile && checkout_stage == CHECKOUT_ALL)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--stage=all", "--no-temp");
+
 	/*
 	 * when --prefix is specified we do not want to update cache.
 	 */
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a5155cf..71e6036 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,7 +1,6 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
-#include "blob.h"
 #include "branch.h"
 #include "cache-tree.h"
 #include "checkout.h"
@@ -9,19 +8,28 @@
 #include "config.h"
 #include "diff.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "hook.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "lockfile.h"
+#include "mem-pool.h"
 #include "merge-recursive.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "refs.h"
 #include "remote.h"
 #include "resolve-undo.h"
 #include "revision.h"
-#include "run-command.h"
+#include "setup.h"
 #include "submodule.h"
-#include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
@@ -75,7 +83,7 @@
 	const char *ignore_unmerged_opt;
 	int ignore_unmerged;
 	int pathspec_file_nul;
-	const char *pathspec_from_file;
+	char *pathspec_from_file;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -83,7 +91,7 @@
 	int new_branch_log;
 	enum branch_track track;
 	struct diff_options diff_options;
-	char *conflict_style;
+	int conflict_style;
 
 	int branch_exists;
 	const char *prefix;
@@ -92,6 +100,8 @@
 	struct tree *source_tree;
 };
 
+#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 }
+
 struct branch_info {
 	char *name; /* The short name used */
 	char *path; /* The full name of a real branch */
@@ -243,7 +253,8 @@
 }
 
 static int checkout_merged(int pos, const struct checkout *state,
-			   int *nr_checkouts, struct mem_pool *ce_mem_pool)
+			   int *nr_checkouts, struct mem_pool *ce_mem_pool,
+			   int conflict_style)
 {
 	struct cache_entry *ce = the_index.cache[pos];
 	const char *path = ce->name;
@@ -254,7 +265,7 @@
 	mmbuffer_t result_buf;
 	struct object_id threeway[3];
 	unsigned mode = 0;
-	struct ll_merge_options ll_opts;
+	struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
 	int renormalize = 0;
 
 	memset(threeway, 0, sizeof(threeway));
@@ -276,9 +287,9 @@
 	read_mmblob(&ours, &threeway[1]);
 	read_mmblob(&theirs, &threeway[2]);
 
-	memset(&ll_opts, 0, sizeof(ll_opts));
 	git_config_get_bool("merge.renormalize", &renormalize);
 	ll_opts.renormalize = renormalize;
+	ll_opts.conflict_style = conflict_style;
 	merge_status = ll_merge(&result_buf, path, &ancestor, "base",
 				&ours, "ours", &theirs, "theirs",
 				state->istate, &ll_opts);
@@ -409,7 +420,8 @@
 			else if (opts->merge)
 				errs |= checkout_merged(pos, &state,
 							&nr_unmerged,
-							&ce_mem_pool);
+							&ce_mem_pool,
+							opts->conflict_style);
 			pos = skip_same_name(ce, pos) - 1;
 		}
 	}
@@ -432,8 +444,8 @@
 					      "Updated %d paths from %s",
 					      nr_checkouts),
 				   nr_checkouts,
-				   find_unique_abbrev(&opts->source_tree->object.oid,
-						      DEFAULT_ABBREV));
+				   repo_find_unique_abbrev(the_repository, &opts->source_tree->object.oid,
+							   DEFAULT_ABBREV));
 		else if (!nr_unmerged || nr_checkouts)
 			fprintf_ln(stderr, Q_("Updated %d path from the index",
 					      "Updated %d paths from the index",
@@ -489,15 +501,37 @@
 		die(_("'%s' must be used when '%s' is not specified"),
 		    "--worktree", "--source");
 
-	if (opts->checkout_index && !opts->checkout_worktree &&
-	    opts->writeout_stage)
-		die(_("'%s' or '%s' cannot be used with %s"),
-		    "--ours", "--theirs", "--staged");
+	/*
+	 * Reject --staged option to the restore command when combined with
+	 * merge-related options. Use the accept_ref flag to distinguish it
+	 * from the checkout command, which does not accept --staged anyway.
+	 *
+	 * `restore --ours|--theirs --worktree --staged` could mean resolving
+	 * conflicted paths to one side in both the worktree and the index,
+	 * but does not currently.
+	 *
+	 * `restore --merge|--conflict=<style>` already recreates conflicts
+	 * in both the worktree and the index, so adding --staged would be
+	 * meaningless.
+	 */
+	if (!opts->accept_ref && opts->checkout_index) {
+		if (opts->writeout_stage)
+			die(_("'%s' or '%s' cannot be used with %s"),
+			    "--ours", "--theirs", "--staged");
 
-	if (opts->checkout_index && !opts->checkout_worktree &&
-	    opts->merge)
-		die(_("'%s' or '%s' cannot be used with %s"),
-		    "--merge", "--conflict", "--staged");
+		if (opts->merge)
+			die(_("'%s' or '%s' cannot be used with %s"),
+			    "--merge", "--conflict", "--staged");
+	}
+
+	/*
+	 * recreating unmerged index entries and writing out data from
+	 * unmerged index entries would make no sense when checking out
+	 * of a tree-ish.
+	 */
+	if ((opts->merge || opts->writeout_stage) && opts->source_tree)
+		die(_("'%s', '%s', or '%s' cannot be used when checking out of a tree"),
+		    "--merge", "--ours", "--theirs");
 
 	if (opts->patch_mode) {
 		enum add_p_mode patch_mode;
@@ -536,6 +570,8 @@
 
 	if (opts->source_tree)
 		read_tree_some(opts->source_tree, &opts->pathspec);
+	if (opts->merge)
+		unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
 
 	ps_matched = xcalloc(opts->pathspec.nr, 1);
 
@@ -559,10 +595,6 @@
 	}
 	free(ps_matched);
 
-	/* "checkout -m path" to recreate conflicted state */
-	if (opts->merge)
-		unmerge_marked_index(&the_index);
-
 	/* Any unmerged paths? */
 	for (pos = 0; pos < the_index.cache_nr; pos++) {
 		const struct cache_entry *ce = the_index.cache[pos];
@@ -640,14 +672,16 @@
 {
 	struct strbuf sb = STRBUF_INIT;
 
-	if (!parse_commit(commit))
+	if (!repo_parse_commit(the_repository, commit))
 		pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
 	if (print_sha1_ellipsis()) {
 		fprintf(stderr, "%s %s... %s\n", msg,
-			find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+			sb.buf);
 	} else {
 		fprintf(stderr, "%s %s %s\n", msg,
-			find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf);
+			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
+			sb.buf);
 	}
 	strbuf_release(&sb);
 }
@@ -674,8 +708,9 @@
 	init_checkout_metadata(&opts.meta, info->refname,
 			       info->commit ? &info->commit->object.oid : null_oid(),
 			       NULL);
-	parse_tree(tree);
-	init_tree_desc(&tree_desc, tree->buffer, tree->size);
+	if (parse_tree(tree) < 0)
+		return 128;
+	init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size);
 	switch (unpack_trees(1, &tree_desc, &opts)) {
 	case -2:
 		*writeout_error = 1;
@@ -701,7 +736,8 @@
 	 * If this is a ref, resolve it; otherwise, look up the OID for our
 	 * expression.  Failure here is okay.
 	 */
-	if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0))
+	if (!repo_dwim_ref(the_repository, branch->name, strlen(branch->name),
+			   &branch->oid, &branch->refname, 0))
 		repo_get_oid_committish(the_repository, branch->name, &branch->oid);
 
 	strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
@@ -752,8 +788,15 @@
 		if (new_branch_info->commit)
 			BUG("'switch --orphan' should never accept a commit as starting point");
 		new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
-	} else
-		new_tree = get_commit_tree(new_branch_info->commit);
+		if (!new_tree)
+			BUG("unable to read empty tree");
+	} else {
+		new_tree = repo_get_commit_tree(the_repository,
+						new_branch_info->commit);
+		if (!new_tree)
+			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)
@@ -787,10 +830,13 @@
 			die(_("unable to parse commit %s"),
 				oid_to_hex(old_commit_oid));
 
-		init_tree_desc(&trees[0], tree->buffer, tree->size);
-		parse_tree(new_tree);
+		init_tree_desc(&trees[0], &tree->object.oid,
+			       tree->buffer, tree->size);
+		if (parse_tree(new_tree) < 0)
+			exit(128);
 		tree = new_tree;
-		init_tree_desc(&trees[1], tree->buffer, tree->size);
+		init_tree_desc(&trees[1], &tree->object.oid,
+			       tree->buffer, tree->size);
 
 		ret = unpack_trees(2, trees, &topts);
 		clear_unpack_trees_porcelain(&topts);
@@ -815,7 +861,8 @@
 			 */
 			if (!old_branch_info->commit)
 				return 1;
-			old_tree = get_commit_tree(old_branch_info->commit);
+			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 "
@@ -835,7 +882,8 @@
 			 * entries in the index.
 			 */
 
-			add_files_to_cache(NULL, NULL, 0);
+			add_files_to_cache(the_repository, NULL, NULL, NULL, 0,
+					   0);
 			init_merge_options(&o, the_repository);
 			o.verbosity = 0;
 			work = write_in_core_index_as_tree(the_repository);
@@ -854,6 +902,7 @@
 			}
 			o.branch1 = new_branch_info->name;
 			o.branch2 = "local";
+			o.conflict_style = opts->conflict_style;
 			ret = merge_trees(&o,
 					  new_tree,
 					  work,
@@ -887,7 +936,7 @@
 	struct strbuf sb = STRBUF_INIT;
 	struct branch *branch = branch_get(new_branch_info->name);
 
-	if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))
+	if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL, 1))
 		return;
 	fputs(sb.buf, stdout);
 	strbuf_release(&sb);
@@ -987,7 +1036,8 @@
 	remove_branch_state(the_repository, !opts->quiet);
 	strbuf_release(&msg);
 	if (!opts->quiet &&
-	    (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
+	    !opts->force_detach &&
+	    (new_branch_info->path || !strcmp(new_branch_info->name, "HEAD")))
 		report_tracking(new_branch_info);
 }
 
@@ -1004,7 +1054,7 @@
 	strbuf_addstr(sb, "  ");
 	strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV);
 	strbuf_addch(sb, ' ');
-	if (!parse_commit(commit))
+	if (!repo_parse_commit(the_repository, commit))
 		pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
 	strbuf_addch(sb, '\n');
 }
@@ -1060,7 +1110,7 @@
 			" git branch <new-branch-name> %s\n\n",
 			/* Give ngettext() the count */
 			lost),
-			find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+			repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
 }
 
 /*
@@ -1160,11 +1210,14 @@
 	return ret || writeout_error;
 }
 
-static int git_checkout_config(const char *var, const char *value, void *cb)
+static int git_checkout_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	struct checkout_opts *opts = cb;
 
 	if (!strcmp(var, "diff.ignoresubmodules")) {
+		if (!value)
+			return config_error_nonbool(var);
 		handle_ignore_submodules_arg(&opts->diff_options, value);
 		return 0;
 	}
@@ -1176,7 +1229,7 @@
 	if (starts_with(var, "submodule."))
 		return git_default_submodule_config(var, value, NULL);
 
-	return git_xmerge_config(var, value, NULL);
+	return git_xmerge_config(var, value, ctx, NULL);
 }
 
 static void setup_new_branch_info_and_source_tree(
@@ -1188,7 +1241,9 @@
 	struct tree **source_tree = &opts->source_tree;
 	struct object_id branch_rev;
 
-	new_branch_info->name = xstrdup(arg);
+	/* treat '@' as a shortcut for 'HEAD' */
+	new_branch_info->name = !strcmp(arg, "@") ? xstrdup("HEAD") :
+						    xstrdup(arg);
 	setup_branch_path(new_branch_info);
 
 	if (!check_refname_format(new_branch_info->path, 0) &&
@@ -1202,9 +1257,15 @@
 	if (!new_branch_info->commit) {
 		/* not a commit */
 		*source_tree = parse_tree_indirect(rev);
+		if (!*source_tree)
+			die(_("unable to read tree (%s)"), oid_to_hex(rev));
 	} else {
 		parse_commit_or_die(new_branch_info->commit);
-		*source_tree = get_commit_tree(new_branch_info->commit);
+		*source_tree = repo_get_commit_tree(the_repository,
+						    new_branch_info->commit);
+		if (!*source_tree)
+			die(_("unable to read tree (%s)"),
+			    oid_to_hex(&new_branch_info->commit->object.oid));
 	}
 }
 
@@ -1322,7 +1383,7 @@
 	if (!strcmp(arg, "-"))
 		arg = "@{-1}";
 
-	if (get_oid_mb(arg, rev)) {
+	if (repo_get_oid_mb(the_repository, arg, rev)) {
 		/*
 		 * Either case (3) or (4), with <something> not being
 		 * a commit, or an attempt to use case (1) with an
@@ -1419,7 +1480,8 @@
 	char *to_free;
 	int code;
 
-	if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) {
+	if (repo_dwim_ref(the_repository, branch_info->name,
+			  strlen(branch_info->name), &oid, &to_free, 0) == 1) {
 		const char *ref = to_free;
 
 		if (skip_prefix(ref, "refs/tags/", &ref))
@@ -1477,6 +1539,26 @@
 	wt_status_state_free_buffers(&state);
 }
 
+/*
+ * die if attempting to checkout an existing branch that is in use
+ * in another worktree, unless ignore-other-wortrees option is given.
+ * The check is bypassed when the branch is already the current one,
+ * as it will not make things any worse.
+ */
+static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
+						const char *full_ref)
+{
+	int flags;
+	char *head_ref;
+
+	if (opts->ignore_other_worktrees)
+		return;
+	head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+	if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
+		die_if_checked_out(full_ref, 1);
+	free(head_ref);
+}
+
 static int checkout_branch(struct checkout_opts *opts,
 			   struct branch_info *new_branch_info)
 {
@@ -1537,14 +1619,15 @@
 	if (!opts->can_switch_when_in_progress)
 		die_if_some_operation_in_progress();
 
-	if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
-	    !opts->ignore_other_worktrees) {
-		int flag;
-		char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
-		if (head_ref &&
-		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
-			die_if_checked_out(new_branch_info->path, 1);
-		free(head_ref);
+	/* "git checkout <branch>" */
+	if (new_branch_info->path && !opts->force_detach && !opts->new_branch)
+		die_if_switching_to_a_branch_in_use(opts, new_branch_info->path);
+
+	/* "git checkout -B <branch>" */
+	if (opts->new_branch_force) {
+		char *full_ref = xstrfmt("refs/heads/%s", opts->new_branch);
+		die_if_switching_to_a_branch_in_use(opts, full_ref);
+		free(full_ref);
 	}
 
 	if (!new_branch_info->commit && opts->new_branch) {
@@ -1558,6 +1641,24 @@
 	return switch_branches(opts, new_branch_info);
 }
 
+static int parse_opt_conflict(const struct option *o, const char *arg, int unset)
+{
+	struct checkout_opts *opts = o->value;
+
+	if (unset) {
+		opts->conflict_style = -1;
+		return 0;
+	}
+	opts->conflict_style = parse_conflict_style_name(arg);
+	if (opts->conflict_style < 0)
+		return error(_("unknown conflict style '%s'"), arg);
+	/* --conflict overrides a previous --no-merge */
+	if (!opts->merge)
+		opts->merge = -1;
+
+	return 0;
+}
+
 static struct option *add_common_options(struct checkout_opts *opts,
 					 struct option *prevopts)
 {
@@ -1568,8 +1669,9 @@
 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
 		OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
 		OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
-		OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-			   N_("conflict style (merge, diff3, or zdiff3)")),
+		OPT_CALLBACK(0, "conflict", opts, N_("style"),
+			     N_("conflict style (merge, diff3, or zdiff3)"),
+			     parse_opt_conflict),
 		OPT_END()
 	};
 	struct option *newopts = parse_options_concat(prevopts, options);
@@ -1588,7 +1690,7 @@
 			parse_opt_tracking_mode),
 		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
-		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unborn branch")),
 		OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
 			   N_("update ignored files (default)"),
 			   PARSE_OPT_NOCOMPLETE),
@@ -1628,10 +1730,11 @@
 
 static int checkout_main(int argc, const char **argv, const char *prefix,
 			 struct checkout_opts *opts, struct option *options,
-			 const char * const usagestr[],
-			 struct branch_info *new_branch_info)
+			 const char * const usagestr[])
 {
 	int parseopt_flags = 0;
+	struct branch_info new_branch_info = { 0 };
+	int ret;
 
 	opts->overwrite_ignore = 1;
 	opts->prefix = prefix;
@@ -1660,10 +1763,10 @@
 			opts->show_progress = isatty(2);
 	}
 
-	if (opts->conflict_style) {
-		opts->merge = 1; /* implied */
-		git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL);
-	}
+	/* --conflicts implies --merge */
+	if (opts->merge == -1)
+		opts->merge = opts->conflict_style >= 0;
+
 	if (opts->force) {
 		opts->discard_changes = 1;
 		opts->ignore_unmerged_opt = "--force";
@@ -1742,16 +1845,16 @@
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     new_branch_info, opts, &rev);
+					     &new_branch_info, opts, &rev);
 		argv += n;
 		argc -= n;
 	} else if (!opts->accept_ref && opts->from_treeish) {
 		struct object_id rev;
 
-		if (get_oid_mb(opts->from_treeish, &rev))
+		if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
 			die(_("could not resolve %s"), opts->from_treeish);
 
-		setup_new_branch_info_and_source_tree(new_branch_info,
+		setup_new_branch_info_and_source_tree(&new_branch_info,
 						      opts, &rev,
 						      opts->from_treeish);
 
@@ -1771,7 +1874,7 @@
 		 * Try to give more helpful suggestion.
 		 * new_branch && argc > 1 will be caught later.
 		 */
-		if (opts->new_branch && argc == 1 && !new_branch_info->commit)
+		if (opts->new_branch && argc == 1 && !new_branch_info.commit)
 			die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
 				argv[0], opts->new_branch);
 
@@ -1821,14 +1924,21 @@
 	}
 
 	if (opts->patch_mode || opts->pathspec.nr)
-		return checkout_paths(opts, new_branch_info);
+		ret = checkout_paths(opts, &new_branch_info);
 	else
-		return checkout_branch(opts, new_branch_info);
+		ret = checkout_branch(opts, &new_branch_info);
+
+	branch_info_release(&new_branch_info);
+	clear_pathspec(&opts->pathspec);
+	free(opts->pathspec_from_file);
+	free(options);
+
+	return ret;
 }
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts opts = CHECKOUT_OPTS_INIT;
 	struct option *options;
 	struct option checkout_options[] = {
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -1841,10 +1951,7 @@
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_END()
 	};
-	int ret;
-	struct branch_info new_branch_info = { 0 };
 
-	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.switch_branch_doing_nothing_is_ok = 1;
 	opts.only_merge_on_switching_branches = 0;
@@ -1872,17 +1979,13 @@
 	options = add_common_switch_branch_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
-	ret = checkout_main(argc, argv, prefix, &opts,
-			    options, checkout_usage, &new_branch_info);
-	branch_info_release(&new_branch_info);
-	clear_pathspec(&opts.pathspec);
-	FREE_AND_NULL(options);
-	return ret;
+	return checkout_main(argc, argv, prefix, &opts, options,
+			     checkout_usage);
 }
 
 int cmd_switch(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts opts = CHECKOUT_OPTS_INIT;
 	struct option *options = NULL;
 	struct option switch_options[] = {
 		OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
@@ -1895,10 +1998,7 @@
 			 N_("throw away local modifications")),
 		OPT_END()
 	};
-	int ret;
-	struct branch_info new_branch_info = { 0 };
 
-	memset(&opts, 0, sizeof(opts));
 	opts.dwim_new_local_branch = 1;
 	opts.accept_ref = 1;
 	opts.accept_pathspec = 0;
@@ -1915,16 +2015,13 @@
 
 	cb_option = 'c';
 
-	ret = checkout_main(argc, argv, prefix, &opts,
-			    options, switch_branch_usage, &new_branch_info);
-	branch_info_release(&new_branch_info);
-	FREE_AND_NULL(options);
-	return ret;
+	return checkout_main(argc, argv, prefix, &opts, options,
+			     switch_branch_usage);
 }
 
 int cmd_restore(int argc, const char **argv, const char *prefix)
 {
-	struct checkout_opts opts;
+	struct checkout_opts opts = CHECKOUT_OPTS_INIT;
 	struct option *options;
 	struct option restore_options[] = {
 		OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
@@ -1938,10 +2035,7 @@
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
 		OPT_END()
 	};
-	int ret;
-	struct branch_info new_branch_info = { 0 };
 
-	memset(&opts, 0, sizeof(opts));
 	opts.accept_ref = 0;
 	opts.accept_pathspec = 1;
 	opts.empty_pathspec_ok = 0;
@@ -1954,9 +2048,6 @@
 	options = add_common_options(&opts, options);
 	options = add_checkout_path_options(&opts, options);
 
-	ret = checkout_main(argc, argv, prefix, &opts,
-			    options, restore_usage, &new_branch_info);
-	branch_info_release(&new_branch_info);
-	FREE_AND_NULL(options);
-	return ret;
+	return checkout_main(argc, argv, prefix, &opts, options,
+			     restore_usage);
 }
diff --git a/builtin/clean.c b/builtin/clean.c
index 10aaa8c..29efe84 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -8,10 +8,15 @@
 
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
 #include "config.h"
 #include "dir.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "string-list.h"
 #include "quote.h"
 #include "column.h"
@@ -20,7 +25,7 @@
 #include "help.h"
 #include "prompt.h"
 
-static int force = -1; /* unset */
+static int require_force = -1; /* unset */
 static int interactive;
 static struct string_list del_list = STRING_LIST_INIT_DUP;
 static unsigned int colopts;
@@ -99,7 +104,8 @@
 
 define_list_config_array(color_interactive_slots);
 
-static int git_clean_config(const char *var, const char *value, void *cb)
+static int git_clean_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -122,12 +128,14 @@
 	}
 
 	if (!strcmp(var, "clean.requireforce")) {
-		force = !git_config_bool(var, value);
+		require_force = git_config_bool(var, value);
 		return 0;
 	}
 
-	/* inspect the color.ui config variable and others */
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, ctx, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
@@ -912,7 +920,7 @@
 {
 	int i, res;
 	int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
-	int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
+	int ignored_only = 0, force = 0, errors = 0, gone = 1;
 	int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
 	struct strbuf abs_path = STRBUF_INIT;
 	struct dir_struct dir = DIR_INIT;
@@ -938,22 +946,12 @@
 	};
 
 	git_config(git_clean_config, NULL);
-	if (force < 0)
-		force = 0;
-	else
-		config_set = 1;
 
 	argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
 			     0);
 
-	if (!interactive && !dry_run && !force) {
-		if (config_set)
-			die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
-				  "refusing to clean"));
-		else
-			die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
-				  " refusing to clean"));
-	}
+	if (require_force != 0 && !force && !interactive && !dry_run)
+		die(_("clean.requireForce is true and -f not given: refusing to clean"));
 
 	if (force > 1)
 		rm_flags = 0;
@@ -963,7 +961,7 @@
 	dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
 	if (ignored && ignored_only)
-		die(_("-x and -X cannot be used together"));
+		die(_("options '%s' and '%s' cannot be used together"), "-x", "-X");
 	if (!ignored)
 		setup_standard_excludes(&dir);
 	if (ignored_only)
diff --git a/builtin/clone.c b/builtin/clone.c
index 65b5b7d..74ec145 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -10,13 +10,19 @@
 
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "parse-options.h"
-#include "fetch-pack.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
@@ -29,8 +35,11 @@
 #include "branch.h"
 #include "remote.h"
 #include "run-command.h"
+#include "setup.h"
 #include "connected.h"
 #include "packfile.h"
+#include "path.h"
+#include "pkt-line.h"
 #include "list-objects-filter-options.h"
 #include "hook.h"
 #include "bundle.h"
@@ -62,6 +71,7 @@
 static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
+static const char *ref_format;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
@@ -106,7 +116,7 @@
 	OPT_HIDDEN_BOOL(0, "naked", &option_bare,
 			N_("create a bare repository")),
 	OPT_BOOL(0, "mirror", &option_mirror,
-		 N_("create a mirror repository (implies bare)")),
+		 N_("create a mirror repository (implies --bare)")),
 	OPT_BOOL('l', "local", &option_local,
 		N_("to clone from a local repository")),
 	OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks,
@@ -147,14 +157,13 @@
 		    N_("any cloned submodules will be shallow")),
 	OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
 		   N_("separate git dir from working tree")),
+	OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+		   N_("specify the reference format to use")),
 	OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
 			N_("set config inside the new repository")),
 	OPT_STRING_LIST(0, "server-option", &server_options,
 			N_("server-specific"), N_("option to transmit")),
-	OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
-			TRANSPORT_FAMILY_IPV4),
-	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
-			TRANSPORT_FAMILY_IPV6),
+	OPT_IPVERSION(&family),
 	OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 	OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules,
 		    N_("apply partial clone filters to submodules")),
@@ -326,8 +335,18 @@
 
 	iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
 
-	if (!iter)
+	if (!iter) {
+		if (errno == ENOTDIR) {
+			int saved_errno = errno;
+			struct stat st;
+
+			if (!lstat(src->buf, &st) && S_ISLNK(st.st_mode))
+				die(_("'%s' is a symlink, refusing to clone with --local"),
+				    src->buf);
+			errno = saved_errno;
+		}
 		die_errno(_("failed to start iterator over '%s'"), src->buf);
+	}
 
 	strbuf_addch(src, '/');
 	src_len = src->len;
@@ -547,9 +566,9 @@
 			continue;
 		if (ends_with(ref->name, "^{}"))
 			continue;
-		if (!has_object_file_with_flags(&ref->old_oid,
-						OBJECT_INFO_QUICK |
-						OBJECT_INFO_SKIP_FETCH_OBJECT))
+		if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid,
+						     OBJECT_INFO_QUICK |
+						     OBJECT_INFO_SKIP_FETCH_OBJECT))
 			continue;
 		update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
 			   UPDATE_REFS_DIE_ON_ERR);
@@ -719,8 +738,9 @@
 	tree = parse_tree_indirect(&oid);
 	if (!tree)
 		die(_("unable to parse commit %s"), oid_to_hex(&oid));
-	parse_tree(tree);
-	init_tree_desc(&t, tree->buffer, tree->size);
+	if (parse_tree(tree) < 0)
+		exit(128);
+	init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
 	if (unpack_trees(1, &t, &opts) < 0)
 		die(_("unable to checkout working tree"));
 
@@ -770,9 +790,12 @@
 	return err;
 }
 
-static int git_clone_config(const char *k, const char *v, void *cb)
+static int git_clone_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "clone.defaultremotename")) {
+		if (!v)
+			return config_error_nonbool(k);
 		free(remote_name);
 		remote_name = xstrdup(v);
 	}
@@ -781,17 +804,19 @@
 	if (!strcmp(k, "clone.filtersubmodules"))
 		config_filter_submodules = git_config_bool(k, v);
 
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
-static int write_one_config(const char *key, const char *value, void *data)
+static int write_one_config(const char *key, const char *value,
+			    const struct config_context *ctx,
+			    void *data)
 {
 	/*
 	 * give git_clone_config a chance to write config values back to the
 	 * environment, since git_config_set_multivar_gently only deals with
 	 * config-file writes
 	 */
-	int apply_failed = git_clone_config(key, value, data);
+	int apply_failed = git_clone_config(key, value, ctx, data);
 	if (apply_failed)
 		return apply_failed;
 
@@ -902,6 +927,7 @@
 	struct ref *mapped_refs = NULL;
 	const struct ref *ref;
 	struct strbuf key = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
 	const char *src_ref_prefix = "refs/heads/";
@@ -909,6 +935,9 @@
 	int err = 0, complete_refs_before_fetch = 1;
 	int submodule_progress;
 	int filter_submodules = 0;
+	int hash_algo;
+	unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+	const int do_not_override_repo_unix_permissions = -1;
 
 	struct transport_ls_refs_options transport_ls_refs_options =
 		TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -933,6 +962,12 @@
 	if (option_single_branch == -1)
 		option_single_branch = deepen ? 1 : 0;
 
+	if (ref_format) {
+		ref_storage_format = ref_storage_format_by_name(ref_format);
+		if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+			die(_("unknown ref storage format '%s'"), ref_format);
+	}
+
 	if (option_mirror)
 		option_bare = 1;
 
@@ -943,7 +978,9 @@
 	}
 
 	if (bundle_uri && deepen)
-		die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude"));
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--bundle-uri",
+		    "--depth/--shallow-since/--shallow-exclude");
 
 	repo_name = argv[0];
 
@@ -1075,8 +1112,15 @@
 		}
 	}
 
-	init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
-		INIT_DB_QUIET);
+	/*
+	 * Initialize the repository, but skip initializing the reference
+	 * database. We do not yet know about the object format of the
+	 * 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,
+		ref_storage_format, NULL,
+		do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
 
 	if (real_git_dir) {
 		free((char *)git_dir);
@@ -1084,6 +1128,50 @@
 	}
 
 	/*
+	 * We have a chicken-and-egg situation between initializing the refdb
+	 * and spawning transport helpers:
+	 *
+	 *   - Initializing the refdb requires us to know about the object
+	 *     format. We thus have to spawn the transport helper to learn
+	 *     about it.
+	 *
+	 *   - The transport helper may want to access the Git repository. But
+	 *     because the refdb has not been initialized, we don't have "HEAD"
+	 *     or "refs/". Thus, the helper cannot find the Git repository.
+	 *
+	 * Ideally, we would have structured the helper protocol such that it's
+	 * mandatory for the helper to first announce its capabilities without
+	 * yet assuming a fully initialized repository. Like that, we could
+	 * have added a "lazy-refdb-init" capability that announces whether the
+	 * helper is ready to handle not-yet-initialized refdbs. If any helper
+	 * didn't support them, we would have fully initialized the refdb with
+	 * the SHA1 object format, but later on bailed out if we found out that
+	 * the remote repository used a different object format.
+	 *
+	 * But we didn't, and thus we use the following workaround to partially
+	 * initialize the repository's refdb such that it can be discovered by
+	 * Git commands. To do so, we:
+	 *
+	 *   - Create an invalid HEAD ref pointing at "refs/heads/.invalid".
+	 *
+	 *   - Create the "refs/" directory.
+	 *
+	 *   - Set up the ref storage format and repository version as
+	 *     required.
+	 *
+	 * This is sufficient for Git commands to discover the Git directory.
+	 */
+	initialize_repository_version(GIT_HASH_UNKNOWN,
+				      the_repository->ref_storage_format, 1);
+
+	strbuf_addf(&buf, "%s/HEAD", git_dir);
+	write_file(buf.buf, "ref: refs/heads/.invalid");
+
+	strbuf_reset(&buf);
+	strbuf_addf(&buf, "%s/refs", git_dir);
+	safe_create_dir(buf.buf, 1);
+
+	/*
 	 * additional config can be injected with -c, make sure it's included
 	 * after init_db, which clears the entire config environment.
 	 */
@@ -1163,10 +1251,7 @@
 	if (option_required_reference.nr || option_optional_reference.nr)
 		setup_reference();
 
-	if (option_sparse_checkout && git_sparse_checkout_init(dir))
-		return 1;
-
-	remote = remote_get(remote_name);
+	remote = remote_get_early(remote_name);
 
 	refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
 			branch_top.buf);
@@ -1244,6 +1329,27 @@
 	if (transport->smart_options && !deepen && !filter_options.choice)
 		transport->smart_options->check_self_contained_and_connected = 1;
 
+	strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+	refspec_ref_prefixes(&remote->fetch,
+			     &transport_ls_refs_options.ref_prefixes);
+	if (option_branch)
+		expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
+				  option_branch);
+	if (!option_no_tags)
+		strvec_push(&transport_ls_refs_options.ref_prefixes,
+			    "refs/tags/");
+
+	refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
+
+	/*
+	 * Now that we know what algorithm the remote side is using, let's set
+	 * 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);
+	repo_set_hash_algo(the_repository, hash_algo);
+	create_reference_database(the_repository->ref_storage_format, NULL, 1);
+
 	/*
 	 * Before fetching from the remote, download and install bundle
 	 * data from the --bundle-uri option.
@@ -1259,24 +1365,7 @@
 				bundle_uri);
 		else if (has_heuristic)
 			git_config_set_gently("fetch.bundleuri", bundle_uri);
-	}
-
-	strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
-	refspec_ref_prefixes(&remote->fetch,
-			     &transport_ls_refs_options.ref_prefixes);
-	if (option_branch)
-		expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
-				  option_branch);
-	if (!option_no_tags)
-		strvec_push(&transport_ls_refs_options.ref_prefixes,
-			    "refs/tags/");
-
-	refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
-
-	if (refs)
-		mapped_refs = wanted_peer_refs(refs, &remote->fetch);
-
-	if (!bundle_uri) {
+	} else {
 		/*
 		* Populate transport->got_remote_bundle_uri and
 		* transport->bundle_uri. We might get nothing.
@@ -1297,15 +1386,10 @@
 		}
 	}
 
-	if (mapped_refs) {
-		int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+	if (refs)
+		mapped_refs = wanted_peer_refs(refs, &remote->fetch);
 
-		/*
-		 * Now that we know what algorithm the remote side is using,
-		 * let's set ours to the same thing.
-		 */
-		initialize_repository_version(hash_algo, 1);
-		repo_set_hash_algo(the_repository, hash_algo);
+	if (mapped_refs) {
 		/*
 		 * transport_get_remote_refs() may return refs with null sha-1
 		 * in mapped_refs (see struct transport->get_refs_list
@@ -1406,12 +1490,16 @@
 		dissociate_from_references();
 	}
 
+	if (option_sparse_checkout && git_sparse_checkout_init(dir))
+		return 1;
+
 	junk_mode = JUNK_LEAVE_REPO;
 	err = checkout(submodule_progress, filter_submodules);
 
 	free(remote_name);
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_top);
+	strbuf_release(&buf);
 	strbuf_release(&key);
 	free_refs(mapped_refs);
 	free_refs(remote_head_points_at);
diff --git a/builtin/column.c b/builtin/column.c
index 158fdf5..10ff7e0 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -1,6 +1,6 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "gettext.h"
 #include "strbuf.h"
 #include "parse-options.h"
 #include "string-list.h"
@@ -12,7 +12,8 @@
 };
 static unsigned int colopts;
 
-static int column_config(const char *var, const char *value, void *cb)
+static int column_config(const char *var, const char *value,
+			 const struct config_context *ctx UNUSED, void *cb)
 {
 	return git_column_config(var, value, cb, &colopts);
 }
@@ -44,6 +45,8 @@
 	memset(&copts, 0, sizeof(copts));
 	copts.padding = 1;
 	argc = parse_options(argc, argv, prefix, options, builtin_column_usage, 0);
+	if (copts.padding < 0)
+		die(_("%s must be non-negative"), "--padding");
 	if (argc)
 		usage_with_options(builtin_column_usage, options);
 	if (real_command || command) {
@@ -55,5 +58,7 @@
 		string_list_append(&list, sb.buf);
 
 	print_columns(&list, colopts, &copts);
+	strbuf_release(&sb);
+	string_list_clear(&list, 0);
 	return 0;
 }
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 93704f9..7102ee9 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -1,13 +1,18 @@
 #include "builtin.h"
+#include "commit.h"
 #include "config.h"
-#include "dir.h"
-#include "lockfile.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "commit-graph.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "progress.h"
+#include "replace-object.h"
+#include "strbuf.h"
 #include "tag.h"
+#include "trace2.h"
 
 #define BUILTIN_COMMIT_GRAPH_VERIFY_USAGE \
 	N_("git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]")
@@ -16,7 +21,7 @@
 	N_("git commit-graph write [--object-dir <dir>] [--append]\n" \
 	   "                       [--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]\n" \
 	   "                       [--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]\n" \
-	   "                       <split options>")
+	   "                       <split-options>")
 
 static const char * builtin_commit_graph_verify_usage[] = {
 	BUILTIN_COMMIT_GRAPH_VERIFY_USAGE,
@@ -63,10 +68,12 @@
 	struct commit_graph *graph = NULL;
 	struct object_directory *odb = NULL;
 	char *graph_name;
-	int open_ok;
+	char *chain_name;
+	enum { OPENED_NONE, OPENED_GRAPH, OPENED_CHAIN } opened = OPENED_NONE;
 	int fd;
 	struct stat st;
 	int flags = 0;
+	int incomplete_chain = 0;
 	int ret;
 
 	static struct option builtin_commit_graph_verify_options[] = {
@@ -96,24 +103,39 @@
 
 	odb = find_odb(the_repository, opts.obj_dir);
 	graph_name = get_commit_graph_filename(odb);
-	open_ok = open_commit_graph(graph_name, &fd, &st);
-	if (!open_ok && errno != ENOENT)
+	chain_name = get_commit_graph_chain_filename(odb);
+	if (open_commit_graph(graph_name, &fd, &st))
+		opened = OPENED_GRAPH;
+	else if (errno != ENOENT)
 		die_errno(_("Could not open commit-graph '%s'"), graph_name);
+	else if (open_commit_graph_chain(chain_name, &fd, &st))
+		opened = OPENED_CHAIN;
+	else if (errno != ENOENT)
+		die_errno(_("could not open commit-graph chain '%s'"), chain_name);
 
 	FREE_AND_NULL(graph_name);
+	FREE_AND_NULL(chain_name);
 	FREE_AND_NULL(options);
 
-	if (open_ok)
+	if (opened == OPENED_NONE)
+		return 0;
+	else if (opened == OPENED_GRAPH)
 		graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb);
 	else
-		graph = read_commit_graph_one(the_repository, odb);
+		graph = load_commit_graph_chain_fd_st(the_repository, fd, &st,
+						      &incomplete_chain);
 
-	/* Return failure if open_ok predicted success */
 	if (!graph)
-		return !!open_ok;
+		return 1;
 
 	ret = verify_commit_graph(the_repository, graph, flags);
 	free_commit_graph(graph);
+
+	if (incomplete_chain) {
+		error("one or more commit-graph chain files could not be loaded");
+		ret |= 1;
+	}
+
 	return ret;
 }
 
@@ -181,10 +203,11 @@
 }
 
 static int git_commit_graph_write_config(const char *var, const char *value,
+					 const struct config_context *ctx,
 					 void *cb UNUSED)
 {
 	if (!strcmp(var, "commitgraph.maxnewfilters"))
-		write_opts.max_new_filters = git_config_int(var, value);
+		write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
 	/*
 	 * No need to fall-back to 'git_default_config', since this was already
 	 * called in 'cmd_commit_graph()'.
@@ -304,6 +327,7 @@
 	FREE_AND_NULL(options);
 	string_list_clear(&pack_indexes, 0);
 	strbuf_release(&buf);
+	oidset_clear(&commits);
 	return result;
 }
 
@@ -319,7 +343,7 @@
 
 	git_config(git_default_config, NULL);
 
-	read_replace_refs = 0;
+	disable_replace_refs();
 	save_commit_buffer = 0;
 
 	argc = parse_options(argc, argv, prefix, options,
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index cc8d584..1bb7819 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -3,15 +3,14 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "repository.h"
 #include "commit.h"
-#include "tree.h"
-#include "builtin.h"
-#include "utf8.h"
-#include "gpg-interface.h"
 #include "parse-options.h"
 
 static const char * const commit_tree_usage[] = {
@@ -37,14 +36,6 @@
 	commit_list_insert(parent, parents_p);
 }
 
-static int commit_tree_config(const char *var, const char *value, void *cb)
-{
-	int status = git_gpg_config(var, value, NULL);
-	if (status)
-		return status;
-	return git_default_config(var, value, cb);
-}
-
 static int parse_parent_arg_callback(const struct option *opt,
 		const char *arg, int unset)
 {
@@ -53,7 +44,7 @@
 
 	BUG_ON_OPT_NEG_NOARG(unset, arg);
 
-	if (get_oid_commit(arg, &oid))
+	if (repo_get_oid_commit(the_repository, arg, &oid))
 		die(_("not a valid object name %s"), arg);
 
 	assert_oid_type(&oid, OBJ_COMMIT);
@@ -121,7 +112,7 @@
 		OPT_END()
 	};
 
-	git_config(commit_tree_config, NULL);
+	git_config(git_default_config, NULL);
 
 	if (argc < 2 || !strcmp(argv[1], "-h"))
 		usage_with_options(commit_tree_usage, options);
@@ -131,7 +122,7 @@
 	if (argc != 1)
 		die(_("must give exactly one tree"));
 
-	if (get_oid_tree(argv[0], &tree_oid))
+	if (repo_get_oid_tree(the_repository, argv[0], &tree_oid))
 		die(_("not a valid object name %s"), argv[0]);
 
 	if (!buffer.len) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 985a044..6e14844 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -6,33 +6,33 @@
  */
 
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
 #include "config.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "color.h"
 #include "dir.h"
-#include "builtin.h"
+#include "editor.h"
+#include "environment.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "commit.h"
+#include "gettext.h"
 #include "revision.h"
 #include "wt-status.h"
 #include "run-command.h"
-#include "hook.h"
-#include "refs.h"
-#include "log-tree.h"
 #include "strbuf.h"
-#include "utf8.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "unpack-trees.h"
-#include "quote.h"
-#include "submodule.h"
-#include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
+#include "sparse-index.h"
 #include "mailmap.h"
 #include "help.h"
 #include "commit-reach.h"
@@ -331,8 +331,9 @@
 	tree = parse_tree_indirect(&current_head->object.oid);
 	if (!tree)
 		die(_("failed to unpack HEAD tree object"));
-	parse_tree(tree);
-	init_tree_desc(&t, tree->buffer, tree->size);
+	if (parse_tree(tree) < 0)
+		exit(128);
+	init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
 	if (unpack_trees(1, &t, &opts))
 		exit(128); /* We've already reported the error, finish dying */
 }
@@ -440,15 +441,21 @@
 	 * (B) on failure, rollback the real index.
 	 */
 	if (all || (also && pathspec.nr)) {
+		char *ps_matched = xcalloc(pathspec.nr, 1);
 		repo_hold_locked_index(the_repository, &index_lock,
 				       LOCK_DIE_ON_ERROR);
-		add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+		add_files_to_cache(the_repository, also ? prefix : NULL,
+				   &pathspec, ps_matched, 0, 0);
+		if (!all && report_path_error(ps_matched, &pathspec))
+			exit(128);
+
 		refresh_cache_or_die(refresh_flags);
 		cache_tree_update(&the_index, WRITE_TREE_SILENT);
 		if (write_locked_index(&the_index, &index_lock, 0))
-			die(_("unable to write new_index file"));
+			die(_("unable to write new index file"));
 		commit_style = COMMIT_NORMAL;
 		ret = get_lock_file_path(&index_lock);
+		free(ps_matched);
 		goto out;
 	}
 
@@ -470,7 +477,7 @@
 			cache_tree_update(&the_index, WRITE_TREE_SILENT);
 		if (write_locked_index(&the_index, &index_lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-			die(_("unable to write new_index file"));
+			die(_("unable to write new index file"));
 		commit_style = COMMIT_AS_IS;
 		ret = get_index_file();
 		goto out;
@@ -518,7 +525,7 @@
 	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
 	cache_tree_update(&the_index, WRITE_TREE_SILENT);
 	if (write_locked_index(&the_index, &index_lock, 0))
-		die(_("unable to write new_index file"));
+		die(_("unable to write new index file"));
 
 	hold_lock_file_for_update(&false_lock,
 				  git_path("next-index-%"PRIuMAX,
@@ -557,7 +564,7 @@
 	s->index_file = index_file;
 	s->fp = fp;
 	s->nowarn = nowarn;
-	s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
+	s->is_initial = repo_get_oid(the_repository, s->reference, &oid) ? 1 : 0;
 	if (!s->is_initial)
 		oidcpy(&s->oid_commit, &oid);
 	s->status_format = status_format;
@@ -683,9 +690,10 @@
 	char *candidate;
 	const char *p;
 
-	comment_line_char = candidates[0];
-	if (!memchr(sb->buf, comment_line_char, sb->len))
+	if (!memchr(sb->buf, candidates[0], sb->len)) {
+		comment_line_str = xstrfmt("%c", candidates[0]);
 		return;
+	}
 
 	p = sb->buf;
 	candidate = strchr(candidates, *p);
@@ -704,7 +712,7 @@
 	if (!*p)
 		die(_("unable to select a comment character that is not used\n"
 		      "in the current commit message"));
-	comment_line_char = *p;
+	comment_line_str = xstrfmt("%c", *p);
 }
 
 static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
@@ -712,15 +720,15 @@
 {
 	const char *buffer, *subject, *fmt;
 
-	buffer = get_commit_buffer(commit, NULL);
+	buffer = repo_get_commit_buffer(the_repository, commit, NULL);
 	find_commit_subject(buffer, &subject);
 	/*
 	 * If we amend the 'amend!' commit then we don't want to
 	 * duplicate the subject line.
 	 */
 	fmt = starts_with(subject, "amend!") ? "%b" : "%B";
-	format_commit_message(commit, fmt, sb, ctx);
-	unuse_commit_buffer(commit, buffer);
+	repo_format_commit_message(the_repository, commit, fmt, sb, ctx);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 static int prepare_to_commit(const char *index_file, const char *prefix,
@@ -736,7 +744,6 @@
 	const char *hook_arg2 = NULL;
 	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
 	int old_display_comment_prefix;
-	int merge_contains_scissors = 0;
 	int invoked_hook;
 
 	/* This checks and barfs if author is badly specified */
@@ -758,10 +765,11 @@
 			struct commit *c;
 			c = lookup_commit_reference_by_name(squash_message);
 			if (!c)
-				die(_("could not lookup commit %s"), squash_message);
+				die(_("could not lookup commit '%s'"), squash_message);
 			ctx.output_encoding = get_commit_output_encoding();
-			format_commit_message(c, "squash! %s\n\n", &sb,
-					      &ctx);
+			repo_format_commit_message(the_repository, c,
+						   "squash! %s\n\n", &sb,
+						   &ctx);
 		}
 	}
 
@@ -792,10 +800,11 @@
 		char *fmt;
 		commit = lookup_commit_reference_by_name(fixup_commit);
 		if (!commit)
-			die(_("could not lookup commit %s"), fixup_commit);
+			die(_("could not lookup commit '%s'"), fixup_commit);
 		ctx.output_encoding = get_commit_output_encoding();
 		fmt = xstrfmt("%s! %%s\n\n", fixup_prefix);
-		format_commit_message(commit, fmt, &sb, &ctx);
+		repo_format_commit_message(the_repository, commit, fmt, &sb,
+					   &ctx);
 		free(fmt);
 		hook_arg1 = "message";
 
@@ -838,7 +847,7 @@
 		    wt_status_locate_end(sb.buf + merge_msg_start,
 					 sb.len - merge_msg_start) <
 				sb.len - merge_msg_start)
-			merge_contains_scissors = 1;
+			s->added_cut_line = 1;
 	} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
 		if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
 			die_errno(_("could not read SQUASH_MSG"));
@@ -886,10 +895,10 @@
 	s->hints = 0;
 
 	if (clean_message_contents)
-		strbuf_stripspace(&sb, 0);
+		strbuf_stripspace(&sb, NULL);
 
 	if (signoff)
-		append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
+		append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0);
 
 	if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
 		die_errno(_("could not write commit template"));
@@ -906,24 +915,23 @@
 		struct ident_split ci, ai;
 		const char *hint_cleanup_all = allow_empty_message ?
 			_("Please enter the commit message for your changes."
-			  " Lines starting\nwith '%c' will be ignored.\n") :
+			  " Lines starting\nwith '%s' will be ignored.\n") :
 			_("Please enter the commit message for your changes."
-			  " Lines starting\nwith '%c' will be ignored, and an empty"
+			  " Lines starting\nwith '%s' will be ignored, and an empty"
 			  " message aborts the commit.\n");
 		const char *hint_cleanup_space = allow_empty_message ?
 			_("Please enter the commit message for your changes."
 			  " Lines starting\n"
-			  "with '%c' will be kept; you may remove them"
+			  "with '%s' will be kept; you may remove them"
 			  " yourself if you want to.\n") :
 			_("Please enter the commit message for your changes."
 			  " Lines starting\n"
-			  "with '%c' will be kept; you may remove them"
+			  "with '%s' will be kept; you may remove them"
 			  " yourself if you want to.\n"
 			  "An empty message aborts the commit.\n");
 		if (whence != FROM_COMMIT) {
-			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
-				!merge_contains_scissors)
-				wt_status_add_cut_line(s->fp);
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+				wt_status_add_cut_line(s);
 			status_printf_ln(
 				s, GIT_COLOR_NORMAL,
 				whence == FROM_MERGE ?
@@ -941,12 +949,12 @@
 
 		fprintf(s->fp, "\n");
 		if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
-			status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
+			status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_str);
 		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
-			if (whence == FROM_COMMIT && !merge_contains_scissors)
-				wt_status_add_cut_line(s->fp);
+			if (whence == FROM_COMMIT)
+				wt_status_add_cut_line(s);
 		} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
-			status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char);
+			status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_str);
 
 		/*
 		 * These should never fail because they come from our own
@@ -991,16 +999,13 @@
 		struct object_id oid;
 		const char *parent = "HEAD";
 
-		if (!the_index.cache_nr) {
-			discard_index(&the_index);
-			if (repo_read_index(the_repository) < 0)
-				die(_("Cannot read index"));
-		}
+		if (!the_index.initialized && repo_read_index(the_repository) < 0)
+			die(_("Cannot read index"));
 
 		if (amend)
 			parent = "HEAD^1";
 
-		if (get_oid(parent, &oid)) {
+		if (repo_get_oid(the_repository, parent, &oid)) {
 			int i, ita_nr = 0;
 
 			/* TODO: audit for interaction with sparse-index. */
@@ -1036,7 +1041,8 @@
 		struct child_process run_trailer = CHILD_PROCESS_INIT;
 
 		strvec_pushl(&run_trailer.args, "interpret-trailers",
-			     "--in-place", git_path_commit_editmsg(), NULL);
+			     "--in-place", "--no-divider",
+			     git_path_commit_editmsg(), NULL);
 		strvec_pushv(&run_trailer.args, trailer_args.v);
 		run_trailer.git_cmd = 1;
 		if (run_command(&run_trailer))
@@ -1135,7 +1141,8 @@
 		struct pretty_print_context ctx = {0};
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
-		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+		repo_format_commit_message(the_repository, commit,
+					   "%aN <%aE>", &buf, &ctx);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
@@ -1156,22 +1163,45 @@
 		die(_("Invalid ignored mode '%s'"), ignored_arg);
 }
 
-static void handle_untracked_files_arg(struct wt_status *s)
+static enum untracked_status_type parse_untracked_setting_name(const char *u)
 {
-	if (!untracked_files_arg)
-		; /* default already initialized */
-	else if (!strcmp(untracked_files_arg, "no"))
-		s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-	else if (!strcmp(untracked_files_arg, "normal"))
-		s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-	else if (!strcmp(untracked_files_arg, "all"))
-		s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
 	/*
 	 * Please update $__git_untracked_file_modes in
 	 * git-completion.bash when you add new options
 	 */
+	switch (git_parse_maybe_bool(u)) {
+	case 0:
+		u = "no";
+		break;
+	case 1:
+		u = "normal";
+		break;
+	default:
+		break;
+	}
+
+	if (!strcmp(u, "no"))
+		return SHOW_NO_UNTRACKED_FILES;
+	else if (!strcmp(u, "normal"))
+		return SHOW_NORMAL_UNTRACKED_FILES;
+	else if (!strcmp(u, "all"))
+		return SHOW_ALL_UNTRACKED_FILES;
 	else
-		die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
+		return SHOW_UNTRACKED_FILES_ERROR;
+}
+
+static void handle_untracked_files_arg(struct wt_status *s)
+{
+	enum untracked_status_type u;
+
+	if (!untracked_files_arg)
+		return; /* default already initialized */
+
+	u = parse_untracked_setting_name(untracked_files_arg);
+	if (u == SHOW_UNTRACKED_FILES_ERROR)
+		die(_("Invalid untracked files mode '%s'"),
+		    untracked_files_arg);
+	s->show_untracked_files = u;
 }
 
 static const char *read_commit_message(const char *name)
@@ -1181,9 +1211,9 @@
 
 	commit = lookup_commit_reference_by_name(name);
 	if (!commit)
-		die(_("could not lookup commit %s"), name);
+		die(_("could not lookup commit '%s'"), name);
 	out_enc = get_commit_output_encoding();
-	return logmsg_reencode(commit, NULL, out_enc);
+	return repo_logmsg_reencode(the_repository, commit, NULL, out_enc);
 }
 
 /*
@@ -1397,7 +1427,8 @@
 	return LOOKUP_CONFIG(color_status_slots, slot);
 }
 
-static int git_status_config(const char *k, const char *v, void *cb)
+static int git_status_config(const char *k, const char *v,
+			     const struct config_context *ctx, void *cb)
 {
 	struct wt_status *s = cb;
 	const char *slot_name;
@@ -1406,7 +1437,8 @@
 		return git_column_config(k, v, "status", &s->colopts);
 	if (!strcmp(k, "status.submodulesummary")) {
 		int is_bool;
-		s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+		s->submodule_summary = git_config_bool_or_int(k, v, ctx->kvi,
+							      &is_bool);
 		if (is_bool && s->submodule_summary)
 			s->submodule_summary = -1;
 		return 0;
@@ -1452,25 +1484,21 @@
 		return 0;
 	}
 	if (!strcmp(k, "status.showuntrackedfiles")) {
-		if (!v)
-			return config_error_nonbool(k);
-		else if (!strcmp(v, "no"))
-			s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
-		else if (!strcmp(v, "normal"))
-			s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-		else if (!strcmp(v, "all"))
-			s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
-		else
+		enum untracked_status_type u;
+
+		u = parse_untracked_setting_name(v);
+		if (u == SHOW_UNTRACKED_FILES_ERROR)
 			return error(_("Invalid untracked files mode '%s'"), v);
+		s->show_untracked_files = u;
 		return 0;
 	}
 	if (!strcmp(k, "diff.renamelimit")) {
 		if (s->rename_limit == -1)
-			s->rename_limit = git_config_int(k, v);
+			s->rename_limit = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "status.renamelimit")) {
-		s->rename_limit = git_config_int(k, v);
+		s->rename_limit = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "diff.renames")) {
@@ -1482,7 +1510,7 @@
 		s->detect_rename = git_config_rename(k, v);
 		return 0;
 	}
-	return git_diff_ui_config(k, v, NULL);
+	return git_diff_ui_config(k, v, ctx, NULL);
 }
 
 int cmd_status(int argc, const char **argv, const char *prefix)
@@ -1567,7 +1595,7 @@
 	else
 		fd = -1;
 
-	s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
+	s.is_initial = repo_get_oid(the_repository, s.reference, &oid) ? 1 : 0;
 	if (!s.is_initial)
 		oidcpy(&s.oid_commit, &oid);
 
@@ -1597,10 +1625,10 @@
 	return 0;
 }
 
-static int git_commit_config(const char *k, const char *v, void *cb)
+static int git_commit_config(const char *k, const char *v,
+			     const struct config_context *ctx, void *cb)
 {
 	struct wt_status *s = cb;
-	int status;
 
 	if (!strcmp(k, "commit.template"))
 		return git_config_pathname(&template_file, k, v);
@@ -1616,14 +1644,12 @@
 	}
 	if (!strcmp(k, "commit.verbose")) {
 		int is_bool;
-		config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+		config_commit_verbose = git_config_bool_or_int(k, v, ctx->kvi,
+							       &is_bool);
 		return 0;
 	}
 
-	status = git_gpg_config(k, v, NULL);
-	if (status)
-		return status;
-	return git_status_config(k, v, s);
+	return git_status_config(k, v, ctx, s);
 }
 
 int cmd_commit(int argc, const char **argv, const char *prefix)
@@ -1714,11 +1740,11 @@
 	status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
 	s.colopts = 0;
 
-	if (get_oid("HEAD", &oid))
+	if (repo_get_oid(the_repository, "HEAD", &oid))
 		current_head = NULL;
 	else {
 		current_head = lookup_commit_or_die(&oid, "HEAD");
-		if (parse_commit(current_head))
+		if (repo_parse_commit(the_repository, current_head))
 			die(_("could not parse HEAD commit"));
 	}
 	verbose = -1; /* unspecified */
@@ -1852,7 +1878,7 @@
 
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
-		      "new_index file. Check that disk is not full and quota is\n"
+		      "new index file. Check that disk is not full and quota is\n"
 		      "not exceeded, and then \"git restore --staged :/\" to recover."));
 
 	git_test_write_commit_graph_or_die();
@@ -1875,7 +1901,7 @@
 				     &oid, flags);
 	}
 
-	apply_autostash(git_path_merge_autostash(the_repository));
+	apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 
 cleanup:
 	strbuf_release(&author_ident);
diff --git a/builtin/config.c b/builtin/config.c
index 060cf9f..0015620 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -1,10 +1,18 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
 #include "config.h"
 #include "color.h"
+#include "editor.h"
+#include "environment.h"
+#include "repository.h"
+#include "gettext.h"
+#include "ident.h"
 #include "parse-options.h"
 #include "urlmatch.h"
+#include "path.h"
 #include "quote.h"
+#include "setup.h"
+#include "strbuf.h"
 #include "worktree.h"
 
 static const char *const builtin_config_usage[] = {
@@ -36,6 +44,7 @@
 static int show_origin;
 static int show_scope;
 static int fixed_value;
+static const char *comment;
 
 #define ACTION_GET (1<<0)
 #define ACTION_GET_ALL (1<<1)
@@ -165,6 +174,7 @@
 	OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
 	OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
 	OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
+	OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
 	OPT_END(),
 };
 
@@ -185,37 +195,42 @@
 	usage_builtin_config();
 }
 
-static void show_config_origin(struct strbuf *buf)
+static void show_config_origin(const struct key_value_info *kvi,
+			       struct strbuf *buf)
 {
 	const char term = end_nul ? '\0' : '\t';
 
-	strbuf_addstr(buf, current_config_origin_type());
+	strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
 	strbuf_addch(buf, ':');
 	if (end_nul)
-		strbuf_addstr(buf, current_config_name());
+		strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
 	else
-		quote_c_style(current_config_name(), buf, NULL, 0);
+		quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
 	strbuf_addch(buf, term);
 }
 
-static void show_config_scope(struct strbuf *buf)
+static void show_config_scope(const struct key_value_info *kvi,
+			      struct strbuf *buf)
 {
 	const char term = end_nul ? '\0' : '\t';
-	const char *scope = config_scope_name(current_config_scope());
+	const char *scope = config_scope_name(kvi->scope);
 
 	strbuf_addstr(buf, N_(scope));
 	strbuf_addch(buf, term);
 }
 
 static int show_all_config(const char *key_, const char *value_,
+			   const struct config_context *ctx,
 			   void *cb UNUSED)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	if (show_origin || show_scope) {
 		struct strbuf buf = STRBUF_INIT;
 		if (show_scope)
-			show_config_scope(&buf);
+			show_config_scope(kvi, &buf);
 		if (show_origin)
-			show_config_origin(&buf);
+			show_config_origin(kvi, &buf);
 		/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
@@ -233,12 +248,13 @@
 	int alloc;
 };
 
-static int format_config(struct strbuf *buf, const char *key_, const char *value_)
+static int format_config(struct strbuf *buf, const char *key_,
+			 const char *value_, const struct key_value_info *kvi)
 {
 	if (show_scope)
-		show_config_scope(buf);
+		show_config_scope(kvi, buf);
 	if (show_origin)
-		show_config_origin(buf);
+		show_config_origin(kvi, buf);
 	if (show_keys)
 		strbuf_addstr(buf, key_);
 	if (!omit_values) {
@@ -247,13 +263,14 @@
 
 		if (type == TYPE_INT)
 			strbuf_addf(buf, "%"PRId64,
-				    git_config_int64(key_, value_ ? value_ : ""));
+				    git_config_int64(key_, value_ ? value_ : "", kvi));
 		else if (type == TYPE_BOOL)
 			strbuf_addstr(buf, git_config_bool(key_, value_) ?
 				      "true" : "false");
 		else if (type == TYPE_BOOL_OR_INT) {
 			int is_bool, v;
-			v = git_config_bool_or_int(key_, value_, &is_bool);
+			v = git_config_bool_or_int(key_, value_, kvi,
+						   &is_bool);
 			if (is_bool)
 				strbuf_addstr(buf, v ? "true" : "false");
 			else
@@ -292,9 +309,11 @@
 	return 0;
 }
 
-static int collect_config(const char *key_, const char *value_, void *cb)
+static int collect_config(const char *key_, const char *value_,
+			  const struct config_context *ctx, void *cb)
 {
 	struct strbuf_list *values = cb;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (!use_key_regexp && strcmp(key_, key))
 		return 0;
@@ -309,7 +328,7 @@
 	ALLOC_GROW(values->items, values->nr + 1, values->alloc);
 	strbuf_init(&values->items[values->nr], 0);
 
-	return format_config(&values->items[values->nr++], key_, value_);
+	return format_config(&values->items[values->nr++], key_, value_, kvi);
 }
 
 static int get_value(const char *key_, const char *regex_, unsigned flags)
@@ -367,14 +386,18 @@
 	}
 
 	config_with_options(collect_config, &values,
-			    &given_config_source, &config_options);
+			    &given_config_source, the_repository,
+			    &config_options);
 
 	if (!values.nr && default_value) {
+		struct key_value_info kvi = KVI_INIT;
 		struct strbuf *item;
+
+		kvi_from_param(&kvi);
 		ALLOC_GROW(values.items, values.nr + 1, values.alloc);
 		item = &values.items[values.nr++];
 		strbuf_init(item, 0);
-		if (format_config(item, key_, default_value) < 0)
+		if (format_config(item, key_, default_value, &kvi) < 0)
 			die(_("failed to format default config value: %s"),
 				default_value);
 	}
@@ -403,7 +426,8 @@
 	return ret;
 }
 
-static char *normalize_value(const char *key, const char *value)
+static char *normalize_value(const char *key, const char *value,
+			     struct key_value_info *kvi)
 {
 	if (!value)
 		return NULL;
@@ -418,12 +442,12 @@
 		 */
 		return xstrdup(value);
 	if (type == TYPE_INT)
-		return xstrfmt("%"PRId64, git_config_int64(key, value));
+		return xstrfmt("%"PRId64, git_config_int64(key, value, kvi));
 	if (type == TYPE_BOOL)
 		return xstrdup(git_config_bool(key, value) ?  "true" : "false");
 	if (type == TYPE_BOOL_OR_INT) {
 		int is_bool, v;
-		v = git_config_bool_or_int(key, value, &is_bool);
+		v = git_config_bool_or_int(key, value, kvi, &is_bool);
 		if (!is_bool)
 			return xstrfmt("%d", v);
 		else
@@ -460,6 +484,7 @@
 static char parsed_color[COLOR_MAXLEN];
 
 static int git_get_color_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
 				void *cb UNUSED)
 {
 	if (!strcmp(var, get_color_slot)) {
@@ -478,7 +503,8 @@
 	get_color_found = 0;
 	parsed_color[0] = '\0';
 	config_with_options(git_get_color_config, NULL,
-			    &given_config_source, &config_options);
+			    &given_config_source, the_repository,
+			    &config_options);
 
 	if (!get_color_found && def_color) {
 		if (color_parse(def_color, parsed_color) < 0)
@@ -492,6 +518,7 @@
 static int get_diff_color_found;
 static int get_color_ui_found;
 static int git_get_colorbool_config(const char *var, const char *value,
+				    const struct config_context *ctx UNUSED,
 				    void *data UNUSED)
 {
 	if (!strcmp(var, get_colorbool_slot))
@@ -510,7 +537,8 @@
 	get_diff_color_found = -1;
 	get_color_ui_found = -1;
 	config_with_options(git_get_colorbool_config, NULL,
-			    &given_config_source, &config_options);
+			    &given_config_source, the_repository,
+			    &config_options);
 
 	if (get_colorbool_found < 0) {
 		if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -547,13 +575,17 @@
 struct urlmatch_current_candidate_value {
 	char value_is_null;
 	struct strbuf value;
+	struct key_value_info kvi;
 };
 
-static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+static int urlmatch_collect_fn(const char *var, const char *value,
+			       const struct config_context *ctx,
+			       void *cb)
 {
 	struct string_list *values = cb;
 	struct string_list_item *item = string_list_insert(values, var);
 	struct urlmatch_current_candidate_value *matched = item->util;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (!matched) {
 		matched = xmalloc(sizeof(*matched));
@@ -562,6 +594,7 @@
 	} else {
 		strbuf_reset(&matched->value);
 	}
+	matched->kvi = *kvi;
 
 	if (value) {
 		strbuf_addstr(&matched->value, value);
@@ -599,7 +632,8 @@
 	}
 
 	config_with_options(urlmatch_config_entry, &config,
-			    &given_config_source, &config_options);
+			    &given_config_source, the_repository,
+			    &config_options);
 
 	ret = !values.nr;
 
@@ -608,7 +642,8 @@
 		struct strbuf buf = STRBUF_INIT;
 
 		format_config(&buf, item->string,
-			      matched->value_is_null ? NULL : matched->value.buf);
+			      matched->value_is_null ? NULL : matched->value.buf,
+			      &matched->kvi);
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
 
@@ -642,6 +677,7 @@
 	char *value = NULL;
 	int flags = 0;
 	int ret = 0;
+	struct key_value_info default_kvi = KVI_INIT;
 
 	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
@@ -674,10 +710,8 @@
 	}
 
 	if (use_global_config) {
-		char *user_config, *xdg_config;
-
-		git_global_config(&user_config, &xdg_config);
-		if (!user_config)
+		given_config_source.file = git_global_config();
+		if (!given_config_source.file)
 			/*
 			 * It is unknown if HOME/.gitconfig exists, so
 			 * we do not know if we should write to XDG
@@ -685,19 +719,8 @@
 			 * is set and points at a sane location.
 			 */
 			die(_("$HOME not set"));
-
 		given_config_source.scope = CONFIG_SCOPE_GLOBAL;
-
-		if (access_or_warn(user_config, R_OK, 0) &&
-		    xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
-			given_config_source.file = xdg_config;
-			free(user_config);
-		} else {
-			given_config_source.file = user_config;
-			free(xdg_config);
-		}
-	}
-	else if (use_system_config) {
+	} else if (use_system_config) {
 		given_config_source.file = git_system_config();
 		given_config_source.scope = CONFIG_SCOPE_SYSTEM;
 	} else if (use_local_config) {
@@ -705,7 +728,7 @@
 		given_config_source.scope = CONFIG_SCOPE_LOCAL;
 	} else if (use_worktree_config) {
 		struct worktree **worktrees = get_worktrees();
-		if (repository_format_worktree_config)
+		if (the_repository->repository_format_worktree_config)
 			given_config_source.file = git_pathdup("config.worktree");
 		else if (worktrees[0] && worktrees[1])
 			die(_("--worktree cannot be used with multiple "
@@ -726,7 +749,6 @@
 		given_config_source.scope = CONFIG_SCOPE_COMMAND;
 	}
 
-
 	if (respect_includes_opt == -1)
 		config_options.respect_includes = !given_config_source.file;
 	else
@@ -777,6 +799,12 @@
 		usage_builtin_config();
 	}
 
+	if (comment &&
+	    !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
+		error(_("--comment is only applicable to add/set/replace operations"));
+		usage_builtin_config();
+	}
+
 	/* check usage of --fixed-value */
 	if (fixed_value) {
 		int allowed_usage = 0;
@@ -813,13 +841,15 @@
 		flags |= CONFIG_FLAGS_FIXED_VALUE;
 	}
 
+	comment = git_config_prepare_comment_string(comment);
+
 	if (actions & PAGING_ACTIONS)
 		setup_auto_pager("config", 1);
 
 	if (actions == ACTION_LIST) {
 		check_argc(argc, 0, 0);
 		if (config_with_options(show_all_config, NULL,
-					&given_config_source,
+					&given_config_source, the_repository,
 					&config_options) < 0) {
 			if (given_config_source.file)
 				die_errno(_("unable to read config file '%s'"),
@@ -859,8 +889,8 @@
 	else if (actions == ACTION_SET) {
 		check_write();
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
-		ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
+		ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
 		if (ret == CONFIG_NOTHING_SET)
 			error(_("cannot overwrite multiple values with a single value\n"
 			"       Use a regexp, --add or --replace-all to change %s."), argv[0]);
@@ -868,27 +898,27 @@
 	else if (actions == ACTION_SET_ALL) {
 		check_write();
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value, argv[2],
-							     flags);
+							     comment, flags);
 	}
 	else if (actions == ACTION_ADD) {
 		check_write();
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value,
 							     CONFIG_REGEX_NONE,
-							     flags);
+							     comment, flags);
 	}
 	else if (actions == ACTION_REPLACE_ALL) {
 		check_write();
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1]);
+		value = normalize_value(argv[0], argv[1], &default_kvi);
 		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
 							     argv[0], value, argv[2],
-							     flags | CONFIG_FLAGS_MULTI_REPLACE);
+							     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_GET) {
 		check_argc(argc, 1, 2);
@@ -916,17 +946,17 @@
 		if (argc == 2)
 			return git_config_set_multivar_in_file_gently(given_config_source.file,
 								      argv[0], NULL, argv[1],
-								      flags);
+								      NULL, flags);
 		else
 			return git_config_set_in_file_gently(given_config_source.file,
-							     argv[0], NULL);
+							     argv[0], NULL, NULL);
 	}
 	else if (actions == ACTION_UNSET_ALL) {
 		check_write();
 		check_argc(argc, 1, 2);
 		return git_config_set_multivar_in_file_gently(given_config_source.file,
 							      argv[0], NULL, argv[1],
-							      flags | CONFIG_FLAGS_MULTI_REPLACE);
+							      NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_RENAME_SECTION) {
 		check_write();
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 07b9419..2d4bb5e 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -4,15 +4,17 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
 #include "repository.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "quote.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static unsigned long garbage;
 static off_t size_garbage;
@@ -57,7 +59,8 @@
 		report_garbage(PACKDIR_FILE_GARBAGE, path);
 }
 
-static int count_loose(const struct object_id *oid, const char *path, void *data)
+static int count_loose(const struct object_id *oid, const char *path,
+		       void *data UNUSED)
 {
 	struct stat st;
 
@@ -72,13 +75,14 @@
 	return 0;
 }
 
-static int count_cruft(const char *basename, const char *path, void *data)
+static int count_cruft(const char *basename UNUSED, const char *path,
+		       void *data UNUSED)
 {
 	loose_garbage(path);
 	return 0;
 }
 
-static int print_alternate(struct object_directory *odb, void *data)
+static int print_alternate(struct object_directory *odb, void *data UNUSED)
 {
 	printf("alternate: ");
 	quote_c_style(odb->path, NULL, stdout, 0);
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 338058b..17f929d 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "object-file.h"
 #include "parse-options.h"
 
 #ifndef NO_UNIX_SOCKETS
@@ -34,19 +37,22 @@
 	int i;
 	for (i = 0; i < entries_nr; i++) {
 		struct credential *e = &entries[i].item;
-		if (credential_match(c, e))
+		if (credential_match(c, e, 0))
 			return &entries[i];
 	}
 	return NULL;
 }
 
-static void remove_credential(const struct credential *c)
+static void remove_credential(const struct credential *c, int match_password)
 {
 	struct credential_cache_entry *e;
 
-	e = lookup_credential(c);
-	if (e)
-		e->expiration = 0;
+	int i;
+	for (i = 0; i < entries_nr; i++) {
+		e = &entries[i];
+		if (credential_match(c, &e->item, match_password))
+			e->expiration = 0;
+	}
 }
 
 static timestamp_t check_expirations(void)
@@ -130,6 +136,9 @@
 			if (e->item.password_expiry_utc != TIME_MAX)
 				fprintf(out, "password_expiry_utc=%"PRItime"\n",
 					e->item.password_expiry_utc);
+			if (e->item.oauth_refresh_token)
+				fprintf(out, "oauth_refresh_token=%s\n",
+					e->item.oauth_refresh_token);
 		}
 	}
 	else if (!strcmp(action.buf, "exit")) {
@@ -144,14 +153,14 @@
 		exit(0);
 	}
 	else if (!strcmp(action.buf, "erase"))
-		remove_credential(&c);
+		remove_credential(&c, 1);
 	else if (!strcmp(action.buf, "store")) {
 		if (timeout < 0)
 			warning("cache client didn't specify a timeout");
 		else if (!c.username || !c.password)
 			warning("cache client gave us a partial credential");
 		else {
-			remove_credential(&c);
+			remove_credential(&c, 0);
 			cache_credential(&c, timeout);
 		}
 	}
@@ -285,6 +294,8 @@
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 	socket_path = argv[0];
 
+	if (!have_unix_sockets())
+		die(_("credential-cache--daemon unavailable; no unix socket support"));
 	if (!socket_path)
 		usage_with_options(usage, options);
 
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index 78c02ad..bef120b 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,10 +1,12 @@
 #include "builtin.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
+#include "strbuf.h"
+#include "write-or-die.h"
 
 #ifndef NO_UNIX_SOCKETS
 
-#include "credential.h"
-#include "string-list.h"
 #include "unix-socket.h"
 #include "run-command.h"
 
@@ -147,6 +149,9 @@
 		usage_with_options(usage, options);
 	op = argv[0];
 
+	if (!have_unix_sockets())
+		die(_("credential-cache unavailable; no unix socket support"));
+
 	if (!socket_path)
 		socket_path = get_socket_path();
 	if (!socket_path)
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index 62a4f3c..4a49241 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -1,16 +1,20 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "lockfile.h"
 #include "credential.h"
+#include "path.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "write-or-die.h"
 
 static struct lock_file credential_lock;
 
 static int parse_credential_file(const char *fn,
 				  struct credential *c,
 				  void (*match_cb)(struct credential *),
-				  void (*other_cb)(struct strbuf *))
+				  void (*other_cb)(struct strbuf *),
+				  int match_password)
 {
 	FILE *fh;
 	struct strbuf line = STRBUF_INIT;
@@ -27,7 +31,7 @@
 	while (strbuf_getline_lf(&line, fh) != EOF) {
 		if (!credential_from_url_gently(&entry, line.buf, 1) &&
 		    entry.username && entry.password &&
-		    credential_match(c, &entry)) {
+		    credential_match(c, &entry, match_password)) {
 			found_credential = 1;
 			if (match_cb) {
 				match_cb(&entry);
@@ -57,7 +61,7 @@
 }
 
 static void rewrite_credential_file(const char *fn, struct credential *c,
-				    struct strbuf *extra)
+				    struct strbuf *extra, int match_password)
 {
 	int timeout_ms = 1000;
 
@@ -66,11 +70,30 @@
 		die_errno(_("unable to get credential storage lock in %d ms"), timeout_ms);
 	if (extra)
 		print_line(extra);
-	parse_credential_file(fn, c, NULL, print_line);
+	parse_credential_file(fn, c, NULL, print_line, match_password);
 	if (commit_lock_file(&credential_lock) < 0)
 		die_errno("unable to write credential store");
 }
 
+static int is_rfc3986_unreserved(char ch)
+{
+	return isalnum(ch) ||
+		ch == '-' || ch == '_' || ch == '.' || ch == '~';
+}
+
+static int is_rfc3986_reserved_or_unreserved(char ch)
+{
+	if (is_rfc3986_unreserved(ch))
+		return 1;
+	switch (ch) {
+		case '!': case '*': case '\'': case '(': case ')': case ';':
+		case ':': case '@': case '&': case '=': case '+': case '$':
+		case ',': case '/': case '?': case '#': case '[': case ']':
+			return 1;
+	}
+	return 0;
+}
+
 static void store_credential_file(const char *fn, struct credential *c)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -88,7 +111,7 @@
 					is_rfc3986_reserved_or_unreserved);
 	}
 
-	rewrite_credential_file(fn, c, &buf);
+	rewrite_credential_file(fn, c, &buf, 0);
 	strbuf_release(&buf);
 }
 
@@ -135,7 +158,7 @@
 		return;
 	for_each_string_list_item(fn, fns)
 		if (!access(fn->string, F_OK))
-			rewrite_credential_file(fn->string, c, NULL);
+			rewrite_credential_file(fn->string, c, NULL, 1);
 }
 
 static void lookup_credential(const struct string_list *fns, struct credential *c)
@@ -143,7 +166,7 @@
 	struct string_list_item *fn;
 
 	for_each_string_list_item(fn, fns)
-		if (parse_credential_file(fn->string, c, print_entry, NULL))
+		if (parse_credential_file(fn->string, c, print_entry, NULL, 0))
 			return; /* Found credential */
 }
 
diff --git a/builtin/credential.c b/builtin/credential.c
index d7b304f..7010752 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -6,7 +6,7 @@
 static const char usage_msg[] =
 	"git credential (fill|approve|reject)";
 
-int cmd_credential(int argc, const char **argv, const char *prefix)
+int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
 {
 	const char *op;
 	struct credential c = CREDENTIAL_INIT;
diff --git a/builtin/describe.c b/builtin/describe.c
index eea1e33..d6c77a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,24 +1,29 @@
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
-#include "blob.h"
 #include "refs.h"
-#include "builtin.h"
-#include "exec-cmd.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
 #include "revision.h"
 #include "diff.h"
 #include "hashmap.h"
+#include "setup.h"
 #include "strvec.h"
 #include "run-command.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "list-objects.h"
 #include "commit-slab.h"
+#include "wildmatch.h"
 
 #define MAX_TAGS	(FLAG_BITS - 1)
+#define DEFAULT_CANDIDATES 10
 
 define_commit_slab(commit_names, struct commit_name *);
 
@@ -35,7 +40,7 @@
 static int longformat;
 static int first_parent;
 static int abbrev = -1; /* unspecified */
-static int max_candidates = 10;
+static int max_candidates = DEFAULT_CANDIDATES;
 static struct hashmap names;
 static int have_util;
 static struct string_list patterns = STRING_LIST_INIT_NODUP;
@@ -261,7 +266,7 @@
 			best->depth++;
 		while (parents) {
 			struct commit *p = parents->item;
-			parse_commit(p);
+			repo_parse_commit(the_repository, p);
 			if (!(p->object.flags & SEEN))
 				commit_list_insert_by_date(p, list);
 			p->object.flags |= c->object.flags;
@@ -298,7 +303,8 @@
 
 static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
 {
-	strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev));
+	strbuf_addf(dst, "-%d-g%s", depth,
+		    repo_find_unique_abbrev(the_repository, oid, abbrev));
 }
 
 static void describe_commit(struct object_id *oid, struct strbuf *dst)
@@ -403,7 +409,7 @@
 		}
 		while (parents) {
 			struct commit *p = parents->item;
-			parse_commit(p);
+			repo_parse_commit(the_repository, p);
 			if (!(p->object.flags & SEEN))
 				commit_list_insert_by_date(p, &list);
 			p->object.flags |= c->object.flags;
@@ -531,7 +537,7 @@
 	if (debug)
 		fprintf(stderr, _("describe %s\n"), arg);
 
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		die(_("Not a valid object name %s"), arg);
 	cmit = lookup_commit_reference_gently(the_repository, &oid, 1);
 
@@ -550,6 +556,17 @@
 	strbuf_release(&sb);
 }
 
+static int option_parse_exact_match(const struct option *opt, const char *arg,
+				    int unset)
+{
+	int *val = opt->value;
+
+	BUG_ON_OPT_ARG(arg);
+
+	*val = unset ? DEFAULT_CANDIDATES : 0;
+	return 0;
+}
+
 int cmd_describe(int argc, const char **argv, const char *prefix)
 {
 	int contains = 0;
@@ -561,8 +578,9 @@
 		OPT_BOOL(0, "long",       &longformat, N_("always use long format")),
 		OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
 		OPT__ABBREV(&abbrev),
-		OPT_SET_INT(0, "exact-match", &max_candidates,
-			    N_("only output exact matches"), 0),
+		OPT_CALLBACK_F(0, "exact-match", &max_candidates, NULL,
+			       N_("only output exact matches"),
+			       PARSE_OPT_NOARG, option_parse_exact_match),
 		OPT_INTEGER(0, "candidates", &max_candidates,
 			    N_("consider <n> most recent tags (default: 10)")),
 		OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
@@ -650,9 +668,11 @@
 			struct lock_file index_lock = LOCK_INIT;
 			struct rev_info revs;
 			struct strvec args = STRVEC_INIT;
-			int fd, result;
+			int fd;
 
 			setup_work_tree();
+			prepare_repo_settings(the_repository);
+			the_repository->settings.command_requires_full_index = 0;
 			repo_read_index(the_repository);
 			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
 				      NULL, NULL, NULL);
@@ -665,9 +685,9 @@
 			strvec_pushv(&args, diff_index_args);
 			if (setup_revisions(args.nr, args.v, &revs, NULL) != 1)
 				BUG("malformed internal diff-index command line");
-			result = run_diff_index(&revs, 0);
+			run_diff_index(&revs, 0);
 
-			if (!diff_result_code(&revs.diffopt, result))
+			if (!diff_result_code(&revs.diffopt))
 				suffix = NULL;
 			else
 				suffix = dirty;
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index d52015c..4f22eb2 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "object-file.h"
 #include "parse-options.h"
 #include "diagnose.h"
 
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index dc991f7..018011f 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -3,14 +3,14 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "diff.h"
 #include "diff-merges.h"
 #include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
 #include "revision.h"
-#include "builtin.h"
-#include "submodule.h"
 
 static const char diff_files_usage[] =
 "git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]"
@@ -27,6 +27,10 @@
 		usage(diff_files_usage);
 
 	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+	prepare_repo_settings(the_repository);
+	the_repository->settings.command_requires_full_index = 0;
+
 	repo_init_revisions(the_repository, &rev, prefix);
 	rev.abbrev = 0;
 
@@ -75,14 +79,10 @@
 	    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
 		diff_merges_set_dense_combined_if_unset(&rev);
 
-	if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
-		perror("repo_read_index_preload");
-		result = -1;
-		goto cleanup;
-	}
-	result = run_diff_files(&rev, options);
-	result = diff_result_code(&rev.diffopt, result);
-cleanup:
+	if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0)
+		die_errno("repo_read_index_preload");
+	run_diff_files(&rev, options);
+	result = diff_result_code(&rev.diffopt);
 	release_revisions(&rev);
 	return result;
 }
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 35dc9b2..3e05260 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -1,11 +1,12 @@
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "diff.h"
 #include "diff-merges.h"
 #include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
 #include "revision.h"
-#include "builtin.h"
-#include "submodule.h"
+#include "setup.h"
 
 static const char diff_cache_usage[] =
 "git diff-index [-m] [--cached] [--merge-base] "
@@ -69,8 +70,8 @@
 		perror("repo_read_index");
 		return -1;
 	}
-	result = run_diff_index(&rev, option);
-	result = diff_result_code(&rev.diffopt, result);
+	run_diff_index(&rev, option);
+	result = diff_result_code(&rev.diffopt);
 	release_revisions(&rev);
 	return result;
 }
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 25b853b..a8e68ce 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,12 +1,15 @@
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "diff.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "log-tree.h"
-#include "builtin.h"
-#include "submodule.h"
+#include "read-cache-ll.h"
 #include "repository.h"
+#include "revision.h"
+#include "tree.h"
 
 static struct rev_info log_tree_opt;
 
@@ -95,7 +98,7 @@
 "  --root        include the initial commit as diff against /dev/null\n"
 COMMON_DIFF_OPTIONS_HELP;
 
-static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+static void diff_tree_tweak_rev(struct rev_info *rev)
 {
 	if (!rev->diffopt.output_format) {
 		if (rev->dense_combined_merges)
@@ -119,6 +122,10 @@
 		usage(diff_tree_usage);
 
 	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+	prepare_repo_settings(the_repository);
+	the_repository->settings.command_requires_full_index = 0;
+
 	repo_init_revisions(the_repository, opt, prefix);
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
@@ -224,5 +231,5 @@
 		diff_free(&opt->diffopt);
 	}
 
-	return diff_result_code(&opt->diffopt, 0);
+	return diff_result_code(&opt->diffopt);
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index 26f1e53..6e196e0 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -4,22 +4,24 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "ewah/ewok.h"
 #include "lockfile.h"
 #include "color.h"
 #include "commit.h"
-#include "blob.h"
+#include "gettext.h"
 #include "tag.h"
 #include "diff.h"
 #include "diff-merges.h"
 #include "diffcore.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
 #include "revision.h"
 #include "log-tree.h"
-#include "builtin.h"
-#include "submodule.h"
+#include "setup.h"
 #include "oid-array.h"
+#include "tree.h"
 
 #define DIFF_NO_INDEX_EXPLICIT 1
 #define DIFF_NO_INDEX_IMPLICIT 2
@@ -73,9 +75,9 @@
 	diff_queue(&diff_queued_diff, one, two);
 }
 
-static int builtin_diff_b_f(struct rev_info *revs,
-			    int argc, const char **argv,
-			    struct object_array_entry **blob)
+static void builtin_diff_b_f(struct rev_info *revs,
+			     int argc, const char **argv UNUSED,
+			     struct object_array_entry **blob)
 {
 	/* Blob vs file in the working tree*/
 	struct stat st;
@@ -105,12 +107,11 @@
 		     path);
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
-	return 0;
 }
 
-static int builtin_diff_blobs(struct rev_info *revs,
-			      int argc, const char **argv,
-			      struct object_array_entry **blob)
+static void builtin_diff_blobs(struct rev_info *revs,
+			       int argc, const char **argv UNUSED,
+			       struct object_array_entry **blob)
 {
 	const unsigned mode = canon_mode(S_IFREG | 0644);
 
@@ -130,11 +131,10 @@
 		     blob_path(blob[0]), blob_path(blob[1]));
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
-	return 0;
 }
 
-static int builtin_diff_index(struct rev_info *revs,
-			      int argc, const char **argv)
+static void builtin_diff_index(struct rev_info *revs,
+			       int argc, const char **argv)
 {
 	unsigned int option = 0;
 	while (1 < argc) {
@@ -159,20 +159,18 @@
 		setup_work_tree();
 		if (repo_read_index_preload(the_repository,
 					    &revs->diffopt.pathspec, 0) < 0) {
-			perror("repo_read_index_preload");
-			return -1;
+			die_errno("repo_read_index_preload");
 		}
 	} else if (repo_read_index(the_repository) < 0) {
-		perror("repo_read_cache");
-		return -1;
+		die_errno("repo_read_cache");
 	}
-	return run_diff_index(revs, option);
+	run_diff_index(revs, option);
 }
 
-static int builtin_diff_tree(struct rev_info *revs,
-			     int argc, const char **argv,
-			     struct object_array_entry *ent0,
-			     struct object_array_entry *ent1)
+static void builtin_diff_tree(struct rev_info *revs,
+			      int argc, const char **argv,
+			      struct object_array_entry *ent0,
+			      struct object_array_entry *ent1)
 {
 	const struct object_id *(oid[2]);
 	struct object_id mb_oid;
@@ -205,13 +203,12 @@
 	}
 	diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
 	log_tree_diff_flush(revs);
-	return 0;
 }
 
-static int builtin_diff_combined(struct rev_info *revs,
-				 int argc, const char **argv,
-				 struct object_array_entry *ent,
-				 int ents, int first_non_parent)
+static void builtin_diff_combined(struct rev_info *revs,
+				  int argc, const char **argv UNUSED,
+				  struct object_array_entry *ent,
+				  int ents, int first_non_parent)
 {
 	struct oid_array parents = OID_ARRAY_INIT;
 	int i;
@@ -232,7 +229,6 @@
 	}
 	diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs);
 	oid_array_clear(&parents);
-	return 0;
 }
 
 static void refresh_index_quietly(void)
@@ -250,7 +246,7 @@
 	repo_update_index_if_able(the_repository, &lock_file);
 }
 
-static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
+static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
 {
 	unsigned int options = 0;
 
@@ -265,8 +261,10 @@
 			options |= DIFF_SILENT_ON_REMOVED;
 		else if (!strcmp(argv[1], "-h"))
 			usage(builtin_diff_usage);
-		else
-			return error(_("invalid option: %s"), argv[1]);
+		else {
+			error(_("invalid option: %s"), argv[1]);
+			usage(builtin_diff_usage);
+		}
 		argv++; argc--;
 	}
 
@@ -283,10 +281,9 @@
 	setup_work_tree();
 	if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
 				    0) < 0) {
-		perror("repo_read_index_preload");
-		return -1;
+		die_errno("repo_read_index_preload");
 	}
-	return run_diff_files(revs, options);
+	run_diff_files(revs, options);
 }
 
 struct symdiff {
@@ -400,7 +397,7 @@
 	int blobs = 0, paths = 0;
 	struct object_array_entry *blob[2];
 	int nongit = 0, no_index = 0;
-	int result = 0;
+	int result;
 	struct symdiff sdiff;
 
 	/*
@@ -475,8 +472,7 @@
 	repo_init_revisions(the_repository, &rev, prefix);
 
 	/* Set up defaults that will apply to both no-index and regular diffs. */
-	rev.diffopt.stat_width = -1;
-	rev.diffopt.stat_graph_width = -1;
+	init_diffstat_widths(&rev.diffopt);
 	rev.diffopt.flags.allow_external = 1;
 	rev.diffopt.flags.allow_textconv = 1;
 
@@ -548,7 +544,8 @@
 		if (!obj)
 			die(_("invalid object '%s' given."), name);
 		if (obj->type == OBJ_COMMIT)
-			obj = &get_commit_tree(((struct commit *)obj))->object;
+			obj = &repo_get_commit_tree(the_repository,
+						    ((struct commit *)obj))->object;
 
 		if (obj->type == OBJ_TREE) {
 			if (sdiff.skip && bitmap_get(sdiff.skip, i))
@@ -578,17 +575,17 @@
 	if (!ent.nr) {
 		switch (blobs) {
 		case 0:
-			result = builtin_diff_files(&rev, argc, argv);
+			builtin_diff_files(&rev, argc, argv);
 			break;
 		case 1:
 			if (paths != 1)
 				usage(builtin_diff_usage);
-			result = builtin_diff_b_f(&rev, argc, argv, blob);
+			builtin_diff_b_f(&rev, argc, argv, blob);
 			break;
 		case 2:
 			if (paths)
 				usage(builtin_diff_usage);
-			result = builtin_diff_blobs(&rev, argc, argv, blob);
+			builtin_diff_blobs(&rev, argc, argv, blob);
 			break;
 		default:
 			usage(builtin_diff_usage);
@@ -597,18 +594,18 @@
 	else if (blobs)
 		usage(builtin_diff_usage);
 	else if (ent.nr == 1)
-		result = builtin_diff_index(&rev, argc, argv);
+		builtin_diff_index(&rev, argc, argv);
 	else if (ent.nr == 2) {
 		if (sdiff.warn)
 			warning(_("%s...%s: multiple merge bases, using %s"),
 				sdiff.left, sdiff.right, sdiff.base);
-		result = builtin_diff_tree(&rev, argc, argv,
-					   &ent.objects[0], &ent.objects[1]);
+		builtin_diff_tree(&rev, argc, argv,
+				  &ent.objects[0], &ent.objects[1]);
 	} else
-		result = builtin_diff_combined(&rev, argc, argv,
-					       ent.objects, ent.nr,
-					       first_non_parent);
-	result = diff_result_code(&rev.diffopt, result);
+		builtin_diff_combined(&rev, argc, argv,
+				      ent.objects, ent.nr,
+				      first_non_parent);
+	result = diff_result_code(&rev.diffopt);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	release_revisions(&rev);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index dbbfb19..a3c72b8 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -12,18 +12,25 @@
  * Copyright (C) 2016 Johannes Schindelin
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "abspath.h"
+#include "config.h"
+#include "copy.h"
 #include "run-command.h"
-#include "exec-cmd.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "sparse-index.h"
 #include "strvec.h"
 #include "strbuf.h"
 #include "lockfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "dir.h"
 #include "entry.h"
+#include "setup.h"
 
 static int trust_exit_code;
 
@@ -32,14 +39,15 @@
 	NULL
 };
 
-static int difftool_config(const char *var, const char *value, void *cb)
+static int difftool_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "difftool.trustexitcode")) {
 		trust_exit_code = git_config_bool(var, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int print_tool_help(void)
@@ -295,7 +303,8 @@
 	} else {
 		enum object_type type;
 		unsigned long size;
-		data = read_object_file(oid, &type, &size);
+		data = repo_read_object_file(the_repository, oid, &type,
+					     &size);
 		if (!data)
 			die(_("could not read object %s for symlink %s"),
 				oid_to_hex(oid), path);
@@ -684,7 +693,7 @@
 
 int cmd_difftool(int argc, const char **argv, const char *prefix)
 {
-	int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
+	int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0,
 	    tool_help = 0, no_index = 0;
 	static char *difftool_cmd = NULL, *extcmd = NULL;
 	struct option builtin_difftool_options[] = {
@@ -734,13 +743,21 @@
 	} else if (dir_diff)
 		die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
 
-	die_for_incompatible_opt3(use_gui_tool, "--gui",
+	die_for_incompatible_opt3(use_gui_tool == 1, "--gui",
 				  !!difftool_cmd, "--tool",
 				  !!extcmd, "--extcmd");
 
-	if (use_gui_tool)
+	/*
+	 * Explicitly specified GUI option is forwarded to git-mergetool--lib.sh;
+	 * empty or unset means "use the difftool.guiDefault config or default to
+	 * false".
+	 */
+	if (use_gui_tool == 1)
 		setenv("GIT_MERGETOOL_GUI", "true", 1);
-	else if (difftool_cmd) {
+	else if (use_gui_tool == 0)
+		setenv("GIT_MERGETOOL_GUI", "false", 1);
+
+	if (difftool_cmd) {
 		if (*difftool_cmd)
 			setenv("GIT_DIFF_TOOL", difftool_cmd, 1);
 		else
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 39a890f..4693d18 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -4,11 +4,13 @@
  * Copyright (C) 2007 Johannes E. Schindelin
  */
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
@@ -23,7 +25,6 @@
 #include "quote.h"
 #include "remote.h"
 #include "blob.h"
-#include "commit-slab.h"
 
 static const char *fast_export_usage[] = {
 	N_("git fast-export [<rev-list-opts>]"),
@@ -31,9 +32,9 @@
 };
 
 static int progress;
-static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
-static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
-static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
+static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
+static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
+static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
 static int fake_missing_tagger;
 static int use_done_feature;
 static int no_data;
@@ -51,16 +52,18 @@
 static int parse_opt_signed_tag_mode(const struct option *opt,
 				     const char *arg, int unset)
 {
+	enum signed_tag_mode *val = opt->value;
+
 	if (unset || !strcmp(arg, "abort"))
-		signed_tag_mode = SIGNED_TAG_ABORT;
+		*val = SIGNED_TAG_ABORT;
 	else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
-		signed_tag_mode = VERBATIM;
+		*val = VERBATIM;
 	else if (!strcmp(arg, "warn"))
-		signed_tag_mode = WARN;
+		*val = WARN;
 	else if (!strcmp(arg, "warn-strip"))
-		signed_tag_mode = WARN_STRIP;
+		*val = WARN_STRIP;
 	else if (!strcmp(arg, "strip"))
-		signed_tag_mode = STRIP;
+		*val = STRIP;
 	else
 		return error("Unknown signed-tags mode: %s", arg);
 	return 0;
@@ -69,12 +72,14 @@
 static int parse_opt_tag_of_filtered_mode(const struct option *opt,
 					  const char *arg, int unset)
 {
+	enum tag_of_filtered_mode *val = opt->value;
+
 	if (unset || !strcmp(arg, "abort"))
-		tag_of_filtered_mode = TAG_FILTERING_ABORT;
+		*val = TAG_FILTERING_ABORT;
 	else if (!strcmp(arg, "drop"))
-		tag_of_filtered_mode = DROP;
+		*val = DROP;
 	else if (!strcmp(arg, "rewrite"))
-		tag_of_filtered_mode = REWRITE;
+		*val = REWRITE;
 	else
 		return error("Unknown tag-of-filtered mode: %s", arg);
 	return 0;
@@ -83,21 +88,23 @@
 static int parse_opt_reencode_mode(const struct option *opt,
 				   const char *arg, int unset)
 {
+	enum reencode_mode *val = opt->value;
+
 	if (unset) {
-		reencode_mode = REENCODE_ABORT;
+		*val = REENCODE_ABORT;
 		return 0;
 	}
 
 	switch (git_parse_maybe_bool(arg)) {
 	case 0:
-		reencode_mode = REENCODE_NO;
+		*val = REENCODE_NO;
 		break;
 	case 1:
-		reencode_mode = REENCODE_YES;
+		*val = REENCODE_YES;
 		break;
 	default:
 		if (!strcasecmp(arg, "abort"))
-			reencode_mode = REENCODE_ABORT;
+			*val = REENCODE_ABORT;
 		else
 			return error("Unknown reencoding mode: %s", arg);
 	}
@@ -109,7 +116,7 @@
 static uint32_t last_idnum;
 struct anonymized_entry {
 	struct hashmap_entry hash;
-	const char *anon;
+	char *anon;
 	const char orig[FLEX_ARRAY];
 };
 
@@ -129,8 +136,7 @@
 	a = container_of(eptr, const struct anonymized_entry, hash);
 	if (keydata) {
 		const struct anonymized_entry_key *key = keydata;
-		int equal = !strncmp(a->orig, key->orig, key->orig_len) &&
-			    !a->orig[key->orig_len];
+		int equal = !xstrncmpz(a->orig, key->orig, key->orig_len);
 		return !equal;
 	}
 
@@ -138,43 +144,56 @@
 	return strcmp(a->orig, b->orig);
 }
 
+static struct anonymized_entry *add_anonymized_entry(struct hashmap *map,
+						     unsigned hash,
+						     const char *orig, size_t len,
+						     char *anon)
+{
+	struct anonymized_entry *ret, *old;
+
+	if (!map->cmpfn)
+		hashmap_init(map, anonymized_entry_cmp, NULL, 0);
+
+	FLEX_ALLOC_MEM(ret, orig, orig, len);
+	hashmap_entry_init(&ret->hash, hash);
+	ret->anon = anon;
+	old = hashmap_put_entry(map, ret, hash);
+
+	if (old) {
+		free(old->anon);
+		free(old);
+	}
+
+	return ret;
+}
+
 /*
  * Basically keep a cache of X->Y so that we can repeatedly replace
  * the same anonymized string with another. The actual generation
  * is farmed out to the generate function.
  */
 static const char *anonymize_str(struct hashmap *map,
-				 char *(*generate)(void *),
-				 const char *orig, size_t len,
-				 void *data)
+				 char *(*generate)(void),
+				 const char *orig, size_t len)
 {
 	struct anonymized_entry_key key;
 	struct anonymized_entry *ret;
 
-	if (!map->cmpfn)
-		hashmap_init(map, anonymized_entry_cmp, NULL, 0);
-
 	hashmap_entry_init(&key.hash, memhash(orig, len));
 	key.orig = orig;
 	key.orig_len = len;
 
 	/* First check if it's a token the user configured manually... */
-	if (anonymized_seeds.cmpfn)
-		ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
-	else
-		ret = NULL;
+	ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
 
 	/* ...otherwise check if we've already seen it in this context... */
 	if (!ret)
 		ret = hashmap_get_entry(map, &key, hash, &key);
 
 	/* ...and finally generate a new mapping if necessary */
-	if (!ret) {
-		FLEX_ALLOC_MEM(ret, orig, orig, len);
-		hashmap_entry_init(&ret->hash, key.hash.hash);
-		ret->anon = generate(data);
-		hashmap_put(map, &ret->hash);
-	}
+	if (!ret)
+		ret = add_anonymized_entry(map, key.hash.hash,
+					   orig, len, generate());
 
 	return ret->anon;
 }
@@ -187,12 +206,12 @@
  */
 static void anonymize_path(struct strbuf *out, const char *path,
 			   struct hashmap *map,
-			   char *(*generate)(void *))
+			   char *(*generate)(void))
 {
 	while (*path) {
 		const char *end_of_component = strchrnul(path, '/');
 		size_t len = end_of_component - path;
-		const char *c = anonymize_str(map, generate, path, len, NULL);
+		const char *c = anonymize_str(map, generate, path, len);
 		strbuf_addstr(out, c);
 		path = end_of_component;
 		if (*path)
@@ -296,7 +315,7 @@
 		object = (struct object *)lookup_blob(the_repository, oid);
 		eaten = 0;
 	} else {
-		buf = read_object_file(oid, &type, &size);
+		buf = repo_read_object_file(the_repository, oid, &type, &size);
 		if (!buf)
 			die("could not read blob %s", oid_to_hex(oid));
 		if (check_object_signature(the_repository, oid, buf, size,
@@ -367,7 +386,7 @@
 		printf("%s", path);
 }
 
-static char *anonymize_path_component(void *data)
+static char *anonymize_path_component(void)
 {
 	static int counter;
 	struct strbuf out = STRBUF_INIT;
@@ -389,7 +408,7 @@
 	}
 }
 
-static char *generate_fake_oid(void *data)
+static char *generate_fake_oid(void)
 {
 	static uint32_t counter = 1; /* avoid null oid */
 	const unsigned hashsz = the_hash_algo->rawsz;
@@ -405,7 +424,7 @@
 {
 	static struct hashmap objs;
 	size_t len = strlen(oid_hex);
-	return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL);
+	return anonymize_str(&objs, generate_fake_oid, oid_hex, len);
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
@@ -502,7 +521,7 @@
 	return bol;
 }
 
-static char *anonymize_ref_component(void *data)
+static char *anonymize_ref_component(void)
 {
 	static int counter;
 	struct strbuf out = STRBUF_INIT;
@@ -542,13 +561,13 @@
  * We do not even bother to cache commit messages, as they are unlikely
  * to be repeated verbatim, and it is not that interesting when they are.
  */
-static char *anonymize_commit_message(const char *old)
+static char *anonymize_commit_message(void)
 {
 	static int counter;
 	return xstrfmt("subject %d\n\nbody\n", counter++);
 }
 
-static char *anonymize_ident(void *data)
+static char *anonymize_ident(void)
 {
 	static int counter;
 	struct strbuf out = STRBUF_INIT;
@@ -591,7 +610,7 @@
 
 		len = split.mail_end - split.name_begin;
 		ident = anonymize_str(&idents, anonymize_ident,
-				      split.name_begin, len, NULL);
+				      split.name_begin, len);
 		strbuf_addstr(out, ident);
 		strbuf_addch(out, ' ');
 		strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
@@ -618,7 +637,7 @@
 	rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
 
 	parse_commit_or_die(commit);
-	commit_buffer = get_commit_buffer(commit, NULL);
+	commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL);
 	author = strstr(commit_buffer, "\nauthor ");
 	if (!author)
 		die("could not find author in commit %s",
@@ -669,7 +688,7 @@
 
 	mark_next_object(&commit->object);
 	if (anonymize) {
-		reencoded = anonymize_commit_message(message);
+		reencoded = anonymize_commit_message();
 	} else if (encoding) {
 		switch(reencode_mode) {
 		case REENCODE_YES:
@@ -699,7 +718,7 @@
 			  ? strlen(message) : 0),
 	       reencoded ? reencoded : message ? message : "");
 	free(reencoded);
-	unuse_commit_buffer(commit, commit_buffer);
+	repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
 
 	for (i = 0, p = commit->parents; p; p = p->next) {
 		struct object *obj = &p->item->object;
@@ -732,7 +751,7 @@
 	show_progress();
 }
 
-static char *anonymize_tag(void *data)
+static char *anonymize_tag(void)
 {
 	static int counter;
 	struct strbuf out = STRBUF_INIT;
@@ -766,7 +785,8 @@
 		return;
 	}
 
-	buf = read_object_file(&tag->object.oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &tag->object.oid, &type,
+				    &size);
 	if (!buf)
 		die("could not read tag %s", oid_to_hex(&tag->object.oid));
 	message = memmem(buf, size, "\n\n", 2);
@@ -794,7 +814,7 @@
 		if (message) {
 			static struct hashmap tags;
 			message = anonymize_str(&tags, anonymize_tag,
-						message, message_size, NULL);
+						message, message_size);
 			message_size = strlen(message);
 		}
 	}
@@ -917,7 +937,8 @@
 		if (e->flags & UNINTERESTING)
 			continue;
 
-		if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1)
+		if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
+				  &oid, &full_name, 0) != 1)
 			continue;
 
 		if (refspecs.nr) {
@@ -1125,11 +1146,6 @@
 	}
 }
 
-static char *anonymize_seed(void *data)
-{
-	return xstrdup(data);
-}
-
 static int parse_opt_anonymize_map(const struct option *opt,
 				   const char *arg, int unset)
 {
@@ -1151,7 +1167,8 @@
 	if (!keylen || !*value)
 		return error(_("--anonymize-map token cannot be empty"));
 
-	anonymize_str(map, anonymize_seed, arg, keylen, (void *)value);
+	add_anonymized_entry(map, memhash(arg, keylen), arg, keylen,
+			     xstrdup(value));
 
 	return 0;
 }
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 7134683..782bda0 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1,5 +1,8 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "lockfile.h"
@@ -9,13 +12,16 @@
 #include "commit.h"
 #include "delta.h"
 #include "pack.h"
+#include "path.h"
 #include "refs.h"
 #include "csum-file.h"
 #include "quote.h"
 #include "dir.h"
 #include "run-command.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "mem-pool.h"
 #include "commit-reach.h"
 #include "khash.h"
@@ -175,6 +181,7 @@
 static unsigned int show_stats = 1;
 static int global_argc;
 static const char **global_argv;
+static const char *global_prefix;
 
 /* Memory pools */
 static struct mem_pool fi_mem_pool = {
@@ -436,7 +443,7 @@
 
 #else
 
-static void checkpoint_signal(int signo)
+static void checkpoint_signal(int signo UNUSED)
 {
 	checkpoint_requested = 1;
 }
@@ -1095,6 +1102,7 @@
 		|| (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size)
 		cycle_packfile();
 
+	the_hash_algo->init_fn(&checkpoint.ctx);
 	hashfile_checkpoint(pack_file, &checkpoint);
 	offset = checkpoint.offset;
 
@@ -1228,20 +1236,6 @@
 	return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep);
 }
 
-static const char *get_mode(const char *str, uint16_t *modep)
-{
-	unsigned char c;
-	uint16_t mode = 0;
-
-	while ((c = *str++) != ' ') {
-		if (c < '0' || c > '7')
-			return NULL;
-		mode = (mode << 3) + (c - '0');
-	}
-	*modep = mode;
-	return str;
-}
-
 static void load_tree(struct tree_entry *root)
 {
 	struct object_id *oid = &root->versions[1].oid;
@@ -1265,7 +1259,7 @@
 			die("Can't load tree %s", oid_to_hex(oid));
 	} else {
 		enum object_type type;
-		buf = read_object_file(oid, &type, &size);
+		buf = repo_read_object_file(the_repository, oid, &type, &size);
 		if (!buf || type != OBJ_TREE)
 			die("Can't load tree %s", oid_to_hex(oid));
 	}
@@ -1279,7 +1273,7 @@
 		t->entries[t->entry_count++] = e;
 
 		e->tree = NULL;
-		c = get_mode(c, &e->versions[1].mode);
+		c = parse_mode(c, &e->versions[1].mode);
 		if (!c)
 			die("Corrupt mode in %s", oid_to_hex(oid));
 		e->versions[0].mode = e->versions[1].mode;
@@ -1617,6 +1611,7 @@
 		oidclr(&old_oid);
 	if (!force_update && !is_null_oid(&old_oid)) {
 		struct commit *old_cmit, *new_cmit;
+		int ret;
 
 		old_cmit = lookup_commit_reference_gently(the_repository,
 							  &old_oid, 0);
@@ -1625,7 +1620,10 @@
 		if (!old_cmit || !new_cmit)
 			return error("Branch %s is missing commits.", b->name);
 
-		if (!in_merge_bases(old_cmit, new_cmit)) {
+		ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit);
+		if (ret < 0)
+			exit(128);
+		if (!ret) {
 			warning("Not updating %s"
 				" (new tip %s does not contain %s)",
 				b->name, oid_to_hex(&b->oid),
@@ -2268,7 +2266,7 @@
 	struct object_id oid;
 	uint16_t mode, inline_data = 0;
 
-	p = get_mode(p, &mode);
+	p = parse_mode(p, &mode);
 	if (!p)
 		die("Corrupt mode: %s", command_buf.buf);
 	switch (mode) {
@@ -2486,7 +2484,7 @@
 		if (commit_oe->type != OBJ_COMMIT)
 			die("Mark :%" PRIuMAX " not a commit", commit_mark);
 		oidcpy(&commit_oid, &commit_oe->idx.oid);
-	} else if (!get_oid(p, &commit_oid)) {
+	} else if (!repo_get_oid(the_repository, p, &commit_oid)) {
 		unsigned long size;
 		char *buf = read_object_with_reference(the_repository,
 						       &commit_oid,
@@ -2599,7 +2597,7 @@
 			} else
 				parse_from_existing(b);
 		}
-	} else if (!get_oid(objectish, &b->oid)) {
+	} else if (!repo_get_oid(the_repository, objectish, &b->oid)) {
 		parse_from_existing(b);
 		if (is_null_oid(&b->oid))
 			b->delete = 1;
@@ -2654,7 +2652,7 @@
 			if (oe->type != OBJ_COMMIT)
 				die("Mark :%" PRIuMAX " not a commit", idnum);
 			oidcpy(&n->oid, &oe->idx.oid);
-		} else if (!get_oid(from, &n->oid)) {
+		} else if (!repo_get_oid(the_repository, from, &n->oid)) {
 			unsigned long size;
 			char *buf = read_object_with_reference(the_repository,
 							       &n->oid,
@@ -2801,8 +2799,7 @@
 	enum object_type type;
 	const char *v;
 
-	t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag));
-	memset(t, 0, sizeof(struct tag));
+	t = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct tag));
 	t->name = mem_pool_strdup(&fi_mem_pool, arg);
 	if (last_tag)
 		last_tag->next_tag = t;
@@ -2827,7 +2824,7 @@
 		oe = find_mark(marks, from_mark);
 		type = oe->type;
 		oidcpy(&oid, &oe->idx.oid);
-	} else if (!get_oid(from, &oid)) {
+	} else if (!repo_get_oid(the_repository, from, &oid)) {
 		struct object_entry *oe = find_object(&oid);
 		if (!oe) {
 			type = oid_object_info(the_repository, &oid, NULL);
@@ -2936,7 +2933,7 @@
 	char *buf;
 
 	if (!oe || oe->pack_id == MAX_PACK_ID) {
-		buf = read_object_file(oid, &type, &size);
+		buf = repo_read_object_file(the_repository, oid, &type, &size);
 	} else {
 		type = oe->type;
 		buf = gfi_unpack_entry(oe, &size);
@@ -3044,7 +3041,8 @@
 		buf = gfi_unpack_entry(oe, &size);
 	} else {
 		enum object_type unused;
-		buf = read_object_file(oid, &unused, &size);
+		buf = repo_read_object_file(the_repository, oid, &unused,
+					    &size);
 	}
 	if (!buf)
 		die("Can't load object %s", oid_to_hex(oid));
@@ -3245,7 +3243,7 @@
 static char* make_fast_import_path(const char *path)
 {
 	if (!relative_marks_paths || is_absolute_path(path))
-		return xstrdup(path);
+		return prefix_filename(global_prefix, path);
 	return git_pathdup("info/fast-import/%s", path);
 }
 
@@ -3316,9 +3314,11 @@
 
 static void option_export_pack_edges(const char *edges)
 {
+	char *fn = prefix_filename(global_prefix, edges);
 	if (pack_edges)
 		fclose(pack_edges);
-	pack_edges = xfopen(edges, "a");
+	pack_edges = xfopen(fn, "a");
+	free(fn);
 }
 
 static void option_rewrite_submodules(const char *arg, struct string_list *list)
@@ -3333,11 +3333,13 @@
 	f++;
 	CALLOC_ARRAY(ms, 1);
 
+	f = prefix_filename(global_prefix, f);
 	fp = fopen(f, "r");
 	if (!fp)
 		die_errno("cannot read '%s'", f);
 	read_mark_file(&ms, fp, insert_oid_entry);
 	fclose(fp);
+	free(f);
 
 	string_list_insert(list, s)->util = ms;
 }
@@ -3551,6 +3553,7 @@
 
 	global_argc = argc;
 	global_argv = argv;
+	global_prefix = prefix;
 
 	rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free));
 	for (i = 0; i < (cmd_save - 1); i++)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index afe6793..44c05ee 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1,4 +1,7 @@
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
 #include "pkt-line.h"
 #include "fetch-pack.h"
 #include "remote.h"
@@ -40,7 +43,7 @@
 	(*sought)[*nr - 1] = ref;
 }
 
-int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
 {
 	int i, ret;
 	struct ref *ref = NULL;
@@ -211,8 +214,8 @@
 		int flags = args.verbose ? CONNECT_VERBOSE : 0;
 		if (args.diag_url)
 			flags |= CONNECT_DIAG_URL;
-		conn = git_connect(fd, dest, args.uploadpack,
-				   flags);
+		conn = git_connect(fd, dest, "git-upload-pack",
+				   args.uploadpack, flags);
 		if (!conn)
 			return args.diag_url ? 0 : 1;
 	}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index a09606b..5857d86 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1,15 +1,20 @@
 /*
  * "git fetch"
  */
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
 #include "config.h"
+#include "gettext.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "oidset.h"
+#include "oid-array.h"
 #include "commit.h"
-#include "builtin.h"
 #include "string-list.h"
 #include "remote.h"
 #include "transport.h"
@@ -21,14 +26,17 @@
 #include "connected.h"
 #include "strvec.h"
 #include "utf8.h"
-#include "packfile.h"
+#include "pager.h"
+#include "path.h"
+#include "pkt-line.h"
 #include "list-objects-filter-options.h"
 #include "commit-reach.h"
 #include "branch.h"
 #include "promisor-remote.h"
 #include "commit-graph.h"
 #include "shallow.h"
-#include "worktree.h"
+#include "trace.h"
+#include "trace2.h"
 #include "bundle-uri.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -47,25 +55,35 @@
 	TAGS_SET = 2
 };
 
-static int fetch_prune_config = -1; /* unspecified */
-static int fetch_show_forced_updates = 1;
+enum display_format {
+	DISPLAY_FORMAT_FULL,
+	DISPLAY_FORMAT_COMPACT,
+	DISPLAY_FORMAT_PORCELAIN,
+};
+
+struct display_state {
+	struct strbuf buf;
+
+	int refcol_width;
+	enum display_format format;
+
+	char *url;
+	int url_len, shown_url;
+};
+
 static uint64_t forced_updates_ms = 0;
 static int prefetch = 0;
 static int prune = -1; /* unspecified */
 #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
 
-static int fetch_prune_tags_config = -1; /* unspecified */
 static int prune_tags = -1; /* unspecified */
 #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 
-static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int append, dry_run, force, keep, update_head_ok;
 static int write_fetch_head = 1;
 static int verbosity, deepen_relative, set_upstream, refetch;
 static int progress = -1;
-static int enable_auto_gc = 1;
-static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_jobs = -1, submodule_fetch_jobs_config = -1;
-static int fetch_parallel_config = 1;
+static int tags = TAGS_DEFAULT, update_shallow, deepen;
 static int atomic_fetch;
 static enum transport_family family;
 static const char *depth;
@@ -75,60 +93,84 @@
 static struct strbuf default_rla = STRBUF_INIT;
 static struct transport *gtransport;
 static struct transport *gsecondary;
-static const char *submodule_prefix = "";
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
-static int shown_url = 0;
 static struct refspec refmap = REFSPEC_INIT_FETCH;
 static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
 static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
-static int fetch_write_commit_graph = -1;
-static int stdin_refspecs = 0;
-static int negotiate_only;
 
-static int git_fetch_config(const char *k, const char *v, void *cb)
+struct fetch_config {
+	enum display_format display_format;
+	int all;
+	int prune;
+	int prune_tags;
+	int show_forced_updates;
+	int recurse_submodules;
+	int parallel;
+	int submodule_fetch_jobs;
+};
+
+static int git_fetch_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
+	struct fetch_config *fetch_config = cb;
+
+	if (!strcmp(k, "fetch.all")) {
+		fetch_config->all = git_config_bool(k, v);
+		return 0;
+	}
+
 	if (!strcmp(k, "fetch.prune")) {
-		fetch_prune_config = git_config_bool(k, v);
+		fetch_config->prune = git_config_bool(k, v);
 		return 0;
 	}
 
 	if (!strcmp(k, "fetch.prunetags")) {
-		fetch_prune_tags_config = git_config_bool(k, v);
+		fetch_config->prune_tags = git_config_bool(k, v);
 		return 0;
 	}
 
 	if (!strcmp(k, "fetch.showforcedupdates")) {
-		fetch_show_forced_updates = git_config_bool(k, v);
+		fetch_config->show_forced_updates = git_config_bool(k, v);
 		return 0;
 	}
 
 	if (!strcmp(k, "submodule.recurse")) {
 		int r = git_config_bool(k, v) ?
 			RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
-		recurse_submodules = r;
+		fetch_config->recurse_submodules = r;
+		return 0;
 	}
 
 	if (!strcmp(k, "submodule.fetchjobs")) {
-		submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
+		fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v, ctx->kvi);
 		return 0;
 	} else if (!strcmp(k, "fetch.recursesubmodules")) {
-		recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+		fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
 		return 0;
 	}
 
 	if (!strcmp(k, "fetch.parallel")) {
-		fetch_parallel_config = git_config_int(k, v);
-		if (fetch_parallel_config < 0)
+		fetch_config->parallel = git_config_int(k, v, ctx->kvi);
+		if (fetch_config->parallel < 0)
 			die(_("fetch.parallel cannot be negative"));
-		if (!fetch_parallel_config)
-			fetch_parallel_config = online_cpus();
+		if (!fetch_config->parallel)
+			fetch_config->parallel = online_cpus();
 		return 0;
 	}
 
-	return git_default_config(k, v, cb);
+	if (!strcmp(k, "fetch.output")) {
+		if (!v)
+			return config_error_nonbool(k);
+		else if (!strcasecmp(v, "full"))
+			fetch_config->display_format = DISPLAY_FORMAT_FULL;
+		else if (!strcasecmp(v, "compact"))
+			fetch_config->display_format = DISPLAY_FORMAT_COMPACT;
+		else
+			die(_("invalid value for '%s': '%s'"),
+			    "fetch.output", v);
+	}
+
+	return git_default_config(k, v, ctx, cb);
 }
 
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
@@ -139,97 +181,11 @@
 	 * "git fetch --refmap='' origin foo"
 	 * can be used to tell the command not to store anywhere
 	 */
-	refspec_append(&refmap, arg);
+	refspec_append(opt->value, arg);
 
 	return 0;
 }
 
-static struct option builtin_fetch_options[] = {
-	OPT__VERBOSITY(&verbosity),
-	OPT_BOOL(0, "all", &all,
-		 N_("fetch from all remotes")),
-	OPT_BOOL(0, "set-upstream", &set_upstream,
-		 N_("set upstream for git pull/fetch")),
-	OPT_BOOL('a', "append", &append,
-		 N_("append to .git/FETCH_HEAD instead of overwriting")),
-	OPT_BOOL(0, "atomic", &atomic_fetch,
-		 N_("use atomic transaction to update references")),
-	OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
-		   N_("path to upload pack on remote end")),
-	OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
-	OPT_BOOL('m', "multiple", &multiple,
-		 N_("fetch from multiple remotes")),
-	OPT_SET_INT('t', "tags", &tags,
-		    N_("fetch all tags and associated objects"), TAGS_SET),
-	OPT_SET_INT('n', NULL, &tags,
-		    N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
-	OPT_INTEGER('j', "jobs", &max_jobs,
-		    N_("number of submodules fetched in parallel")),
-	OPT_BOOL(0, "prefetch", &prefetch,
-		 N_("modify the refspec to place all refs within refs/prefetch/")),
-	OPT_BOOL('p', "prune", &prune,
-		 N_("prune remote-tracking branches no longer on remote")),
-	OPT_BOOL('P', "prune-tags", &prune_tags,
-		 N_("prune local tags no longer on remote and clobber changed tags")),
-	OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
-		    N_("control recursive fetching of submodules"),
-		    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
-	OPT_BOOL(0, "dry-run", &dry_run,
-		 N_("dry run")),
-	OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
-		 N_("write fetched references to the FETCH_HEAD file")),
-	OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
-	OPT_BOOL('u', "update-head-ok", &update_head_ok,
-		    N_("allow updating of HEAD ref")),
-	OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
-	OPT_STRING(0, "depth", &depth, N_("depth"),
-		   N_("deepen history of shallow clone")),
-	OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
-		   N_("deepen history of shallow repository based on time")),
-	OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
-			N_("deepen history of shallow clone, excluding rev")),
-	OPT_INTEGER(0, "deepen", &deepen_relative,
-		    N_("deepen history of shallow clone")),
-	OPT_SET_INT_F(0, "unshallow", &unshallow,
-		      N_("convert to a complete repository"),
-		      1, PARSE_OPT_NONEG),
-	OPT_SET_INT_F(0, "refetch", &refetch,
-		      N_("re-fetch without negotiating common commits"),
-		      1, PARSE_OPT_NONEG),
-	{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
-		   N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
-	OPT_CALLBACK_F(0, "recurse-submodules-default",
-		   &recurse_submodules_default, N_("on-demand"),
-		   N_("default for recursive fetching of submodules "
-		      "(lower priority than config files)"),
-		   PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
-	OPT_BOOL(0, "update-shallow", &update_shallow,
-		 N_("accept refs that update .git/shallow")),
-	OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
-		       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_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
-			TRANSPORT_FAMILY_IPV4),
-	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
-			TRANSPORT_FAMILY_IPV6),
-	OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
-			N_("report that we have only objects reachable from this object")),
-	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),
-	OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
-		 N_("run 'maintenance --auto' after fetching")),
-	OPT_BOOL(0, "auto-gc", &enable_auto_gc,
-		 N_("run 'maintenance --auto' after fetching")),
-	OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
-		 N_("check for forced-updates on all updated branches")),
-	OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
-		 N_("write the commit-graph after fetching")),
-	OPT_BOOL(0, "stdin", &stdin_refspecs,
-		 N_("accept refspecs from stdin")),
-	OPT_END()
-};
-
 static void unlock_pack(unsigned int flags)
 {
 	if (gtransport)
@@ -357,7 +313,7 @@
 
 
 static void add_already_queued_tags(const char *refname,
-				    const struct object_id *old_oid,
+				    const struct object_id *old_oid UNUSED,
 				    const struct object_id *new_oid,
 				    void *cb_data)
 {
@@ -407,9 +363,9 @@
 		 */
 		if (ends_with(ref->name, "^{}")) {
 			if (item &&
-			    !has_object_file_with_flags(&ref->old_oid, quick_flags) &&
+			    !repo_has_object_file_with_flags(the_repository, &ref->old_oid, quick_flags) &&
 			    !oidset_contains(&fetch_oids, &ref->old_oid) &&
-			    !has_object_file_with_flags(&item->oid, quick_flags) &&
+			    !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
 			    !oidset_contains(&fetch_oids, &item->oid))
 				clear_item(item);
 			item = NULL;
@@ -423,7 +379,7 @@
 		 * fetch.
 		 */
 		if (item &&
-		    !has_object_file_with_flags(&item->oid, quick_flags) &&
+		    !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
 		    !oidset_contains(&fetch_oids, &item->oid))
 			clear_item(item);
 
@@ -444,7 +400,7 @@
 	 * checked to see if it needs fetching.
 	 */
 	if (item &&
-	    !has_object_file_with_flags(&item->oid, quick_flags) &&
+	    !repo_has_object_file_with_flags(the_repository, &item->oid, quick_flags) &&
 	    !oidset_contains(&fetch_oids, &item->oid))
 		clear_item(item);
 
@@ -493,9 +449,8 @@
 			continue;
 		if (!rs->items[i].dst ||
 		    (rs->items[i].src &&
-		     !strncmp(rs->items[i].src,
-			      ref_namespace[NAMESPACE_TAGS].ref,
-			      strlen(ref_namespace[NAMESPACE_TAGS].ref)))) {
+		     starts_with(rs->items[i].src,
+				 ref_namespace[NAMESPACE_TAGS].ref))) {
 			int j;
 
 			free(rs->items[i].src);
@@ -741,76 +696,97 @@
 	return ret;
 }
 
-static int refcol_width = 10;
-static int compact_format;
-
-static void adjust_refcol_width(const struct ref *ref)
+static int refcol_width(const struct ref *ref_map, int compact_format)
 {
-	int max, rlen, llen, len;
+	const struct ref *ref;
+	int max, width = 10;
 
-	/* uptodate lines are only shown on high verbosity level */
-	if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
-		return;
-
-	max    = term_columns();
-	rlen   = utf8_strwidth(prettify_refname(ref->name));
-
-	llen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));
-
-	/*
-	 * rough estimation to see if the output line is too long and
-	 * should not be counted (we can't do precise calculation
-	 * anyway because we don't know if the error explanation part
-	 * will be printed in update_local_ref)
-	 */
-	if (compact_format) {
-		llen = 0;
+	max = term_columns();
+	if (compact_format)
 		max = max * 2 / 3;
-	}
-	len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
-	if (len >= max)
-		return;
 
-	/*
-	 * Not precise calculation for compact mode because '*' can
-	 * appear on the left hand side of '->' and shrink the column
-	 * back.
-	 */
-	if (refcol_width < rlen)
-		refcol_width = rlen;
+	for (ref = ref_map; ref; ref = ref->next) {
+		int rlen, llen = 0, len;
+
+		if (ref->status == REF_STATUS_REJECT_SHALLOW ||
+		    !ref->peer_ref ||
+		    !strcmp(ref->name, "HEAD"))
+			continue;
+
+		/* uptodate lines are only shown on high verbosity level */
+		if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
+			continue;
+
+		rlen = utf8_strwidth(prettify_refname(ref->name));
+		if (!compact_format)
+			llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+		/*
+		 * rough estimation to see if the output line is too long and
+		 * should not be counted (we can't do precise calculation
+		 * anyway because we don't know if the error explanation part
+		 * will be printed in update_local_ref)
+		 */
+		len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+		if (len >= max)
+			continue;
+
+		if (width < rlen)
+			width = rlen;
+	}
+
+	return width;
 }
 
-static void prepare_format_display(struct ref *ref_map)
+static void display_state_init(struct display_state *display_state, struct ref *ref_map,
+			       const char *raw_url, enum display_format format)
 {
-	struct ref *rm;
-	const char *format = "full";
+	int i;
+
+	memset(display_state, 0, sizeof(*display_state));
+	strbuf_init(&display_state->buf, 0);
+	display_state->format = format;
+
+	if (raw_url)
+		display_state->url = transport_anonymize_url(raw_url);
+	else
+		display_state->url = xstrdup("foreign");
+
+	display_state->url_len = strlen(display_state->url);
+	for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--)
+		;
+	display_state->url_len = i + 1;
+	if (4 < i && !strncmp(".git", display_state->url + i - 3, 4))
+		display_state->url_len = i - 3;
 
 	if (verbosity < 0)
 		return;
 
-	git_config_get_string_tmp("fetch.output", &format);
-	if (!strcasecmp(format, "full"))
-		compact_format = 0;
-	else if (!strcasecmp(format, "compact"))
-		compact_format = 1;
-	else
-		die(_("invalid value for '%s': '%s'"),
-		    "fetch.output", format);
-
-	for (rm = ref_map; rm; rm = rm->next) {
-		if (rm->status == REF_STATUS_REJECT_SHALLOW ||
-		    !rm->peer_ref ||
-		    !strcmp(rm->name, "HEAD"))
-			continue;
-
-		adjust_refcol_width(rm);
+	switch (display_state->format) {
+	case DISPLAY_FORMAT_FULL:
+	case DISPLAY_FORMAT_COMPACT:
+		display_state->refcol_width = refcol_width(ref_map,
+							   display_state->format == DISPLAY_FORMAT_COMPACT);
+		break;
+	case DISPLAY_FORMAT_PORCELAIN:
+		/* We don't need to precompute anything here. */
+		break;
+	default:
+		BUG("unexpected display format %d", display_state->format);
 	}
 }
 
-static void print_remote_to_local(struct strbuf *display,
+static void display_state_release(struct display_state *display_state)
+{
+	strbuf_release(&display_state->buf);
+	free(display_state->url);
+}
+
+static void print_remote_to_local(struct display_state *display_state,
 				  const char *remote, const char *local)
 {
-	strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+	strbuf_addf(&display_state->buf, "%-*s -> %s",
+		    display_state->refcol_width, remote, local);
 }
 
 static int find_and_replace(struct strbuf *haystack,
@@ -840,14 +816,14 @@
 	return 1;
 }
 
-static void print_compact(struct strbuf *display,
+static void print_compact(struct display_state *display_state,
 			  const char *remote, const char *local)
 {
 	struct strbuf r = STRBUF_INIT;
 	struct strbuf l = STRBUF_INIT;
 
 	if (!strcmp(remote, local)) {
-		strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+		strbuf_addf(&display_state->buf, "%-*s -> *", display_state->refcol_width, remote);
 		return;
 	}
 
@@ -856,40 +832,74 @@
 
 	if (!find_and_replace(&r, local, "*"))
 		find_and_replace(&l, remote, "*");
-	print_remote_to_local(display, r.buf, l.buf);
+	print_remote_to_local(display_state, r.buf, l.buf);
 
 	strbuf_release(&r);
 	strbuf_release(&l);
 }
 
-static void format_display(struct strbuf *display, char code,
-			   const char *summary, const char *error,
-			   const char *remote, const char *local,
-			   int summary_width)
+static void display_ref_update(struct display_state *display_state, char code,
+			       const char *summary, const char *error,
+			       const char *remote, const char *local,
+			       const struct object_id *old_oid,
+			       const struct object_id *new_oid,
+			       int summary_width)
 {
-	int width;
+	FILE *f = stderr;
 
 	if (verbosity < 0)
 		return;
 
-	width = (summary_width + strlen(summary) - gettext_width(summary));
+	strbuf_reset(&display_state->buf);
 
-	strbuf_addf(display, "%c %-*s ", code, width, summary);
-	if (!compact_format)
-		print_remote_to_local(display, remote, local);
-	else
-		print_compact(display, remote, local);
-	if (error)
-		strbuf_addf(display, "  (%s)", error);
+	switch (display_state->format) {
+	case DISPLAY_FORMAT_FULL:
+	case DISPLAY_FORMAT_COMPACT: {
+		int width;
+
+		if (!display_state->shown_url) {
+			strbuf_addf(&display_state->buf, _("From %.*s\n"),
+				    display_state->url_len, display_state->url);
+			display_state->shown_url = 1;
+		}
+
+		width = (summary_width + strlen(summary) - gettext_width(summary));
+		remote = prettify_refname(remote);
+		local = prettify_refname(local);
+
+		strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
+
+		if (display_state->format != DISPLAY_FORMAT_COMPACT)
+			print_remote_to_local(display_state, remote, local);
+		else
+			print_compact(display_state, remote, local);
+
+		if (error)
+			strbuf_addf(&display_state->buf, "  (%s)", error);
+
+		break;
+	}
+	case DISPLAY_FORMAT_PORCELAIN:
+		strbuf_addf(&display_state->buf, "%c %s %s %s", code,
+			    oid_to_hex(old_oid), oid_to_hex(new_oid), local);
+		f = stdout;
+		break;
+	default:
+		BUG("unexpected display format %d", display_state->format);
+	};
+	strbuf_addch(&display_state->buf, '\n');
+
+	fputs(display_state->buf.buf, f);
 }
 
 static int update_local_ref(struct ref *ref,
 			    struct ref_transaction *transaction,
-			    const char *remote, const struct ref *remote_ref,
-			    struct strbuf *display, int summary_width)
+			    struct display_state *display_state,
+			    const struct ref *remote_ref,
+			    int summary_width,
+			    const struct fetch_config *config)
 {
 	struct commit *current = NULL, *updated;
-	const char *pretty_ref = prettify_refname(ref->name);
 	int fast_forward = 0;
 
 	if (!repo_has_object_file(the_repository, &ref->new_oid))
@@ -897,8 +907,9 @@
 
 	if (oideq(&ref->old_oid, &ref->new_oid)) {
 		if (verbosity > 0)
-			format_display(display, '=', _("[up to date]"), NULL,
-				       remote, pretty_ref, summary_width);
+			display_ref_update(display_state, '=', _("[up to date]"), NULL,
+					   remote_ref->name, ref->name,
+					   &ref->old_oid, &ref->new_oid, summary_width);
 		return 0;
 	}
 
@@ -909,9 +920,10 @@
 		 * If this is the head, and it's not okay to update
 		 * the head, and the old value of the head isn't empty...
 		 */
-		format_display(display, '!', _("[rejected]"),
-			       _("can't fetch into checked-out branch"),
-			       remote, pretty_ref, summary_width);
+		display_ref_update(display_state, '!', _("[rejected]"),
+				   _("can't fetch into checked-out branch"),
+				   remote_ref->name, ref->name,
+				   &ref->old_oid, &ref->new_oid, summary_width);
 		return 1;
 	}
 
@@ -920,13 +932,16 @@
 		if (force || ref->force) {
 			int r;
 			r = s_update_ref("updating tag", ref, transaction, 0);
-			format_display(display, r ? '!' : 't', _("[tag update]"),
-				       r ? _("unable to update local ref") : NULL,
-				       remote, pretty_ref, summary_width);
+			display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
+					   r ? _("unable to update local ref") : NULL,
+					   remote_ref->name, ref->name,
+					   &ref->old_oid, &ref->new_oid, summary_width);
 			return r;
 		} else {
-			format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
-				       remote, pretty_ref, summary_width);
+			display_ref_update(display_state, '!', _("[rejected]"),
+					   _("would clobber existing tag"),
+					   remote_ref->name, ref->name,
+					   &ref->old_oid, &ref->new_oid, summary_width);
 			return 1;
 		}
 	}
@@ -944,11 +959,10 @@
 		 * Base this on the remote's ref name, as it's
 		 * more likely to follow a standard layout.
 		 */
-		const char *name = remote_ref ? remote_ref->name : "";
-		if (starts_with(name, "refs/tags/")) {
+		if (starts_with(remote_ref->name, "refs/tags/")) {
 			msg = "storing tag";
 			what = _("[new tag]");
-		} else if (starts_with(name, "refs/heads/")) {
+		} else if (starts_with(remote_ref->name, "refs/heads/")) {
 			msg = "storing head";
 			what = _("[new branch]");
 		} else {
@@ -957,15 +971,19 @@
 		}
 
 		r = s_update_ref(msg, ref, transaction, 0);
-		format_display(display, r ? '!' : '*', what,
-			       r ? _("unable to update local ref") : NULL,
-			       remote, pretty_ref, summary_width);
+		display_ref_update(display_state, r ? '!' : '*', what,
+				   r ? _("unable to update local ref") : NULL,
+				   remote_ref->name, ref->name,
+				   &ref->old_oid, &ref->new_oid, summary_width);
 		return r;
 	}
 
-	if (fetch_show_forced_updates) {
+	if (config->show_forced_updates) {
 		uint64_t t_before = getnanotime();
-		fast_forward = in_merge_bases(current, updated);
+		fast_forward = repo_in_merge_bases(the_repository, current,
+						   updated);
+		if (fast_forward < 0)
+			exit(128);
 		forced_updates_ms += (getnanotime() - t_before) / 1000000;
 	} else {
 		fast_forward = 1;
@@ -979,9 +997,10 @@
 		strbuf_addstr(&quickref, "..");
 		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
 		r = s_update_ref("fast-forward", ref, transaction, 1);
-		format_display(display, r ? '!' : ' ', quickref.buf,
-			       r ? _("unable to update local ref") : NULL,
-			       remote, pretty_ref, summary_width);
+		display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
+				   r ? _("unable to update local ref") : NULL,
+				   remote_ref->name, ref->name,
+				   &ref->old_oid, &ref->new_oid, summary_width);
 		strbuf_release(&quickref);
 		return r;
 	} else if (force || ref->force) {
@@ -991,14 +1010,16 @@
 		strbuf_addstr(&quickref, "...");
 		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
 		r = s_update_ref("forced-update", ref, transaction, 1);
-		format_display(display, r ? '!' : '+', quickref.buf,
-			       r ? _("unable to update local ref") : _("forced update"),
-			       remote, pretty_ref, summary_width);
+		display_ref_update(display_state, r ? '!' : '+', quickref.buf,
+				   r ? _("unable to update local ref") : _("forced update"),
+				   remote_ref->name, ref->name,
+				   &ref->old_oid, &ref->new_oid, summary_width);
 		strbuf_release(&quickref);
 		return r;
 	} else {
-		format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
-			       remote, pretty_ref, summary_width);
+		display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
+				   remote_ref->name, ref->name,
+				   &ref->old_oid, &ref->new_oid, summary_width);
 		return 1;
 	}
 }
@@ -1108,39 +1129,35 @@
    "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
    "to avoid this check\n");
 
-static int store_updated_refs(const char *raw_url, const char *remote_name,
+static int store_updated_refs(struct display_state *display_state,
+			      const char *remote_name,
 			      int connectivity_checked,
 			      struct ref_transaction *transaction, struct ref *ref_map,
-			      struct fetch_head *fetch_head)
+			      struct fetch_head *fetch_head,
+			      const struct fetch_config *config)
 {
-	int url_len, i, rc = 0;
+	int rc = 0;
 	struct strbuf note = STRBUF_INIT;
 	const char *what, *kind;
 	struct ref *rm;
-	char *url;
 	int want_status;
 	int summary_width = 0;
 
 	if (verbosity >= 0)
 		summary_width = transport_summary_width(ref_map);
 
-	if (raw_url)
-		url = transport_anonymize_url(raw_url);
-	else
-		url = xstrdup("foreign");
-
 	if (!connectivity_checked) {
 		struct check_connected_options opt = CHECK_CONNECTED_INIT;
 
+		opt.exclude_hidden_refs_section = "fetch";
 		rm = ref_map;
 		if (check_connected(iterate_ref_map, &rm, &opt)) {
-			rc = error(_("%s did not send all necessary objects\n"), url);
+			rc = error(_("%s did not send all necessary objects\n"),
+				   display_state->url);
 			goto abort;
 		}
 	}
 
-	prepare_format_display(ref_map);
-
 	/*
 	 * We do a pass for each fetch_head_status type in their enum order, so
 	 * merged entries are written before not-for-merge. That lets readers
@@ -1200,7 +1217,7 @@
 				ref->force = rm->peer_ref->force;
 			}
 
-			if (recurse_submodules != RECURSE_SUBMODULES_OFF &&
+			if (config->recurse_submodules != RECURSE_SUBMODULES_OFF &&
 			    (!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) {
 				check_for_new_submodule_commits(&rm->old_oid);
 			}
@@ -1208,25 +1225,17 @@
 			if (!strcmp(rm->name, "HEAD")) {
 				kind = "";
 				what = "";
-			}
-			else if (skip_prefix(rm->name, "refs/heads/", &what))
+			} else if (skip_prefix(rm->name, "refs/heads/", &what)) {
 				kind = "branch";
-			else if (skip_prefix(rm->name, "refs/tags/", &what))
+			} else if (skip_prefix(rm->name, "refs/tags/", &what)) {
 				kind = "tag";
-			else if (skip_prefix(rm->name, "refs/remotes/", &what))
+			} else if (skip_prefix(rm->name, "refs/remotes/", &what)) {
 				kind = "remote-tracking branch";
-			else {
+			} else {
 				kind = "";
 				what = rm->name;
 			}
 
-			url_len = strlen(url);
-			for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
-				;
-			url_len = i + 1;
-			if (4 < i && !strncmp(".git", url + i - 3, 4))
-				url_len = i - 3;
-
 			strbuf_reset(&note);
 			if (*what) {
 				if (*kind)
@@ -1236,12 +1245,12 @@
 
 			append_fetch_head(fetch_head, &rm->old_oid,
 					  rm->fetch_head_status,
-					  note.buf, url, url_len);
+					  note.buf, display_state->url,
+					  display_state->url_len);
 
-			strbuf_reset(&note);
 			if (ref) {
-				rc |= update_local_ref(ref, transaction, what,
-						       rm, &note, summary_width);
+				rc |= update_local_ref(ref, transaction, display_state,
+						       rm, summary_width, config);
 				free(ref);
 			} else if (write_fetch_head || dry_run) {
 				/*
@@ -1249,18 +1258,12 @@
 				 * would be written to FETCH_HEAD, if --dry-run
 				 * is set).
 				 */
-				format_display(&note, '*',
-					       *kind ? kind : "branch", NULL,
-					       *what ? what : "HEAD",
-					       "FETCH_HEAD", summary_width);
-			}
-			if (note.len) {
-				if (!shown_url) {
-					fprintf(stderr, _("From %.*s\n"),
-							url_len, url);
-					shown_url = 1;
-				}
-				fprintf(stderr, " %s\n", note.buf);
+				display_ref_update(display_state, '*',
+						   *kind ? kind : "branch", NULL,
+						   rm->name,
+						   "FETCH_HEAD",
+						   &rm->new_oid, &rm->old_oid,
+						   summary_width);
 			}
 		}
 	}
@@ -1271,7 +1274,7 @@
 		      "branches"), remote_name);
 
 	if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) {
-		if (!fetch_show_forced_updates) {
+		if (!config->show_forced_updates) {
 			warning(_(warn_show_forced_updates));
 		} else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
 			warning(_(warn_time_show_forced_updates),
@@ -1281,7 +1284,6 @@
 
  abort:
 	strbuf_release(&note);
-	free(url);
 	return rc;
 }
 
@@ -1319,19 +1321,22 @@
 	 * we need all direct targets to exist.
 	 */
 	for (r = rm; r; r = r->next) {
-		if (!has_object_file_with_flags(&r->old_oid,
-						OBJECT_INFO_SKIP_FETCH_OBJECT))
+		if (!repo_has_object_file_with_flags(the_repository, &r->old_oid,
+						     OBJECT_INFO_SKIP_FETCH_OBJECT))
 			return -1;
 	}
 
 	opt.quiet = 1;
+	opt.exclude_hidden_refs_section = "fetch";
 	return check_connected(iterate_ref_map, &rm, &opt);
 }
 
-static int fetch_and_consume_refs(struct transport *transport,
+static int fetch_and_consume_refs(struct display_state *display_state,
+				  struct transport *transport,
 				  struct ref_transaction *transaction,
 				  struct ref *ref_map,
-				  struct fetch_head *fetch_head)
+				  struct fetch_head *fetch_head,
+				  const struct fetch_config *config)
 {
 	int connectivity_checked = 1;
 	int ret;
@@ -1352,9 +1357,9 @@
 	}
 
 	trace2_region_enter("fetch", "consume_refs", the_repository);
-	ret = store_updated_refs(transport->url, transport->remote->name,
+	ret = store_updated_refs(display_state, transport->remote->name,
 				 connectivity_checked, transaction, ref_map,
-				 fetch_head);
+				 fetch_head, config);
 	trace2_region_leave("fetch", "consume_refs", the_repository);
 
 out:
@@ -1362,32 +1367,18 @@
 	return ret;
 }
 
-static int prune_refs(struct refspec *rs,
+static int prune_refs(struct display_state *display_state,
+		      struct refspec *rs,
 		      struct ref_transaction *transaction,
-		      struct ref *ref_map,
-		      const char *raw_url)
+		      struct ref *ref_map)
 {
-	int url_len, i, result = 0;
+	int result = 0;
 	struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
 	struct strbuf err = STRBUF_INIT;
-	char *url;
 	const char *dangling_msg = dry_run
 		? _("   (%s will become dangling)")
 		: _("   (%s has become dangling)");
 
-	if (raw_url)
-		url = transport_anonymize_url(raw_url);
-	else
-		url = xstrdup("foreign");
-
-	url_len = strlen(url);
-	for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
-		;
-
-	url_len = i + 1;
-	if (4 < i && !strncmp(".git", url + i - 3, 4))
-		url_len = i - 3;
-
 	if (!dry_run) {
 		if (transaction) {
 			for (ref = stale_refs; ref; ref = ref->next) {
@@ -1411,23 +1402,16 @@
 		int summary_width = transport_summary_width(stale_refs);
 
 		for (ref = stale_refs; ref; ref = ref->next) {
-			struct strbuf sb = STRBUF_INIT;
-			if (!shown_url) {
-				fprintf(stderr, _("From %.*s\n"), url_len, url);
-				shown_url = 1;
-			}
-			format_display(&sb, '-', _("[deleted]"), NULL,
-				       _("(none)"), prettify_refname(ref->name),
-				       summary_width);
-			fprintf(stderr, " %s\n",sb.buf);
-			strbuf_release(&sb);
+			display_ref_update(display_state, '-', _("[deleted]"), NULL,
+					   _("(none)"), ref->name,
+					   &ref->new_oid, &ref->old_oid,
+					   summary_width);
 			warn_dangling_symref(stderr, dangling_msg, ref->name);
 		}
 	}
 
 cleanup:
 	strbuf_release(&err);
-	free(url);
 	free_refs(stale_refs);
 	return result;
 }
@@ -1487,7 +1471,7 @@
 		int old_nr;
 		if (!has_glob_specials(s)) {
 			struct object_id oid;
-			if (get_oid(s, &oid))
+			if (repo_get_oid(the_repository, s, &oid))
 				die(_("%s is not a valid object"), s);
 			if (!has_object(the_repository, &oid, 0))
 				die(_("the object %s does not exist"), s);
@@ -1542,10 +1526,12 @@
 	return transport;
 }
 
-static int backfill_tags(struct transport *transport,
+static int backfill_tags(struct display_state *display_state,
+			 struct transport *transport,
 			 struct ref_transaction *transaction,
 			 struct ref *ref_map,
-			 struct fetch_head *fetch_head)
+			 struct fetch_head *fetch_head,
+			 const struct fetch_config *config)
 {
 	int retcode, cannot_reuse;
 
@@ -1566,7 +1552,8 @@
 	transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
 	transport_set_option(transport, TRANS_OPT_DEPTH, "0");
 	transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-	retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head);
+	retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map,
+					 fetch_head, config);
 
 	if (gsecondary) {
 		transport_disconnect(gsecondary);
@@ -1577,10 +1564,12 @@
 }
 
 static int do_fetch(struct transport *transport,
-		    struct refspec *rs)
+		    struct refspec *rs,
+		    const struct fetch_config *config)
 {
 	struct ref_transaction *transaction = NULL;
 	struct ref *ref_map = NULL;
+	struct display_state display_state = { 0 };
 	int autotags = (transport->remote->fetch_tags == 1);
 	int retcode = 0;
 	const struct ref *remote_refs;
@@ -1662,10 +1651,13 @@
 	if (retcode)
 		goto cleanup;
 
+	display_state_init(&display_state, ref_map, transport->url,
+			   config->display_format);
+
 	if (atomic_fetch) {
 		transaction = ref_transaction_begin(&err);
 		if (!transaction) {
-			retcode = error("%s", err.buf);
+			retcode = -1;
 			goto cleanup;
 		}
 	}
@@ -1679,17 +1671,17 @@
 		 * don't care whether --tags was specified.
 		 */
 		if (rs->nr) {
-			retcode = prune_refs(rs, transaction, ref_map, transport->url);
+			retcode = prune_refs(&display_state, rs, transaction, ref_map);
 		} else {
-			retcode = prune_refs(&transport->remote->fetch,
-					     transaction, ref_map,
-					     transport->url);
+			retcode = prune_refs(&display_state, &transport->remote->fetch,
+					     transaction, ref_map);
 		}
 		if (retcode != 0)
 			retcode = 1;
 	}
 
-	if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head)) {
+	if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map,
+				   &fetch_head, config)) {
 		retcode = 1;
 		goto cleanup;
 	}
@@ -1711,8 +1703,8 @@
 			 * when `--atomic` is passed: in that case we'll abort
 			 * the transaction and don't commit anything.
 			 */
-			if (backfill_tags(transport, transaction, tags_ref_map,
-					  &fetch_head))
+			if (backfill_tags(&display_state, transport, transaction, tags_ref_map,
+					  &fetch_head, config))
 				retcode = 1;
 		}
 
@@ -1725,7 +1717,6 @@
 
 		retcode = ref_transaction_commit(transaction, &err);
 		if (retcode) {
-			error("%s", err.buf);
 			ref_transaction_free(transaction);
 			transaction = NULL;
 			goto cleanup;
@@ -1789,11 +1780,17 @@
 	}
 
 cleanup:
-	if (retcode && transaction) {
-		ref_transaction_abort(transaction, &err);
-		error("%s", err.buf);
+	if (retcode) {
+		if (err.len) {
+			error("%s", err.buf);
+			strbuf_reset(&err);
+		}
+		if (transaction && ref_transaction_abort(transaction, &err) &&
+		    err.len)
+			error("%s", err.buf);
 	}
 
+	display_state_release(&display_state);
 	close_fetch_head(&fetch_head);
 	strbuf_release(&err);
 	free_refs(ref_map);
@@ -1813,7 +1810,9 @@
 	struct string_list *list;
 };
 
-static int get_remote_group(const char *key, const char *value, void *priv)
+static int get_remote_group(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *priv)
 {
 	struct remote_group_data *g = priv;
 
@@ -1848,7 +1847,8 @@
 	return 1;
 }
 
-static void add_options_to_argv(struct strvec *argv)
+static void add_options_to_argv(struct strvec *argv,
+				const struct fetch_config *config)
 {
 	if (dry_run)
 		strvec_push(argv, "--dry-run");
@@ -1862,9 +1862,11 @@
 		strvec_push(argv, "--force");
 	if (keep)
 		strvec_push(argv, "--keep");
-	if (recurse_submodules == RECURSE_SUBMODULES_ON)
+	if (config->recurse_submodules == RECURSE_SUBMODULES_ON)
 		strvec_push(argv, "--recurse-submodules");
-	else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+	else if (config->recurse_submodules == RECURSE_SUBMODULES_OFF)
+		strvec_push(argv, "--no-recurse-submodules");
+	else if (config->recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
 		strvec_push(argv, "--recurse-submodules=on-demand");
 	if (tags == TAGS_SET)
 		strvec_push(argv, "--tags");
@@ -1880,6 +1882,10 @@
 		strvec_push(argv, "--ipv4");
 	else if (family == TRANSPORT_FAMILY_IPV6)
 		strvec_push(argv, "--ipv6");
+	if (!write_fetch_head)
+		strvec_push(argv, "--no-write-fetch-head");
+	if (config->display_format == DISPLAY_FORMAT_PORCELAIN)
+		strvec_pushf(argv, "--porcelain");
 }
 
 /* Fetch multiple remotes in parallel */
@@ -1888,9 +1894,11 @@
 	const char **argv;
 	struct string_list *remotes;
 	int next, result;
+	const struct fetch_config *config;
 };
 
-static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
+static int fetch_next_remote(struct child_process *cp,
+			     struct strbuf *out UNUSED,
 			     void *cb, void **task_cb)
 {
 	struct parallel_fetch_state *state = cb;
@@ -1906,13 +1914,14 @@
 	strvec_push(&cp->args, remote);
 	cp->git_cmd = 1;
 
-	if (verbosity >= 0)
+	if (verbosity >= 0 && state->config->display_format != DISPLAY_FORMAT_PORCELAIN)
 		printf(_("Fetching %s\n"), remote);
 
 	return 1;
 }
 
-static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
+static int fetch_failed_to_start(struct strbuf *out UNUSED,
+				 void *cb, void *task_cb)
 {
 	struct parallel_fetch_state *state = cb;
 	const char *remote = task_cb;
@@ -1937,7 +1946,8 @@
 	return 0;
 }
 
-static int fetch_multiple(struct string_list *list, int max_children)
+static int fetch_multiple(struct string_list *list, int max_children,
+			  const struct fetch_config *config)
 {
 	int i, result = 0;
 	struct strvec argv = STRVEC_INIT;
@@ -1948,12 +1958,17 @@
 			return errcode;
 	}
 
-	strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc",
+	/*
+	 * Cancel out the fetch.bundleURI config when running subprocesses,
+	 * to avoid fetching from the same bundle list multiple times.
+	 */
+	strvec_pushl(&argv, "-c", "fetch.bundleURI=",
+		     "fetch", "--append", "--no-auto-gc",
 		     "--no-write-commit-graph", NULL);
-	add_options_to_argv(&argv);
+	add_options_to_argv(&argv, config);
 
 	if (max_children != 1 && list->nr != 1) {
-		struct parallel_fetch_state state = { argv.v, list, 0, 0 };
+		struct parallel_fetch_state state = { argv.v, list, 0, 0, config };
 		const struct run_process_parallel_opts opts = {
 			.tr2_category = "fetch",
 			.tr2_label = "parallel/fetch",
@@ -1977,7 +1992,7 @@
 
 			strvec_pushv(&cmd.args, argv.v);
 			strvec_push(&cmd.args, name);
-			if (verbosity >= 0)
+			if (verbosity >= 0 && config->display_format != DISPLAY_FORMAT_PORCELAIN)
 				printf(_("Fetching %s\n"), name);
 			cmd.git_cmd = 1;
 			if (run_command(&cmd)) {
@@ -2007,7 +2022,7 @@
 	 * If no prior partial clone/fetch and the current fetch DID NOT
 	 * request a partial-fetch, do a normal fetch.
 	 */
-	if (!has_promisor_remote() && !filter_options.choice)
+	if (!repo_has_promisor_remote(the_repository) && !filter_options.choice)
 		return;
 
 	/*
@@ -2032,7 +2047,8 @@
 }
 
 static int fetch_one(struct remote *remote, int argc, const char **argv,
-		     int prune_tags_ok, int use_stdin_refspecs)
+		     int prune_tags_ok, int use_stdin_refspecs,
+		     const struct fetch_config *config)
 {
 	struct refspec rs = REFSPEC_INIT_FETCH;
 	int i;
@@ -2050,8 +2066,8 @@
 		/* no command line request */
 		if (0 <= remote->prune)
 			prune = remote->prune;
-		else if (0 <= fetch_prune_config)
-			prune = fetch_prune_config;
+		else if (0 <= config->prune)
+			prune = config->prune;
 		else
 			prune = PRUNE_BY_DEFAULT;
 	}
@@ -2060,8 +2076,8 @@
 		/* no command line request */
 		if (0 <= remote->prune_tags)
 			prune_tags = remote->prune_tags;
-		else if (0 <= fetch_prune_tags_config)
-			prune_tags = fetch_prune_tags_config;
+		else if (0 <= config->prune_tags)
+			prune_tags = config->prune_tags;
 		else
 			prune_tags = PRUNE_TAGS_BY_DEFAULT;
 	}
@@ -2099,7 +2115,7 @@
 	sigchain_push_common(unlock_pack_on_signal);
 	atexit(unlock_pack_atexit);
 	sigchain_push(SIGPIPE, SIG_IGN);
-	exit_code = do_fetch(gtransport, &rs);
+	exit_code = do_fetch(gtransport, &rs, config);
 	sigchain_pop(SIGPIPE);
 	refspec_clear(&rs);
 	transport_disconnect(gtransport);
@@ -2109,12 +2125,116 @@
 
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
-	int i;
+	struct fetch_config config = {
+		.display_format = DISPLAY_FORMAT_FULL,
+		.prune = -1,
+		.prune_tags = -1,
+		.show_forced_updates = 1,
+		.recurse_submodules = RECURSE_SUBMODULES_DEFAULT,
+		.parallel = 1,
+		.submodule_fetch_jobs = -1,
+	};
+	const char *submodule_prefix = "";
 	const char *bundle_uri;
 	struct string_list list = STRING_LIST_INIT_DUP;
 	struct remote *remote = NULL;
+	int all = -1, multiple = 0;
 	int result = 0;
 	int prune_tags_ok = 1;
+	int enable_auto_gc = 1;
+	int unshallow = 0;
+	int max_jobs = -1;
+	int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
+	int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
+	int fetch_write_commit_graph = -1;
+	int stdin_refspecs = 0;
+	int negotiate_only = 0;
+	int porcelain = 0;
+	int i;
+
+	struct option builtin_fetch_options[] = {
+		OPT__VERBOSITY(&verbosity),
+		OPT_BOOL(0, "all", &all,
+			 N_("fetch from all remotes")),
+		OPT_BOOL(0, "set-upstream", &set_upstream,
+			 N_("set upstream for git pull/fetch")),
+		OPT_BOOL('a', "append", &append,
+			 N_("append to .git/FETCH_HEAD instead of overwriting")),
+		OPT_BOOL(0, "atomic", &atomic_fetch,
+			 N_("use atomic transaction to update references")),
+		OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
+			   N_("path to upload pack on remote end")),
+		OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
+		OPT_BOOL('m', "multiple", &multiple,
+			 N_("fetch from multiple remotes")),
+		OPT_SET_INT('t', "tags", &tags,
+			    N_("fetch all tags and associated objects"), TAGS_SET),
+		OPT_SET_INT('n', NULL, &tags,
+			    N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+		OPT_INTEGER('j', "jobs", &max_jobs,
+			    N_("number of submodules fetched in parallel")),
+		OPT_BOOL(0, "prefetch", &prefetch,
+			 N_("modify the refspec to place all refs within refs/prefetch/")),
+		OPT_BOOL('p', "prune", &prune,
+			 N_("prune remote-tracking branches no longer on remote")),
+		OPT_BOOL('P', "prune-tags", &prune_tags,
+			 N_("prune local tags no longer on remote and clobber changed tags")),
+		OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
+			    N_("control recursive fetching of submodules"),
+			    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
+		OPT_BOOL(0, "dry-run", &dry_run,
+			 N_("dry run")),
+		OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+		OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+			 N_("write fetched references to the FETCH_HEAD file")),
+		OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+		OPT_BOOL('u', "update-head-ok", &update_head_ok,
+			    N_("allow updating of HEAD ref")),
+		OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+		OPT_STRING(0, "depth", &depth, N_("depth"),
+			   N_("deepen history of shallow clone")),
+		OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+			   N_("deepen history of shallow repository based on time")),
+		OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+				N_("deepen history of shallow clone, excluding rev")),
+		OPT_INTEGER(0, "deepen", &deepen_relative,
+			    N_("deepen history of shallow clone")),
+		OPT_SET_INT_F(0, "unshallow", &unshallow,
+			      N_("convert to a complete repository"),
+			      1, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "refetch", &refetch,
+			      N_("re-fetch without negotiating common commits"),
+			      1, PARSE_OPT_NONEG),
+		{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+			   N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
+		OPT_CALLBACK_F(0, "recurse-submodules-default",
+			   &recurse_submodules_default, N_("on-demand"),
+			   N_("default for recursive fetching of submodules "
+			      "(lower priority than config files)"),
+			   PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
+		OPT_BOOL(0, "update-shallow", &update_shallow,
+			 N_("accept refs that update .git/shallow")),
+		OPT_CALLBACK_F(0, "refmap", &refmap, N_("refmap"),
+			       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"),
+				N_("report that we have only objects reachable from this object")),
+		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),
+		OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
+			 N_("run 'maintenance --auto' after fetching")),
+		OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+			 N_("run 'maintenance --auto' after fetching")),
+		OPT_BOOL(0, "show-forced-updates", &config.show_forced_updates,
+			 N_("check for forced-updates on all updated branches")),
+		OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
+			 N_("write the commit-graph after fetching")),
+		OPT_BOOL(0, "stdin", &stdin_refspecs,
+			 N_("accept refspecs from stdin")),
+		OPT_END()
+	};
 
 	packet_trace_identity("fetch");
 
@@ -2128,7 +2248,7 @@
 		free(anon);
 	}
 
-	git_config(git_fetch_config, NULL);
+	git_config(git_fetch_config, &config);
 	if (the_repository->gitdir) {
 		prepare_repo_settings(the_repository);
 		the_repository->settings.command_requires_full_index = 0;
@@ -2138,7 +2258,7 @@
 			     builtin_fetch_options, builtin_fetch_usage, 0);
 
 	if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
-		recurse_submodules = recurse_submodules_cli;
+		config.recurse_submodules = recurse_submodules_cli;
 
 	if (negotiate_only) {
 		switch (recurse_submodules_cli) {
@@ -2149,7 +2269,7 @@
 			 * submodules. Skip it by setting recurse_submodules to
 			 * RECURSE_SUBMODULES_OFF.
 			 */
-			recurse_submodules = RECURSE_SUBMODULES_OFF;
+			config.recurse_submodules = RECURSE_SUBMODULES_OFF;
 			break;
 
 		default:
@@ -2158,15 +2278,35 @@
 		}
 	}
 
-	if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
-		int *sfjc = submodule_fetch_jobs_config == -1
-			    ? &submodule_fetch_jobs_config : NULL;
-		int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT
-			  ? &recurse_submodules : NULL;
+	if (config.recurse_submodules != RECURSE_SUBMODULES_OFF) {
+		int *sfjc = config.submodule_fetch_jobs == -1
+			    ? &config.submodule_fetch_jobs : NULL;
+		int *rs = config.recurse_submodules == RECURSE_SUBMODULES_DEFAULT
+			  ? &config.recurse_submodules : NULL;
 
 		fetch_config_from_gitmodules(sfjc, rs);
 	}
 
+
+	if (porcelain) {
+		switch (recurse_submodules_cli) {
+		case RECURSE_SUBMODULES_OFF:
+		case RECURSE_SUBMODULES_DEFAULT:
+			/*
+			 * Reference updates in submodules would be ambiguous
+			 * in porcelain mode, so we reject this combination.
+			 */
+			config.recurse_submodules = RECURSE_SUBMODULES_OFF;
+			break;
+
+		default:
+			die(_("options '%s' and '%s' cannot be used together"),
+			    "--porcelain", "--recurse-submodules");
+		}
+
+		config.display_format = DISPLAY_FORMAT_PORCELAIN;
+	}
+
 	if (negotiate_only && !negotiation_tip.nr)
 		die(_("--negotiate-only needs one or more --negotiation-tip=*"));
 
@@ -2203,11 +2343,20 @@
 	    fetch_bundle_uri(the_repository, bundle_uri, NULL))
 		warning(_("failed to fetch bundles from '%s'"), bundle_uri);
 
+	if (all < 0) {
+		/*
+		 * no --[no-]all given;
+		 * only use config option if no remote was explicitly specified
+		 */
+		all = (!argc) ? config.all : 0;
+	}
+
 	if (all) {
 		if (argc == 1)
 			die(_("fetch --all does not take a repository argument"));
 		else if (argc > 1)
 			die(_("fetch --all does not make sense with refspecs"));
+
 		(void) for_each_remote(get_one_remote_for_fetch, &list);
 
 		/* do not do fetch_multiple() of one */
@@ -2263,9 +2412,10 @@
 			printf("%s\n", oid_to_hex(oid));
 		oidset_clear(&acked_commits);
 	} else if (remote) {
-		if (filter_options.choice || has_promisor_remote())
+		if (filter_options.choice || repo_has_promisor_remote(the_repository))
 			fetch_one_setup_partial(remote);
-		result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
+		result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs,
+				   &config);
 	} else {
 		int max_children = max_jobs;
 
@@ -2282,13 +2432,12 @@
 			      "from one remote"));
 
 		if (max_children < 0)
-			max_children = fetch_parallel_config;
+			max_children = config.parallel;
 
 		/* TODO should this also die if we have a previous partial-clone? */
-		result = fetch_multiple(&list, max_children);
+		result = fetch_multiple(&list, max_children, &config);
 	}
 
-
 	/*
 	 * This is only needed after fetch_one(), which does not fetch
 	 * submodules by itself.
@@ -2298,20 +2447,20 @@
 	 * the fetched history from each remote, so there is no need
 	 * to fetch submodules from here.
 	 */
-	if (!result && remote && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+	if (!result && remote && (config.recurse_submodules != RECURSE_SUBMODULES_OFF)) {
 		struct strvec options = STRVEC_INIT;
 		int max_children = max_jobs;
 
 		if (max_children < 0)
-			max_children = submodule_fetch_jobs_config;
+			max_children = config.submodule_fetch_jobs;
 		if (max_children < 0)
-			max_children = fetch_parallel_config;
+			max_children = config.parallel;
 
-		add_options_to_argv(&options);
+		add_options_to_argv(&options, &config);
 		result = fetch_submodules(the_repository,
 					  &options,
 					  submodule_prefix,
-					  recurse_submodules,
+					  config.recurse_submodules,
 					  recurse_submodules_default,
 					  verbosity < 0,
 					  max_children);
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 8d8fd39..0f9855b 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "config.h"
 #include "fmt-merge-msg.h"
+#include "gettext.h"
 #include "parse-options.h"
 
 static const char * const fmt_merge_msg_usage[] = {
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 6f62f40..919282e 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -1,10 +1,12 @@
 #include "builtin.h"
-#include "cache.h"
+#include "commit.h"
 #include "config.h"
-#include "refs.h"
+#include "gettext.h"
 #include "object.h"
 #include "parse-options.h"
 #include "ref-filter.h"
+#include "strbuf.h"
+#include "strvec.h"
 
 static char const * const for_each_ref_usage[] = {
 	N_("git for-each-ref [<options>] [<pattern>]"),
@@ -16,15 +18,13 @@
 
 int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
-	int i;
 	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
-	int maxcount = 0, icase = 0;
-	struct ref_array array;
-	struct ref_filter filter;
+	int icase = 0, include_root_refs = 0, from_stdin = 0;
+	struct ref_filter filter = REF_FILTER_INIT;
 	struct ref_format format = REF_FORMAT_INIT;
-	struct strbuf output = STRBUF_INIT;
-	struct strbuf err = STRBUF_INIT;
+	unsigned int flags = FILTER_REFS_REGULAR;
+	struct strvec vec = STRVEC_INIT;
 
 	struct option opts[] = {
 		OPT_BIT('s', "shell", &format.quote_style,
@@ -35,11 +35,14 @@
 			N_("quote placeholders suitably for python"), QUOTE_PYTHON),
 		OPT_BIT(0 , "tcl",  &format.quote_style,
 			N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
+		OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
+			N_("do not output a newline after empty formatted refs")),
 
 		OPT_GROUP(""),
-		OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
+		OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
 		OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
 		OPT__COLOR(&format.use_color, N_("respect format colors")),
+		OPT_REF_FILTER_EXCLUDE(&filter),
 		OPT_REF_SORT(&sorting_options),
 		OPT_CALLBACK(0, "points-at", &filter.points_at,
 			     N_("object"), N_("print only refs which points at the given object"),
@@ -49,19 +52,21 @@
 		OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
 		OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
 		OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+		OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
+		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
 		OPT_END(),
 	};
 
-	memset(&array, 0, sizeof(array));
-	memset(&filter, 0, sizeof(filter));
-
 	format.format = "%(objectname) %(objecttype)\t%(refname)";
 
 	git_config(git_default_config, NULL);
 
+	/* Set default (refname) sorting */
+	string_list_append(&sorting_options, "refname");
+
 	parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
-	if (maxcount < 0) {
-		error("invalid --count argument: `%d'", maxcount);
+	if (format.array_opts.max_count < 0) {
+		error("invalid --count argument: `%d'", format.array_opts.max_count);
 		usage_with_options(for_each_ref_usage, opts);
 	}
 	if (HAS_MULTI_BITS(format.quote_style)) {
@@ -75,27 +80,31 @@
 	ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
 	filter.ignore_case = icase;
 
-	filter.name_patterns = argv;
-	filter.match_as_path = 1;
-	filter_refs(&array, &filter, FILTER_REFS_ALL);
-	ref_array_sort(sorting, &array);
+	if (from_stdin) {
+		struct strbuf line = STRBUF_INIT;
 
-	if (!maxcount || array.nr < maxcount)
-		maxcount = array.nr;
-	for (i = 0; i < maxcount; i++) {
-		strbuf_reset(&err);
-		strbuf_reset(&output);
-		if (format_ref_array_item(array.items[i], &format, &output, &err))
-			die("%s", err.buf);
-		fwrite(output.buf, 1, output.len, stdout);
-		putchar('\n');
+		if (argv[0])
+			die(_("unknown arguments supplied with --stdin"));
+
+		while (strbuf_getline(&line, stdin) != EOF)
+			strvec_push(&vec, line.buf);
+
+		strbuf_release(&line);
+
+		/* vec.v is NULL-terminated, just like 'argv'. */
+		filter.name_patterns = vec.v;
+	} else {
+		filter.name_patterns = argv;
 	}
 
-	strbuf_release(&err);
-	strbuf_release(&output);
-	ref_array_clear(&array);
-	free_commit_list(filter.with_commit);
-	free_commit_list(filter.no_commit);
+	if (include_root_refs)
+		flags |= FILTER_REFS_ROOT_REFS;
+
+	filter.match_as_path = 1;
+	filter_and_format_refs(&filter, flags, sorting, &format);
+
+	ref_filter_clear(&filter);
 	ref_sorting_release(sorting);
+	strvec_clear(&vec);
 	return 0;
 }
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 6aeac37..28186b3 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -1,7 +1,9 @@
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
+#include "repository.h"
 #include "run-command.h"
 #include "string-list.h"
 
@@ -32,6 +34,7 @@
 	static const char *config_key = NULL;
 	int i, result = 0;
 	const struct string_list *values;
+	int err;
 
 	const struct option options[] = {
 		OPT_STRING(0, "config", &config_key, N_("config"),
@@ -45,14 +48,11 @@
 	if (!config_key)
 		die(_("missing --config=<config>"));
 
-	values = repo_config_get_value_multi(the_repository,
-					     config_key);
-
-	/*
-	 * Do nothing on an empty list, which is equivalent to the case
-	 * where the config variable does not exist at all.
-	 */
-	if (!values)
+	err = repo_config_get_string_multi(the_repository, config_key, &values);
+	if (err < 0)
+		usage_msg_optf(_("got bad config --config=%s"),
+			       for_each_repo_usage, options, config_key);
+	else if (err)
 		return 0;
 
 	for (i = 0; !result && i < values->nr; i++)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index d207bd9..f892487 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,6 +1,6 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "commit.h"
@@ -10,18 +10,23 @@
 #include "refs.h"
 #include "pack.h"
 #include "cache-tree.h"
-#include "tree-walk.h"
 #include "fsck.h"
 #include "parse-options.h"
-#include "dir.h"
 #include "progress.h"
 #include "streaming.h"
-#include "decorate.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "replace-object.h"
 #include "resolve-undo.h"
 #include "run-command.h"
+#include "sparse-index.h"
 #include "worktree.h"
+#include "pack-revindex.h"
+#include "pack-bitmap.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -51,6 +56,8 @@
 #define ERROR_REFS 010
 #define ERROR_COMMIT_GRAPH 020
 #define ERROR_MULTI_PACK_INDEX 040
+#define ERROR_PACK_REV_INDEX 0100
+#define ERROR_BITMAP 0200
 
 static const char *describe_object(const struct object_id *oid)
 {
@@ -82,11 +89,11 @@
 	return -1;
 }
 
-static int fsck_error_func(struct fsck_options *o,
+static int fsck_error_func(struct fsck_options *o UNUSED,
 			   const struct object_id *oid,
 			   enum object_type object_type,
 			   enum fsck_msg_type msg_type,
-			   enum fsck_msg_id msg_id,
+			   enum fsck_msg_id msg_id UNUSED,
 			   const char *message)
 {
 	switch (msg_type) {
@@ -111,7 +118,7 @@
 static struct object_array pending;
 
 static int mark_object(struct object *obj, enum object_type type,
-		       void *data, struct fsck_options *options)
+		       void *data, struct fsck_options *options UNUSED)
 {
 	struct object *parent = data;
 
@@ -196,8 +203,8 @@
 	return !!result;
 }
 
-static int mark_used(struct object *obj, enum object_type object_type,
-		     void *data, struct fsck_options *options)
+static int mark_used(struct object *obj, enum object_type type UNUSED,
+		     void *data UNUSED, struct fsck_options *options UNUSED)
 {
 	if (!obj)
 		return 1;
@@ -233,17 +240,17 @@
 }
 
 static int mark_loose_unreachable_referents(const struct object_id *oid,
-					    const char *path,
-					    void *data)
+					    const char *path UNUSED,
+					    void *data UNUSED)
 {
 	mark_unreachable_referents(oid);
 	return 0;
 }
 
 static int mark_packed_unreachable_referents(const struct object_id *oid,
-					     struct packed_git *pack,
-					     uint32_t pos,
-					     void *data)
+					     struct packed_git *pack UNUSED,
+					     uint32_t pos UNUSED,
+					     void *data UNUSED)
 {
 	mark_unreachable_referents(oid);
 	return 0;
@@ -502,9 +509,7 @@
 	return 0;
 }
 
-static int fsck_handle_reflog(const char *logname,
-			      const struct object_id *oid UNUSED,
-			      int flag UNUSED, void *cb_data)
+static int fsck_handle_reflog(const char *logname, void *cb_data)
 {
 	struct strbuf refname = STRBUF_INIT;
 
@@ -661,14 +666,15 @@
 	return 0; /* keep checking other objects, even if we saw an error */
 }
 
-static int fsck_cruft(const char *basename, const char *path, void *data)
+static int fsck_cruft(const char *basename, const char *path,
+		      void *data UNUSED)
 {
 	if (!starts_with(basename, "tmp_obj_"))
 		fprintf_ln(stderr, _("bad sha1 file: %s"), path);
 	return 0;
 }
 
-static int fsck_subdir(unsigned int nr, const char *path, void *data)
+static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
 {
 	struct for_each_loose_cb *cb_data = data;
 	struct progress *progress = cb_data->progress;
@@ -732,19 +738,19 @@
 	return 0;
 }
 
-static int fsck_cache_tree(struct cache_tree *it)
+static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 {
 	int i;
 	int err = 0;
 
 	if (verbose)
-		fprintf_ln(stderr, _("Checking cache tree"));
+		fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
 
 	if (0 <= it->entry_count) {
 		struct object *obj = parse_object(the_repository, &it->oid);
 		if (!obj) {
-			error(_("%s: invalid sha1 pointer in cache-tree"),
-			      oid_to_hex(&it->oid));
+			error(_("%s: invalid sha1 pointer in cache-tree of %s"),
+			      oid_to_hex(&it->oid), index_path);
 			errors_found |= ERROR_REFS;
 			return 1;
 		}
@@ -755,11 +761,12 @@
 			err |= objerror(obj, _("non-tree in cache-tree"));
 	}
 	for (i = 0; i < it->subtree_nr; i++)
-		err |= fsck_cache_tree(it->down[i]->cache_tree);
+		err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
 	return err;
 }
 
-static int fsck_resolve_undo(struct index_state *istate)
+static int fsck_resolve_undo(struct index_state *istate,
+			     const char *index_path)
 {
 	struct string_list_item *item;
 	struct string_list *resolve_undo = istate->resolve_undo;
@@ -782,8 +789,9 @@
 
 			obj = parse_object(the_repository, &ru->oid[i]);
 			if (!obj) {
-				error(_("%s: invalid sha1 pointer in resolve-undo"),
-				      oid_to_hex(&ru->oid[i]));
+				error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
+				      oid_to_hex(&ru->oid[i]),
+				      index_path);
 				errors_found |= ERROR_REFS;
 				continue;
 			}
@@ -796,6 +804,38 @@
 	return 0;
 }
 
+static void fsck_index(struct index_state *istate, const char *index_path,
+		       int is_current_worktree)
+{
+	unsigned int i;
+
+	/* TODO: audit for interaction with sparse-index. */
+	ensure_full_index(istate);
+	for (i = 0; i < istate->cache_nr; i++) {
+		unsigned int mode;
+		struct blob *blob;
+		struct object *obj;
+
+		mode = istate->cache[i]->ce_mode;
+		if (S_ISGITLINK(mode))
+			continue;
+		blob = lookup_blob(the_repository,
+				   &istate->cache[i]->oid);
+		if (!blob)
+			continue;
+		obj = &blob->object;
+		obj->flags |= USED;
+		fsck_put_object_name(&fsck_walk_options, &obj->oid,
+				     "%s:%s",
+				     is_current_worktree ? "" : index_path,
+				     istate->cache[i]->name);
+		mark_object_reachable(obj);
+	}
+	if (istate->cache_tree)
+		fsck_cache_tree(istate->cache_tree, index_path);
+	fsck_resolve_undo(istate, index_path);
+}
+
 static void mark_object_for_connectivity(const struct object_id *oid)
 {
 	struct object *obj = lookup_unknown_object(the_repository, oid);
@@ -803,22 +843,54 @@
 }
 
 static int mark_loose_for_connectivity(const struct object_id *oid,
-				       const char *path,
-				       void *data)
+				       const char *path UNUSED,
+				       void *data UNUSED)
 {
 	mark_object_for_connectivity(oid);
 	return 0;
 }
 
 static int mark_packed_for_connectivity(const struct object_id *oid,
-					struct packed_git *pack,
-					uint32_t pos,
-					void *data)
+					struct packed_git *pack UNUSED,
+					uint32_t pos UNUSED,
+					void *data UNUSED)
 {
 	mark_object_for_connectivity(oid);
 	return 0;
 }
 
+static int check_pack_rev_indexes(struct repository *r, int show_progress)
+{
+	struct progress *progress = NULL;
+	uint32_t pack_count = 0;
+	int res = 0;
+
+	if (show_progress) {
+		for (struct packed_git *p = get_all_packs(r); p; p = p->next)
+			pack_count++;
+		progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count);
+		pack_count = 0;
+	}
+
+	for (struct packed_git *p = get_all_packs(r); p; p = p->next) {
+		int load_error = load_pack_revindex_from_disk(p);
+
+		if (load_error < 0) {
+			error(_("unable to load rev-index for pack '%s'"), p->pack_name);
+			res = ERROR_PACK_REV_INDEX;
+		} else if (!load_error &&
+			   !load_pack_revindex(r, p) &&
+			   verify_pack_revindex(p)) {
+			error(_("invalid rev-index for pack '%s'"), p->pack_name);
+			res = ERROR_PACK_REV_INDEX;
+		}
+		display_progress(progress, ++pack_count);
+	}
+	stop_progress(&progress);
+
+	return res;
+}
+
 static char const * const fsck_usage[] = {
 	N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
 	   "         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
@@ -854,7 +926,7 @@
 	fetch_if_missing = 0;
 
 	errors_found = 0;
-	read_replace_refs = 0;
+	disable_replace_refs();
 	save_commit_buffer = 0;
 
 	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
@@ -923,7 +995,7 @@
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
 		struct object_id oid;
-		if (!get_oid(arg, &oid)) {
+		if (!repo_get_oid(the_repository, arg, &oid)) {
 			struct object *obj = lookup_object(the_repository,
 							   &oid);
 
@@ -956,34 +1028,36 @@
 	}
 
 	if (keep_cache_objects) {
+		struct worktree **worktrees, **p;
+
 		verify_index_checksum = 1;
 		verify_ce_order = 1;
-		repo_read_index(the_repository);
-		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
-		for (i = 0; i < the_index.cache_nr; i++) {
-			unsigned int mode;
-			struct blob *blob;
-			struct object *obj;
 
-			mode = the_index.cache[i]->ce_mode;
-			if (S_ISGITLINK(mode))
-				continue;
-			blob = lookup_blob(the_repository,
-					   &the_index.cache[i]->oid);
-			if (!blob)
-				continue;
-			obj = &blob->object;
-			obj->flags |= USED;
-			fsck_put_object_name(&fsck_walk_options, &obj->oid,
-					     ":%s", the_index.cache[i]->name);
-			mark_object_reachable(obj);
+		worktrees = get_worktrees();
+		for (p = worktrees; *p; p++) {
+			struct worktree *wt = *p;
+			struct index_state istate =
+				INDEX_STATE_INIT(the_repository);
+			char *path;
+
+			/*
+			 * Make a copy since the buffer is reusable
+			 * and may get overwritten by other calls
+			 * while we're examining the index.
+			 */
+			path = xstrdup(worktree_git_path(wt, "index"));
+			read_index_from(&istate, path, get_worktree_git_dir(wt));
+			fsck_index(&istate, path, wt->is_current);
+			discard_index(&istate);
+			free(path);
 		}
-		if (the_index.cache_tree)
-			fsck_cache_tree(the_index.cache_tree);
-		fsck_resolve_undo(&the_index);
+		free_worktrees(worktrees);
 	}
 
+	errors_found |= check_pack_rev_indexes(the_repository, show_progress);
+	if (verify_bitmap_files(the_repository))
+		errors_found |= ERROR_BITMAP;
+
 	check_connectivity();
 
 	if (the_repository->settings.core_commit_graph) {
@@ -995,6 +1069,10 @@
 			commit_graph_verify.git_cmd = 1;
 			strvec_pushl(&commit_graph_verify.args, "commit-graph",
 				     "verify", "--object-dir", odb->path, NULL);
+			if (show_progress)
+				strvec_push(&commit_graph_verify.args, "--progress");
+			else
+				strvec_push(&commit_graph_verify.args, "--no-progress");
 			if (run_command(&commit_graph_verify))
 				errors_found |= ERROR_COMMIT_GRAPH;
 		}
@@ -1009,6 +1087,10 @@
 			midx_verify.git_cmd = 1;
 			strvec_pushl(&midx_verify.args, "multi-pack-index",
 				     "verify", "--object-dir", odb->path, NULL);
+			if (show_progress)
+				strvec_push(&midx_verify.args, "--progress");
+			else
+				strvec_push(&midx_verify.args, "--no-progress");
 			if (run_command(&midx_verify))
 				errors_found |= ERROR_MULTI_PACK_INDEX;
 		}
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index 0feef8c..1593713 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -1,15 +1,22 @@
 #include "builtin.h"
+#include "abspath.h"
 #include "config.h"
+#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
 #include "parse-options.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-ipc.h"
-#include "fsmonitor-path-utils.h"
+#include "fsmonitor-settings.h"
 #include "compat/fsmonitor/fsm-health.h"
 #include "compat/fsmonitor/fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "repository.h"
 #include "simple-ipc.h"
 #include "khash.h"
-#include "pkt-line.h"
+#include "run-command.h"
+#include "trace.h"
+#include "trace2.h"
 
 static const char * const builtin_fsmonitor__daemon_usage[] = {
 	N_("git fsmonitor--daemon start [<options>]"),
@@ -32,10 +39,11 @@
 #define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
 static int fsmonitor__announce_startup = 0;
 
-static int fsmonitor_config(const char *var, const char *value, void *cb)
+static int fsmonitor_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, FSMONITOR__IPC_THREADS)) {
-		int i = git_config_int(var, value);
+		int i = git_config_int(var, value, ctx->kvi);
 		if (i < 1)
 			return error(_("value of '%s' out of range: %d"),
 				     FSMONITOR__IPC_THREADS, i);
@@ -44,7 +52,7 @@
 	}
 
 	if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
-		int i = git_config_int(var, value);
+		int i = git_config_int(var, value, ctx->kvi);
 		if (i < 0)
 			return error(_("value of '%s' out of range: %d"),
 				     FSMONITOR__START_TIMEOUT, i);
@@ -54,7 +62,7 @@
 
 	if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
 		int is_bool;
-		int i = git_config_bool_or_int(var, value, &is_bool);
+		int i = git_config_bool_or_int(var, value, ctx->kvi, &is_bool);
 		if (i < 0)
 			return error(_("value of '%s' not bool or int: %d"),
 				     var, i);
@@ -62,7 +70,7 @@
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
@@ -122,8 +130,9 @@
 	enum fsmonitor_cookie_item_result result;
 };
 
-static int cookies_cmp(const void *data, const struct hashmap_entry *he1,
-		     const struct hashmap_entry *he2, const void *keydata)
+static int cookies_cmp(const void *data UNUSED,
+		       const struct hashmap_entry *he1,
+		       const struct hashmap_entry *he2, const void *keydata)
 {
 	const struct fsmonitor_cookie_item *a =
 		container_of(he1, const struct fsmonitor_cookie_item, entry);
@@ -1405,7 +1414,7 @@
 	return err;
 }
 
-static int try_to_run_foreground_daemon(int detach_console)
+static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
 {
 	/*
 	 * Technically, we don't need to probe for an existing daemon
@@ -1435,7 +1444,8 @@
 
 static start_bg_wait_cb bg_wait_cb;
 
-static int bg_wait_cb(const struct child_process *cp, void *cb_data)
+static int bg_wait_cb(const struct child_process *cp UNUSED,
+		      void *cb_data UNUSED)
 {
 	enum ipc_active_state s = fsmonitor_ipc__get_state();
 
@@ -1574,7 +1584,7 @@
 }
 
 #else
-int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
+int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix UNUSED)
 {
 	struct option options[] = {
 		OPT_END()
diff --git a/builtin/gc.c b/builtin/gc.c
index 02455fd..d187cec 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,6 +11,10 @@
  */
 
 #include "builtin.h"
+#include "abspath.h"
+#include "date.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "tempfile.h"
@@ -22,16 +26,21 @@
 #include "commit.h"
 #include "commit-graph.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "pack.h"
 #include "pack-objects.h"
+#include "path.h"
 #include "blob.h"
 #include "tree.h"
 #include "promisor-remote.h"
 #include "refs.h"
 #include "remote.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "hook.h"
+#include "setup.h"
+#include "trace2.h"
 
 #define FAILED_RUN "failed to run %s"
 
@@ -42,7 +51,8 @@
 
 static int pack_refs = 1;
 static int prune_reflogs = 1;
-static int cruft_packs = -1;
+static int cruft_packs = 1;
+static unsigned long max_cruft_size;
 static int aggressive_depth = 50;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
@@ -52,6 +62,8 @@
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
+static char *repack_filter;
+static char *repack_filter_to;
 static unsigned long big_pack_threshold;
 static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
 
@@ -154,6 +166,7 @@
 	git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
 	git_config_get_bool("gc.autodetach", &detach_auto);
 	git_config_get_bool("gc.cruftpacks", &cruft_packs);
+	git_config_get_ulong("gc.maxcruftsize", &max_cruft_size);
 	git_config_get_expiry("gc.pruneexpire", &prune_expire);
 	git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
 	git_config_get_expiry("gc.logexpiry", &gc_log_expire);
@@ -161,16 +174,57 @@
 	git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold);
 	git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size);
 
+	git_config_get_string("gc.repackfilter", &repack_filter);
+	git_config_get_string("gc.repackfilterto", &repack_filter_to);
+
 	git_config(git_default_config, NULL);
 }
 
-struct maintenance_run_opts;
+enum schedule_priority {
+	SCHEDULE_NONE = 0,
+	SCHEDULE_WEEKLY = 1,
+	SCHEDULE_DAILY = 2,
+	SCHEDULE_HOURLY = 3,
+};
+
+static enum schedule_priority parse_schedule(const char *value)
+{
+	if (!value)
+		return SCHEDULE_NONE;
+	if (!strcasecmp(value, "hourly"))
+		return SCHEDULE_HOURLY;
+	if (!strcasecmp(value, "daily"))
+		return SCHEDULE_DAILY;
+	if (!strcasecmp(value, "weekly"))
+		return SCHEDULE_WEEKLY;
+	return SCHEDULE_NONE;
+}
+
+struct maintenance_run_opts {
+	int auto_flag;
+	int quiet;
+	enum schedule_priority schedule;
+};
+
+static int pack_refs_condition(void)
+{
+	/*
+	 * The auto-repacking logic for refs is handled by the ref backends and
+	 * exposed via `git pack-refs --auto`. We thus always return truish
+	 * here and let the backend decide for us.
+	 */
+	return 1;
+}
+
 static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
 
 	cmd.git_cmd = 1;
 	strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL);
+	if (opts->auto_flag)
+		strvec_push(&cmd.args, "--auto");
+
 	return run_command(&cmd);
 }
 
@@ -213,7 +267,7 @@
 	struct packed_git *p, *base = NULL;
 
 	for (p = get_all_packs(the_repository); p; p = p->next) {
-		if (!p->pack_local)
+		if (!p->pack_local || p->is_cruft)
 			continue;
 		if (limit) {
 			if (p->pack_size >= limit)
@@ -284,7 +338,7 @@
 
 static uint64_t estimate_repack_memory(struct packed_git *pack)
 {
-	unsigned long nr_objects = approximate_object_count();
+	unsigned long nr_objects = repo_approximate_object_count(the_repository);
 	size_t os_cache, heap;
 
 	if (!pack || !nr_objects)
@@ -338,6 +392,9 @@
 		strvec_push(&repack, "--cruft");
 		if (prune_expire)
 			strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire);
+		if (max_cruft_size)
+			strvec_pushf(&repack, "--max-cruft-size=%lu",
+				     max_cruft_size);
 	} else {
 		strvec_push(&repack, "-A");
 		if (prune_expire)
@@ -346,6 +403,11 @@
 
 	if (keep_pack)
 		for_each_string_list(keep_pack, keep_one_pack, NULL);
+
+	if (repack_filter && *repack_filter)
+		strvec_pushf(&repack, "--filter=%s", repack_filter);
+	if (repack_filter_to && *repack_filter_to)
+		strvec_pushf(&repack, "--filter-to=%s", repack_filter_to);
 }
 
 static void add_repack_incremental_option(void)
@@ -523,7 +585,7 @@
 	return ret;
 }
 
-static void gc_before_repack(void)
+static void gc_before_repack(struct maintenance_run_opts *opts)
 {
 	/*
 	 * We may be called twice, as both the pre- and
@@ -534,7 +596,7 @@
 	if (done++)
 		return;
 
-	if (pack_refs && maintenance_task_pack_refs(NULL))
+	if (pack_refs && maintenance_task_pack_refs(opts))
 		die(FAILED_RUN, "pack-refs");
 
 	if (prune_reflogs) {
@@ -550,7 +612,6 @@
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
 	int aggressive = 0;
-	int auto_gc = 0;
 	int quiet = 0;
 	int force = 0;
 	const char *name;
@@ -559,6 +620,7 @@
 	int keep_largest_pack = -1;
 	timestamp_t dummy;
 	struct child_process rerere_cmd = CHILD_PROCESS_INIT;
+	struct maintenance_run_opts opts = {0};
 
 	struct option builtin_gc_options[] = {
 		OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -566,8 +628,10 @@
 			N_("prune unreferenced objects"),
 			PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
 		OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")),
+		OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
+			      N_("with --cruft, limit the size of new cruft packs")),
 		OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
-		OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
+		OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_BOOL_F(0, "force", &force,
 			   N_("force running gc even if there may be another gc running"),
@@ -602,10 +666,6 @@
 	if (prune_expire && parse_expiry_date(prune_expire, &dummy))
 		die(_("failed to parse prune expiry value %s"), prune_expire);
 
-	prepare_repo_settings(the_repository);
-	if (cruft_packs < 0)
-		cruft_packs = the_repository->settings.gc_cruft_packs;
-
 	if (aggressive) {
 		strvec_push(&repack, "-f");
 		if (aggressive_depth > 0)
@@ -616,7 +676,7 @@
 	if (quiet)
 		strvec_push(&repack, "-q");
 
-	if (auto_gc) {
+	if (opts.auto_flag) {
 		/*
 		 * Auto-gc should be least intrusive as possible.
 		 */
@@ -641,7 +701,7 @@
 
 			if (lock_repo_for_gc(force, &pid))
 				return 0;
-			gc_before_repack(); /* dies on failure */
+			gc_before_repack(&opts); /* dies on failure */
 			delete_tempfile(&pidfile);
 
 			/*
@@ -666,7 +726,7 @@
 
 	name = lock_repo_for_gc(force, &pid);
 	if (name) {
-		if (auto_gc)
+		if (opts.auto_flag)
 			return 0; /* be quiet on --auto */
 		die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
 		    name, (uintmax_t)pid);
@@ -681,7 +741,7 @@
 		atexit(process_log_file_at_exit);
 	}
 
-	gc_before_repack();
+	gc_before_repack(&opts);
 
 	if (!repository_format_precious_objects) {
 		struct child_process repack_cmd = CHILD_PROCESS_INIT;
@@ -699,7 +759,7 @@
 			strvec_push(&prune, prune_expire);
 			if (quiet)
 				strvec_push(&prune, "--no-progress");
-			if (has_promisor_remote())
+			if (repo_has_promisor_remote(the_repository))
 				strvec_push(&prune,
 					    "--exclude-promisor-objects");
 			prune_cmd.git_cmd = 1;
@@ -736,7 +796,7 @@
 					     !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
 					     NULL);
 
-	if (auto_gc && too_many_loose_objects())
+	if (opts.auto_flag && too_many_loose_objects())
 		warning(_("There are too many unreachable loose objects; "
 			"run 'git prune' to remove them."));
 
@@ -751,26 +811,6 @@
 	NULL
 };
 
-enum schedule_priority {
-	SCHEDULE_NONE = 0,
-	SCHEDULE_WEEKLY = 1,
-	SCHEDULE_DAILY = 2,
-	SCHEDULE_HOURLY = 3,
-};
-
-static enum schedule_priority parse_schedule(const char *value)
-{
-	if (!value)
-		return SCHEDULE_NONE;
-	if (!strcasecmp(value, "hourly"))
-		return SCHEDULE_HOURLY;
-	if (!strcasecmp(value, "daily"))
-		return SCHEDULE_DAILY;
-	if (!strcasecmp(value, "weekly"))
-		return SCHEDULE_WEEKLY;
-	return SCHEDULE_NONE;
-}
-
 static int maintenance_opt_schedule(const struct option *opt, const char *arg,
 				    int unset)
 {
@@ -787,12 +827,6 @@
 	return 0;
 }
 
-struct maintenance_run_opts {
-	int auto_flag;
-	int quiet;
-	enum schedule_priority schedule;
-};
-
 /* Remember to update object flag allocation in object.h */
 #define SEEN		(1u<<0)
 
@@ -820,7 +854,7 @@
 	commit = lookup_commit(the_repository, oid);
 	if (!commit)
 		return 0;
-	if (parse_commit(commit) ||
+	if (repo_parse_commit(the_repository, commit) ||
 	    commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
 		return 0;
 
@@ -837,7 +871,7 @@
 		commit = pop_commit(&stack);
 
 		for (parent = commit->parents; parent; parent = parent->next) {
-			if (parse_commit(parent->item) ||
+			if (repo_parse_commit(the_repository, parent->item) ||
 			    commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH ||
 			    parent->item->object.flags & SEEN)
 				continue;
@@ -976,9 +1010,9 @@
 
 static int loose_object_auto_limit = 100;
 
-static int loose_object_count(const struct object_id *oid,
-			       const char *path,
-			       void *data)
+static int loose_object_count(const struct object_id *oid UNUSED,
+			      const char *path UNUSED,
+			      void *data)
 {
 	int *count = (int*)data;
 	if (++(*count) >= loose_object_auto_limit)
@@ -1003,15 +1037,15 @@
 					     NULL, NULL, &count);
 }
 
-static int bail_on_loose(const struct object_id *oid,
-			 const char *path,
-			 void *data)
+static int bail_on_loose(const struct object_id *oid UNUSED,
+			 const char *path UNUSED,
+			 void *data UNUSED)
 {
 	return 1;
 }
 
 static int write_loose_object_to_stdin(const struct object_id *oid,
-				       const char *path,
+				       const char *path UNUSED,
 				       void *data)
 {
 	struct write_loose_object_data *d = (struct write_loose_object_data *)data;
@@ -1274,7 +1308,7 @@
 	[TASK_PACK_REFS] = {
 		"pack-refs",
 		maintenance_task_pack_refs,
-		NULL,
+		pack_refs_condition,
 	},
 };
 
@@ -1398,7 +1432,7 @@
 	strbuf_release(&config_name);
 }
 
-static int task_option_parse(const struct option *opt,
+static int task_option_parse(const struct option *opt UNUSED,
 			     const char *arg, int unset)
 {
 	int i, num_selected = 0;
@@ -1493,7 +1527,6 @@
 	};
 	int found = 0;
 	const char *key = "maintenance.repo";
-	char *config_value;
 	char *maintpath = get_maintpath();
 	struct string_list_item *item;
 	const struct string_list *list;
@@ -1508,13 +1541,10 @@
 	git_config_set("maintenance.auto", "false");
 
 	/* Set maintenance strategy, if unset */
-	if (!git_config_get_string("maintenance.strategy", &config_value))
-		free(config_value);
-	else
+	if (git_config_get("maintenance.strategy"))
 		git_config_set("maintenance.strategy", "incremental");
 
-	list = git_config_get_value_multi(key);
-	if (list) {
+	if (!git_config_get_string_multi(key, &list)) {
 		for_each_string_list_item(item, list) {
 			if (!strcmp(maintpath, item->string)) {
 				found = 1;
@@ -1525,19 +1555,18 @@
 
 	if (!found) {
 		int rc;
-		char *user_config = NULL, *xdg_config = NULL;
+		char *global_config_file = NULL;
 
 		if (!config_file) {
-			git_global_config(&user_config, &xdg_config);
-			config_file = user_config;
-			if (!user_config)
-				die(_("$HOME not set"));
+			global_config_file = git_global_config();
+			config_file = global_config_file;
 		}
+		if (!config_file)
+			die(_("$HOME not set"));
 		rc = git_config_set_multivar_in_file_gently(
 			config_file, "maintenance.repo", maintpath,
-			CONFIG_REGEX_NONE, 0);
-		free(user_config);
-		free(xdg_config);
+			CONFIG_REGEX_NONE, NULL, 0);
+		free(global_config_file);
 
 		if (rc)
 			die(_("unable to add '%s' value of '%s'"),
@@ -1580,11 +1609,10 @@
 	if (config_file) {
 		git_configset_init(&cs);
 		git_configset_add_file(&cs, config_file);
-		list = git_configset_get_value_multi(&cs, key);
-	} else {
-		list = git_config_get_value_multi(key);
 	}
-	if (list) {
+	if (!(config_file
+	      ? git_configset_get_string_multi(&cs, key, &list)
+	      : git_config_get_string_multi(key, &list))) {
 		for_each_string_list_item(item, list) {
 			if (!strcmp(maintpath, item->string)) {
 				found = 1;
@@ -1595,18 +1623,18 @@
 
 	if (found) {
 		int rc;
-		char *user_config = NULL, *xdg_config = NULL;
+		char *global_config_file = NULL;
+
 		if (!config_file) {
-			git_global_config(&user_config, &xdg_config);
-			config_file = user_config;
-			if (!user_config)
-				die(_("$HOME not set"));
+			global_config_file = git_global_config();
+			config_file = global_config_file;
 		}
+		if (!config_file)
+			die(_("$HOME not set"));
 		rc = git_config_set_multivar_in_file_gently(
-			config_file, key, NULL, maintpath,
+			config_file, key, NULL, maintpath, NULL,
 			CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
-		free(user_config);
-		free(xdg_config);
+		free(global_config_file);
 
 		if (rc &&
 		    (!force || rc == CONFIG_NOTHING_SET))
@@ -1686,11 +1714,11 @@
 	if (is_available)
 		*is_available = 0;
 
-	string_list_split_in_place(&list, testing, ',', -1);
+	string_list_split_in_place(&list, testing, ",", -1);
 	for_each_string_list_item(item, &list) {
 		struct string_list pair = STRING_LIST_INIT_NODUP;
 
-		if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)
+		if (string_list_split_in_place(&pair, item->string, ":", 2) != 2)
 			continue;
 
 		if (!strcmp(*cmd, pair.items[0].string)) {
@@ -1708,6 +1736,15 @@
 	return 1;
 }
 
+static int get_random_minute(void)
+{
+	/* Use a static value when under tests. */
+	if (getenv("GIT_TEST_MAINT_SCHEDULER"))
+		return 13;
+
+	return git_rand() % 60;
+}
+
 static int is_launchctl_available(void)
 {
 	const char *cmd = "launchctl";
@@ -1820,6 +1857,7 @@
 	struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
 	struct stat st;
 	const char *cmd = "launchctl";
+	int minute = get_random_minute();
 
 	get_schedule_cmd(&cmd, NULL);
 	preamble = "<?xml version=\"1.0\"?>\n"
@@ -1845,29 +1883,30 @@
 	case SCHEDULE_HOURLY:
 		repeat = "<dict>\n"
 			 "<key>Hour</key><integer>%d</integer>\n"
-			 "<key>Minute</key><integer>0</integer>\n"
+			 "<key>Minute</key><integer>%d</integer>\n"
 			 "</dict>\n";
 		for (i = 1; i <= 23; i++)
-			strbuf_addf(&plist, repeat, i);
+			strbuf_addf(&plist, repeat, i, minute);
 		break;
 
 	case SCHEDULE_DAILY:
 		repeat = "<dict>\n"
 			 "<key>Day</key><integer>%d</integer>\n"
 			 "<key>Hour</key><integer>0</integer>\n"
-			 "<key>Minute</key><integer>0</integer>\n"
+			 "<key>Minute</key><integer>%d</integer>\n"
 			 "</dict>\n";
 		for (i = 1; i <= 6; i++)
-			strbuf_addf(&plist, repeat, i);
+			strbuf_addf(&plist, repeat, i, minute);
 		break;
 
 	case SCHEDULE_WEEKLY:
-		strbuf_addstr(&plist,
-			      "<dict>\n"
-			      "<key>Day</key><integer>0</integer>\n"
-			      "<key>Hour</key><integer>0</integer>\n"
-			      "<key>Minute</key><integer>0</integer>\n"
-			      "</dict>\n");
+		strbuf_addf(&plist,
+			    "<dict>\n"
+			    "<key>Day</key><integer>0</integer>\n"
+			    "<key>Hour</key><integer>0</integer>\n"
+			    "<key>Minute</key><integer>%d</integer>\n"
+			    "</dict>\n",
+			    minute);
 		break;
 
 	default:
@@ -1923,7 +1962,7 @@
 	       launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY);
 }
 
-static int launchctl_update_schedule(int run_maintenance, int fd)
+static int launchctl_update_schedule(int run_maintenance, int fd UNUSED)
 {
 	if (run_maintenance)
 		return launchctl_add_plists();
@@ -1984,6 +2023,7 @@
 	const char *frequency = get_frequency(schedule);
 	char *name = schtasks_task_name(frequency);
 	struct strbuf tfilename = STRBUF_INIT;
+	int minute = get_random_minute();
 
 	get_schedule_cmd(&cmd, NULL);
 
@@ -2004,7 +2044,7 @@
 	switch (schedule) {
 	case SCHEDULE_HOURLY:
 		fprintf(tfile->fp,
-			"<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n"
+			"<StartBoundary>2020-01-01T01:%02d:00</StartBoundary>\n"
 			"<Enabled>true</Enabled>\n"
 			"<ScheduleByDay>\n"
 			"<DaysInterval>1</DaysInterval>\n"
@@ -2013,12 +2053,13 @@
 			"<Interval>PT1H</Interval>\n"
 			"<Duration>PT23H</Duration>\n"
 			"<StopAtDurationEnd>false</StopAtDurationEnd>\n"
-			"</Repetition>\n");
+			"</Repetition>\n",
+			minute);
 		break;
 
 	case SCHEDULE_DAILY:
 		fprintf(tfile->fp,
-			"<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
+			"<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n"
 			"<Enabled>true</Enabled>\n"
 			"<ScheduleByWeek>\n"
 			"<DaysOfWeek>\n"
@@ -2030,19 +2071,21 @@
 			"<Saturday />\n"
 			"</DaysOfWeek>\n"
 			"<WeeksInterval>1</WeeksInterval>\n"
-			"</ScheduleByWeek>\n");
+			"</ScheduleByWeek>\n",
+			minute);
 		break;
 
 	case SCHEDULE_WEEKLY:
 		fprintf(tfile->fp,
-			"<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
+			"<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n"
 			"<Enabled>true</Enabled>\n"
 			"<ScheduleByWeek>\n"
 			"<DaysOfWeek>\n"
 			"<Sunday />\n"
 			"</DaysOfWeek>\n"
 			"<WeeksInterval>1</WeeksInterval>\n"
-			"</ScheduleByWeek>\n");
+			"</ScheduleByWeek>\n",
+			minute);
 		break;
 
 	default:
@@ -2068,7 +2111,7 @@
 	      "</Settings>\n"
 	      "<Actions Context=\"Author\">\n"
 	      "<Exec>\n"
-	      "<Command>\"%s\\git.exe\"</Command>\n"
+	      "<Command>\"%s\\headless-git.exe\"</Command>\n"
 	      "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
 	      "</Exec>\n"
 	      "</Actions>\n"
@@ -2100,7 +2143,7 @@
 	       schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY);
 }
 
-static int schtasks_update_schedule(int run_maintenance, int fd)
+static int schtasks_update_schedule(int run_maintenance, int fd UNUSED)
 {
 	if (run_maintenance)
 		return schtasks_schedule_tasks();
@@ -2159,6 +2202,7 @@
 	FILE *cron_list, *cron_in;
 	struct strbuf line = STRBUF_INIT;
 	struct tempfile *tmpedit = NULL;
+	int minute = get_random_minute();
 
 	get_schedule_cmd(&cmd, NULL);
 	strvec_split(&crontab_list.args, cmd);
@@ -2213,11 +2257,11 @@
 			"# replaced in the future by a Git command.\n\n");
 
 		strbuf_addf(&line_format,
-			    "%%s %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
+			    "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
 			    exec_path, exec_path);
-		fprintf(cron_in, line_format.buf, "0", "1-23", "*", "hourly");
-		fprintf(cron_in, line_format.buf, "0", "0", "1-6", "daily");
-		fprintf(cron_in, line_format.buf, "0", "0", "0", "weekly");
+		fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly");
+		fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily");
+		fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly");
 		strbuf_release(&line_format);
 
 		fprintf(cron_in, "\n%s\n", END_LINE);
@@ -2276,77 +2320,54 @@
 	return xdg_config_home_for("systemd/user", filename);
 }
 
-static int systemd_timer_enable_unit(int enable,
-				     enum schedule_priority schedule)
-{
-	const char *cmd = "systemctl";
-	struct child_process child = CHILD_PROCESS_INIT;
-	const char *frequency = get_frequency(schedule);
+#define SYSTEMD_UNIT_FORMAT "git-maintenance@%s.%s"
 
-	/*
-	 * Disabling the systemd unit while it is already disabled makes
-	 * systemctl print an error.
-	 * Let's ignore it since it means we already are in the expected state:
-	 * the unit is disabled.
-	 *
-	 * On the other hand, enabling a systemd unit which is already enabled
-	 * produces no error.
-	 */
-	if (!enable)
-		child.no_stderr = 1;
-
-	get_schedule_cmd(&cmd, NULL);
-	strvec_split(&child.args, cmd);
-	strvec_pushl(&child.args, "--user", enable ? "enable" : "disable",
-		     "--now", NULL);
-	strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency);
-
-	if (start_command(&child))
-		return error(_("failed to start systemctl"));
-	if (finish_command(&child))
-		/*
-		 * Disabling an already disabled systemd unit makes
-		 * systemctl fail.
-		 * Let's ignore this failure.
-		 *
-		 * Enabling an enabled systemd unit doesn't fail.
-		 */
-		if (enable)
-			return error(_("failed to run systemctl"));
-	return 0;
-}
-
-static int systemd_timer_delete_unit_templates(void)
+static int systemd_timer_delete_timer_file(enum schedule_priority priority)
 {
 	int ret = 0;
-	char *filename = xdg_config_home_systemd("git-maintenance@.timer");
-	if (unlink(filename) && !is_missing_file_error(errno))
-		ret = error_errno(_("failed to delete '%s'"), filename);
-	FREE_AND_NULL(filename);
+	const char *frequency = get_frequency(priority);
+	char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer");
+	char *filename = xdg_config_home_systemd(local_timer_name);
 
-	filename = xdg_config_home_systemd("git-maintenance@.service");
 	if (unlink(filename) && !is_missing_file_error(errno))
 		ret = error_errno(_("failed to delete '%s'"), filename);
 
 	free(filename);
+	free(local_timer_name);
 	return ret;
 }
 
-static int systemd_timer_delete_units(void)
+static int systemd_timer_delete_service_template(void)
 {
-	return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) ||
-	       systemd_timer_enable_unit(0, SCHEDULE_DAILY) ||
-	       systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) ||
-	       systemd_timer_delete_unit_templates();
+	int ret = 0;
+	char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service");
+	char *filename = xdg_config_home_systemd(local_service_name);
+	if (unlink(filename) && !is_missing_file_error(errno))
+		ret = error_errno(_("failed to delete '%s'"), filename);
+
+	free(filename);
+	free(local_service_name);
+	return ret;
 }
 
-static int systemd_timer_write_unit_templates(const char *exec_path)
+/*
+ * Write the schedule information into a git-maintenance@<schedule>.timer
+ * file using a custom minute. This timer file cannot use the templating
+ * system, so we generate a specific file for each.
+ */
+static int systemd_timer_write_timer_file(enum schedule_priority schedule,
+					  int minute)
 {
+	int res = -1;
 	char *filename;
 	FILE *file;
 	const char *unit;
+	char *schedule_pattern = NULL;
+	const char *frequency = get_frequency(schedule);
+	char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer");
 
-	filename = xdg_config_home_systemd("git-maintenance@.timer");
+	filename = xdg_config_home_systemd(local_timer_name);
+
 	if (safe_create_leading_directories(filename)) {
 		error(_("failed to create directories for '%s'"), filename);
 		goto error;
@@ -2355,6 +2376,23 @@
 	if (!file)
 		goto error;
 
+	switch (schedule) {
+	case SCHEDULE_HOURLY:
+		schedule_pattern = xstrfmt("*-*-* 1..23:%02d:00", minute);
+		break;
+
+	case SCHEDULE_DAILY:
+		schedule_pattern = xstrfmt("Tue..Sun *-*-* 0:%02d:00", minute);
+		break;
+
+	case SCHEDULE_WEEKLY:
+		schedule_pattern = xstrfmt("Mon 0:%02d:00", minute);
+		break;
+
+	default:
+		BUG("Unhandled schedule_priority");
+	}
+
 	unit = "# This file was created and is maintained by Git.\n"
 	       "# Any edits made in this file might be replaced in the future\n"
 	       "# by a Git command.\n"
@@ -2363,12 +2401,12 @@
 	       "Description=Optimize Git repositories data\n"
 	       "\n"
 	       "[Timer]\n"
-	       "OnCalendar=%i\n"
+	       "OnCalendar=%s\n"
 	       "Persistent=true\n"
 	       "\n"
 	       "[Install]\n"
 	       "WantedBy=timers.target\n";
-	if (fputs(unit, file) == EOF) {
+	if (fprintf(file, unit, schedule_pattern) < 0) {
 		error(_("failed to write to '%s'"), filename);
 		fclose(file);
 		goto error;
@@ -2377,9 +2415,36 @@
 		error_errno(_("failed to flush '%s'"), filename);
 		goto error;
 	}
-	free(filename);
 
-	filename = xdg_config_home_systemd("git-maintenance@.service");
+	res = 0;
+
+error:
+	free(schedule_pattern);
+	free(local_timer_name);
+	free(filename);
+	return res;
+}
+
+/*
+ * No matter the schedule, we use the same service and can make use of the
+ * templating system. When installing git-maintenance@<schedule>.timer,
+ * systemd will notice that git-maintenance@.service exists as a template
+ * and will use this file and insert the <schedule> into the template at
+ * the position of "%i".
+ */
+static int systemd_timer_write_service_template(const char *exec_path)
+{
+	int res = -1;
+	char *filename;
+	FILE *file;
+	const char *unit;
+	char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service");
+
+	filename = xdg_config_home_systemd(local_service_name);
+	if (safe_create_leading_directories(filename)) {
+		error(_("failed to create directories for '%s'"), filename);
+		goto error;
+	}
 	file = fopen_or_warn(filename, "w");
 	if (!file)
 		goto error;
@@ -2397,7 +2462,7 @@
 	       "LockPersonality=yes\n"
 	       "MemoryDenyWriteExecute=yes\n"
 	       "NoNewPrivileges=yes\n"
-	       "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\n"
+	       "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_VSOCK\n"
 	       "RestrictNamespaces=yes\n"
 	       "RestrictRealtime=yes\n"
 	       "RestrictSUIDSGID=yes\n"
@@ -2412,29 +2477,114 @@
 		error_errno(_("failed to flush '%s'"), filename);
 		goto error;
 	}
-	free(filename);
-	return 0;
+
+	res = 0;
 
 error:
+	free(local_service_name);
 	free(filename);
-	systemd_timer_delete_unit_templates();
-	return -1;
+	return res;
+}
+
+static int systemd_timer_enable_unit(int enable,
+				     enum schedule_priority schedule,
+				     int minute)
+{
+	const char *cmd = "systemctl";
+	struct child_process child = CHILD_PROCESS_INIT;
+	const char *frequency = get_frequency(schedule);
+
+	/*
+	 * Disabling the systemd unit while it is already disabled makes
+	 * systemctl print an error.
+	 * Let's ignore it since it means we already are in the expected state:
+	 * the unit is disabled.
+	 *
+	 * On the other hand, enabling a systemd unit which is already enabled
+	 * produces no error.
+	 */
+	if (!enable)
+		child.no_stderr = 1;
+	else if (systemd_timer_write_timer_file(schedule, minute))
+		return -1;
+
+	get_schedule_cmd(&cmd, NULL);
+	strvec_split(&child.args, cmd);
+	strvec_pushl(&child.args, "--user", enable ? "enable" : "disable",
+		     "--now", NULL);
+	strvec_pushf(&child.args, SYSTEMD_UNIT_FORMAT, frequency, "timer");
+
+	if (start_command(&child))
+		return error(_("failed to start systemctl"));
+	if (finish_command(&child))
+		/*
+		 * Disabling an already disabled systemd unit makes
+		 * systemctl fail.
+		 * Let's ignore this failure.
+		 *
+		 * Enabling an enabled systemd unit doesn't fail.
+		 */
+		if (enable)
+			return error(_("failed to run systemctl"));
+	return 0;
+}
+
+/*
+ * A previous version of Git wrote the timer units as template files.
+ * Clean these up, if they exist.
+ */
+static void systemd_timer_delete_stale_timer_templates(void)
+{
+	char *timer_template_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "timer");
+	char *filename = xdg_config_home_systemd(timer_template_name);
+
+	if (unlink(filename) && !is_missing_file_error(errno))
+		warning(_("failed to delete '%s'"), filename);
+
+	free(filename);
+	free(timer_template_name);
+}
+
+static int systemd_timer_delete_unit_files(void)
+{
+	systemd_timer_delete_stale_timer_templates();
+
+	/* Purposefully not short-circuited to make sure all are called. */
+	return systemd_timer_delete_timer_file(SCHEDULE_HOURLY) |
+	       systemd_timer_delete_timer_file(SCHEDULE_DAILY) |
+	       systemd_timer_delete_timer_file(SCHEDULE_WEEKLY) |
+	       systemd_timer_delete_service_template();
+}
+
+static int systemd_timer_delete_units(void)
+{
+	int minute = get_random_minute();
+	/* Purposefully not short-circuited to make sure all are called. */
+	return systemd_timer_enable_unit(0, SCHEDULE_HOURLY, minute) |
+	       systemd_timer_enable_unit(0, SCHEDULE_DAILY, minute) |
+	       systemd_timer_enable_unit(0, SCHEDULE_WEEKLY, minute) |
+	       systemd_timer_delete_unit_files();
 }
 
 static int systemd_timer_setup_units(void)
 {
+	int minute = get_random_minute();
 	const char *exec_path = git_exec_path();
 
-	int ret = systemd_timer_write_unit_templates(exec_path) ||
-		  systemd_timer_enable_unit(1, SCHEDULE_HOURLY) ||
-		  systemd_timer_enable_unit(1, SCHEDULE_DAILY) ||
-		  systemd_timer_enable_unit(1, SCHEDULE_WEEKLY);
+	int ret = systemd_timer_write_service_template(exec_path) ||
+		  systemd_timer_enable_unit(1, SCHEDULE_HOURLY, minute) ||
+		  systemd_timer_enable_unit(1, SCHEDULE_DAILY, minute) ||
+		  systemd_timer_enable_unit(1, SCHEDULE_WEEKLY, minute);
+
 	if (ret)
 		systemd_timer_delete_units();
+	else
+		systemd_timer_delete_stale_timer_templates();
+
 	return ret;
 }
 
-static int systemd_timer_update_schedule(int run_maintenance, int fd)
+static int systemd_timer_update_schedule(int run_maintenance, int fd UNUSED)
 {
 	if (run_maintenance)
 		return systemd_timer_setup_units();
@@ -2606,9 +2756,12 @@
 	opts.scheduler = resolve_scheduler(opts.scheduler);
 	validate_scheduler(opts.scheduler);
 
+	if (update_background_schedule(&opts, 1))
+		die(_("failed to set up maintenance schedule"));
+
 	if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL))
 		warning(_("failed to add repo to global config"));
-	return update_background_schedule(&opts, 1);
+	return 0;
 }
 
 static const char *const builtin_maintenance_stop_usage[] = {
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 491af92..66a7389 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -1,11 +1,9 @@
 /*
  * Copyright (c) 2005, 2006 Rene Scharfe
  */
-#include "cache.h"
+#include "builtin.h"
 #include "commit.h"
 #include "tar.h"
-#include "builtin.h"
-#include "quote.h"
 
 static const char builtin_get_tar_commit_id_usage[] =
 "git get-tar-commit-id";
@@ -14,7 +12,7 @@
 #define RECORDSIZE	(512)
 #define HEADERSIZE (2 * RECORDSIZE)
 
-int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix)
 {
 	char buffer[HEADERSIZE];
 	struct ustar_header *header = (struct ustar_header *)buffer;
@@ -24,6 +22,8 @@
 	long len;
 	char *end;
 
+	BUG_ON_NON_EMPTY_PREFIX(prefix);
+
 	if (argc != 1)
 		usage(builtin_get_tar_commit_id_usage);
 
@@ -32,7 +32,7 @@
 		die_errno("git get-tar-commit-id: read error");
 	if (n != HEADERSIZE)
 		die_errno("git get-tar-commit-id: EOF before reading tar header");
-	if (header->typeflag[0] != 'g')
+	if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER)
 		return 1;
 
 	len = strtol(content, &end, 10);
diff --git a/builtin/grep.c b/builtin/grep.c
index f7821c5..5777ba8 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -3,27 +3,32 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#include "cache.h"
+#include "builtin.h"
+#include "abspath.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
-#include "blob.h"
-#include "tree.h"
-#include "commit.h"
 #include "tag.h"
 #include "tree-walk.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "run-command.h"
-#include "userdiff.h"
 #include "grep.h"
 #include "quote.h"
 #include "dir.h"
 #include "pathspec.h"
+#include "setup.h"
 #include "submodule.h"
 #include "submodule-config.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "packfile.h"
+#include "pager.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "write-or-die.h"
 
 static const char *grep_prefix;
 
@@ -282,14 +287,18 @@
 	return hit;
 }
 
-static int grep_cmd_config(const char *var, const char *value, void *cb)
+static int grep_cmd_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
-	int st = grep_config(var, value, cb);
-	if (git_color_default_config(var, value, NULL) < 0)
+	int st = grep_config(var, value, ctx, cb);
+
+	if (git_color_config(var, value, cb) < 0)
+		st = -1;
+	else if (git_default_config(var, value, ctx, cb) < 0)
 		st = -1;
 
 	if (!strcmp(var, "grep.threads")) {
-		num_threads = git_config_int(var, value);
+		num_threads = git_config_int(var, value, ctx->kvi);
 		if (num_threads < 0)
 			die(_("invalid number of threads specified (%d) for %s"),
 			    num_threads, var);
@@ -518,7 +527,7 @@
 		strbuf_addstr(&base, filename);
 		strbuf_addch(&base, '/');
 
-		init_tree_desc(&tree, data, size);
+		init_tree_desc(&tree, oid, data, size);
 		hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
 				object_type == OBJ_COMMIT);
 		strbuf_release(&base);
@@ -560,8 +569,11 @@
 			void *data;
 			unsigned long size;
 
-			data = read_object_file(&ce->oid, &type, &size);
-			init_tree_desc(&tree, data, size);
+			data = repo_read_object_file(the_repository, &ce->oid,
+						     &type, &size);
+			if (!data)
+				die(_("unable to read tree %s"), oid_to_hex(&ce->oid));
+			init_tree_desc(&tree, &ce->oid, data, size);
 
 			hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
 			strbuf_setlen(&name, name_base_len);
@@ -630,7 +642,7 @@
 			strbuf_addstr(&name, base->buf + tn_len);
 			match = tree_entry_interesting(repo->index,
 						       &entry, &name,
-						       0, pathspec);
+						       pathspec);
 			strbuf_setlen(&name, name_base_len);
 
 			if (match == all_entries_not_interesting)
@@ -650,13 +662,14 @@
 			void *data;
 			unsigned long size;
 
-			data = read_object_file(&entry.oid, &type, &size);
+			data = repo_read_object_file(the_repository,
+						     &entry.oid, &type, &size);
 			if (!data)
 				die(_("unable to read tree (%s)"),
 				    oid_to_hex(&entry.oid));
 
 			strbuf_addch(base, '/');
-			init_tree_desc(&sub, data, size);
+			init_tree_desc(&sub, &entry.oid, data, size);
 			hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
 					 check_attr);
 			free(data);
@@ -700,7 +713,7 @@
 			strbuf_add(&base, name, len);
 			strbuf_addch(&base, ':');
 		}
-		init_tree_desc(&tree, data, size);
+		init_tree_desc(&tree, &obj->oid, data, size);
 		hit = grep_tree(opt, pathspec, &tree, &base, base.len,
 				obj->type == OBJ_COMMIT);
 		strbuf_release(&base);
@@ -798,14 +811,20 @@
 {
 	struct grep_opt *grep_opt = opt->value;
 	int from_stdin;
+	const char *filename = arg;
 	FILE *patterns;
 	int lno = 0;
 	struct strbuf sb = STRBUF_INIT;
 
 	BUG_ON_OPT_NEG(unset);
 
-	from_stdin = !strcmp(arg, "-");
-	patterns = from_stdin ? stdin : fopen(arg, "r");
+	if (!*filename)
+		; /* leave it as-is */
+	else
+		filename = prefix_filename_except_for_dash(grep_prefix, filename);
+
+	from_stdin = !strcmp(filename, "-");
+	patterns = from_stdin ? stdin : fopen(filename, "r");
 	if (!patterns)
 		die_errno(_("cannot open '%s'"), arg);
 	while (strbuf_getline(&sb, patterns) == 0) {
@@ -819,6 +838,8 @@
 	if (!from_stdin)
 		fclose(patterns);
 	strbuf_release(&sb);
+	if (filename != arg)
+		free((void *)filename);
 	return 0;
 }
 
@@ -910,9 +931,8 @@
 			 N_("process binary files with textconv filters")),
 		OPT_SET_INT('r', "recursive", &opt.max_depth,
 			    N_("search in subdirectories (default)"), -1),
-		{ OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
-			N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
-			NULL, 1 },
+		OPT_INTEGER_F(0, "max-depth", &opt.max_depth,
+			N_("descend at most <n> levels"), PARSE_OPT_NONEG),
 		OPT_GROUP(""),
 		OPT_SET_INT('E', "extended-regexp", &opt.pattern_type_option,
 			    N_("use extended POSIX regular expressions"),
@@ -976,7 +996,7 @@
 		OPT_CALLBACK_F(0, "and", &opt, NULL,
 			N_("combine patterns specified with -e"),
 			PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback),
-		OPT_BOOL(0, "or", &dummy, ""),
+		OPT_BOOL_F(0, "or", &dummy, "", PARSE_OPT_NONEG),
 		OPT_CALLBACK_F(0, "not", &opt, NULL, "",
 			PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback),
 		OPT_CALLBACK_F('(', NULL, &opt, NULL, "",
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 44db83f..82ca6d2 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -5,12 +5,18 @@
  * Copyright (C) Junio C Hamano, 2005
  */
 #include "builtin.h"
+#include "abspath.h"
 #include "config.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
-#include "exec-cmd.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "write-or-die.h"
 
 /*
  * This is to create corrupt objects for debugging and as such it
diff --git a/builtin/help.c b/builtin/help.c
index 53f2812..dc1fbe2 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -1,15 +1,18 @@
 /*
  * Builtin help command
  */
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
 #include "exec-cmd.h"
+#include "gettext.h"
+#include "pager.h"
 #include "parse-options.h"
+#include "path.h"
 #include "run-command.h"
 #include "config-list.h"
 #include "help.h"
 #include "alias.h"
+#include "setup.h"
 
 #ifndef DEFAULT_HELP_FORMAT
 #define DEFAULT_HELP_FORMAT "man"
@@ -394,7 +397,8 @@
 	return 0;
 }
 
-static int git_help_config(const char *var, const char *value, void *cb)
+static int git_help_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "help.format")) {
 		if (!value)
@@ -417,7 +421,7 @@
 	if (starts_with(var, "man."))
 		return add_man_viewer_info(var, value);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static struct cmdnames main_cmds, other_cmds;
diff --git a/builtin/hook.c b/builtin/hook.c
index f95b796..5234693 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -1,9 +1,8 @@
-#include "cache.h"
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "hook.h"
 #include "parse-options.h"
-#include "strbuf.h"
 #include "strvec.h"
 
 #define BUILTIN_HOOK_RUN_USAGE \
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 6648f2d..856428f 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1,23 +1,30 @@
 #include "builtin.h"
 #include "config.h"
 #include "delta.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "pack.h"
 #include "csum-file.h"
 #include "blob.h"
 #include "commit.h"
-#include "tag.h"
 #include "tree.h"
 #include "progress.h"
 #include "fsck.h"
-#include "exec-cmd.h"
+#include "strbuf.h"
 #include "streaming.h"
 #include "thread-utils.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "pack-revindex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "replace-object.h"
 #include "promisor-remote.h"
+#include "setup.h"
 
 static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
 
 struct object_entry {
 	struct pack_idx_entry idx;
@@ -212,7 +219,8 @@
 }
 
 static int mark_link(struct object *obj, enum object_type type,
-		     void *data, struct fsck_options *options)
+		     void *data UNUSED,
+		     struct fsck_options *options UNUSED)
 {
 	if (!obj)
 		return -1;
@@ -801,7 +809,8 @@
 	if (startup_info->have_repository) {
 		read_lock();
 		collision_test_needed =
-			has_object_file_with_flags(oid, OBJECT_INFO_QUICK);
+			repo_has_object_file_with_flags(the_repository, oid,
+							OBJECT_INFO_QUICK);
 		read_unlock();
 	}
 
@@ -821,7 +830,8 @@
 			die(_("cannot read existing object info %s"), oid_to_hex(oid));
 		if (has_type != type || has_size != size)
 			die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
-		has_data = read_object_file(oid, &has_type, &has_size);
+		has_data = repo_read_object_file(the_repository, oid,
+						 &has_type, &has_size);
 		read_unlock();
 		if (!data)
 			data = new_data = get_data_from_pack(obj_entry);
@@ -1154,6 +1164,7 @@
 	struct ofs_delta_entry *ofs_delta = ofs_deltas;
 	struct object_id ref_delta_oid;
 	struct stat st;
+	git_hash_ctx tmp_ctx;
 
 	if (verbose)
 		progress = start_progress(
@@ -1190,7 +1201,9 @@
 
 	/* Check pack integrity */
 	flush();
-	the_hash_algo->final_fn(hash, &input_ctx);
+	the_hash_algo->init_fn(&tmp_ctx);
+	the_hash_algo->clone_fn(&tmp_ctx, &input_ctx);
+	the_hash_algo->final_fn(hash, &tmp_ctx);
 	if (!hasheq(fill(the_hash_algo->rawsz), hash))
 		die(_("pack is corrupted (SHA1 mismatch)"));
 	use(the_hash_algo->rawsz);
@@ -1242,6 +1255,7 @@
 	base_cache_limit = delta_base_cache_limit * nr_threads;
 	if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
 		init_thread();
+		work_lock();
 		for (i = 0; i < nr_threads; i++) {
 			int ret = pthread_create(&thread_data[i].thread, NULL,
 						 threaded_second_pass, thread_data + i);
@@ -1249,6 +1263,7 @@
 				die(_("unable to create thread: %s"),
 				    strerror(ret));
 		}
+		work_unlock();
 		for (i = 0; i < nr_threads; i++)
 			pthread_join(thread_data[i].thread, NULL);
 		cleanup_thread();
@@ -1388,7 +1403,7 @@
 		sorted_by_pos[i] = &ref_deltas[i];
 	QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
 
-	if (has_promisor_remote()) {
+	if (repo_has_promisor_remote(the_repository)) {
 		/*
 		 * Prefetch the delta bases.
 		 */
@@ -1414,7 +1429,8 @@
 
 		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
 			continue;
-		data = read_object_file(&d->oid, &type, &size);
+		data = repo_read_object_file(the_repository, &d->oid, &type,
+					     &size);
 		if (!data)
 			continue;
 
@@ -1508,14 +1524,12 @@
 	struct strbuf pack_name = STRBUF_INIT;
 	struct strbuf index_name = STRBUF_INIT;
 	struct strbuf rev_index_name = STRBUF_INIT;
-	int err;
 
 	if (!from_stdin) {
 		close(input_fd);
 	} else {
 		fsync_component_or_die(FSYNC_COMPONENT_PACK, output_fd, curr_pack_name);
-		err = close(output_fd);
-		if (err)
+		if (close(output_fd))
 			die_errno(_("error while closing pack file"));
 	}
 
@@ -1550,17 +1564,8 @@
 		write_or_die(1, buf.buf, buf.len);
 		strbuf_release(&buf);
 
-		/*
-		 * Let's just mimic git-unpack-objects here and write
-		 * the last part of the input buffer to stdout.
-		 */
-		while (input_len) {
-			err = xwrite(1, input_buffer + input_offset, input_len);
-			if (err <= 0)
-				break;
-			input_len -= err;
-			input_offset += err;
-		}
+		/* Write the last part of the buffer to stdout */
+		write_in_full(1, input_buffer + input_offset, input_len);
 	}
 
 	strbuf_release(&rev_index_name);
@@ -1568,18 +1573,19 @@
 	strbuf_release(&pack_name);
 }
 
-static int git_index_pack_config(const char *k, const char *v, void *cb)
+static int git_index_pack_config(const char *k, const char *v,
+				 const struct config_context *ctx, void *cb)
 {
 	struct pack_idx_option *opts = cb;
 
 	if (!strcmp(k, "pack.indexversion")) {
-		opts->version = git_config_int(k, v);
+		opts->version = git_config_int(k, v, ctx->kvi);
 		if (opts->version > 2)
 			die(_("bad pack.indexVersion=%"PRIu32), opts->version);
 		return 0;
 	}
 	if (!strcmp(k, "pack.threads")) {
-		nr_threads = git_config_int(k, v);
+		nr_threads = git_config_int(k, v, ctx->kvi);
 		if (nr_threads < 0)
 			die(_("invalid number of threads specified (%d)"),
 			    nr_threads);
@@ -1595,7 +1601,7 @@
 		else
 			opts->flags &= ~WRITE_REV;
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 static int cmp_uint32(const void *a_, const void *b_)
@@ -1739,16 +1745,17 @@
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(index_pack_usage);
 
-	read_replace_refs = 0;
+	disable_replace_refs();
 	fsck_options.walk = mark_link;
 
 	reset_pack_idx_option(&opts);
+	opts.flags |= WRITE_REV;
 	git_config(git_index_pack_config, &opts);
 	if (prefix && chdir(prefix))
 		die(_("Cannot come back to cwd"));
 
-	if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
-		rev_index = 1;
+	if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
+		rev_index = 0;
 	else
 		rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
 
@@ -1767,8 +1774,9 @@
 			} else if (!strcmp(arg, "--check-self-contained-and-connected")) {
 				strict = 1;
 				check_self_contained_and_connected = 1;
-			} else if (!strcmp(arg, "--fsck-objects")) {
+			} else if (skip_to_optional_arg(arg, "--fsck-objects", &arg)) {
 				do_fsck_object = 1;
+				fsck_set_msg_types(&fsck_options, arg);
 			} else if (!strcmp(arg, "--verify")) {
 				verify = 1;
 			} else if (!strcmp(arg, "--verify-stat")) {
diff --git a/builtin/init-db.c b/builtin/init-db.c
index dcaaf10..0170469 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -3,476 +3,17 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
-#include "config.h"
-#include "refs.h"
 #include "builtin.h"
-#include "exec-cmd.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
 #include "parse-options.h"
-#include "worktree.h"
-
-#ifndef DEFAULT_GIT_TEMPLATE_DIR
-#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-#endif
-
-#ifdef NO_TRUSTABLE_FILEMODE
-#define TEST_FILEMODE 0
-#else
-#define TEST_FILEMODE 1
-#endif
-
-#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
-
-static int init_is_bare_repository = 0;
-static int init_shared_repository = -1;
-
-static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
-			     DIR *dir)
-{
-	size_t path_baselen = path->len;
-	size_t template_baselen = template_path->len;
-	struct dirent *de;
-
-	/* Note: if ".git/hooks" file exists in the repository being
-	 * re-initialized, /etc/core-git/templates/hooks/update would
-	 * cause "git init" to fail here.  I think this is sane but
-	 * it means that the set of templates we ship by default, along
-	 * with the way the namespace under .git/ is organized, should
-	 * be really carefully chosen.
-	 */
-	safe_create_dir(path->buf, 1);
-	while ((de = readdir(dir)) != NULL) {
-		struct stat st_git, st_template;
-		int exists = 0;
-
-		strbuf_setlen(path, path_baselen);
-		strbuf_setlen(template_path, template_baselen);
-
-		if (de->d_name[0] == '.')
-			continue;
-		strbuf_addstr(path, de->d_name);
-		strbuf_addstr(template_path, de->d_name);
-		if (lstat(path->buf, &st_git)) {
-			if (errno != ENOENT)
-				die_errno(_("cannot stat '%s'"), path->buf);
-		}
-		else
-			exists = 1;
-
-		if (lstat(template_path->buf, &st_template))
-			die_errno(_("cannot stat template '%s'"), template_path->buf);
-
-		if (S_ISDIR(st_template.st_mode)) {
-			DIR *subdir = opendir(template_path->buf);
-			if (!subdir)
-				die_errno(_("cannot opendir '%s'"), template_path->buf);
-			strbuf_addch(path, '/');
-			strbuf_addch(template_path, '/');
-			copy_templates_1(path, template_path, subdir);
-			closedir(subdir);
-		}
-		else if (exists)
-			continue;
-		else if (S_ISLNK(st_template.st_mode)) {
-			struct strbuf lnk = STRBUF_INIT;
-			if (strbuf_readlink(&lnk, template_path->buf,
-					    st_template.st_size) < 0)
-				die_errno(_("cannot readlink '%s'"), template_path->buf);
-			if (symlink(lnk.buf, path->buf))
-				die_errno(_("cannot symlink '%s' '%s'"),
-					  lnk.buf, path->buf);
-			strbuf_release(&lnk);
-		}
-		else if (S_ISREG(st_template.st_mode)) {
-			if (copy_file(path->buf, template_path->buf, st_template.st_mode))
-				die_errno(_("cannot copy '%s' to '%s'"),
-					  template_path->buf, path->buf);
-		}
-		else
-			error(_("ignoring template %s"), template_path->buf);
-	}
-}
-
-static void copy_templates(const char *template_dir, const char *init_template_dir)
-{
-	struct strbuf path = STRBUF_INIT;
-	struct strbuf template_path = STRBUF_INIT;
-	size_t template_len;
-	struct repository_format template_format = REPOSITORY_FORMAT_INIT;
-	struct strbuf err = STRBUF_INIT;
-	DIR *dir;
-	char *to_free = NULL;
-
-	if (!template_dir)
-		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
-	if (!template_dir)
-		template_dir = init_template_dir;
-	if (!template_dir)
-		template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
-	if (!template_dir[0]) {
-		free(to_free);
-		return;
-	}
-
-	strbuf_addstr(&template_path, template_dir);
-	strbuf_complete(&template_path, '/');
-	template_len = template_path.len;
-
-	dir = opendir(template_path.buf);
-	if (!dir) {
-		warning(_("templates not found in %s"), template_dir);
-		goto free_return;
-	}
-
-	/* Make sure that template is from the correct vintage */
-	strbuf_addstr(&template_path, "config");
-	read_repository_format(&template_format, template_path.buf);
-	strbuf_setlen(&template_path, template_len);
-
-	/*
-	 * No mention of version at all is OK, but anything else should be
-	 * verified.
-	 */
-	if (template_format.version >= 0 &&
-	    verify_repository_format(&template_format, &err) < 0) {
-		warning(_("not copying templates from '%s': %s"),
-			  template_dir, err.buf);
-		strbuf_release(&err);
-		goto close_free_return;
-	}
-
-	strbuf_addstr(&path, get_git_common_dir());
-	strbuf_complete(&path, '/');
-	copy_templates_1(&path, &template_path, dir);
-close_free_return:
-	closedir(dir);
-free_return:
-	free(to_free);
-	strbuf_release(&path);
-	strbuf_release(&template_path);
-	clear_repository_format(&template_format);
-}
-
-/*
- * If the git_dir is not directly inside the working tree, then git will not
- * find it by default, and we need to set the worktree explicitly.
- */
-static int needs_work_tree_config(const char *git_dir, const char *work_tree)
-{
-	if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
-		return 0;
-	if (skip_prefix(git_dir, work_tree, &git_dir) &&
-	    !strcmp(git_dir, "/.git"))
-		return 0;
-	return 1;
-}
-
-void initialize_repository_version(int hash_algo, int reinit)
-{
-	char repo_version_string[10];
-	int repo_version = GIT_REPO_VERSION;
-
-	if (hash_algo != GIT_HASH_SHA1)
-		repo_version = GIT_REPO_VERSION_READ;
-
-	/* This forces creation of new config file */
-	xsnprintf(repo_version_string, sizeof(repo_version_string),
-		  "%d", repo_version);
-	git_config_set("core.repositoryformatversion", repo_version_string);
-
-	if (hash_algo != GIT_HASH_SHA1)
-		git_config_set("extensions.objectformat",
-			       hash_algos[hash_algo].name);
-	else if (reinit)
-		git_config_set_gently("extensions.objectformat", NULL);
-}
-
-static int create_default_files(const char *template_path,
-				const char *original_git_dir,
-				const char *initial_branch,
-				const struct repository_format *fmt,
-				int quiet)
-{
-	struct stat st1;
-	struct strbuf buf = STRBUF_INIT;
-	char *path;
-	char junk[2];
-	int reinit;
-	int filemode;
-	struct strbuf err = STRBUF_INIT;
-	const char *init_template_dir = NULL;
-	const char *work_tree = get_git_work_tree();
-
-	/*
-	 * First copy the templates -- we might have the default
-	 * config file there, in which case we would want to read
-	 * from it after installing.
-	 *
-	 * Before reading that config, we also need to clear out any cached
-	 * values (since we've just potentially changed what's available on
-	 * disk).
-	 */
-	git_config_get_pathname("init.templatedir", &init_template_dir);
-	copy_templates(template_path, init_template_dir);
-	free((char *)init_template_dir);
-	git_config_clear();
-	reset_shared_repository();
-	git_config(git_default_config, NULL);
-
-	/*
-	 * We must make sure command-line options continue to override any
-	 * values we might have just re-read from the config.
-	 */
-	is_bare_repository_cfg = init_is_bare_repository || !work_tree;
-	if (init_shared_repository != -1)
-		set_shared_repository(init_shared_repository);
-
-	/*
-	 * We would have created the above under user's umask -- under
-	 * shared-repository settings, we would need to fix them up.
-	 */
-	if (get_shared_repository()) {
-		adjust_shared_perm(get_git_dir());
-	}
-
-	/*
-	 * We need to create a "refs" dir in any case so that older
-	 * versions of git can tell that this is a repository.
-	 */
-	safe_create_dir(git_path("refs"), 1);
-	adjust_shared_perm(git_path("refs"));
-
-	if (refs_init_db(&err))
-		die("failed to set up refs db: %s", err.buf);
-
-	/*
-	 * Point the HEAD symref to the initial branch with if HEAD does
-	 * not yet exist.
-	 */
-	path = git_path_buf(&buf, "HEAD");
-	reinit = (!access(path, R_OK)
-		  || readlink(path, junk, sizeof(junk)-1) != -1);
-	if (!reinit) {
-		char *ref;
-
-		if (!initial_branch)
-			initial_branch = git_default_branch_name(quiet);
-
-		ref = xstrfmt("refs/heads/%s", initial_branch);
-		if (check_refname_format(ref, 0) < 0)
-			die(_("invalid initial branch name: '%s'"),
-			    initial_branch);
-
-		if (create_symref("HEAD", ref, NULL) < 0)
-			exit(1);
-		free(ref);
-	}
-
-	initialize_repository_version(fmt->hash_algo, 0);
-
-	/* Check filemode trustability */
-	path = git_path_buf(&buf, "config");
-	filemode = TEST_FILEMODE;
-	if (TEST_FILEMODE && !lstat(path, &st1)) {
-		struct stat st2;
-		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
-				!lstat(path, &st2) &&
-				st1.st_mode != st2.st_mode &&
-				!chmod(path, st1.st_mode));
-		if (filemode && !reinit && (st1.st_mode & S_IXUSR))
-			filemode = 0;
-	}
-	git_config_set("core.filemode", filemode ? "true" : "false");
-
-	if (is_bare_repository())
-		git_config_set("core.bare", "true");
-	else {
-		git_config_set("core.bare", "false");
-		/* allow template config file to override the default */
-		if (log_all_ref_updates == LOG_REFS_UNSET)
-			git_config_set("core.logallrefupdates", "true");
-		if (needs_work_tree_config(original_git_dir, work_tree))
-			git_config_set("core.worktree", work_tree);
-	}
-
-	if (!reinit) {
-		/* Check if symlink is supported in the work tree */
-		path = git_path_buf(&buf, "tXXXXXX");
-		if (!close(xmkstemp(path)) &&
-		    !unlink(path) &&
-		    !symlink("testing", path) &&
-		    !lstat(path, &st1) &&
-		    S_ISLNK(st1.st_mode))
-			unlink(path); /* good */
-		else
-			git_config_set("core.symlinks", "false");
-
-		/* Check if the filesystem is case-insensitive */
-		path = git_path_buf(&buf, "CoNfIg");
-		if (!access(path, F_OK))
-			git_config_set("core.ignorecase", "true");
-		probe_utf8_pathname_composition();
-	}
-
-	strbuf_release(&buf);
-	return reinit;
-}
-
-static void create_object_directory(void)
-{
-	struct strbuf path = STRBUF_INIT;
-	size_t baselen;
-
-	strbuf_addstr(&path, get_object_directory());
-	baselen = path.len;
-
-	safe_create_dir(path.buf, 1);
-
-	strbuf_setlen(&path, baselen);
-	strbuf_addstr(&path, "/pack");
-	safe_create_dir(path.buf, 1);
-
-	strbuf_setlen(&path, baselen);
-	strbuf_addstr(&path, "/info");
-	safe_create_dir(path.buf, 1);
-
-	strbuf_release(&path);
-}
-
-static void separate_git_dir(const char *git_dir, const char *git_link)
-{
-	struct stat st;
-
-	if (!stat(git_link, &st)) {
-		const char *src;
-
-		if (S_ISREG(st.st_mode))
-			src = read_gitfile(git_link);
-		else if (S_ISDIR(st.st_mode))
-			src = git_link;
-		else
-			die(_("unable to handle file type %d"), (int)st.st_mode);
-
-		if (rename(src, git_dir))
-			die_errno(_("unable to move %s to %s"), src, git_dir);
-		repair_worktrees(NULL, NULL);
-	}
-
-	write_file(git_link, "gitdir: %s", git_dir);
-}
-
-static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
-{
-	const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
-	/*
-	 * If we already have an initialized repo, don't allow the user to
-	 * specify a different algorithm, as that could cause corruption.
-	 * Otherwise, if the user has specified one on the command line, use it.
-	 */
-	if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
-		die(_("attempt to reinitialize repository with different hash"));
-	else if (hash != GIT_HASH_UNKNOWN)
-		repo_fmt->hash_algo = hash;
-	else if (env) {
-		int env_algo = hash_algo_by_name(env);
-		if (env_algo == GIT_HASH_UNKNOWN)
-			die(_("unknown hash algorithm '%s'"), env);
-		repo_fmt->hash_algo = env_algo;
-	}
-}
-
-int init_db(const char *git_dir, const char *real_git_dir,
-	    const char *template_dir, int hash, const char *initial_branch,
-	    unsigned int flags)
-{
-	int reinit;
-	int exist_ok = flags & INIT_DB_EXIST_OK;
-	char *original_git_dir = real_pathdup(git_dir, 1);
-	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-
-	if (real_git_dir) {
-		struct stat st;
-
-		if (!exist_ok && !stat(git_dir, &st))
-			die(_("%s already exists"), 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 = get_git_dir();
-		separate_git_dir(git_dir, original_git_dir);
-	}
-	else {
-		set_git_dir(git_dir, 1);
-		git_dir = get_git_dir();
-	}
-	startup_info->have_repository = 1;
-
-	/* Ensure `core.hidedotfiles` is processed */
-	git_config(platform_core_config, NULL);
-
-	safe_create_dir(git_dir, 0);
-
-	init_is_bare_repository = is_bare_repository();
-
-	/* Check to see if the repository version is right.
-	 * Note that a newly created repository does not have
-	 * 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);
-
-	validate_hash_algorithm(&repo_fmt, hash);
-
-	reinit = create_default_files(template_dir, original_git_dir,
-				      initial_branch, &repo_fmt,
-				      flags & INIT_DB_QUIET);
-	if (reinit && initial_branch)
-		warning(_("re-init: ignored --initial-branch=%s"),
-			initial_branch);
-
-	create_object_directory();
-
-	if (get_shared_repository()) {
-		char buf[10];
-		/* We do not spell "group" and such, so that
-		 * the configuration can be read by older version
-		 * of git. Note, we use octal numbers for new share modes,
-		 * and compatibility values for PERM_GROUP and
-		 * PERM_EVERYBODY.
-		 */
-		if (get_shared_repository() < 0)
-			/* force to the mode value */
-			xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
-		else if (get_shared_repository() == PERM_GROUP)
-			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
-		else if (get_shared_repository() == PERM_EVERYBODY)
-			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
-		else
-			BUG("invalid value for shared_repository");
-		git_config_set("core.sharedrepository", buf);
-		git_config_set("receive.denyNonFastforwards", "true");
-	}
-
-	if (!(flags & INIT_DB_QUIET)) {
-		int len = strlen(git_dir);
-
-		if (reinit)
-			printf(get_shared_repository()
-			       ? _("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(get_shared_repository()
-			       ? _("Initialized empty shared Git repository in %s%s\n")
-			       : _("Initialized empty Git repository in %s%s\n"),
-			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
-	}
-
-	free(original_git_dir);
-	return 0;
-}
+#include "path.h"
+#include "refs.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
 
 static int guess_repository_type(const char *git_dir)
 {
@@ -517,6 +58,7 @@
 static const char *const init_db_usage[] = {
 	N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 	   "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+	   "         [--ref-format=<format>]\n"
 	   "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 	   "         [--shared[=<permissions>]] [<directory>]"),
 	NULL
@@ -536,8 +78,11 @@
 	const char *template_dir = NULL;
 	unsigned int flags = 0;
 	const char *object_format = NULL;
+	const char *ref_format = NULL;
 	const char *initial_branch = NULL;
 	int hash_algo = GIT_HASH_UNKNOWN;
+	unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+	int init_shared_repository = -1;
 	const struct option init_db_options[] = {
 		OPT_STRING(0, "template", &template_dir, N_("template-directory"),
 				N_("directory from which templates will be used")),
@@ -554,6 +99,8 @@
 			   N_("override the name of the initial branch")),
 		OPT_STRING(0, "object-format", &object_format, N_("hash"),
 			   N_("specify the hash algorithm to use")),
+		OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+			   N_("specify the reference format to use")),
 		OPT_END()
 	};
 
@@ -617,6 +164,12 @@
 			die(_("unknown hash algorithm '%s'"), object_format);
 	}
 
+	if (ref_format) {
+		ref_storage_format = ref_storage_format_by_name(ref_format);
+		if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+			die(_("unknown ref storage format '%s'"), ref_format);
+	}
+
 	if (init_shared_repository != -1)
 		set_shared_repository(init_shared_repository);
 
@@ -695,5 +248,6 @@
 
 	flags |= INIT_DB_EXIST_OK;
 	return init_db(git_dir, real_git_dir, template_dir, hash_algo,
-		       initial_branch, flags);
+		       ref_storage_format, initial_branch,
+		       init_shared_repository, flags);
 }
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index e58627c..8768bfe 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -5,16 +5,17 @@
  *
  */
 
-#include "cache.h"
 #include "builtin.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "string-list.h"
+#include "tempfile.h"
 #include "trailer.h"
 #include "config.h"
 
 static const char * const git_interpret_trailers_usage[] = {
 	N_("git interpret-trailers [--in-place] [--trim-empty]\n"
-	   "                       [(--trailer <token>[(=|:)<value>])...]\n"
+	   "                       [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
 	   "                       [--parse] [<file>...]"),
 	NULL
 };
@@ -24,21 +25,24 @@
 static enum trailer_if_missing if_missing;
 
 static int option_parse_where(const struct option *opt,
-			      const char *arg, int unset)
+			      const char *arg, int unset UNUSED)
 {
-	return trailer_set_where(&where, arg);
+	/* unset implies NULL arg, which is handled in our helper */
+	return trailer_set_where(opt->value, arg);
 }
 
 static int option_parse_if_exists(const struct option *opt,
-				  const char *arg, int unset)
+				  const char *arg, int unset UNUSED)
 {
-	return trailer_set_if_exists(&if_exists, arg);
+	/* unset implies NULL arg, which is handled in our helper */
+	return trailer_set_if_exists(opt->value, arg);
 }
 
 static int option_parse_if_missing(const struct option *opt,
-				   const char *arg, int unset)
+				   const char *arg, int unset UNUSED)
 {
-	return trailer_set_if_missing(&if_missing, arg);
+	/* unset implies NULL arg, which is handled in our helper */
+	return trailer_set_if_missing(opt->value, arg);
 }
 
 static void new_trailers_clear(struct list_head *trailers)
@@ -88,6 +92,102 @@
 	return 0;
 }
 
+static struct tempfile *trailers_tempfile;
+
+static FILE *create_in_place_tempfile(const char *file)
+{
+	struct stat st;
+	struct strbuf filename_template = STRBUF_INIT;
+	const char *tail;
+	FILE *outfile;
+
+	if (stat(file, &st))
+		die_errno(_("could not stat %s"), file);
+	if (!S_ISREG(st.st_mode))
+		die(_("file %s is not a regular file"), file);
+	if (!(st.st_mode & S_IWUSR))
+		die(_("file %s is not writable by user"), file);
+
+	/* Create temporary file in the same directory as the original */
+	tail = strrchr(file, '/');
+	if (tail)
+		strbuf_add(&filename_template, file, tail - file + 1);
+	strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
+
+	trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
+	strbuf_release(&filename_template);
+	outfile = fdopen_tempfile(trailers_tempfile, "w");
+	if (!outfile)
+		die_errno(_("could not open temporary file"));
+
+	return outfile;
+}
+
+static void read_input_file(struct strbuf *sb, const char *file)
+{
+	if (file) {
+		if (strbuf_read_file(sb, file, 0) < 0)
+			die_errno(_("could not read input file '%s'"), file);
+	} else {
+		if (strbuf_read(sb, fileno(stdin), 0) < 0)
+			die_errno(_("could not read from stdin"));
+	}
+}
+
+static void interpret_trailers(const struct process_trailer_options *opts,
+			       struct list_head *new_trailer_head,
+			       const char *file)
+{
+	LIST_HEAD(head);
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf trailer_block = STRBUF_INIT;
+	struct trailer_info info;
+	FILE *outfile = stdout;
+
+	trailer_config_init();
+
+	read_input_file(&sb, file);
+
+	if (opts->in_place)
+		outfile = create_in_place_tempfile(file);
+
+	parse_trailers(opts, &info, sb.buf, &head);
+
+	/* Print the lines before the trailers */
+	if (!opts->only_trailers)
+		fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+
+	if (!opts->only_trailers && !info.blank_line_before_trailer)
+		fprintf(outfile, "\n");
+
+
+	if (!opts->only_input) {
+		LIST_HEAD(config_head);
+		LIST_HEAD(arg_head);
+		parse_trailers_from_config(&config_head);
+		parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
+		list_splice(&config_head, &arg_head);
+		process_trailers_lists(&head, &arg_head);
+	}
+
+	/* Print trailer block. */
+	format_trailers(opts, &head, &trailer_block);
+	free_trailers(&head);
+	fwrite(trailer_block.buf, 1, trailer_block.len, outfile);
+	strbuf_release(&trailer_block);
+
+	/* Print the lines after the trailers as is */
+	if (!opts->only_trailers)
+		fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
+	trailer_info_release(&info);
+
+	if (opts->in_place)
+		if (rename_tempfile(&trailers_tempfile, file))
+			die_errno(_("could not rename temporary file to %s"), file);
+
+	strbuf_release(&sb);
+}
+
 int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
 {
 	struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
@@ -97,19 +197,19 @@
 		OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
 		OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
 
-		OPT_CALLBACK(0, "where", NULL, N_("action"),
+		OPT_CALLBACK(0, "where", &where, N_("placement"),
 			     N_("where to place the new trailer"), option_parse_where),
-		OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
+		OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"),
 			     N_("action if trailer already exists"), option_parse_if_exists),
-		OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
+		OPT_CALLBACK(0, "if-missing", &if_missing, N_("action"),
 			     N_("action if trailer is missing"), option_parse_if_missing),
 
 		OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
-		OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
-		OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
-		OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"),
+		OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")),
+		OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")),
+		OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"),
 			PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse),
-		OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
+		OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")),
 		OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
 				N_("trailer(s) to add"), option_parse_trailer),
 		OPT_END()
@@ -129,11 +229,11 @@
 	if (argc) {
 		int i;
 		for (i = 0; i < argc; i++)
-			process_trailers(argv[i], &opts, &trailers);
+			interpret_trailers(&opts, &trailers, argv[i]);
 	} else {
 		if (opts.in_place)
 			die(_("no input file given for in-place editing"));
-		process_trailers(NULL, &opts, &trailers);
+		interpret_trailers(&opts, &trailers, NULL);
 	}
 
 	new_trailers_clear(&trailers);
diff --git a/builtin/log.c b/builtin/log.c
index a70fba1..c0a8bb9 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -4,10 +4,17 @@
  * (C) Copyright 2006 Linus Torvalds
  *		 2006 Junio Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
@@ -15,10 +22,10 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "oid-array.h"
 #include "tag.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
-#include "run-command.h"
 #include "shortlog.h"
 #include "remote.h"
 #include "string-list.h"
@@ -28,13 +35,14 @@
 #include "streaming.h"
 #include "version.h"
 #include "mailmap.h"
-#include "gpg-interface.h"
 #include "progress.h"
 #include "commit-slab.h"
 #include "repository.h"
 #include "commit-reach.h"
 #include "range-diff.h"
 #include "tmp-objdir.h"
+#include "tree.h"
+#include "write-or-die.h"
 
 #define MAIL_DEFAULT_WRAP 72
 #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
@@ -56,6 +64,7 @@
 static const char *fmt_patch_subject_prefix = "PATCH";
 static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
 static const char *fmt_pretty;
+static int format_no_prefix;
 
 static const char * const builtin_log_usage[] = {
 	N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
@@ -107,16 +116,19 @@
 static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
 static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
 
-static int clear_decorations_callback(const struct option *opt,
-					    const char *arg, int unset)
+static int clear_decorations_callback(const struct option *opt UNUSED,
+				      const char *arg, int unset)
 {
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(arg);
 	string_list_clear(&decorate_refs_include, 0);
 	string_list_clear(&decorate_refs_exclude, 0);
 	use_default_decoration_filter = 0;
 	return 0;
 }
 
-static int decorate_callback(const struct option *opt, const char *arg, int unset)
+static int decorate_callback(const struct option *opt UNUSED, const char *arg,
+			     int unset)
 {
 	if (unset)
 		decoration_style = 0;
@@ -162,16 +174,15 @@
 	if (default_follow)
 		rev->diffopt.flags.default_follow_renames = 1;
 	rev->verbose_header = 1;
+	init_diffstat_widths(&rev->diffopt);
 	rev->diffopt.flags.recursive = 1;
-	rev->diffopt.stat_width = -1; /* use full terminal width */
-	rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
+	rev->diffopt.flags.allow_textconv = 1;
 	rev->abbrev_commit = default_abbrev_commit;
 	rev->show_root_diff = default_show_root;
 	rev->subject_prefix = fmt_patch_subject_prefix;
 	rev->patch_name_max = fmt_patch_name_max;
 	rev->show_signature = default_show_signature;
 	rev->encode_email_headers = default_encode_email_headers;
-	rev->diffopt.flags.allow_textconv = 1;
 
 	if (default_date_mode)
 		parse_date_format(default_date_mode, &rev->date_mode);
@@ -182,10 +193,10 @@
 	int i;
 	char *value = NULL;
 	struct string_list *include = decoration_filter->include_ref_pattern;
-	const struct string_list *config_exclude =
-			git_config_get_value_multi("log.excludeDecoration");
+	const struct string_list *config_exclude;
 
-	if (config_exclude) {
+	if (!git_config_get_string_multi("log.excludeDecoration",
+					 &config_exclude)) {
 		struct string_list_item *item;
 		for_each_string_list_item(item, config_exclude)
 			string_list_append(decoration_filter->exclude_ref_config_pattern,
@@ -436,7 +447,7 @@
 	setitimer(ITIMER_REAL, &early_output_timer, NULL);
 }
 
-static void early_output(int signal)
+static void early_output(int signal UNUSED)
 {
 	show_early_output = log_show_early;
 }
@@ -538,7 +549,7 @@
 	    rev->diffopt.flags.check_failed) {
 		return 02;
 	}
-	return diff_result_code(&rev->diffopt, 0);
+	return diff_result_code(&rev->diffopt);
 }
 
 static int cmd_log_walk(struct rev_info *rev)
@@ -552,7 +563,8 @@
 	return retval;
 }
 
-static int git_log_config(const char *var, const char *value, void *cb)
+static int git_log_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 
@@ -561,7 +573,7 @@
 	if (!strcmp(var, "format.subjectprefix"))
 		return git_config_string(&fmt_patch_subject_prefix, var, value);
 	if (!strcmp(var, "format.filenamemaxlength")) {
-		fmt_patch_name_max = git_config_int(var, value);
+		fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "format.encodeemailheaders")) {
@@ -580,8 +592,11 @@
 			decoration_style = 0; /* maybe warn? */
 		return 0;
 	}
-	if (!strcmp(var, "log.diffmerges"))
+	if (!strcmp(var, "log.diffmerges")) {
+		if (!value)
+			return config_error_nonbool(var);
 		return diff_merges_config(value);
+	}
 	if (!strcmp(var, "log.showroot")) {
 		default_show_root = git_config_bool(var, value);
 		return 0;
@@ -601,9 +616,7 @@
 		return 0;
 	}
 
-	if (git_gpg_config(var, value, cb) < 0)
-		return -1;
-	return git_diff_ui_config(var, value, cb);
+	return git_diff_ui_config(var, value, ctx, cb);
 }
 
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
@@ -675,7 +688,7 @@
 {
 	unsigned long size;
 	enum object_type type;
-	char *buf = read_object_file(oid, &type, &size);
+	char *buf = repo_read_object_file(the_repository, oid, &type, &size);
 	int offset = 0;
 
 	if (!buf)
@@ -708,8 +721,7 @@
 	return 0;
 }
 
-static void show_setup_revisions_tweak(struct rev_info *rev,
-				       struct setup_revision_opt *opt)
+static void show_setup_revisions_tweak(struct rev_info *rev)
 {
 	if (rev->first_parent_only)
 		diff_merges_default_to_first_parent(rev);
@@ -852,11 +864,10 @@
 	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
-static void log_setup_revisions_tweak(struct rev_info *rev,
-				      struct setup_revision_opt *opt)
+static void log_setup_revisions_tweak(struct rev_info *rev)
 {
 	if (rev->diffopt.flags.default_follow_renames &&
-	    rev->prune_data.nr == 1)
+	    diff_check_follow_pathspec(&rev->prune_data, 0))
 		rev->diffopt.flags.follow_renames = 1;
 
 	if (rev->first_parent_only)
@@ -969,7 +980,8 @@
 		die(_("%s: invalid cover from description mode"), arg);
 }
 
-static int git_format_config(const char *var, const char *value, void *cb)
+static int git_format_config(const char *var, const char *value,
+			     const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "format.headers")) {
 		if (!value)
@@ -1084,8 +1096,21 @@
 		stdout_mboxrd = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "format.noprefix")) {
+		format_no_prefix = 1;
+		return 0;
+	}
 
-	return git_log_config(var, value, cb);
+	/*
+	 * ignore some porcelain config which would otherwise be parsed by
+	 * git_diff_ui_config(), via git_log_config(); we can't just avoid
+	 * diff_ui_config completely, because we do care about some ui options
+	 * like color.
+	 */
+	if (!strcmp(var, "diff.noprefix"))
+		return 0;
+
+	return git_log_config(var, value, ctx, cb);
 }
 
 static const char *output_directory = NULL;
@@ -1204,7 +1229,8 @@
 		return NULL;
 	ref = rev->cmdline.rev[positive].name;
 	tip_oid = &rev->cmdline.rev[positive].item->oid;
-	if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) &&
+	if (repo_dwim_ref(the_repository, ref, strlen(ref), &branch_oid,
+			  &full_ref, 0) &&
 	    skip_prefix(full_ref, "refs/heads/", &v) &&
 	    oideq(tip_oid, &branch_oid))
 		branch = xstrdup(v);
@@ -1230,7 +1256,15 @@
 	fprintf(rev->diffopt.file, "\n");
 }
 
+static void read_desc_file(struct strbuf *buf, const char *desc_file)
+{
+	if (strbuf_read_file(buf, desc_file, 0) < 0)
+		die_errno(_("unable to read branch description file '%s'"),
+			  desc_file);
+}
+
 static void prepare_cover_text(struct pretty_print_context *pp,
+			       const char *description_file,
 			       const char *branch_name,
 			       struct strbuf *sb,
 			       const char *encoding,
@@ -1244,7 +1278,9 @@
 	if (cover_from_description_mode == COVER_FROM_NONE)
 		goto do_pp;
 
-	if (branch_name && *branch_name)
+	if (description_file && *description_file)
+		read_desc_file(&description_sb, description_file);
+	else if (branch_name && *branch_name)
 		read_branch_desc(&description_sb, branch_name);
 	if (!description_sb.len)
 		goto do_pp;
@@ -1261,7 +1297,7 @@
 		subject = subject_sb.buf;
 
 do_pp:
-	pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+	pp_email_subject(pp, &subject, sb, encoding, need_8bit_cte);
 	pp_remainder(pp, &body, sb, 0);
 
 	strbuf_release(&description_sb);
@@ -1290,6 +1326,7 @@
 static void make_cover_letter(struct rev_info *rev, int use_separate_file,
 			      struct commit *origin,
 			      int nr, struct commit **list,
+			      const char *description_file,
 			      const char *branch_name,
 			      int quiet)
 {
@@ -1314,10 +1351,11 @@
 	log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
 
 	for (i = 0; !need_8bit_cte && i < nr; i++) {
-		const char *buf = get_commit_buffer(list[i], NULL);
+		const char *buf = repo_get_commit_buffer(the_repository,
+							 list[i], NULL);
 		if (has_non_ascii(buf))
 			need_8bit_cte = 1;
-		unuse_commit_buffer(list[i], buf);
+		repo_unuse_commit_buffer(the_repository, list[i], buf);
 	}
 
 	if (!branch_name)
@@ -1326,11 +1364,13 @@
 	pp.fmt = CMIT_FMT_EMAIL;
 	pp.date_mode.type = DATE_RFC2822;
 	pp.rev = rev;
-	pp.print_email_subject = 1;
+	pp.encode_email_headers = rev->encode_email_headers;
 	pp_user_info(&pp, NULL, &sb, committer, encoding);
-	prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
+	prepare_cover_text(&pp, description_file, branch_name, &sb,
+			   encoding, need_8bit_cte);
 	fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
+	free(pp.after_subject);
 	strbuf_release(&sb);
 
 	shortlog_init(&log);
@@ -1370,7 +1410,7 @@
 			.other_arg = &other_arg
 		};
 
-		diff_setup(&opts);
+		repo_diff_setup(the_repository, &opts);
 		opts.file = rev->diffopt.file;
 		opts.use_color = rev->diffopt.use_color;
 		diff_setup_done(&opts);
@@ -1381,7 +1421,7 @@
 	}
 }
 
-static const char *clean_message_id(const char *msg_id)
+static char *clean_message_id(const char *msg_id)
 {
 	char ch;
 	const char *a, *z, *m;
@@ -1399,7 +1439,7 @@
 	if (!z)
 		die(_("insane in-reply-to: %s"), msg_id);
 	if (++z == m)
-		return a;
+		return xstrdup(a);
 	return xmemdupz(a, z - a);
 }
 
@@ -1444,17 +1484,14 @@
 static int subject_prefix_callback(const struct option *opt, const char *arg,
 			    int unset)
 {
-	BUG_ON_OPT_NEG(unset);
-	subject_prefix = 1;
-	((struct rev_info *)opt->value)->subject_prefix = arg;
-	return 0;
-}
+	struct strbuf *sprefix;
 
-static int rfc_callback(const struct option *opt, const char *arg, int unset)
-{
 	BUG_ON_OPT_NEG(unset);
-	BUG_ON_OPT_ARG(arg);
-	return subject_prefix_callback(opt, "RFC PATCH", unset);
+	sprefix = opt->value;
+	subject_prefix = 1;
+	strbuf_reset(sprefix);
+	strbuf_addstr(sprefix, arg);
+	return 0;
 }
 
 static int numbered_cmdline_opt = 0;
@@ -1531,7 +1568,8 @@
 	return 0;
 }
 
-static int header_callback(const struct option *opt, const char *arg, int unset)
+static int header_callback(const struct option *opt UNUSED, const char *arg,
+			   int unset)
 {
 	if (unset) {
 		string_list_clear(&extra_hdr, 0);
@@ -1543,24 +1581,6 @@
 	return 0;
 }
 
-static int to_callback(const struct option *opt, const char *arg, int unset)
-{
-	if (unset)
-		string_list_clear(&extra_to, 0);
-	else
-		string_list_append(&extra_to, arg);
-	return 0;
-}
-
-static int cc_callback(const struct option *opt, const char *arg, int unset)
-{
-	if (unset)
-		string_list_clear(&extra_cc, 0);
-	else
-		string_list_append(&extra_cc, arg);
-	return 0;
-}
-
 static int from_callback(const struct option *opt, const char *arg, int unset)
 {
 	char **from = opt->value;
@@ -1605,7 +1625,7 @@
 {
 	struct commit *base = NULL;
 	struct commit **rev;
-	int i = 0, rev_nr = 0, auto_select, die_on_failure;
+	int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
 
 	switch (auto_base) {
 	case AUTO_BASE_NEVER:
@@ -1638,20 +1658,23 @@
 		struct branch *curr_branch = branch_get(NULL);
 		const char *upstream = branch_get_upstream(curr_branch, NULL);
 		if (upstream) {
-			struct commit_list *base_list;
+			struct commit_list *base_list = NULL;
 			struct commit *commit;
 			struct object_id oid;
 
-			if (get_oid(upstream, &oid)) {
+			if (repo_get_oid(the_repository, upstream, &oid)) {
 				if (die_on_failure)
 					die(_("failed to resolve '%s' as a valid ref"), upstream);
 				else
 					return NULL;
 			}
 			commit = lookup_commit_or_die(&oid, "upstream base");
-			base_list = get_merge_bases_many(commit, total, list);
-			/* There should be one and only one merge base. */
-			if (!base_list || base_list->next) {
+			if (repo_get_merge_bases_many(the_repository,
+						      commit, total,
+						      list,
+						      &base_list) < 0 ||
+			    /* There should be one and only one merge base. */
+			    !base_list || base_list->next) {
 				if (die_on_failure) {
 					die(_("could not find exact merge base"));
 				} else {
@@ -1682,9 +1705,11 @@
 	 */
 	while (rev_nr > 1) {
 		for (i = 0; i < rev_nr / 2; i++) {
-			struct commit_list *merge_base;
-			merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
-			if (!merge_base || merge_base->next) {
+			struct commit_list *merge_base = NULL;
+			if (repo_get_merge_bases(the_repository,
+						 rev[2 * i],
+						 rev[2 * i + 1], &merge_base) < 0 ||
+			    !merge_base || merge_base->next) {
 				if (die_on_failure) {
 					die(_("failed to find exact merge base"));
 				} else {
@@ -1701,7 +1726,10 @@
 		rev_nr = DIV_ROUND_UP(rev_nr, 2);
 	}
 
-	if (!in_merge_bases(base, rev[0])) {
+	ret = repo_in_merge_bases(the_repository, base, rev[0]);
+	if (ret < 0)
+		exit(128);
+	if (!ret) {
 		if (die_on_failure) {
 			die(_("base commit should be the ancestor of revision list"));
 		} else {
@@ -1865,6 +1893,7 @@
 	int quiet = 0;
 	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
+	char *description_file = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
 	struct base_tree_info bases;
@@ -1879,6 +1908,7 @@
 	struct strbuf rdiff_title = STRBUF_INIT;
 	struct strbuf sprefix = STRBUF_INIT;
 	int creation_factor = -1;
+	int rfc = 0;
 
 	const struct option builtin_format_patch_options[] = {
 		OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
@@ -1902,13 +1932,13 @@
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
-		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
-			    N_("use [RFC PATCH] instead of [PATCH]"),
-			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback),
+		OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")),
 		OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
 			    N_("cover-from-description-mode"),
 			    N_("generate parts of a cover letter based on a branch's description")),
-		OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"),
+		OPT_FILENAME(0, "description-file", &description_file,
+			     N_("use branch description from file")),
+		OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"),
 			    N_("use [<prefix>] instead of [PATCH]"),
 			    PARSE_OPT_NONEG, subject_prefix_callback),
 		OPT_CALLBACK_F('o', "output-directory", &output_directory,
@@ -1929,8 +1959,8 @@
 		OPT_GROUP(N_("Messaging")),
 		OPT_CALLBACK(0, "add-header", NULL, N_("header"),
 			    N_("add email header"), header_callback),
-		OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback),
-		OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback),
+		OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")),
+		OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")),
 		OPT_CALLBACK_F(0, "from", &from, N_("ident"),
 			    N_("set From address to <ident> (or committer ident if absent)"),
 			    PARSE_OPT_OPTARG, from_callback),
@@ -1988,11 +2018,14 @@
 	rev.max_parents = 1;
 	rev.diffopt.flags.recursive = 1;
 	rev.diffopt.no_free = 1;
-	rev.subject_prefix = fmt_patch_subject_prefix;
 	memset(&s_r_opt, 0, sizeof(s_r_opt));
 	s_r_opt.def = "HEAD";
 	s_r_opt.revarg_opt = REVARG_COMMITTISH;
 
+	strbuf_addstr(&sprefix, fmt_patch_subject_prefix);
+	if (format_no_prefix)
+		diff_set_noprefix(&rev.diffopt);
+
 	if (default_attach) {
 		rev.mime_boundary = default_attach;
 		rev.no_inline = 1;
@@ -2017,13 +2050,16 @@
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
+	if (rfc)
+		strbuf_insertstr(&sprefix, 0, "RFC ");
+
 	if (reroll_count) {
-		strbuf_addf(&sprefix, "%s v%s",
-			    rev.subject_prefix, reroll_count);
+		strbuf_addf(&sprefix, " v%s", reroll_count);
 		rev.reroll_count = reroll_count;
-		rev.subject_prefix = sprefix.buf;
 	}
 
+	rev.subject_prefix = sprefix.buf;
+
 	for (i = 0; i < extra_hdr.nr; i++) {
 		strbuf_addstr(&buf, extra_hdr.items[i].string);
 		strbuf_addch(&buf, '\n');
@@ -2097,6 +2133,7 @@
 
 	/* Always generate a patch */
 	rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+	rev.always_show_header = 1;
 
 	rev.zero_commit = zero_commit;
 	rev.patch_name_max = fmt_patch_name_max;
@@ -2277,11 +2314,11 @@
 
 	if (in_reply_to || thread || cover_letter) {
 		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
-		string_list_init_nodup(rev.ref_message_ids);
+		string_list_init_dup(rev.ref_message_ids);
 	}
 	if (in_reply_to) {
-		const char *msgid = clean_message_id(in_reply_to);
-		string_list_append(rev.ref_message_ids, msgid);
+		char *msgid = clean_message_id(in_reply_to);
+		string_list_append_nodup(rev.ref_message_ids, msgid);
 	}
 	rev.numbered_files = just_numbers;
 	rev.patch_suffix = fmt_patch_suffix;
@@ -2289,7 +2326,7 @@
 		if (thread)
 			gen_message_id(&rev, "cover");
 		make_cover_letter(&rev, !!output_directory,
-				  origin, nr, list, branch_name, quiet);
+				  origin, nr, list, description_file, branch_name, quiet);
 		print_bases(&bases, rev.diffopt.file);
 		print_signature(rev.diffopt.file);
 		total++;
@@ -2337,8 +2374,8 @@
 				    && (!cover_letter || rev.nr > 1))
 					free(rev.message_id);
 				else
-					string_list_append(rev.ref_message_ids,
-							   rev.message_id);
+					string_list_append_nodup(rev.ref_message_ids,
+								 rev.message_id);
 			}
 			gen_message_id(&rev, oid_to_hex(&commit->object.oid));
 		}
@@ -2387,6 +2424,7 @@
 	strbuf_release(&rdiff_title);
 	strbuf_release(&sprefix);
 	free(to_free);
+	free(rev.message_id);
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
@@ -2396,7 +2434,7 @@
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
 {
 	struct object_id oid;
-	if (get_oid(arg, &oid) == 0) {
+	if (repo_get_oid(the_repository, arg, &oid) == 0) {
 		struct commit *commit = lookup_commit_reference(the_repository,
 								&oid);
 		if (commit) {
@@ -2418,12 +2456,12 @@
 {
 	if (!verbose) {
 		fprintf(file, "%c %s\n", sign,
-		       find_unique_abbrev(&commit->object.oid, abbrev));
+		       repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev));
 	} else {
 		struct strbuf buf = STRBUF_INIT;
 		pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
 		fprintf(file, "%c %s %s\n", sign,
-		       find_unique_abbrev(&commit->object.oid, abbrev),
+		       repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev),
 		       buf.buf);
 		strbuf_release(&buf);
 	}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a03b559..6eeb5cb 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -5,22 +5,27 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "builtin.h"
 #include "repository.h"
 #include "config.h"
+#include "convert.h"
 #include "quote.h"
 #include "dir.h"
-#include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "strbuf.h"
-#include "tree.h"
-#include "cache-tree.h"
 #include "parse-options.h"
 #include "resolve-undo.h"
 #include "string-list.h"
+#include "path.h"
 #include "pathspec.h"
-#include "run-command.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
+#include "object-store.h"
+#include "hex.h"
+
 
 static int abbrev;
 static int show_deleted;
@@ -89,12 +94,15 @@
 
 static void write_name_to_buf(struct strbuf *sb, const char *name)
 {
-	const char *rel = relative_path(name, prefix_len ? prefix : NULL, sb);
+	struct strbuf buf = STRBUF_INIT;
+	const char *rel = relative_path(name, prefix_len ? prefix : NULL, &buf);
 
 	if (line_terminator)
 		quote_c_style(rel, sb, NULL, 0);
 	else
 		strbuf_addstr(sb, rel);
+
+	strbuf_release(&buf);
 }
 
 static const char *get_tag(const struct cache_entry *ce, const char *tag)
@@ -234,68 +242,67 @@
 	repo_clear(&subrepo);
 }
 
-struct show_index_data {
-	const char *pathname;
-	struct index_state *istate;
-	const struct cache_entry *ce;
-};
-
-static size_t expand_show_index(struct strbuf *sb, const char *start,
-				void *context)
+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
+			      const enum object_type type, unsigned int padded)
 {
-	struct show_index_data *data = context;
-	const char *end;
-	const char *p;
-	size_t len = strbuf_expand_literal_cb(sb, start, NULL);
-	struct stat st;
-
-	if (len)
-		return len;
-	if (*start != '(')
-		die(_("bad ls-files format: element '%s' "
-		      "does not start with '('"), start);
-
-	end = strchr(start + 1, ')');
-	if (!end)
-		die(_("bad ls-files format: element '%s' "
-		      "does not end in ')'"), start);
-
-	len = end - start + 1;
-	if (skip_prefix(start, "(objectmode)", &p))
-		strbuf_addf(sb, "%06o", data->ce->ce_mode);
-	else if (skip_prefix(start, "(objectname)", &p))
-		strbuf_add_unique_abbrev(sb, &data->ce->oid, abbrev);
-	else if (skip_prefix(start, "(stage)", &p))
-		strbuf_addf(sb, "%d", ce_stage(data->ce));
-	else if (skip_prefix(start, "(eolinfo:index)", &p))
-		strbuf_addstr(sb, S_ISREG(data->ce->ce_mode) ?
-			      get_cached_convert_stats_ascii(data->istate,
-			      data->ce->name) : "");
-	else if (skip_prefix(start, "(eolinfo:worktree)", &p))
-		strbuf_addstr(sb, !lstat(data->pathname, &st) &&
-			      S_ISREG(st.st_mode) ?
-			      get_wt_convert_stats_ascii(data->pathname) : "");
-	else if (skip_prefix(start, "(eolattr)", &p))
-		strbuf_addstr(sb, get_convert_attr_ascii(data->istate,
-			      data->pathname));
-	else if (skip_prefix(start, "(path)", &p))
-		write_name_to_buf(sb, data->pathname);
-	else
-		die(_("bad ls-files format: %%%.*s"), (int)len, start);
-
-	return len;
+	if (type == OBJ_BLOB) {
+		unsigned long size;
+		if (oid_object_info(the_repository, oid, &size) < 0)
+			die(_("could not get object info about '%s'"),
+			    oid_to_hex(oid));
+		if (padded)
+			strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
+		else
+			strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
+	} else if (padded) {
+		strbuf_addf(line, "%7s", "-");
+	} else {
+		strbuf_addstr(line, "-");
+	}
 }
 
 static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
 			const char *format, const char *fullname) {
-	struct show_index_data data = {
-		.pathname = fullname,
-		.istate = repo->index,
-		.ce = ce,
-	};
 	struct strbuf sb = STRBUF_INIT;
 
-	strbuf_expand(&sb, format, expand_show_index, &data);
+	while (strbuf_expand_step(&sb, &format)) {
+		size_t len;
+		struct stat st;
+
+		if (skip_prefix(format, "%", &format))
+			strbuf_addch(&sb, '%');
+		else if ((len = strbuf_expand_literal(&sb, format)))
+			format += len;
+		else if (skip_prefix(format, "(objectmode)", &format))
+			strbuf_addf(&sb, "%06o", ce->ce_mode);
+		else if (skip_prefix(format, "(objectname)", &format))
+			strbuf_add_unique_abbrev(&sb, &ce->oid, abbrev);
+		else if (skip_prefix(format, "(objecttype)", &format))
+			strbuf_addstr(&sb, type_name(object_type(ce->ce_mode)));
+		else if (skip_prefix(format, "(objectsize:padded)", &format))
+			expand_objectsize(&sb, &ce->oid,
+					  object_type(ce->ce_mode), 1);
+		else if (skip_prefix(format, "(objectsize)", &format))
+			expand_objectsize(&sb, &ce->oid,
+					  object_type(ce->ce_mode), 0);
+		else if (skip_prefix(format, "(stage)", &format))
+			strbuf_addf(&sb, "%d", ce_stage(ce));
+		else if (skip_prefix(format, "(eolinfo:index)", &format))
+			strbuf_addstr(&sb, S_ISREG(ce->ce_mode) ?
+				      get_cached_convert_stats_ascii(repo->index,
+				      ce->name) : "");
+		else if (skip_prefix(format, "(eolinfo:worktree)", &format))
+			strbuf_addstr(&sb, !lstat(fullname, &st) &&
+				      S_ISREG(st.st_mode) ?
+				      get_wt_convert_stats_ascii(fullname) : "");
+		else if (skip_prefix(format, "(eolattr)", &format))
+			strbuf_addstr(&sb, get_convert_attr_ascii(repo->index,
+								 fullname));
+		else if (skip_prefix(format, "(path)", &format))
+			write_name_to_buf(&sb, fullname);
+		else
+			strbuf_expand_bad_format(format, "ls-files");
+	}
 	strbuf_addch(&sb, line_terminator);
 	fwrite(sb.buf, sb.len, 1, stdout);
 	strbuf_release(&sb);
@@ -360,7 +367,7 @@
 			if (!ui->mode[i])
 				continue;
 			printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
-			       find_unique_abbrev(&ui->oid[i], abbrev),
+			       repo_find_unique_abbrev(the_repository, &ui->oid[i], abbrev),
 			       i + 1);
 			write_name(path);
 		}
@@ -509,143 +516,6 @@
 	return common_prefix_len;
 }
 
-static int read_one_entry_opt(struct index_state *istate,
-			      const struct object_id *oid,
-			      struct strbuf *base,
-			      const char *pathname,
-			      unsigned mode, int opt)
-{
-	int len;
-	struct cache_entry *ce;
-
-	if (S_ISDIR(mode))
-		return READ_TREE_RECURSIVE;
-
-	len = strlen(pathname);
-	ce = make_empty_cache_entry(istate, base->len + len);
-
-	ce->ce_mode = create_ce_mode(mode);
-	ce->ce_flags = create_ce_flags(1);
-	ce->ce_namelen = base->len + len;
-	memcpy(ce->name, base->buf, base->len);
-	memcpy(ce->name + base->len, pathname, len+1);
-	oidcpy(&ce->oid, oid);
-	return add_index_entry(istate, ce, opt);
-}
-
-static int read_one_entry(const struct object_id *oid, struct strbuf *base,
-			  const char *pathname, unsigned mode,
-			  void *context)
-{
-	struct index_state *istate = context;
-	return read_one_entry_opt(istate, oid, base, pathname,
-				  mode,
-				  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-}
-
-/*
- * This is used when the caller knows there is no existing entries at
- * the stage that will conflict with the entry being added.
- */
-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
-				const char *pathname, unsigned mode,
-				void *context)
-{
-	struct index_state *istate = context;
-	return read_one_entry_opt(istate, oid, base, pathname,
-				  mode, ADD_CACHE_JUST_APPEND);
-}
-
-/*
- * Read the tree specified with --with-tree option
- * (typically, HEAD) into stage #1 and then
- * squash them down to stage #0.  This is used for
- * --error-unmatch to list and check the path patterns
- * that were given from the command line.  We are not
- * going to write this index out.
- */
-void overlay_tree_on_index(struct index_state *istate,
-			   const char *tree_name, const char *prefix)
-{
-	struct tree *tree;
-	struct object_id oid;
-	struct pathspec pathspec;
-	struct cache_entry *last_stage0 = NULL;
-	int i;
-	read_tree_fn_t fn = NULL;
-	int err;
-
-	if (get_oid(tree_name, &oid))
-		die("tree-ish %s not found.", tree_name);
-	tree = parse_tree_indirect(&oid);
-	if (!tree)
-		die("bad tree-ish %s", tree_name);
-
-	/* Hoist the unmerged entries up to stage #3 to make room */
-	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(istate);
-	for (i = 0; i < istate->cache_nr; i++) {
-		struct cache_entry *ce = istate->cache[i];
-		if (!ce_stage(ce))
-			continue;
-		ce->ce_flags |= CE_STAGEMASK;
-	}
-
-	if (prefix) {
-		static const char *(matchbuf[1]);
-		matchbuf[0] = NULL;
-		parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
-			       PATHSPEC_PREFER_CWD, prefix, matchbuf);
-	} else
-		memset(&pathspec, 0, sizeof(pathspec));
-
-	/*
-	 * See if we have cache entry at the stage.  If so,
-	 * do it the original slow way, otherwise, append and then
-	 * sort at the end.
-	 */
-	for (i = 0; !fn && i < istate->cache_nr; i++) {
-		const struct cache_entry *ce = istate->cache[i];
-		if (ce_stage(ce) == 1)
-			fn = read_one_entry;
-	}
-
-	if (!fn)
-		fn = read_one_entry_quick;
-	err = read_tree(the_repository, tree, &pathspec, fn, istate);
-	clear_pathspec(&pathspec);
-	if (err)
-		die("unable to read tree entries %s", tree_name);
-
-	/*
-	 * Sort the cache entry -- we need to nuke the cache tree, though.
-	 */
-	if (fn == read_one_entry_quick) {
-		cache_tree_free(&istate->cache_tree);
-		QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
-	}
-
-	for (i = 0; i < istate->cache_nr; i++) {
-		struct cache_entry *ce = istate->cache[i];
-		switch (ce_stage(ce)) {
-		case 0:
-			last_stage0 = ce;
-			/* fallthru */
-		default:
-			continue;
-		case 1:
-			/*
-			 * If there is stage #0 entry for this, we do not
-			 * need to show it.  We use CE_UPDATE bit to mark
-			 * such an entry.
-			 */
-			if (last_stage0 &&
-			    !strcmp(last_stage0->name, ce->name))
-				ce->ce_flags |= CE_UPDATE;
-		}
-	}
-}
-
 static const char * const ls_files_usage[] = {
 	N_("git ls-files [<options>] [<file>...]"),
 	NULL
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 6516177..e8d65eb 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -1,9 +1,12 @@
 #include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
 #include "transport.h"
+#include "pkt-line.h"
 #include "ref-filter.h"
 #include "remote.h"
-#include "refs.h"
+#include "parse-options.h"
+#include "wildmatch.h"
 
 static const char * const ls_remote_usage[] = {
 	N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
@@ -54,6 +57,7 @@
 	struct transport *transport;
 	const struct ref *ref;
 	struct ref_array ref_array;
+	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 
 	struct option options[] = {
@@ -137,13 +141,8 @@
 		item->symref = xstrdup_or_null(ref->symref);
 	}
 
-	if (sorting_options.nr) {
-		struct ref_sorting *sorting;
-
-		sorting = ref_sorting_options(&sorting_options);
-		ref_array_sort(sorting, &ref_array);
-		ref_sorting_release(sorting);
-	}
+	sorting = ref_sorting_options(&sorting_options);
+	ref_array_sort(sorting, &ref_array);
 
 	for (i = 0; i < ref_array.nr; i++) {
 		const struct ref_array_item *ref = ref_array.items[i];
@@ -153,6 +152,7 @@
 		status = 0; /* we found something */
 	}
 
+	ref_sorting_release(sorting);
 	ref_array_clear(&ref_array);
 	if (transport_disconnect(transport))
 		status = 1;
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 8cc8c99..7bf84b2 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -3,14 +3,15 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
-#include "config.h"
-#include "object-store.h"
-#include "blob.h"
-#include "tree.h"
-#include "commit.h"
-#include "quote.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "tree.h"
+#include "path.h"
+#include "quote.h"
 #include "parse-options.h"
 #include "pathspec.h"
 
@@ -47,68 +48,10 @@
 		LS_SHOW_TREES = 1 << 2,
 	} ls_options;
 	struct pathspec pathspec;
-	int chomp_prefix;
-	const char *ls_tree_prefix;
+	const char *prefix;
 	const char *format;
 };
 
-struct show_tree_data {
-	struct ls_tree_options *options;
-	unsigned mode;
-	enum object_type type;
-	const struct object_id *oid;
-	const char *pathname;
-	struct strbuf *base;
-};
-
-static size_t expand_show_tree(struct strbuf *sb, const char *start,
-			       void *context)
-{
-	struct show_tree_data *data = context;
-	struct ls_tree_options *options = data->options;
-	const char *end;
-	const char *p;
-	unsigned int errlen;
-	size_t len = strbuf_expand_literal_cb(sb, start, NULL);
-
-	if (len)
-		return len;
-	if (*start != '(')
-		die(_("bad ls-tree format: element '%s' does not start with '('"), start);
-
-	end = strchr(start + 1, ')');
-	if (!end)
-		die(_("bad ls-tree format: element '%s' does not end in ')'"), start);
-
-	len = end - start + 1;
-	if (skip_prefix(start, "(objectmode)", &p)) {
-		strbuf_addf(sb, "%06o", data->mode);
-	} else if (skip_prefix(start, "(objecttype)", &p)) {
-		strbuf_addstr(sb, type_name(data->type));
-	} else if (skip_prefix(start, "(objectsize:padded)", &p)) {
-		expand_objectsize(sb, data->oid, data->type, 1);
-	} else if (skip_prefix(start, "(objectsize)", &p)) {
-		expand_objectsize(sb, data->oid, data->type, 0);
-	} else if (skip_prefix(start, "(objectname)", &p)) {
-		strbuf_add_unique_abbrev(sb, data->oid, options->abbrev);
-	} else if (skip_prefix(start, "(path)", &p)) {
-		const char *name = data->base->buf;
-		const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
-		struct strbuf sbuf = STRBUF_INIT;
-		size_t baselen = data->base->len;
-
-		strbuf_addstr(data->base, data->pathname);
-		name = relative_path(data->base->buf, prefix, &sbuf);
-		quote_c_style(name, sb, NULL, 0);
-		strbuf_setlen(data->base, baselen);
-		strbuf_release(&sbuf);
-	} else {
-		errlen = (unsigned long)len;
-		die(_("bad ls-tree format: %%%.*s"), errlen, start);
-	}
-	return len;
-}
-
 static int show_recursive(struct ls_tree_options *options, const char *base,
 			  size_t baselen, const char *pathname)
 {
@@ -147,14 +90,7 @@
 	int recurse = 0;
 	struct strbuf sb = STRBUF_INIT;
 	enum object_type type = object_type(mode);
-	struct show_tree_data cb_data = {
-		.options = options,
-		.mode = mode,
-		.type = type,
-		.oid = oid,
-		.pathname = pathname,
-		.base = base,
-	};
+	const char *format = options->format;
 
 	if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
 		recurse = READ_TREE_RECURSIVE;
@@ -163,7 +99,37 @@
 	if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
 		return 0;
 
-	strbuf_expand(&sb, options->format, expand_show_tree, &cb_data);
+	while (strbuf_expand_step(&sb, &format)) {
+		size_t len;
+
+		if (skip_prefix(format, "%", &format))
+			strbuf_addch(&sb, '%');
+		else if ((len = strbuf_expand_literal(&sb, format)))
+			format += len;
+		else if (skip_prefix(format, "(objectmode)", &format))
+			strbuf_addf(&sb, "%06o", mode);
+		else if (skip_prefix(format, "(objecttype)", &format))
+			strbuf_addstr(&sb, type_name(type));
+		else if (skip_prefix(format, "(objectsize:padded)", &format))
+			expand_objectsize(&sb, oid, type, 1);
+		else if (skip_prefix(format, "(objectsize)", &format))
+			expand_objectsize(&sb, oid, type, 0);
+		else if (skip_prefix(format, "(objectname)", &format))
+			strbuf_add_unique_abbrev(&sb, oid, options->abbrev);
+		else if (skip_prefix(format, "(path)", &format)) {
+			const char *name;
+			const char *prefix = options->prefix;
+			struct strbuf sbuf = STRBUF_INIT;
+			size_t baselen = base->len;
+
+			strbuf_addstr(base, pathname);
+			name = relative_path(base->buf, prefix, &sbuf);
+			quote_c_style(name, &sb, NULL, 0);
+			strbuf_setlen(base, baselen);
+			strbuf_release(&sbuf);
+		} else
+			strbuf_expand_bad_format(format, "ls-tree");
+	}
 	strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
 	fwrite(sb.buf, sb.len, 1, stdout);
 	strbuf_release(&sb);
@@ -195,7 +161,7 @@
 					  const char *pathname,
 					  const size_t baselen)
 {
-	const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
+	const char *prefix = options->prefix;
 
 	strbuf_addstr(base, pathname);
 
@@ -228,7 +194,7 @@
 		return early;
 
 	printf("%06o %s %s\t", mode, type_name(object_type(mode)),
-	       find_unique_abbrev(oid, options->abbrev));
+	       repo_find_unique_abbrev(the_repository, oid, options->abbrev));
 	show_tree_common_default_long(options, base, pathname, base->len);
 	return recurse;
 }
@@ -259,12 +225,14 @@
 	}
 
 	printf("%06o %s %s %7s\t", mode, type_name(type),
-	       find_unique_abbrev(oid, options->abbrev), size_text);
+	       repo_find_unique_abbrev(the_repository, oid, options->abbrev),
+	       size_text);
 	show_tree_common_default_long(options, base, pathname, base->len);
 	return recurse;
 }
 
-static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
+static int show_tree_name_only(const struct object_id *oid UNUSED,
+			       struct strbuf *base,
 			       const char *pathname, unsigned mode,
 			       void *context)
 {
@@ -279,7 +247,7 @@
 	if (early >= 0)
 		return early;
 
-	prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
+	prefix = options->prefix;
 	strbuf_addstr(base, pathname);
 	if (options->null_termination) {
 		struct strbuf sb = STRBUF_INIT;
@@ -310,7 +278,7 @@
 	if (early >= 0)
 		return early;
 
-	str = find_unique_abbrev(oid, options->abbrev);
+	str = repo_find_unique_abbrev(the_repository, oid, options->abbrev);
 	if (options->null_termination) {
 		fputs(str, stdout);
 		fputc('\0', stdout);
@@ -366,6 +334,7 @@
 	struct object_id oid;
 	struct tree *tree;
 	int i, full_tree = 0;
+	int full_name = !prefix || !*prefix;
 	read_tree_fn_t fn = NULL;
 	enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
 	int null_termination = 0;
@@ -387,8 +356,7 @@
 			    MODE_NAME_STATUS),
 		OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
 			    MODE_OBJECT_ONLY),
-		OPT_SET_INT(0, "full-name", &options.chomp_prefix,
-			    N_("use full path names"), 0),
+		OPT_BOOL(0, "full-name", &full_name, N_("use full path names")),
 		OPT_BOOL(0, "full-tree", &full_tree,
 			 N_("list entire tree; not just current directory "
 			    "(implies --full-name)")),
@@ -399,21 +367,19 @@
 		OPT_END()
 	};
 	struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+	struct object_context obj_context;
 	int ret;
 
 	git_config(git_default_config, NULL);
-	options.ls_tree_prefix = prefix;
-	if (prefix)
-		options.chomp_prefix = strlen(prefix);
 
 	argc = parse_options(argc, argv, prefix, ls_tree_options,
 			     ls_tree_usage, 0);
 	options.null_termination = null_termination;
 
-	if (full_tree) {
-		options.ls_tree_prefix = prefix = NULL;
-		options.chomp_prefix = 0;
-	}
+	if (full_tree)
+		prefix = NULL;
+	options.prefix = full_name ? NULL : prefix;
+
 	/*
 	 * We wanted to detect conflicts between --name-only and
 	 * --name-status, but once we're done with that subsequent
@@ -433,7 +399,9 @@
 			ls_tree_usage, ls_tree_options);
 	if (argc < 1)
 		usage_with_options(ls_tree_usage, ls_tree_options);
-	if (get_oid(argv[0], &oid))
+	if (get_oid_with_context(the_repository, argv[0],
+				 GET_OID_HASH_ANY, &oid,
+				 &obj_context))
 		die("Not a valid object name %s", argv[0]);
 
 	/*
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 01d16ef..53a2264 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -2,9 +2,10 @@
  * Another stupid program, this one parsing the headers of an
  * email to figure out authorship and subject
  */
-#include "cache.h"
 #include "builtin.h"
-#include "utf8.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
 #include "strbuf.h"
 #include "mailinfo.h"
 #include "parse-options.h"
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 73509f6..3af9ddb 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -4,8 +4,8 @@
  * It just splits a mbox into a list of files: "0001" "0002" ..
  * so you can process them further from there.
  */
-#include "cache.h"
 #include "builtin.h"
+#include "gettext.h"
 #include "string-list.h"
 #include "strbuf.h"
 
@@ -277,6 +277,8 @@
 	const char **argp;
 	static const char *stdin_only[] = { "-", NULL };
 
+	BUG_ON_NON_EMPTY_PREFIX(prefix);
+
 	for (argp = argv+1; *argp; argp++) {
 		const char *arg = *argp;
 
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 6f3941f..5a8e729 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -1,19 +1,22 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "commit.h"
-#include "refs.h"
-#include "diff.h"
-#include "revision.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "commit-reach.h"
 
 static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-	struct commit_list *result, *r;
+	struct commit_list *result = NULL, *r;
 
-	result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
+	if (repo_get_merge_bases_many_dirty(the_repository, rev[0],
+					    rev_nr - 1, rev + 1, &result) < 0) {
+		free_commit_list(result);
+		return -1;
+	}
 
 	if (!result)
 		return 1;
@@ -42,7 +45,7 @@
 	struct object_id revkey;
 	struct commit *r;
 
-	if (get_oid(arg, &revkey))
+	if (repo_get_oid(the_repository, arg, &revkey))
 		die("Not a valid object name %s", arg);
 	r = lookup_commit_reference(the_repository, &revkey);
 	if (!r)
@@ -74,13 +77,17 @@
 static int handle_octopus(int count, const char **args, int show_all)
 {
 	struct commit_list *revs = NULL;
-	struct commit_list *result, *rev;
+	struct commit_list *result = NULL, *rev;
 	int i;
 
 	for (i = count - 1; i >= 0; i--)
 		commit_list_insert(get_commit_reference(args[i]), &revs);
 
-	result = get_octopus_merge_bases(revs);
+	if (get_octopus_merge_bases(revs, &result) < 0) {
+		free_commit_list(revs);
+		free_commit_list(result);
+		return 128;
+	}
 	free_commit_list(revs);
 	reduce_heads_replace(&result);
 
@@ -100,12 +107,16 @@
 static int handle_is_ancestor(int argc, const char **argv)
 {
 	struct commit *one, *two;
+	int ret;
 
 	if (argc != 2)
 		die("--is-ancestor takes exactly two commits");
 	one = get_commit_reference(argv[0]);
 	two = get_commit_reference(argv[1]);
-	if (in_merge_bases(one, two))
+	ret = repo_in_merge_bases(the_repository, one, two);
+	if (ret < 0)
+		exit(128);
+	if (ret)
 		return 0;
 	else
 		return 1;
@@ -118,7 +129,7 @@
 	const char *commitname;
 
 	commitname = (argc == 2) ? argv[1] : "HEAD";
-	if (get_oid(commitname, &oid))
+	if (repo_get_oid(the_repository, commitname, &oid))
 		die("Not a valid object name: '%s'", commitname);
 
 	derived = lookup_commit_reference(the_repository, &oid);
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index c923bbf..1f98733 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -1,6 +1,12 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
+#include "diff.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store.h"
 #include "config.h"
+#include "gettext.h"
+#include "setup.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 #include "parse-options.h"
@@ -23,16 +29,41 @@
 	return 0;
 }
 
+static int set_diff_algorithm(xpparam_t *xpp,
+			      const char *alg)
+{
+	long diff_algorithm = parse_algorithm_value(alg);
+	if (diff_algorithm < 0)
+		return -1;
+	xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
+	return 0;
+}
+
+static int diff_algorithm_cb(const struct option *opt,
+				const char *arg, int unset)
+{
+	xpparam_t *xpp = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (set_diff_algorithm(xpp, arg))
+		return error(_("option diff-algorithm accepts \"myers\", "
+			       "\"minimal\", \"patience\" and \"histogram\""));
+
+	return 0;
+}
+
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
 	const char *names[3] = { 0 };
 	mmfile_t mmfs[3] = { 0 };
 	mmbuffer_t result = { 0 };
 	xmparam_t xmp = { 0 };
-	int ret = 0, i = 0, to_stdout = 0;
+	int ret = 0, i = 0, to_stdout = 0, object_id = 0;
 	int quiet = 0;
 	struct option options[] = {
 		OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
+		OPT_BOOL(0,   "object-id", &object_id, N_("use object IDs instead of filenames")),
 		OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
 		OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
 				XDL_MERGE_ZEALOUS_DIFF3),
@@ -42,6 +73,9 @@
 			    XDL_MERGE_FAVOR_THEIRS),
 		OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
 			    XDL_MERGE_FAVOR_UNION),
+		OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"),
+			     N_("choose a diff algorithm"),
+			     PARSE_OPT_NONEG, diff_algorithm_cb),
 		OPT_INTEGER(0, "marker-size", &xmp.marker_size,
 			    N_("for conflicts, use this marker size")),
 		OPT__QUIET(&quiet, N_("do not warn about conflicts")),
@@ -69,8 +103,12 @@
 			return error_errno("failed to redirect stderr to /dev/null");
 	}
 
+	if (object_id)
+		setup_git_directory();
+
 	for (i = 0; i < 3; i++) {
 		char *fname;
+		struct object_id oid;
 		mmfile_t *mmf = mmfs + i;
 
 		if (!names[i])
@@ -78,12 +116,22 @@
 
 		fname = prefix_filename(prefix, argv[i]);
 
-		if (read_mmfile(mmf, fname))
+		if (object_id) {
+			if (repo_get_oid(the_repository, argv[i], &oid))
+				ret = error(_("object '%s' does not exist"),
+					      argv[i]);
+			else if (!oideq(&oid, the_hash_algo->empty_blob))
+				read_mmblob(mmf, &oid);
+			else
+				read_mmfile(mmf, "/dev/null");
+		} else if (read_mmfile(mmf, fname)) {
 			ret = -1;
-		else if (mmf->size > MAX_XDIFF_SIZE ||
-			 buffer_is_binary(mmf->ptr, mmf->size))
+		}
+		if (ret != -1 && (mmf->size > MAX_XDIFF_SIZE ||
+		    buffer_is_binary(mmf->ptr, mmf->size))) {
 			ret = error("Cannot merge binary files: %s",
 				    argv[i]);
+		}
 
 		free(fname);
 		if (ret)
@@ -97,20 +145,32 @@
 	ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
 
 	if (ret >= 0) {
-		const char *filename = argv[0];
-		char *fpath = prefix_filename(prefix, argv[0]);
-		FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
+		if (object_id && !to_stdout) {
+			struct object_id oid;
+			if (result.size) {
+				if (write_object_file(result.ptr, result.size, OBJ_BLOB, &oid) < 0)
+					ret = error(_("Could not write object file"));
+			} else {
+				oidcpy(&oid, the_hash_algo->empty_blob);
+			}
+			if (ret >= 0)
+				printf("%s\n", oid_to_hex(&oid));
+		} else {
+			const char *filename = argv[0];
+			char *fpath = prefix_filename(prefix, argv[0]);
+			FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
 
-		if (!f)
-			ret = error_errno("Could not open %s for writing",
-					  filename);
-		else if (result.size &&
-			 fwrite(result.ptr, result.size, 1, f) != 1)
-			ret = error_errno("Could not write to %s", filename);
-		else if (fclose(f))
-			ret = error_errno("Could not close %s", filename);
+			if (!f)
+				ret = error_errno("Could not open %s for writing",
+						  filename);
+			else if (result.size &&
+				 fwrite(result.ptr, result.size, 1, f) != 1)
+				ret = error_errno("Could not write to %s", filename);
+			else if (fclose(f))
+				ret = error_errno("Could not close %s", filename);
+			free(fpath);
+		}
 		free(result.ptr);
-		free(fpath);
 	}
 
 	if (ret > 127)
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 452f833..270d5f6 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,6 +1,10 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
 #include "run-command.h"
+#include "sparse-index.h"
 
 static const char *pgm;
 static int one_shot, quiet;
@@ -70,7 +74,7 @@
 	}
 }
 
-int cmd_merge_index(int argc, const char **argv, const char *prefix)
+int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
 {
 	int i, force_file = 0;
 
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
index 284eb48..932924e 100644
--- a/builtin/merge-ours.c
+++ b/builtin/merge-ours.c
@@ -10,11 +10,12 @@
 #include "git-compat-util.h"
 #include "builtin.h"
 #include "diff.h"
+#include "repository.h"
 
 static const char builtin_merge_ours_usage[] =
 	"git merge-ours <base>... -- HEAD <remote>...";
 
-int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+int cmd_merge_ours(int argc, const char **argv, const char *prefix UNUSED)
 {
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(builtin_merge_ours_usage);
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index b9acbf5..c2ce044 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -1,9 +1,10 @@
-#include "cache.h"
 #include "builtin.h"
-#include "commit.h"
-#include "tag.h"
+#include "advice.h"
+#include "gettext.h"
+#include "hash.h"
 #include "merge-recursive.h"
-#include "xdiff-interface.h"
+#include "object-name.h"
+#include "repository.h"
 
 static const char builtin_merge_recursive_usage[] =
 	"git %s <base>... -- <head> <remote> ...";
@@ -20,7 +21,7 @@
 	return xstrdup(name ? name : branch);
 }
 
-int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
 {
 	const struct object_id *bases[21];
 	unsigned bases_count = 0;
@@ -49,7 +50,7 @@
 		}
 		if (bases_count < ARRAY_SIZE(bases)-1) {
 			struct object_id *oid = xmalloc(sizeof(struct object_id));
-			if (get_oid(argv[i], oid))
+			if (repo_get_oid(the_repository, argv[i], oid))
 				die(_("could not parse object '%s'"), argv[i]);
 			bases[bases_count++] = oid;
 		}
@@ -70,9 +71,9 @@
 	o.branch1 = argv[++i];
 	o.branch2 = argv[++i];
 
-	if (get_oid(o.branch1, &h1))
+	if (repo_get_oid(the_repository, o.branch1, &h1))
 		die(_("could not resolve ref '%s'"), o.branch1);
-	if (get_oid(o.branch2, &h2))
+	if (repo_get_oid(the_repository, o.branch2, &h2))
 		die(_("could not resolve ref '%s'"), o.branch2);
 
 	o.branch1 = better1 = better_branch_name(o.branch1);
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 828dc81..8bdb439 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -3,16 +3,21 @@
 #include "tree-walk.h"
 #include "xdiff-interface.h"
 #include "help.h"
+#include "gettext.h"
+#include "hex.h"
 #include "commit.h"
 #include "commit-reach.h"
 #include "merge-ort.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "blob.h"
-#include "exec-cmd.h"
 #include "merge-blobs.h"
 #include "quote.h"
+#include "tree.h"
+#include "config.h"
+#include "strvec.h"
 
 static int line_termination = '\n';
 
@@ -69,7 +74,9 @@
 	const char *path = entry->path;
 
 	if (!entry->stage)
-		return read_object_file(&entry->blob->object.oid, &type, size);
+		return repo_read_object_file(the_repository,
+					     &entry->blob->object.oid, &type,
+					     size);
 	base = NULL;
 	if (entry->stage == 1) {
 		base = entry->blob;
@@ -92,8 +99,9 @@
 	enum object_type type;
 	while (entry) {
 		if (entry->stage == 2)
-			return read_object_file(&entry->blob->object.oid,
-						&type, size);
+			return repo_read_object_file(the_repository,
+						     &entry->blob->object.oid,
+						     &type, size);
 		entry = entry->link;
 	}
 	return NULL;
@@ -316,7 +324,9 @@
  * The successful merge rules are the same as for the three-way merge
  * in git-read-tree.
  */
-static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
+static int threeway_callback(int n UNUSED, unsigned long mask,
+			     unsigned long dirmask UNUSED,
+			     struct name_entry *entry, struct traverse_info *info)
 {
 	/* Same in both? */
 	if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) {
@@ -404,6 +414,7 @@
 	int show_messages;
 	int name_only;
 	int use_stdin;
+	struct merge_options merge_options;
 };
 
 static int real_merge(struct merge_tree_options *o,
@@ -413,46 +424,61 @@
 {
 	struct commit *parent1, *parent2;
 	struct commit_list *merge_bases = NULL;
-	struct merge_options opt;
 	struct merge_result result = { 0 };
 	int show_messages = o->show_messages;
+	struct merge_options opt;
 
-	parent1 = get_merge_parent(branch1);
-	if (!parent1)
-		help_unknown_ref(branch1, "merge-tree",
-				 _("not something we can merge"));
-
-	parent2 = get_merge_parent(branch2);
-	if (!parent2)
-		help_unknown_ref(branch2, "merge-tree",
-				 _("not something we can merge"));
-
-	init_merge_options(&opt, the_repository);
-
+	copy_merge_options(&opt, &o->merge_options);
 	opt.show_rename_progress = 0;
 
 	opt.branch1 = branch1;
 	opt.branch2 = branch2;
 
 	if (merge_base) {
-		struct commit *base_commit;
 		struct tree *base_tree, *parent1_tree, *parent2_tree;
 
-		base_commit = lookup_commit_reference_by_name(merge_base);
-		if (!base_commit)
-			die(_("could not lookup commit %s"), merge_base);
+		/*
+		 * We actually only need the trees because we already
+		 * have a merge base.
+		 */
+		struct object_id base_oid, head_oid, merge_oid;
+
+		if (repo_get_oid_treeish(the_repository, merge_base, &base_oid))
+			die(_("could not parse as tree '%s'"), merge_base);
+		base_tree = parse_tree_indirect(&base_oid);
+		if (!base_tree)
+			die(_("unable to read tree (%s)"), oid_to_hex(&base_oid));
+		if (repo_get_oid_treeish(the_repository, branch1, &head_oid))
+			die(_("could not parse as tree '%s'"), branch1);
+		parent1_tree = parse_tree_indirect(&head_oid);
+		if (!parent1_tree)
+			die(_("unable to read tree (%s)"), oid_to_hex(&head_oid));
+		if (repo_get_oid_treeish(the_repository, branch2, &merge_oid))
+			die(_("could not parse as tree '%s'"), branch2);
+		parent2_tree = parse_tree_indirect(&merge_oid);
+		if (!parent2_tree)
+			die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid));
 
 		opt.ancestor = merge_base;
-		base_tree = get_commit_tree(base_commit);
-		parent1_tree = get_commit_tree(parent1);
-		parent2_tree = get_commit_tree(parent2);
 		merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
 	} else {
+		parent1 = get_merge_parent(branch1);
+		if (!parent1)
+			help_unknown_ref(branch1, "merge-tree",
+					 _("not something we can merge"));
+
+		parent2 = get_merge_parent(branch2);
+		if (!parent2)
+			help_unknown_ref(branch2, "merge-tree",
+					 _("not something we can merge"));
+
 		/*
 		 * Get the merge bases, in reverse order; see comment above
 		 * merge_incore_recursive in merge-ort.h
 		 */
-		merge_bases = get_merge_bases(parent1, parent2);
+		if (repo_get_merge_bases(the_repository, parent1,
+					 parent2, &merge_bases) < 0)
+			exit(128);
 		if (!merge_bases && !o->allow_unrelated_histories)
 			die(_("refusing to merge unrelated histories"));
 		merge_bases = reverse_commit_list(merge_bases);
@@ -496,12 +522,14 @@
 	if (o->use_stdin)
 		putchar(line_termination);
 	merge_finalize(&opt, &result);
+	clear_merge_options(&opt);
 	return !result.clean; /* result.clean < 0 handled above */
 }
 
 int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 {
 	struct merge_tree_options o = { .show_messages = -1 };
+	struct strvec xopts = STRVEC_INIT;
 	int expected_remaining_argc;
 	int original_argc;
 	const char *merge_base = NULL;
@@ -535,16 +563,27 @@
 			   PARSE_OPT_NONEG),
 		OPT_STRING(0, "merge-base",
 			   &merge_base,
-			   N_("commit"),
+			   N_("tree-ish"),
 			   N_("specify a merge-base for the merge")),
+		OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
+			N_("option for selected merge strategy")),
 		OPT_END()
 	};
 
+	/* Init merge options */
+	init_merge_options(&o.merge_options, the_repository);
+
 	/* Parse arguments */
 	original_argc = argc - 1; /* ignoring argv[0] */
 	argc = parse_options(argc, argv, prefix, mt_options,
 			     merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
 
+	if (xopts.nr && o.mode == MODE_TRIVIAL)
+		die(_("--trivial-merge is incompatible with all other options"));
+	for (int x = 0; x < xopts.nr; x++)
+		if (parse_merge_opt(&o.merge_options, xopts.v[x]))
+			die(_("unknown strategy option: -X%s"), xopts.v[x]);
+
 	/* Handle --stdin */
 	if (o.use_stdin) {
 		struct strbuf buf = STRBUF_INIT;
@@ -552,7 +591,8 @@
 		if (o.mode == MODE_TRIVIAL)
 			die(_("--trivial-merge is incompatible with all other options"));
 		if (merge_base)
-			die(_("--merge-base is incompatible with --stdin"));
+			die(_("options '%s' and '%s' cannot be used together"),
+			    "--merge-base", "--stdin");
 		line_termination = '\0';
 		while (strbuf_getline_lf(&buf, stdin) != EOF) {
 			struct strbuf **split;
@@ -620,6 +660,8 @@
 	if (argc != expected_remaining_argc)
 		usage_with_options(merge_tree_usage, mt_options);
 
+	git_config(git_default_config, NULL);
+
 	/* Do the relevant type of merge */
 	if (o.mode == MODE_REAL)
 		return real_merge(&o, merge_base, argv[0], argv[1], prefix);
diff --git a/builtin/merge.c b/builtin/merge.c
index 0a3c10a..6f4fec8 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -7,10 +7,16 @@
  */
 
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
-#include "config.h"
-#include "parse-options.h"
 #include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "parse-options.h"
 #include "lockfile.h"
 #include "run-command.h"
 #include "hook.h"
@@ -20,24 +26,22 @@
 #include "refspec.h"
 #include "commit.h"
 #include "diffcore.h"
+#include "path.h"
 #include "revision.h"
 #include "unpack-trees.h"
 #include "cache-tree.h"
 #include "dir.h"
-#include "utf8.h"
-#include "log-tree.h"
 #include "color.h"
 #include "rerere.h"
 #include "help.h"
+#include "merge.h"
 #include "merge-recursive.h"
 #include "merge-ort-wrappers.h"
 #include "resolve-undo.h"
 #include "remote.h"
 #include "fmt-merge-msg.h"
-#include "gpg-interface.h"
 #include "sequencer.h"
 #include "string-list.h"
-#include "packfile.h"
 #include "tag.h"
 #include "alias.h"
 #include "branch.h"
@@ -71,8 +75,7 @@
 static struct strbuf merge_msg = STRBUF_INIT;
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
-static const char **xopts;
-static size_t xopts_nr, xopts_alloc;
+static struct strvec xopts = STRVEC_INIT;
 static const char *branch;
 static char *branch_mergeoptions;
 static int verbosity;
@@ -189,8 +192,7 @@
 			int j, found = 0;
 			struct cmdname *ent = main_cmds.names[i];
 			for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++)
-				if (!strncmp(ent->name, all_strategy[j].name, ent->len)
-						&& !all_strategy[j].name[ent->len])
+				if (!xstrncmpz(all_strategy[j].name, ent->name, ent->len))
 					found = 1;
 			if (!found)
 				add_cmdname(&not_strategies, ent->name, ent->len);
@@ -224,7 +226,7 @@
 	use_strategies[use_strategies_nr++] = s;
 }
 
-static int option_parse_strategy(const struct option *opt,
+static int option_parse_strategy(const struct option *opt UNUSED,
 				 const char *name, int unset)
 {
 	if (unset)
@@ -234,29 +236,9 @@
 	return 0;
 }
 
-static int option_parse_x(const struct option *opt,
-			  const char *arg, int unset)
-{
-	if (unset)
-		return 0;
-
-	ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
-	xopts[xopts_nr++] = xstrdup(arg);
-	return 0;
-}
-
-static int option_parse_n(const struct option *opt,
-			  const char *arg, int unset)
-{
-	BUG_ON_OPT_ARG(arg);
-	show_diffstat = unset;
-	return 0;
-}
-
 static struct option builtin_merge_options[] = {
-	OPT_CALLBACK_F('n', NULL, NULL, NULL,
-		N_("do not show a diffstat at the end of the merge"),
-		PARSE_OPT_NOARG, option_parse_n),
+	OPT_SET_INT('n', NULL, &show_diffstat,
+		N_("do not show a diffstat at the end of the merge"), 0),
 	OPT_BOOL(0, "stat", &show_diffstat,
 		N_("show a diffstat at the end of the merge")),
 	OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
@@ -277,10 +259,10 @@
 	OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
 	OPT_BOOL(0, "verify-signatures", &verify_signatures,
 		N_("verify that the named commit has a valid GPG signature")),
-	OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
+	OPT_CALLBACK('s', "strategy", NULL, N_("strategy"),
 		N_("merge strategy to use"), option_parse_strategy),
-	OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
-		N_("option for selected merge strategy"), option_parse_x),
+	OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
+		N_("option for selected merge strategy")),
 	OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
 		N_("merge commit message (for a non-fast-forward merge)"),
 		option_parse_message),
@@ -337,7 +319,7 @@
 	else if (!len)		/* no changes */
 		goto out;
 	strbuf_setlen(&buffer, buffer.len-1);
-	if (get_oid(buffer.buf, stash))
+	if (repo_get_oid(the_repository, buffer.buf, stash))
 		die(_("not a valid object: %s"), buffer.buf);
 	rc = 0;
 out:
@@ -479,8 +461,7 @@
 	if (new_head && show_diffstat) {
 		struct diff_options opts;
 		repo_diff_setup(the_repository, &opts);
-		opts.stat_width = -1; /* use full terminal width */
-		opts.stat_graph_width = -1; /* respect statGraphWidth config */
+		init_diffstat_widths(&opts);
 		opts.output_format |=
 			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
 		opts.detect_rename = DIFF_DETECT_RENAME;
@@ -494,7 +475,7 @@
 	run_hooks_l("post-merge", squash ? "1" : "0", NULL);
 
 	if (new_head)
-		apply_autostash(git_path_merge_autostash(the_repository));
+		apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 	strbuf_release(&reflog_message);
 }
 
@@ -517,7 +498,8 @@
 	if (!remote_head)
 		die(_("'%s' does not point to a commit"), remote);
 
-	if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) {
+	if (repo_dwim_ref(the_repository, remote, strlen(remote), &branch_head,
+			  &found_ref, 0) > 0) {
 		if (starts_with(found_ref, "refs/heads/")) {
 			strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
 				    oid_to_hex(&branch_head), remote);
@@ -613,7 +595,8 @@
 	free(argv);
 }
 
-static int git_merge_config(const char *k, const char *v, void *cb)
+static int git_merge_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
 	int status;
 	const char *str;
@@ -658,13 +641,10 @@
 		return 0;
 	}
 
-	status = fmt_merge_msg_config(k, v, cb);
+	status = fmt_merge_msg_config(k, v, ctx, cb);
 	if (status)
 		return status;
-	status = git_gpg_config(k, v, NULL);
-	if (status)
-		return status;
-	return git_diff_ui_config(k, v, cb);
+	return git_diff_ui_config(k, v, ctx, cb);
 }
 
 static int read_tree_trivial(struct object_id *common, struct object_id *head,
@@ -697,7 +677,8 @@
 	cache_tree_free(&the_index.cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		parse_tree(trees[i]);
-		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+		init_tree_desc(t+i, &trees[i]->object.oid,
+			       trees[i]->buffer, trees[i]->size);
 	}
 	if (unpack_trees(nr_trees, t, &opts))
 		return -1;
@@ -742,9 +723,9 @@
 		o.show_rename_progress =
 			show_progress == -1 ? isatty(2) : show_progress;
 
-		for (x = 0; x < xopts_nr; x++)
-			if (parse_merge_opt(&o, xopts[x]))
-				die(_("unknown strategy option: -X%s"), xopts[x]);
+		for (x = 0; x < xopts.nr; x++)
+			if (parse_merge_opt(&o, xopts.v[x]))
+				die(_("unknown strategy option: -X%s"), xopts.v[x]);
 
 		o.branch1 = head_arg;
 		o.branch2 = merge_remote_util(remoteheads->item)->name;
@@ -770,7 +751,7 @@
 		return clean ? 0 : 1;
 	} else {
 		return try_merge_command(the_repository,
-					 strategy, xopts_nr, xopts,
+					 strategy, xopts.nr, xopts.v,
 					 common, head_arg, remoteheads);
 	}
 }
@@ -841,7 +822,7 @@
 N_("An empty message aborts the commit.\n");
 
 static const char no_scissors_editor_comment[] =
-N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
+N_("Lines starting with '%s' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
 static void write_merge_heads(struct commit_list *);
@@ -872,17 +853,19 @@
 		strbuf_addch(&msg, '\n');
 		if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
 			wt_status_append_cut_line(&msg);
-			strbuf_commented_addf(&msg, "\n");
+			strbuf_commented_addf(&msg, comment_line_str, "\n");
 		}
-		strbuf_commented_addf(&msg, _(merge_editor_comment));
+		strbuf_commented_addf(&msg, comment_line_str,
+				      _(merge_editor_comment));
 		if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
-			strbuf_commented_addf(&msg, _(scissors_editor_comment));
+			strbuf_commented_addf(&msg, comment_line_str,
+					      _(scissors_editor_comment));
 		else
-			strbuf_commented_addf(&msg,
-				_(no_scissors_editor_comment), comment_line_char);
+			strbuf_commented_addf(&msg, comment_line_str,
+				_(no_scissors_editor_comment), comment_line_str);
 	}
 	if (signoff)
-		append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+		append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0);
 	write_merge_heads(remoteheads);
 	write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
 	if (run_commit_hook(0 < option_edit, get_index_file(), NULL,
@@ -1332,7 +1315,8 @@
 	if (abort_current_merge) {
 		int nargc = 2;
 		const char *nargv[] = {"reset", "--merge", NULL};
-		struct strbuf stash_oid = STRBUF_INIT;
+		char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+		struct object_id stash_oid = {0};
 
 		if (orig_argc != 2)
 			usage_msg_opt(_("--abort expects no arguments"),
@@ -1341,17 +1325,17 @@
 		if (!file_exists(git_path_merge_head(the_repository)))
 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
-		if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
-		    READ_ONELINER_SKIP_IF_EMPTY))
-			unlink(git_path_merge_autostash(the_repository));
+		if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
+			delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
 
-		if (stash_oid.len)
-			apply_autostash_oid(stash_oid.buf);
+		if (!is_null_oid(&stash_oid)) {
+			oid_to_hex_r(stash_oid_hex, &stash_oid);
+			apply_autostash_oid(stash_oid_hex);
+		}
 
-		strbuf_release(&stash_oid);
 		goto done;
 	}
 
@@ -1530,12 +1514,20 @@
 
 	if (!remoteheads)
 		; /* already up-to-date */
-	else if (!remoteheads->next)
-		common = get_merge_bases(head_commit, remoteheads->item);
-	else {
+	else if (!remoteheads->next) {
+		if (repo_get_merge_bases(the_repository, head_commit,
+					 remoteheads->item, &common) < 0) {
+			ret = 2;
+			goto done;
+		}
+	} else {
 		struct commit_list *list = remoteheads;
 		commit_list_insert(head_commit, &list);
-		common = get_octopus_merge_bases(list);
+		if (get_octopus_merge_bases(list, &common) < 0) {
+			free(list);
+			ret = 2;
+			goto done;
+		}
 		free(list);
 	}
 
@@ -1567,10 +1559,10 @@
 
 		if (verbosity >= 0) {
 			printf(_("Updating %s..%s\n"),
-			       find_unique_abbrev(&head_commit->object.oid,
-						  DEFAULT_ABBREV),
-			       find_unique_abbrev(&remoteheads->item->object.oid,
-						  DEFAULT_ABBREV));
+			       repo_find_unique_abbrev(the_repository, &head_commit->object.oid,
+						       DEFAULT_ABBREV),
+			       repo_find_unique_abbrev(the_repository, &remoteheads->item->object.oid,
+						       DEFAULT_ABBREV));
 		}
 		commit = remoteheads->item;
 		if (!commit) {
@@ -1579,13 +1571,12 @@
 		}
 
 		if (autostash)
-			create_autostash(the_repository,
-					 git_path_merge_autostash(the_repository));
+			create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 		if (checkout_fast_forward(the_repository,
 					  &head_commit->object.oid,
 					  &commit->object.oid,
 					  overwrite_ignore)) {
-			apply_autostash(git_path_merge_autostash(the_repository));
+			apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 			ret = 1;
 			goto done;
 		}
@@ -1610,7 +1601,8 @@
 			 * Must first ensure that index matches HEAD before
 			 * attempting a trivial merge.
 			 */
-			struct tree *head_tree = get_commit_tree(head_commit);
+			struct tree *head_tree = repo_get_commit_tree(the_repository,
+								      head_commit);
 			struct strbuf sb = STRBUF_INIT;
 
 			if (repo_index_has_changes(the_repository, head_tree,
@@ -1642,15 +1634,21 @@
 		struct commit_list *j;
 
 		for (j = remoteheads; j; j = j->next) {
-			struct commit_list *common_one;
+			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.
 			 */
-			common_one = get_merge_bases(head_commit, j->item);
-			if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
+			if (repo_get_merge_bases(the_repository, head_commit,
+						 j->item, &common_one) < 0)
+				exit(128);
+
+			common_item = common_one->item;
+			free_commit_list(common_one);
+			if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
 				up_to_date = 0;
 				break;
 			}
@@ -1665,8 +1663,7 @@
 		die_ff_impossible();
 
 	if (autostash)
-		create_autostash(the_repository,
-				 git_path_merge_autostash(the_repository));
+		create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 
 	/* We are going to make a new commit. */
 	git_committer_info(IDENT_STRICT);
@@ -1751,7 +1748,7 @@
 		else
 			fprintf(stderr, _("Merge with strategy %s failed.\n"),
 				use_strategies[0]->name);
-		apply_autostash(git_path_merge_autostash(the_repository));
+		apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 		ret = 2;
 		goto done;
 	} else if (best_strategy == wt_strategy)
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 5d22909..4767f1a 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -1,8 +1,11 @@
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
-#include "tag.h"
+#include "strbuf.h"
 #include "replace-object.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "fsck.h"
 #include "config.h"
 
@@ -14,11 +17,11 @@
 
 static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
 
-static int mktag_fsck_error_func(struct fsck_options *o,
-				 const struct object_id *oid,
-				 enum object_type object_type,
+static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
+				 const struct object_id *oid UNUSED,
+				 enum object_type object_type UNUSED,
 				 enum fsck_msg_type msg_type,
-				 enum fsck_msg_id msg_id,
+				 enum fsck_msg_id msg_id UNUSED,
 				 const char *message)
 {
 	switch (msg_type) {
@@ -51,7 +54,8 @@
 	void *buffer;
 	const struct object_id *repl;
 
-	buffer = read_object_file(tagged_oid, &type, &size);
+	buffer = repo_read_object_file(the_repository, tagged_oid, &type,
+				       &size);
 	if (!buffer)
 		die(_("could not read tagged object '%s'"),
 		    oid_to_hex(tagged_oid));
@@ -80,7 +84,7 @@
 	int tagged_type;
 	struct object_id result;
 
-	argc = parse_options(argc, argv, NULL,
+	argc = parse_options(argc, argv, prefix,
 			     builtin_mktag_options,
 			     builtin_mktag_usage, 0);
 
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 06d8140..9a22d4e 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -4,10 +4,13 @@
  * Copyright (c) Junio C Hamano, 2006, 2009
  */
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
 #include "quote.h"
+#include "strbuf.h"
 #include "tree.h"
 #include "parse-options.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static struct treeent {
 	unsigned mode;
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 9a18a82..a72aebe 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -1,10 +1,13 @@
 #include "builtin.h"
-#include "cache.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "midx.h"
+#include "strbuf.h"
 #include "trace2.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 #define BUILTIN_MIDX_WRITE_USAGE \
 	N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -79,6 +82,7 @@
 }
 
 static int git_multi_pack_index_write_config(const char *var, const char *value,
+					     const struct config_context *ctx UNUSED,
 					     void *cb UNUSED)
 {
 	if (!strcmp(var, "pack.writebitmaphashcache")) {
diff --git a/builtin/mv.c b/builtin/mv.c
index edd7b93..22e64fc 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -5,13 +5,21 @@
  */
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object-file.h"
 #include "pathspec.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "cache-tree.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "submodule.h"
 #include "entry.h"
 
@@ -175,7 +183,7 @@
 	int src_dir_nr = 0, src_dir_alloc = 0;
 	struct strbuf a_src_dir = STRBUF_INIT;
 	enum update_mode *modes, dst_mode = 0;
-	struct stat st;
+	struct stat st, dest_st;
 	struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
 	struct lock_file lock_file = LOCK_INIT;
 	struct cache_entry *ce;
@@ -295,8 +303,8 @@
 			goto act_on_entry;
 		}
 		if (S_ISDIR(st.st_mode)
-		    && lstat(dst, &st) == 0) {
-			bad = _("cannot move directory over file");
+		    && lstat(dst, &dest_st) == 0) {
+			bad = _("destination already exists");
 			goto act_on_entry;
 		}
 
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 97959bf..ad9930c 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -1,15 +1,21 @@
 #include "builtin.h"
-#include "cache.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "commit.h"
 #include "tag.h"
 #include "refs.h"
+#include "object-name.h"
+#include "pager.h"
 #include "parse-options.h"
 #include "prio-queue.h"
 #include "hash-lookup.h"
 #include "commit-slab.h"
 #include "commit-graph.h"
+#include "wildmatch.h"
+#include "mem-pool.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -150,30 +156,25 @@
 	return name;
 }
 
-static char *get_parent_name(const struct rev_name *name, int parent_number)
+static char *get_parent_name(const struct rev_name *name, int parent_number,
+			     struct mem_pool *string_pool)
 {
-	struct strbuf sb = STRBUF_INIT;
 	size_t len;
 
 	strip_suffix(name->tip_name, "^0", &len);
 	if (name->generation > 0) {
-		strbuf_grow(&sb, len +
-			    1 + decimal_width(name->generation) +
-			    1 + decimal_width(parent_number));
-		strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
-			    name->generation, parent_number);
+		return mem_pool_strfmt(string_pool, "%.*s~%d^%d",
+				       (int)len, name->tip_name,
+				       name->generation, parent_number);
 	} else {
-		strbuf_grow(&sb, len +
-			    1 + decimal_width(parent_number));
-		strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
-			    parent_number);
+		return mem_pool_strfmt(string_pool, "%.*s^%d",
+				       (int)len, name->tip_name, parent_number);
 	}
-	return strbuf_detach(&sb, NULL);
 }
 
 static void name_rev(struct commit *start_commit,
 		const char *tip_name, timestamp_t taggerdate,
-		int from_tag, int deref)
+		int from_tag, int deref, struct mem_pool *string_pool)
 {
 	struct prio_queue queue;
 	struct commit *commit;
@@ -181,7 +182,7 @@
 	size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
 	struct rev_name *start_name;
 
-	parse_commit(start_commit);
+	repo_parse_commit(the_repository, start_commit);
 	if (commit_is_before_cutoff(start_commit))
 		return;
 
@@ -190,9 +191,10 @@
 	if (!start_name)
 		return;
 	if (deref)
-		start_name->tip_name = xstrfmt("%s^0", tip_name);
+		start_name->tip_name = mem_pool_strfmt(string_pool, "%s^0",
+						       tip_name);
 	else
-		start_name->tip_name = xstrdup(tip_name);
+		start_name->tip_name = mem_pool_strdup(string_pool, tip_name);
 
 	memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */
 	prio_queue_put(&queue, start_commit);
@@ -211,7 +213,7 @@
 			struct rev_name *parent_name;
 			int generation, distance;
 
-			parse_commit(parent);
+			repo_parse_commit(the_repository, parent);
 			if (commit_is_before_cutoff(parent))
 				continue;
 
@@ -230,7 +232,8 @@
 				if (parent_number > 1)
 					parent_name->tip_name =
 						get_parent_name(name,
-								parent_number);
+								parent_number,
+								string_pool);
 				else
 					parent_name->tip_name = name->tip_name;
 				ALLOC_GROW(parents_to_queue,
@@ -410,7 +413,7 @@
 	return 0;
 }
 
-static void name_tips(void)
+static void name_tips(struct mem_pool *string_pool)
 {
 	int i;
 
@@ -423,7 +426,7 @@
 		struct tip_table_entry *e = &tip_table.table[i];
 		if (e->commit) {
 			name_rev(e->commit, e->refname, e->taggerdate,
-				 e->from_tag, e->deref);
+				 e->from_tag, e->deref, string_pool);
 		}
 	}
 }
@@ -493,7 +496,8 @@
 	else if (allow_undefined)
 		printf("undefined\n");
 	else if (always)
-		printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV));
+		printf("%s\n",
+		       repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
 	else
 		die("cannot describe '%s'", oid_to_hex(oid));
 	strbuf_release(&buf);
@@ -527,7 +531,7 @@
 			counter = 0;
 
 			*(p+1) = 0;
-			if (!get_oid(p - (hexsz - 1), &oid)) {
+			if (!repo_get_oid(the_repository, p - (hexsz - 1), &oid)) {
 				struct object *o =
 					lookup_object(the_repository, &oid);
 				if (o)
@@ -555,6 +559,7 @@
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
+	struct mem_pool string_pool;
 	struct object_array revs = OBJECT_ARRAY_INIT;
 	int all = 0, annotate_stdin = 0, transform_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 };
@@ -567,20 +572,21 @@
 				   N_("ignore refs matching <pattern>")),
 		OPT_GROUP(""),
 		OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
-		OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use --annotate-stdin instead")),
+		OPT_BOOL_F(0,
+			   "stdin",
+			   &transform_stdin,
+			   N_("deprecated: use --annotate-stdin instead"),
+			   PARSE_OPT_HIDDEN),
 		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")),
-		{
-			/* A Hidden OPT_BOOL */
-			OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
-			N_("dereference tags in the input (internal use)"),
-			PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
-		},
+		OPT_HIDDEN_BOOL(0, "peel-tag", &peel_tag,
+			   N_("dereference tags in the input (internal use)")),
 		OPT_END(),
 	};
 
+	mem_pool_init(&string_pool, 0);
 	init_commit_rev_name(&rev_names);
 	git_config(git_default_config, NULL);
 	argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
@@ -604,7 +610,7 @@
 		struct object *object;
 		struct commit *commit;
 
-		if (get_oid(*argv, &oid)) {
+		if (repo_get_oid(the_repository, *argv, &oid)) {
 			fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
 					*argv);
 			continue;
@@ -642,7 +648,7 @@
 	adjust_cutoff_timestamp_for_slop();
 
 	for_each_ref(name_ref, &data);
-	name_tips();
+	name_tips(&string_pool);
 
 	if (annotate_stdin) {
 		struct strbuf sb = STRBUF_INIT;
@@ -670,6 +676,7 @@
 				  always, allow_undefined, data.name_only);
 	}
 
+	UNLEAK(string_pool);
 	UNLEAK(revs);
 	return 0;
 }
diff --git a/builtin/notes.c b/builtin/notes.c
index 80d9dfd..cb01130 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -7,13 +7,17 @@
  * and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
  */
 
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "notes.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
-#include "blob.h"
 #include "pretty.h"
 #include "refs.h"
 #include "exec-cmd.h"
@@ -23,12 +27,14 @@
 #include "notes-merge.h"
 #include "notes-utils.h"
 #include "worktree.h"
+#include "write-or-die.h"
 
+static const char *separator = "\n";
 static const char * const git_notes_usage[] = {
 	N_("git notes [--ref <notes-ref>] [list [<object>]]"),
-	N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+	N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 	N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
-	N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+	N_("git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
 	N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
 	N_("git notes [--ref <notes-ref>] show [<object>]"),
 	N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
@@ -96,11 +102,26 @@
 static const char note_template[] =
 	N_("Write/edit the notes for the following object:");
 
+enum notes_stripspace {
+	UNSPECIFIED = -1,
+	NO_STRIPSPACE = 0,
+	STRIPSPACE = 1,
+};
+
+struct note_msg {
+	enum notes_stripspace stripspace;
+	struct strbuf buf;
+};
+
 struct note_data {
 	int given;
 	int use_editor;
+	int stripspace;
 	char *edit_path;
 	struct strbuf buf;
+	struct note_msg **messages;
+	size_t msg_nr;
+	size_t msg_alloc;
 };
 
 static void free_note_data(struct note_data *d)
@@ -110,11 +131,18 @@
 		free(d->edit_path);
 	}
 	strbuf_release(&d->buf);
+
+	while (d->msg_nr--) {
+		strbuf_release(&d->messages[d->msg_nr]->buf);
+		free(d->messages[d->msg_nr]);
+	}
+	free(d->messages);
 }
 
 static int list_each_note(const struct object_id *object_oid,
-		const struct object_id *note_oid, char *note_path,
-		void *cb_data)
+			  const struct object_id *note_oid,
+			  char *note_path UNUSED,
+			  void *cb_data UNUSED)
 {
 	printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid));
 	return 0;
@@ -124,7 +152,7 @@
 {
 	unsigned long size;
 	enum object_type type;
-	char *buf = read_object_file(oid, &type, &size);
+	char *buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (buf) {
 		if (size)
 			write_or_die(fd, buf, size);
@@ -151,7 +179,7 @@
 
 	if (strbuf_read(&buf, show.out, 0) < 0)
 		die_errno(_("could not read 'show' output"));
-	strbuf_add_commented_lines(&cbuf, buf.buf, buf.len);
+	strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_str);
 	write_or_die(fd, cbuf.buf, cbuf.len);
 
 	strbuf_release(&cbuf);
@@ -179,9 +207,10 @@
 			copy_obj_to_fd(fd, old_note);
 
 		strbuf_addch(&buf, '\n');
-		strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
-		strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
-		strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+		strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str);
+		strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)),
+					   comment_line_str);
+		strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str);
 		write_or_die(fd, buf.buf, buf.len);
 
 		write_commented_object(fd, object);
@@ -193,7 +222,8 @@
 		if (launch_editor(d->edit_path, &d->buf, NULL)) {
 			die(_("please supply the note contents using either -m or -F option"));
 		}
-		strbuf_stripspace(&d->buf, 1);
+		if (d->stripspace)
+			strbuf_stripspace(&d->buf, comment_line_str);
 	}
 }
 
@@ -209,66 +239,102 @@
 	}
 }
 
+static void append_separator(struct strbuf *message)
+{
+	size_t sep_len = 0;
+
+	if (!separator)
+		return;
+	else if ((sep_len = strlen(separator)) && separator[sep_len - 1] == '\n')
+		strbuf_addstr(message, separator);
+	else
+		strbuf_addf(message, "%s%s", separator, "\n");
+}
+
+static void concat_messages(struct note_data *d)
+{
+	struct strbuf msg = STRBUF_INIT;
+	size_t i;
+
+	for (i = 0; i < d->msg_nr ; i++) {
+		if (d->buf.len)
+			append_separator(&d->buf);
+		strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
+		strbuf_addbuf(&d->buf, &msg);
+		if ((d->stripspace == UNSPECIFIED &&
+		     d->messages[i]->stripspace == STRIPSPACE) ||
+		    d->stripspace == STRIPSPACE)
+			strbuf_stripspace(&d->buf, NULL);
+		strbuf_reset(&msg);
+	}
+	strbuf_release(&msg);
+}
+
 static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 {
 	struct note_data *d = opt->value;
+	struct note_msg *msg = xmalloc(sizeof(*msg));
 
 	BUG_ON_OPT_NEG(unset);
 
-	strbuf_grow(&d->buf, strlen(arg) + 2);
-	if (d->buf.len)
-		strbuf_addch(&d->buf, '\n');
-	strbuf_addstr(&d->buf, arg);
-	strbuf_stripspace(&d->buf, 0);
-
-	d->given = 1;
+	strbuf_init(&msg->buf, strlen(arg));
+	strbuf_addstr(&msg->buf, arg);
+	ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+	d->messages[d->msg_nr - 1] = msg;
+	msg->stripspace = STRIPSPACE;
 	return 0;
 }
 
 static int parse_file_arg(const struct option *opt, const char *arg, int unset)
 {
 	struct note_data *d = opt->value;
+	struct note_msg *msg = xmalloc(sizeof(*msg));
 
 	BUG_ON_OPT_NEG(unset);
 
-	if (d->buf.len)
-		strbuf_addch(&d->buf, '\n');
+	strbuf_init(&msg->buf , 0);
 	if (!strcmp(arg, "-")) {
-		if (strbuf_read(&d->buf, 0, 1024) < 0)
+		if (strbuf_read(&msg->buf, 0, 1024) < 0)
 			die_errno(_("cannot read '%s'"), arg);
-	} else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
+	} else if (strbuf_read_file(&msg->buf, arg, 1024) < 0)
 		die_errno(_("could not open or read '%s'"), arg);
-	strbuf_stripspace(&d->buf, 0);
 
-	d->given = 1;
+	ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+	d->messages[d->msg_nr - 1] = msg;
+	msg->stripspace = STRIPSPACE;
 	return 0;
 }
 
 static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
 {
 	struct note_data *d = opt->value;
-	char *buf;
+	struct note_msg *msg = xmalloc(sizeof(*msg));
+	char *value;
 	struct object_id object;
 	enum object_type type;
 	unsigned long len;
 
 	BUG_ON_OPT_NEG(unset);
 
-	if (d->buf.len)
-		strbuf_addch(&d->buf, '\n');
-
-	if (get_oid(arg, &object))
+	strbuf_init(&msg->buf, 0);
+	if (repo_get_oid(the_repository, arg, &object))
 		die(_("failed to resolve '%s' as a valid ref."), arg);
-	if (!(buf = read_object_file(&object, &type, &len)))
+	if (!(value = repo_read_object_file(the_repository, &object, &type, &len)))
 		die(_("failed to read object '%s'."), arg);
 	if (type != OBJ_BLOB) {
-		free(buf);
+		strbuf_release(&msg->buf);
+		free(value);
+		free(msg);
 		die(_("cannot read note data from non-blob object '%s'."), arg);
 	}
-	strbuf_add(&d->buf, buf, len);
-	free(buf);
 
-	d->given = 1;
+	strbuf_add(&msg->buf, value, len);
+	free(value);
+
+	msg->buf.len = len;
+	ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+	d->messages[d->msg_nr - 1] = msg;
+	msg->stripspace = NO_STRIPSPACE;
 	return 0;
 }
 
@@ -280,6 +346,16 @@
 	return parse_reuse_arg(opt, arg, unset);
 }
 
+static int parse_separator_arg(const struct option *opt, const char *arg,
+			       int unset)
+{
+	if (unset)
+		*(const char **)opt->value = NULL;
+	else
+		*(const char **)opt->value = arg ? arg : "\n";
+	return 0;
+}
+
 static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -307,9 +383,9 @@
 			die(_("malformed input line: '%s'."), buf.buf);
 		strbuf_rtrim(split[0]);
 		strbuf_rtrim(split[1]);
-		if (get_oid(split[0]->buf, &from_obj))
+		if (repo_get_oid(the_repository, split[0]->buf, &from_obj))
 			die(_("failed to resolve '%s' as a valid ref."), split[0]->buf);
-		if (get_oid(split[1]->buf, &to_obj))
+		if (repo_get_oid(the_repository, split[1]->buf, &to_obj))
 			die(_("failed to resolve '%s' as a valid ref."), split[1]->buf);
 
 		if (rewrite_cmd)
@@ -377,7 +453,7 @@
 
 	t = init_notes_check("list", 0);
 	if (argc) {
-		if (get_oid(argv[0], &object))
+		if (repo_get_oid(the_repository, argv[0], &object))
 			die(_("failed to resolve '%s' as a valid ref."), argv[0]);
 		note = get_note(t, &object);
 		if (note) {
@@ -402,7 +478,8 @@
 	struct notes_tree *t;
 	struct object_id object, new_note;
 	const struct object_id *note;
-	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+	struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
+
 	struct option options[] = {
 		OPT_CALLBACK_F('m', "message", &d, N_("message"),
 			N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -419,6 +496,12 @@
 		OPT_BOOL(0, "allow-empty", &allow_empty,
 			N_("allow storing empty note")),
 		OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+		OPT_CALLBACK_F(0, "separator", &separator,
+			N_("<paragraph-break>"),
+			N_("insert <paragraph-break> between paragraphs"),
+			PARSE_OPT_OPTARG, parse_separator_arg),
+		OPT_BOOL(0, "stripspace", &d.stripspace,
+			N_("remove unnecessary whitespace")),
 		OPT_END()
 	};
 
@@ -430,9 +513,13 @@
 		usage_with_options(git_notes_add_usage, options);
 	}
 
+	if (d.msg_nr)
+		concat_messages(&d);
+	d.given = !!d.buf.len;
+
 	object_ref = argc > 1 ? argv[1] : "HEAD";
 
-	if (get_oid(object_ref, &object))
+	if (repo_get_oid(the_repository, object_ref, &object))
 		die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
 	t = init_notes_check("add", NOTES_INIT_WRITABLE);
@@ -520,12 +607,12 @@
 		usage_with_options(git_notes_copy_usage, options);
 	}
 
-	if (get_oid(argv[0], &from_obj))
+	if (repo_get_oid(the_repository, argv[0], &from_obj))
 		die(_("failed to resolve '%s' as a valid ref."), argv[0]);
 
 	object_ref = 1 < argc ? argv[1] : "HEAD";
 
-	if (get_oid(object_ref, &object))
+	if (repo_get_oid(the_repository, object_ref, &object))
 		die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
 	t = init_notes_check("copy", NOTES_INIT_WRITABLE);
@@ -568,7 +655,7 @@
 	const struct object_id *note;
 	char *logmsg;
 	const char * const *usage;
-	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+	struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
 	struct option options[] = {
 		OPT_CALLBACK_F('m', "message", &d, N_("message"),
 			N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -584,6 +671,12 @@
 			parse_reuse_arg),
 		OPT_BOOL(0, "allow-empty", &allow_empty,
 			N_("allow storing empty note")),
+		OPT_CALLBACK_F(0, "separator", &separator,
+			N_("<paragraph-break>"),
+			N_("insert <paragraph-break> between paragraphs"),
+			PARSE_OPT_OPTARG, parse_separator_arg),
+		OPT_BOOL(0, "stripspace", &d.stripspace,
+			N_("remove unnecessary whitespace")),
 		OPT_END()
 	};
 	int edit = !strcmp(argv[0], "edit");
@@ -597,6 +690,10 @@
 		usage_with_options(usage, options);
 	}
 
+	if (d.msg_nr)
+		concat_messages(&d);
+	d.given = !!d.buf.len;
+
 	if (d.given && edit)
 		fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
 			"for the 'edit' subcommand.\n"
@@ -604,7 +701,7 @@
 
 	object_ref = 1 < argc ? argv[1] : "HEAD";
 
-	if (get_oid(object_ref, &object))
+	if (repo_get_oid(the_repository, object_ref, &object))
 		die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
 	t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
@@ -616,14 +713,19 @@
 		/* Append buf to previous note contents */
 		unsigned long size;
 		enum object_type type;
-		char *prev_buf = read_object_file(note, &type, &size);
+		struct strbuf buf = STRBUF_INIT;
+		char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);
 
-		strbuf_grow(&d.buf, size + 1);
-		if (d.buf.len && prev_buf && size)
-			strbuf_insertstr(&d.buf, 0, "\n");
-		if (prev_buf && size)
-			strbuf_insert(&d.buf, 0, prev_buf, size);
+		if (!prev_buf)
+			die(_("unable to read %s"), oid_to_hex(note));
+		if (size)
+			strbuf_add(&buf, prev_buf, size);
+		if (d.buf.len && size)
+			append_separator(&buf);
+		strbuf_insert(&d.buf, 0, buf.buf, buf.len);
+
 		free(prev_buf);
+		strbuf_release(&buf);
 	}
 
 	if (d.buf.len || allow_empty) {
@@ -666,7 +768,7 @@
 
 	object_ref = argc ? argv[0] : "HEAD";
 
-	if (get_oid(object_ref, &object))
+	if (repo_get_oid(the_repository, object_ref, &object))
 		die(_("failed to resolve '%s' as a valid ref."), object_ref);
 
 	t = init_notes_check("show", 0);
@@ -716,11 +818,11 @@
 	 * and target notes ref from .git/NOTES_MERGE_REF.
 	 */
 
-	if (get_oid("NOTES_MERGE_PARTIAL", &oid))
+	if (repo_get_oid(the_repository, "NOTES_MERGE_PARTIAL", &oid))
 		die(_("failed to read ref NOTES_MERGE_PARTIAL"));
 	else if (!(partial = lookup_commit_reference(the_repository, &oid)))
 		die(_("could not find commit from NOTES_MERGE_PARTIAL."));
-	else if (parse_commit(partial))
+	else if (repo_parse_commit(the_repository, partial))
 		die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
 
 	if (partial->parents)
@@ -741,7 +843,8 @@
 
 	/* Reuse existing commit message in reflog message */
 	memset(&pretty_ctx, 0, sizeof(pretty_ctx));
-	format_commit_message(partial, "%s", &msg, &pretty_ctx);
+	repo_format_commit_message(the_repository, partial, "%s", &msg,
+				   &pretty_ctx);
 	strbuf_trim(&msg);
 	strbuf_insertstr(&msg, 0, "notes: ");
 	update_ref(msg.buf, o->local_ref, &oid,
@@ -895,7 +998,7 @@
 {
 	int status;
 	struct object_id oid;
-	if (get_oid(name, &oid))
+	if (repo_get_oid(the_repository, name, &oid))
 		return error(_("Failed to resolve '%s' as a valid ref."), name);
 	status = remove_note(t, oid.hash);
 	if (status)
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 74a167a..baf0090 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1,13 +1,13 @@
 #include "builtin.h"
-#include "cache.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "attr.h"
 #include "object.h"
-#include "blob.h"
 #include "commit.h"
 #include "tag.h"
-#include "tree.h"
 #include "delta.h"
 #include "pack.h"
 #include "pack-revindex.h"
@@ -16,7 +16,6 @@
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "pack-objects.h"
 #include "progress.h"
@@ -30,13 +29,16 @@
 #include "strvec.h"
 #include "list.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
 #include "dir.h"
 #include "midx.h"
 #include "trace2.h"
 #include "shallow.h"
 #include "promisor-remote.h"
 #include "pack-mtimes.h"
+#include "parse-options.h"
 
 /*
  * Objects we are going to pack are collected in the `to_pack` structure.
@@ -216,13 +218,19 @@
 static int num_preferred_base;
 static struct progress *progress_state;
 
-static struct packed_git *reuse_packfile;
+static struct bitmapped_pack *reuse_packfiles;
+static size_t reuse_packfiles_nr;
+static size_t reuse_packfiles_used_nr;
 static uint32_t reuse_packfile_objects;
 static struct bitmap *reuse_packfile_bitmap;
 
 static int use_bitmap_index_default = 1;
 static int use_bitmap_index = -1;
-static int allow_pack_reuse = 1;
+static enum {
+	NO_PACK_REUSE = 0,
+	SINGLE_PACK_REUSE,
+	MULTI_PACK_REUSE,
+} allow_pack_reuse = SINGLE_PACK_REUSE;
 static enum {
 	WRITE_BITMAP_FALSE = 0,
 	WRITE_BITMAP_QUIET,
@@ -288,11 +296,13 @@
 	void *buf, *base_buf, *delta_buf;
 	enum object_type type;
 
-	buf = read_object_file(&entry->idx.oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &entry->idx.oid, &type,
+				    &size);
 	if (!buf)
 		die(_("unable to read %s"), oid_to_hex(&entry->idx.oid));
-	base_buf = read_object_file(&DELTA(entry)->idx.oid, &type,
-				    &base_size);
+	base_buf = repo_read_object_file(the_repository,
+					 &DELTA(entry)->idx.oid, &type,
+					 &base_size);
 	if (!base_buf)
 		die("unable to read %s",
 		    oid_to_hex(&DELTA(entry)->idx.oid));
@@ -454,7 +464,9 @@
 				       &size, NULL)) != NULL)
 			buf = NULL;
 		else {
-			buf = read_object_file(&entry->idx.oid, &type, &size);
+			buf = repo_read_object_file(the_repository,
+						    &entry->idx.oid, &type,
+						    &size);
 			if (!buf)
 				die(_("unable to read %s"),
 				    oid_to_hex(&entry->idx.oid));
@@ -1004,7 +1016,9 @@
 	return reused_chunks[lo-1].difference;
 }
 
-static void write_reused_pack_one(size_t pos, struct hashfile *out,
+static void write_reused_pack_one(struct packed_git *reuse_packfile,
+				  size_t pos, struct hashfile *out,
+				  off_t pack_start,
 				  struct pack_window **w_curs)
 {
 	off_t offset, next, cur;
@@ -1014,7 +1028,8 @@
 	offset = pack_pos_to_offset(reuse_packfile, pos);
 	next = pack_pos_to_offset(reuse_packfile, pos + 1);
 
-	record_reused_object(offset, offset - hashfile_total(out));
+	record_reused_object(offset,
+			     offset - (hashfile_total(out) - pack_start));
 
 	cur = offset;
 	type = unpack_object_header(reuse_packfile, w_curs, &cur, &size);
@@ -1082,41 +1097,93 @@
 	copy_pack_data(out, reuse_packfile, w_curs, offset, next - offset);
 }
 
-static size_t write_reused_pack_verbatim(struct hashfile *out,
+static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile,
+					 struct hashfile *out,
+					 off_t pack_start,
 					 struct pack_window **w_curs)
 {
-	size_t pos = 0;
+	size_t pos = reuse_packfile->bitmap_pos;
+	size_t end;
 
-	while (pos < reuse_packfile_bitmap->word_alloc &&
-			reuse_packfile_bitmap->words[pos] == (eword_t)~0)
-		pos++;
+	if (pos % BITS_IN_EWORD) {
+		size_t word_pos = (pos / BITS_IN_EWORD);
+		size_t offset = pos % BITS_IN_EWORD;
+		size_t last;
+		eword_t word = reuse_packfile_bitmap->words[word_pos];
 
-	if (pos) {
-		off_t to_write;
+		if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD)
+			last = offset + reuse_packfile->bitmap_nr;
+		else
+			last = BITS_IN_EWORD;
 
-		written = (pos * BITS_IN_EWORD);
-		to_write = pack_pos_to_offset(reuse_packfile, written)
-			- sizeof(struct pack_header);
+		for (; offset < last; offset++) {
+			if (word >> offset == 0)
+				return word_pos;
+			if (!bitmap_get(reuse_packfile_bitmap,
+					word_pos * BITS_IN_EWORD + offset))
+				return word_pos;
+		}
+
+		pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD);
+	}
+
+	/*
+	 * Now we're going to copy as many whole eword_t's as possible.
+	 * "end" is the index of the last whole eword_t we copy, but
+	 * there may be additional bits to process. Those are handled
+	 * individually by write_reused_pack().
+	 *
+	 * Begin by advancing to the first word boundary in range of the
+	 * bit positions occupied by objects in "reuse_packfile". Then
+	 * pick the last word boundary in the same range. If we have at
+	 * least one word's worth of bits to process, continue on.
+	 */
+	end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr;
+	if (end % BITS_IN_EWORD)
+		end -= end % BITS_IN_EWORD;
+	if (pos >= end)
+		return reuse_packfile->bitmap_pos / BITS_IN_EWORD;
+
+	while (pos < end &&
+	       reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0)
+		pos += BITS_IN_EWORD;
+
+	if (pos > end)
+		pos = end;
+
+	if (reuse_packfile->bitmap_pos < pos) {
+		off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0);
+		off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p,
+							pos - reuse_packfile->bitmap_pos);
+
+		written += pos - reuse_packfile->bitmap_pos;
 
 		/* We're recording one chunk, not one object. */
-		record_reused_object(sizeof(struct pack_header), 0);
+		record_reused_object(pack_start_off,
+				     pack_start_off - (hashfile_total(out) - pack_start));
 		hashflush(out);
-		copy_pack_data(out, reuse_packfile, w_curs,
-			sizeof(struct pack_header), to_write);
+		copy_pack_data(out, reuse_packfile->p, w_curs,
+			pack_start_off, pack_end_off - pack_start_off);
 
 		display_progress(progress_state, written);
 	}
-	return pos;
+	if (pos % BITS_IN_EWORD)
+		BUG("attempted to jump past a word boundary to %"PRIuMAX,
+		    (uintmax_t)pos);
+	return pos / BITS_IN_EWORD;
 }
 
-static void write_reused_pack(struct hashfile *f)
+static void write_reused_pack(struct bitmapped_pack *reuse_packfile,
+			      struct hashfile *f)
 {
-	size_t i = 0;
+	size_t i = reuse_packfile->bitmap_pos / BITS_IN_EWORD;
 	uint32_t offset;
+	off_t pack_start = hashfile_total(f) - sizeof(struct pack_header);
 	struct pack_window *w_curs = NULL;
 
 	if (allow_ofs_delta)
-		i = write_reused_pack_verbatim(f, &w_curs);
+		i = write_reused_pack_verbatim(reuse_packfile, f, pack_start,
+					       &w_curs);
 
 	for (; i < reuse_packfile_bitmap->word_alloc; ++i) {
 		eword_t word = reuse_packfile_bitmap->words[i];
@@ -1127,16 +1194,23 @@
 				break;
 
 			offset += ewah_bit_ctz64(word >> offset);
+			if (pos + offset < reuse_packfile->bitmap_pos)
+				continue;
+			if (pos + offset >= reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr)
+				goto done;
 			/*
 			 * Can use bit positions directly, even for MIDX
 			 * bitmaps. See comment in try_partial_reuse()
 			 * for why.
 			 */
-			write_reused_pack_one(pos + offset, f, &w_curs);
+			write_reused_pack_one(reuse_packfile->p,
+					      pos + offset - reuse_packfile->bitmap_pos,
+					      f, pack_start, &w_curs);
 			display_progress(progress_state, ++written);
 		}
 	}
 
+done:
 	unuse_pack(&w_curs);
 }
 
@@ -1188,9 +1262,14 @@
 
 		offset = write_pack_header(f, nr_remaining);
 
-		if (reuse_packfile) {
+		if (reuse_packfiles_nr) {
 			assert(pack_to_stdout);
-			write_reused_pack(f);
+			for (j = 0; j < reuse_packfiles_nr; j++) {
+				reused_chunks_nr = 0;
+				write_reused_pack(&reuse_packfiles[j], f);
+				if (reused_chunks_nr)
+					reuse_packfiles_used_nr++;
+			}
 			offset = hashfile_total(f);
 		}
 
@@ -1320,7 +1399,7 @@
 
 	if (!check)
 		check = attr_check_initl("delta", NULL);
-	git_check_attr(the_repository->index, NULL, path, check);
+	git_check_attr(the_repository->index, path, check);
 	if (ATTR_FALSE(check->items[0].value))
 		return 1;
 	return 0;
@@ -1590,7 +1669,7 @@
 
 static int add_object_entry_from_bitmap(const struct object_id *oid,
 					enum object_type type,
-					int flags, uint32_t name_hash,
+					int flags UNUSED, uint32_t name_hash,
 					struct packed_git *pack, off_t offset)
 {
 	display_progress(progress_state, ++nr_seen);
@@ -1665,7 +1744,7 @@
 	/* Did not find one.  Either we got a bogus request or
 	 * we need to read and perhaps cache.
 	 */
-	data = read_object_file(oid, &type, &size);
+	data = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!data)
 		return NULL;
 	if (type != OBJ_TREE) {
@@ -1747,7 +1826,8 @@
 			tree = pbase_tree_get(&entry.oid);
 			if (!tree)
 				return;
-			init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+			init_tree_desc(&sub, &tree->oid,
+				       tree->tree_data, tree->tree_size);
 
 			add_pbase_object(&sub, down, downlen, fullname);
 			pbase_tree_put(tree);
@@ -1807,7 +1887,8 @@
 		}
 		else {
 			struct tree_desc tree;
-			init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+			init_tree_desc(&tree, &it->pcache.oid,
+				       it->pcache.tree_data, it->pcache.tree_size);
 			add_pbase_object(&tree, name, cmplen, name);
 		}
 	}
@@ -2074,7 +2155,7 @@
 
 	if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
 				     OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) {
-		if (has_promisor_remote()) {
+		if (repo_has_promisor_remote(the_repository)) {
 			prefetch_to_pack(object_index);
 			if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
 						     OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0)
@@ -2525,7 +2606,9 @@
 	/* Load data if not already done */
 	if (!trg->data) {
 		packing_data_lock(&to_pack);
-		trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz);
+		trg->data = repo_read_object_file(the_repository,
+						  &trg_entry->idx.oid, &type,
+						  &sz);
 		packing_data_unlock(&to_pack);
 		if (!trg->data)
 			die(_("object %s cannot be read"),
@@ -2538,7 +2621,9 @@
 	}
 	if (!src->data) {
 		packing_data_lock(&to_pack);
-		src->data = read_object_file(&src_entry->idx.oid, &type, &sz);
+		src->data = repo_read_object_file(the_repository,
+						  &src_entry->idx.oid, &type,
+						  &sz);
 		packing_data_unlock(&to_pack);
 		if (!src->data) {
 			if (src_entry->preferred_base) {
@@ -3120,26 +3205,27 @@
 	free(delta_list);
 }
 
-static int git_pack_config(const char *k, const char *v, void *cb)
+static int git_pack_config(const char *k, const char *v,
+			   const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(k, "pack.window")) {
-		window = git_config_int(k, v);
+		window = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.windowmemory")) {
-		window_memory_limit = git_config_ulong(k, v);
+		window_memory_limit = git_config_ulong(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.depth")) {
-		depth = git_config_int(k, v);
+		depth = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.deltacachesize")) {
-		max_delta_cache_size = git_config_int(k, v);
+		max_delta_cache_size = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.deltacachelimit")) {
-		cache_max_small_delta_size = git_config_int(k, v);
+		cache_max_small_delta_size = git_config_int(k, v, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(k, "pack.writebitmaphashcache")) {
@@ -3161,11 +3247,23 @@
 		return 0;
 	}
 	if (!strcmp(k, "pack.allowpackreuse")) {
-		allow_pack_reuse = git_config_bool(k, v);
+		int res = git_parse_maybe_bool_text(v);
+		if (res < 0) {
+			if (!strcasecmp(v, "single"))
+				allow_pack_reuse = SINGLE_PACK_REUSE;
+			else if (!strcasecmp(v, "multi"))
+				allow_pack_reuse = MULTI_PACK_REUSE;
+			else
+				die(_("invalid pack.allowPackReuse value: '%s'"), v);
+		} else if (res) {
+			allow_pack_reuse = SINGLE_PACK_REUSE;
+		} else {
+			allow_pack_reuse = NO_PACK_REUSE;
+		}
 		return 0;
 	}
 	if (!strcmp(k, "pack.threads")) {
-		delta_search_threads = git_config_int(k, v);
+		delta_search_threads = git_config_int(k, v, ctx->kvi);
 		if (delta_search_threads < 0)
 			die(_("invalid number of threads specified (%d)"),
 			    delta_search_threads);
@@ -3176,7 +3274,7 @@
 		return 0;
 	}
 	if (!strcmp(k, "pack.indexversion")) {
-		pack_idx_opts.version = git_config_int(k, v);
+		pack_idx_opts.version = git_config_int(k, v, ctx->kvi);
 		if (pack_idx_opts.version > 2)
 			die(_("bad pack.indexVersion=%"PRIu32),
 			    pack_idx_opts.version);
@@ -3190,7 +3288,7 @@
 		return 0;
 	}
 	if (!strcmp(k, "uploadpack.blobpackfileuri")) {
-		struct configured_exclusion *ex = xmalloc(sizeof(*ex));
+		struct configured_exclusion *ex;
 		const char *oid_end, *pack_end;
 		/*
 		 * Stores the pack hash. This is not a true object ID, but is
@@ -3198,6 +3296,10 @@
 		 */
 		struct object_id pack_hash;
 
+		if (!v)
+			return config_error_nonbool(k);
+
+		ex = xmalloc(sizeof(*ex));
 		if (parse_oid_hex(v, &ex->e.oid, &oid_end) ||
 		    *oid_end != ' ' ||
 		    parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) ||
@@ -3212,7 +3314,7 @@
 		ex->uri = xstrdup(pack_end + 1);
 		oidmap_put(&configured_exclusions, ex);
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 /* Counters for trace2 output when in --stdin-packs mode. */
@@ -3260,13 +3362,14 @@
 	return 0;
 }
 
-static void show_commit_pack_hint(struct commit *commit, void *_data)
+static void show_commit_pack_hint(struct commit *commit UNUSED,
+				  void *data UNUSED)
 {
 	/* nothing to do; commits don't have a namehash */
 }
 
 static void show_object_pack_hint(struct object *object, const char *name,
-				  void *_data)
+				  void *data UNUSED)
 {
 	struct object_entry *oe = packlist_find(&to_pack, &object->oid);
 	if (!oe)
@@ -3344,16 +3447,16 @@
 	}
 
 	string_list_sort(&include_packs);
+	string_list_remove_duplicates(&include_packs, 0);
 	string_list_sort(&exclude_packs);
+	string_list_remove_duplicates(&exclude_packs, 0);
 
 	for (p = get_all_packs(the_repository); p; p = p->next) {
 		const char *pack_name = pack_basename(p);
 
-		item = string_list_lookup(&include_packs, pack_name);
-		if (!item)
-			item = string_list_lookup(&exclude_packs, pack_name);
-
-		if (item)
+		if ((item = string_list_lookup(&include_packs, pack_name)))
+			item->util = p;
+		if ((item = string_list_lookup(&exclude_packs, pack_name)))
 			item->util = p;
 	}
 
@@ -3464,7 +3567,7 @@
 	return;
 }
 
-static void show_cruft_object(struct object *obj, const char *name, void *data)
+static void show_cruft_object(struct object *obj, const char *name, void *data UNUSED)
 {
 	/*
 	 * if we did not record it earlier, it's at least as old as our
@@ -3484,7 +3587,7 @@
 	show_cruft_object((struct object*)commit, NULL, data);
 }
 
-static int cruft_include_check_obj(struct object *obj, void *data)
+static int cruft_include_check_obj(struct object *obj, void *data UNUSED)
 {
 	return !has_object_kept_pack(&obj->oid, IN_CORE_KEEP_PACKS);
 }
@@ -3588,7 +3691,6 @@
 			string_list_append(&discard_packs, buf.buf + 1);
 		else
 			string_list_append(&fresh_packs, buf.buf);
-		strbuf_reset(&buf);
 	}
 
 	string_list_sort(&discard_packs);
@@ -3663,7 +3765,7 @@
 	}
 }
 
-static void show_commit(struct commit *commit, void *data)
+static void show_commit(struct commit *commit, void *data UNUSED)
 {
 	add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
 
@@ -3674,7 +3776,8 @@
 		propagate_island_marks(commit);
 }
 
-static void show_object(struct object *obj, const char *name, void *data)
+static void show_object(struct object *obj, const char *name,
+			void *data UNUSED)
 {
 	add_preferred_base_object(name);
 	add_object_entry(&obj->oid, obj->type, name, 0);
@@ -3723,7 +3826,7 @@
 	show_object(obj, name, data);
 }
 
-static int option_parse_missing_action(const struct option *opt,
+static int option_parse_missing_action(const struct option *opt UNUSED,
 				       const char *arg, int unset)
 {
 	assert(arg);
@@ -3761,7 +3864,7 @@
 static int add_object_in_unpacked_pack(const struct object_id *oid,
 				       struct packed_git *pack,
 				       uint32_t pos,
-				       void *_data)
+				       void *data UNUSED)
 {
 	if (cruft) {
 		off_t offset;
@@ -3795,7 +3898,7 @@
 }
 
 static int add_loose_object(const struct object_id *oid, const char *path,
-			    void *data)
+			    void *data UNUSED)
 {
 	enum object_type type = oid_object_info(the_repository, oid, NULL);
 
@@ -3915,7 +4018,7 @@
  */
 static int pack_options_allow_reuse(void)
 {
-	return allow_pack_reuse &&
+	return allow_pack_reuse != NO_PACK_REUSE &&
 	       pack_to_stdout &&
 	       !ignore_packed_keep_on_disk &&
 	       !ignore_packed_keep_in_core &&
@@ -3928,13 +4031,18 @@
 	if (!(bitmap_git = prepare_bitmap_walk(revs, 0)))
 		return -1;
 
-	if (pack_options_allow_reuse() &&
-	    !reuse_partial_packfile_from_bitmap(
-			bitmap_git,
-			&reuse_packfile,
-			&reuse_packfile_objects,
-			&reuse_packfile_bitmap)) {
-		assert(reuse_packfile_objects);
+	if (pack_options_allow_reuse())
+		reuse_partial_packfile_from_bitmap(bitmap_git,
+						   &reuse_packfiles,
+						   &reuse_packfiles_nr,
+						   &reuse_packfile_bitmap,
+						   allow_pack_reuse == MULTI_PACK_REUSE);
+
+	if (reuse_packfiles) {
+		reuse_packfile_objects = bitmap_popcount(reuse_packfile_bitmap);
+		if (!reuse_packfile_objects)
+			BUG("expected non-empty reuse bitmap");
+
 		nr_result += reuse_packfile_objects;
 		nr_seen += reuse_packfile_objects;
 		display_progress(progress_state, nr_seen);
@@ -3946,13 +4054,13 @@
 }
 
 static void record_recent_object(struct object *obj,
-				 const char *name,
-				 void *data)
+				 const char *name UNUSED,
+				 void *data UNUSED)
 {
 	oid_array_append(&recent_objects, &obj->oid);
 }
 
-static void record_recent_commit(struct commit *commit, void *data)
+static void record_recent_commit(struct commit *commit, void *data UNUSED)
 {
 	oid_array_append(&recent_objects, &commit->object.oid);
 }
@@ -4101,25 +4209,40 @@
 	}
 }
 
+static int option_parse_quiet(const struct option *opt, const char *arg,
+			      int unset)
+{
+	int *val = opt->value;
+
+	BUG_ON_OPT_ARG(arg);
+
+	if (!unset)
+		*val = 0;
+	else if (!*val)
+		*val = 1;
+	return 0;
+}
+
 static int option_parse_index_version(const struct option *opt,
 				      const char *arg, int unset)
 {
+	struct pack_idx_option *popts = opt->value;
 	char *c;
 	const char *val = arg;
 
 	BUG_ON_OPT_NEG(unset);
 
-	pack_idx_opts.version = strtoul(val, &c, 10);
-	if (pack_idx_opts.version > 2)
+	popts->version = strtoul(val, &c, 10);
+	if (popts->version > 2)
 		die(_("unsupported index version %s"), val);
 	if (*c == ',' && c[1])
-		pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
-	if (*c || pack_idx_opts.off32_limit & 0x80000000)
+		popts->off32_limit = strtoul(c+1, &c, 0);
+	if (*c || popts->off32_limit & 0x80000000)
 		die(_("bad index version '%s'"), val);
 	return 0;
 }
 
-static int option_parse_unpack_unreachable(const struct option *opt,
+static int option_parse_unpack_unreachable(const struct option *opt UNUSED,
 					   const char *arg, int unset)
 {
 	if (unset) {
@@ -4134,7 +4257,7 @@
 	return 0;
 }
 
-static int option_parse_cruft_expiration(const struct option *opt,
+static int option_parse_cruft_expiration(const struct option *opt UNUSED,
 					 const char *arg, int unset)
 {
 	if (unset) {
@@ -4162,8 +4285,9 @@
 		LIST_OBJECTS_FILTER_INIT;
 
 	struct option pack_objects_options[] = {
-		OPT_SET_INT('q', "quiet", &progress,
-			    N_("do not show progress meter"), 0),
+		OPT_CALLBACK_F('q', "quiet", &progress, NULL,
+			       N_("do not show progress meter"),
+			       PARSE_OPT_NOARG, option_parse_quiet),
 		OPT_SET_INT(0, "progress", &progress,
 			    N_("show progress meter"), 1),
 		OPT_SET_INT(0, "all-progress", &progress,
@@ -4171,7 +4295,7 @@
 		OPT_BOOL(0, "all-progress-implied",
 			 &all_progress_implied,
 			 N_("similar to --all-progress when progress meter is shown")),
-		OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"),
+		OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"),
 		  N_("write the pack index file in the specified idx format version"),
 		  PARSE_OPT_NONEG, option_parse_index_version),
 		OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
@@ -4239,8 +4363,8 @@
 				N_("ignore this pack")),
 		OPT_INTEGER(0, "compression", &pack_compression_level,
 			    N_("pack compression level")),
-		OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
-			    N_("do not hide commits by grafts"), 0),
+		OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents,
+			 N_("do not hide commits by grafts")),
 		OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
 			 N_("use a bitmap index if available to speed up counting objects")),
 		OPT_SET_INT(0, "write-bitmap-index", &write_bitmap_index,
@@ -4267,19 +4391,22 @@
 	if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS))
 		BUG("too many dfs states, increase OE_DFS_STATE_BITS");
 
-	read_replace_refs = 0;
+	disable_replace_refs();
 
 	sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1);
 	if (the_repository->gitdir) {
 		prepare_repo_settings(the_repository);
 		if (sparse < 0)
 			sparse = the_repository->settings.pack_use_sparse;
+		if (the_repository->settings.pack_use_multi_pack_reuse)
+			allow_pack_reuse = MULTI_PACK_REUSE;
 	}
 
 	reset_pack_idx_option(&pack_idx_opts);
+	pack_idx_opts.flags |= WRITE_REV;
 	git_config(git_pack_config, NULL);
-	if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
-		pack_idx_opts.flags |= WRITE_REV;
+	if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0))
+		pack_idx_opts.flags &= ~WRITE_REV;
 
 	progress = isatty(2);
 	argc = parse_options(argc, argv, prefix, pack_objects_options,
@@ -4353,7 +4480,7 @@
 
 	if (!HAVE_THREADS && delta_search_threads != 1)
 		warning(_("no threads support, ignoring --threads"));
-	if (!pack_to_stdout && !pack_size_limit && !cruft)
+	if (!pack_to_stdout && !pack_size_limit)
 		pack_size_limit = pack_size_limit_cfg;
 	if (pack_to_stdout && pack_size_limit)
 		die(_("--max-pack-size cannot be used to build a pack for transfer"));
@@ -4370,12 +4497,8 @@
 	if (!rev_list_all || !rev_list_reflog || !rev_list_index)
 		unpack_unreachable_expiration = 0;
 
-	if (filter_options.choice) {
-		if (!pack_to_stdout)
-			die(_("cannot use --filter without --stdout"));
-		if (stdin_packs)
-			die(_("cannot use --filter with --stdin-packs"));
-	}
+	if (stdin_packs && filter_options.choice)
+		die(_("cannot use --filter with --stdin-packs"));
 
 	if (stdin_packs && use_internal_rev_list)
 		die(_("cannot use internal rev list with --stdin-packs"));
@@ -4385,8 +4508,6 @@
 			die(_("cannot use internal rev list with --cruft"));
 		if (stdin_packs)
 			die(_("cannot use --stdin-packs with --cruft"));
-		if (pack_size_limit)
-			die(_("cannot use --max-pack-size with --cruft"));
 	}
 
 	/*
@@ -4491,11 +4612,20 @@
 		fprintf_ln(stderr,
 			   _("Total %"PRIu32" (delta %"PRIu32"),"
 			     " reused %"PRIu32" (delta %"PRIu32"),"
-			     " pack-reused %"PRIu32),
+			     " pack-reused %"PRIu32" (from %"PRIuMAX")"),
 			   written, written_delta, reused, reused_delta,
-			   reuse_packfile_objects);
+			   reuse_packfile_objects,
+			   (uintmax_t)reuse_packfiles_used_nr);
+
+	trace2_data_intmax("pack-objects", the_repository, "written", written);
+	trace2_data_intmax("pack-objects", the_repository, "written/delta", written_delta);
+	trace2_data_intmax("pack-objects", the_repository, "reused", reused);
+	trace2_data_intmax("pack-objects", the_repository, "reused/delta", reused_delta);
+	trace2_data_intmax("pack-objects", the_repository, "pack-reused", reuse_packfile_objects);
+	trace2_data_intmax("pack-objects", the_repository, "packs-reused", reuse_packfiles_used_nr);
 
 cleanup:
+	clear_packing_data(&to_pack);
 	list_objects_filter_release(&filter_options);
 	strvec_clear(&rp);
 
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index ecd49ca..4c735ba 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -7,9 +7,11 @@
 */
 
 #include "builtin.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 #define BLKSIZE 512
 
@@ -557,7 +559,7 @@
 	}
 }
 
-int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED)
 {
 	int i;
 	int i_still_use_this = 0;
@@ -603,6 +605,7 @@
 			"option, '--i-still-use-this', on the command line\n"
 			"and let us know you still use it by sending an e-mail\n"
 			"to <git@vger.kernel.org>.  Thanks.\n"), stderr);
+		die(_("refusing to run without --i-still-use-this"));
 	}
 
 	if (load_all_packs)
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index 27c2ca0..db40825 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -1,24 +1,57 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "repository.h"
+#include "revision.h"
 
 static char const * const pack_refs_usage[] = {
-	N_("git pack-refs [--all] [--no-prune]"),
+	N_("git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]"),
 	NULL
 };
 
 int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 {
-	unsigned int flags = PACK_REFS_PRUNE;
+	struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+	struct string_list included_refs = STRING_LIST_INIT_NODUP;
+	struct pack_refs_opts pack_refs_opts = {
+		.exclusions = &excludes,
+		.includes = &included_refs,
+		.flags = PACK_REFS_PRUNE,
+	};
+	struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
+	struct string_list_item *item;
+	int pack_all = 0;
+	int ret;
+
 	struct option opts[] = {
-		OPT_BIT(0, "all",   &flags, N_("pack everything"), PACK_REFS_ALL),
-		OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+		OPT_BOOL(0, "all",   &pack_all, N_("pack everything")),
+		OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+		OPT_BIT(0, "auto", &pack_refs_opts.flags, N_("auto-pack refs as needed"), PACK_REFS_AUTO),
+		OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
+			N_("references to include")),
+		OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
+			N_("references to exclude")),
 		OPT_END(),
 	};
 	git_config(git_default_config, NULL);
 	if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
 		usage_with_options(pack_refs_usage, opts);
-	return refs_pack_refs(get_main_ref_store(the_repository), flags);
+
+	for_each_string_list_item(item, &option_excluded_refs)
+		add_ref_exclusion(pack_refs_opts.exclusions, item->string);
+
+	if (pack_all)
+		string_list_append(pack_refs_opts.includes, "*");
+
+	if (!pack_refs_opts.includes->nr)
+		string_list_append(pack_refs_opts.includes, "refs/tags/*");
+
+	ret = refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
+
+	clear_ref_exclusions(&excludes);
+	string_list_clear(&included_refs, 0);
+	string_list_clear(&option_excluded_refs, 0);
+	return ret;
 }
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index f840fbf..3894d2b 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -1,7 +1,9 @@
-#include "cache.h"
 #include "builtin.h"
 #include "config.h"
 #include "diff.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "parse-options.h"
 
 static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
@@ -194,7 +196,8 @@
 	int verbatim;
 };
 
-static int git_patch_id_config(const char *var, const char *value, void *cb)
+static int git_patch_id_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	struct patch_id_opts *opts = cb;
 
@@ -207,7 +210,7 @@
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_patch_id(int argc, const char **argv, const char *prefix)
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index da3273a..ca3578e 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "prune-packed.h"
 
diff --git a/builtin/prune.c b/builtin/prune.c
index 2719220..57fe314 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -1,13 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
 #include "commit.h"
 #include "diff.h"
+#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
-#include "builtin.h"
 #include "reachable.h"
 #include "parse-options.h"
+#include "path.h"
 #include "progress.h"
 #include "prune-packed.h"
-#include "object-store.h"
+#include "replace-object.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "shallow.h"
 
 static const char * const prune_usage[] = {
@@ -98,7 +105,8 @@
 	return 0;
 }
 
-static int prune_cruft(const char *basename, const char *path, void *data)
+static int prune_cruft(const char *basename, const char *path,
+		       void *data UNUSED)
 {
 	if (starts_with(basename, "tmp_obj_"))
 		prune_tmp_file(path);
@@ -107,7 +115,8 @@
 	return 0;
 }
 
-static int prune_subdir(unsigned int nr, const char *path, void *data)
+static int prune_subdir(unsigned int nr UNUSED, const char *path,
+			void *data UNUSED)
 {
 	if (!show_only)
 		rmdir(path);
@@ -156,7 +165,7 @@
 
 	expire = TIME_MAX;
 	save_commit_buffer = 0;
-	read_replace_refs = 0;
+	disable_replace_refs();
 	repo_init_revisions(the_repository, &revs, prefix);
 
 	argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
@@ -168,7 +177,7 @@
 		struct object_id oid;
 		const char *name = *argv++;
 
-		if (!get_oid(name, &oid)) {
+		if (!repo_get_oid(the_repository, name, &oid)) {
 			struct object *object = parse_object_or_die(&oid,
 								    name);
 			add_pending_object(&revs, object, "");
diff --git a/builtin/pull.c b/builtin/pull.c
index 1ab4de0..72cbb76 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -6,27 +6,28 @@
  * Fetch one or more remote refs and merge it/them into the current HEAD.
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "advice.h"
+#include "config.h"
+#include "gettext.h"
+#include "hex.h"
+#include "merge.h"
+#include "object-name.h"
 #include "parse-options.h"
-#include "exec-cmd.h"
 #include "run-command.h"
 #include "oid-array.h"
 #include "remote.h"
 #include "dir.h"
+#include "path.h"
+#include "read-cache-ll.h"
 #include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
-#include "revision.h"
 #include "submodule.h"
 #include "submodule-config.h"
-#include "tempfile.h"
-#include "lockfile.h"
 #include "wt-status.h"
 #include "commit-reach.h"
 #include "sequencer.h"
-#include "packfile.h"
 
 /**
  * Parses the value of --rebase. If value is a false value, returns
@@ -357,10 +358,9 @@
 /**
  * Read config variables.
  */
-static int git_pull_config(const char *var, const char *value, void *cb)
+static int git_pull_config(const char *var, const char *value,
+			   const struct config_context *ctx, void *cb)
 {
-	int status;
-
 	if (!strcmp(var, "rebase.autostash")) {
 		config_autostash = git_config_bool(var, value);
 		return 0;
@@ -372,11 +372,7 @@
 		check_trust_level = 0;
 	}
 
-	status = git_gpg_config(var, value, cb);
-	if (status)
-		return status;
-
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /**
@@ -819,7 +815,7 @@
 		const struct object_id *merge_head,
 		const struct object_id *fork_point)
 {
-	struct commit_list *revs = NULL, *result;
+	struct commit_list *revs = NULL, *result = NULL;
 
 	commit_list_insert(lookup_commit_reference(the_repository, curr_head),
 			   &revs);
@@ -829,7 +825,8 @@
 		commit_list_insert(lookup_commit_reference(the_repository, fork_point),
 				   &revs);
 
-	result = get_octopus_merge_bases(revs);
+	if (get_octopus_merge_bases(revs, &result) < 0)
+		exit(128);
 	free_commit_list(revs);
 	reduce_heads_replace(&result);
 
@@ -930,6 +927,8 @@
 	merge_head = lookup_commit_reference(the_repository, orig_merge_head);
 	ret = repo_is_descendant_of(the_repository, merge_head, list);
 	free_commit_list(list);
+	if (ret < 0)
+		exit(128);
 	return ret;
 }
 
@@ -954,6 +953,8 @@
 		commit_list_insert(theirs, &list);
 		ok = repo_is_descendant_of(the_repository, ours, list);
 		free_commit_list(list);
+		if (ok < 0)
+			exit(128);
 		if (!ok)
 			return 0;
 	}
@@ -1036,7 +1037,7 @@
 	if (file_exists(git_path_merge_head(the_repository)))
 		die_conclude_merge();
 
-	if (get_oid("HEAD", &orig_head))
+	if (repo_get_oid(the_repository, "HEAD", &orig_head))
 		oidclr(&orig_head);
 
 	if (opt_rebase) {
@@ -1049,7 +1050,7 @@
 		if (!opt_autostash)
 			require_clean_work_tree(the_repository,
 				N_("pull with rebase"),
-				_("please commit or stash them."), 1, 0);
+				_("Please commit or stash them."), 1, 0);
 
 		if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
 			oidclr(&rebase_fork_point);
@@ -1061,7 +1062,7 @@
 	if (opt_dry_run)
 		return 0;
 
-	if (get_oid("HEAD", &curr_head))
+	if (repo_get_oid(the_repository, "HEAD", &curr_head))
 		oidclr(&curr_head);
 
 	if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
diff --git a/builtin/push.c b/builtin/push.c
index 8f7d326..2fbb31c 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -1,19 +1,23 @@
 /*
  * "git push"
  */
-#include "cache.h"
+#include "builtin.h"
+#include "advice.h"
 #include "branch.h"
 #include "config.h"
-#include "refs.h"
+#include "environment.h"
+#include "gettext.h"
 #include "refspec.h"
 #include "run-command.h"
-#include "builtin.h"
 #include "remote.h"
 #include "transport.h"
 #include "parse-options.h"
+#include "pkt-line.h"
+#include "repository.h"
 #include "submodule.h"
 #include "submodule-config.h"
 #include "send-pack.h"
+#include "trace2.h"
 #include "color.h"
 
 static const char * const push_usage[] = {
@@ -296,21 +300,21 @@
 
 static const char message_advice_pull_before_push[] =
 	N_("Updates were rejected because the tip of your current branch is behind\n"
-	   "its remote counterpart. Integrate the remote changes (e.g.\n"
-	   "'git pull ...') before pushing again.\n"
+	   "its remote counterpart. If you want to integrate the remote changes,\n"
+	   "use 'git pull' before pushing again.\n"
 	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_checkout_pull_push[] =
 	N_("Updates were rejected because a pushed branch tip is behind its remote\n"
-	   "counterpart. Check out this branch and integrate the remote changes\n"
-	   "(e.g. 'git pull ...') before pushing again.\n"
+	   "counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+	   "before pushing again.\n"
 	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_fetch_first[] =
-	N_("Updates were rejected because the remote contains work that you do\n"
-	   "not have locally. This is usually caused by another repository pushing\n"
-	   "to the same ref. You may want to first integrate the remote changes\n"
-	   "(e.g., 'git pull ...') before pushing again.\n"
+	N_("Updates were rejected because the remote contains work that you do not\n"
+	   "have locally. This is usually caused by another repository pushing to\n"
+	   "the same ref. If you want to integrate the remote changes, use\n"
+	   "'git pull' before pushing again.\n"
 	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_already_exists[] =
@@ -322,10 +326,10 @@
 	   "without using the '--force' option.\n");
 
 static const char message_advice_ref_needs_update[] =
-	N_("Updates were rejected because the tip of the remote-tracking\n"
-	   "branch has been updated since the last checkout. You may want\n"
-	   "to integrate those changes locally (e.g., 'git pull ...')\n"
-	   "before forcing an update.\n");
+	N_("Updates were rejected because the tip of the remote-tracking branch has\n"
+	   "been updated since the last checkout. If you want to integrate the\n"
+	   "remote changes, use 'git pull' before pushing again.\n"
+	   "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static void advise_pull_before_push(void)
 {
@@ -387,7 +391,7 @@
 	if (!is_empty_cas(&cas)) {
 		if (!transport->smart_options)
 			die("underlying transport does not support --%s option",
-			    CAS_OPT_NAME);
+			    "force-with-lease");
 		transport->smart_options->cas = &cas;
 	}
 
@@ -504,15 +508,11 @@
 }
 
 
-static int git_push_config(const char *k, const char *v, void *cb)
+static int git_push_config(const char *k, const char *v,
+			   const struct config_context *ctx, void *cb)
 {
 	const char *slot_name;
 	int *flags = cb;
-	int status;
-
-	status = git_gpg_config(k, v, NULL);
-	if (status)
-		return status;
 
 	if (!strcmp(k, "push.followtags")) {
 		if (git_config_bool(k, v))
@@ -525,26 +525,21 @@
 			*flags |= TRANSPORT_PUSH_AUTO_UPSTREAM;
 		return 0;
 	} else if (!strcmp(k, "push.gpgsign")) {
-		const char *value;
-		if (!git_config_get_value("push.gpgsign", &value)) {
-			switch (git_parse_maybe_bool(value)) {
-			case 0:
-				set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
-				break;
-			case 1:
-				set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
-				break;
-			default:
-				if (value && !strcasecmp(value, "if-asked"))
-					set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
-				else
-					return error(_("invalid value for '%s'"), k);
-			}
+		switch (git_parse_maybe_bool(v)) {
+		case 0:
+			set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
+			break;
+		case 1:
+			set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
+			break;
+		default:
+			if (!strcasecmp(v, "if-asked"))
+				set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
+			else
+				return error(_("invalid value for '%s'"), k);
 		}
 	} else if (!strcmp(k, "push.recursesubmodules")) {
-		const char *value;
-		if (!git_config_get_value("push.recursesubmodules", &value))
-			recurse_submodules = parse_push_recurse_submodules_arg(k, value);
+		recurse_submodules = parse_push_recurse_submodules_arg(k, v);
 	} else if (!strcmp(k, "submodule.recurse")) {
 		int val = git_config_bool(k, v) ?
 			RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
@@ -576,7 +571,7 @@
 		return 0;
 	}
 
-	return git_default_config(k, v, NULL);
+	return git_default_config(k, v, ctx, NULL);
 }
 
 int cmd_push(int argc, const char **argv, const char *prefix)
@@ -594,15 +589,16 @@
 	struct option options[] = {
 		OPT__VERBOSITY(&verbosity),
 		OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
-		OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
+		OPT_BIT( 0 , "all", &flags, N_("push all branches"), TRANSPORT_PUSH_ALL),
+		OPT_ALIAS( 0 , "branches", "all"),
 		OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
 			    (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
 		OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")),
-		OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+		OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --branches or --mirror)")),
 		OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
 		OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
 		OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
-		OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+		OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"),
 			       N_("require old value of ref to be at this value"),
 			       PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option),
 		OPT_BIT(0, TRANS_OPT_FORCE_IF_INCLUDES, &flags,
@@ -625,10 +621,7 @@
 				PARSE_OPT_OPTARG, option_parse_push_signed),
 		OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
 		OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
-		OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
-				TRANSPORT_FAMILY_IPV4),
-		OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
-				TRANSPORT_FAMILY_IPV6),
+		OPT_IPVERSION(&family),
 		OPT_END()
 	};
 
@@ -640,8 +633,10 @@
 		: &push_options_config);
 	set_push_cert_flags(&flags, push_cert);
 
-	if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
-		die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
+	die_for_incompatible_opt4(deleterefs, "--delete",
+				  tags, "--tags",
+				  flags & TRANSPORT_PUSH_ALL, "--all/--branches",
+				  flags & TRANSPORT_PUSH_MIRROR, "--mirror");
 	if (deleterefs && argc < 2)
 		die(_("--delete doesn't make sense without any refs"));
 
@@ -678,19 +673,13 @@
 		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
 	if (flags & TRANSPORT_PUSH_ALL) {
-		if (tags)
-			die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags");
 		if (argc >= 2)
 			die(_("--all can't be combined with refspecs"));
 	}
 	if (flags & TRANSPORT_PUSH_MIRROR) {
-		if (tags)
-			die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags");
 		if (argc >= 2)
 			die(_("--mirror can't be combined with refspecs"));
 	}
-	if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
-		die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror");
 
 	if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
 		cas.use_force_if_includes = 1;
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index aecfae1..f02cbac 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -1,9 +1,10 @@
-#include "cache.h"
 #include "builtin.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "range-diff.h"
 #include "config.h"
-#include "revision.h"
+#include "repository.h"
 
 static const char * const builtin_range_diff_usage[] = {
 N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
@@ -65,20 +66,20 @@
 
 	if (dash_dash == 3 ||
 	    (dash_dash < 0 && argc > 2 &&
-	     !get_oid_committish(argv[0], &oid) &&
-	     !get_oid_committish(argv[1], &oid) &&
-	     !get_oid_committish(argv[2], &oid))) {
+	     !repo_get_oid_committish(the_repository, argv[0], &oid) &&
+	     !repo_get_oid_committish(the_repository, argv[1], &oid) &&
+	     !repo_get_oid_committish(the_repository, argv[2], &oid))) {
 		if (dash_dash < 0)
 			; /* already validated arguments */
-		else if (get_oid_committish(argv[0], &oid))
+		else if (repo_get_oid_committish(the_repository, argv[0], &oid))
 			usage_msg_optf(_("not a revision: '%s'"),
 				       builtin_range_diff_usage, options,
 				       argv[0]);
-		else if (get_oid_committish(argv[1], &oid))
+		else if (repo_get_oid_committish(the_repository, argv[1], &oid))
 			usage_msg_optf(_("not a revision: '%s'"),
 				       builtin_range_diff_usage, options,
 				       argv[1]);
-		else if (get_oid_committish(argv[2], &oid))
+		else if (repo_get_oid_committish(the_repository, argv[2], &oid))
 			usage_msg_optf(_("not a revision: '%s'"),
 				       builtin_range_diff_usage, options,
 				       argv[2]);
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 3ce7541..6f89cec 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -5,20 +5,23 @@
  */
 
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "object.h"
+#include "object-name.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
-#include "builtin.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "resolve-undo.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
 
 static int nr_trees;
 static int read_empty;
@@ -44,7 +47,7 @@
 	NULL
 };
 
-static int index_output_cb(const struct option *opt, const char *arg,
+static int index_output_cb(const struct option *opt UNUSED, const char *arg,
 				 int unset)
 {
 	BUG_ON_OPT_NEG(unset);
@@ -87,9 +90,9 @@
 {
 	int i;
 
-	printf("* %d-way merge\n", o->merge_size);
+	printf("* %d-way merge\n", o->internal.merge_size);
 	debug_stage("index", stages[0], o);
-	for (i = 1; i <= o->merge_size; i++) {
+	for (i = 1; i <= o->internal.merge_size; i++) {
 		char buf[24];
 		xsnprintf(buf, sizeof(buf), "ent#%d", i);
 		debug_stage(buf, stages[i], o);
@@ -97,12 +100,13 @@
 	return 0;
 }
 
-static int git_read_tree_config(const char *var, const char *value, void *cb)
+static int git_read_tree_config(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "submodule.recurse"))
 		return git_default_submodule_config(var, value, cb);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
@@ -144,7 +148,7 @@
 		OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
 		OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
 			 N_("skip applying sparse checkout filter")),
-		OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
+		OPT_BOOL(0, "debug-unpack", &opts.internal.debug_unpack,
 			 N_("debug unpack-trees")),
 		OPT_CALLBACK_F(0, "recurse-submodules", NULL,
 			    "checkout", "control recursive updating of submodules",
@@ -198,7 +202,7 @@
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
 
-		if (get_oid(arg, &oid))
+		if (repo_get_oid(the_repository, arg, &oid))
 			die("Not a valid object name %s", arg);
 		if (list_tree(&oid) < 0)
 			die("failed to unpack tree object %s", arg);
@@ -247,7 +251,7 @@
 			opts.head_idx = 1;
 	}
 
-	if (opts.debug_unpack)
+	if (opts.internal.debug_unpack)
 		opts.fn = debug_merge;
 
 	/* If we're going to prime_cache_tree later, skip cache tree update */
@@ -257,13 +261,14 @@
 	cache_tree_free(&the_index.cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		struct tree *tree = trees[i];
-		parse_tree(tree);
-		init_tree_desc(t+i, tree->buffer, tree->size);
+		if (parse_tree(tree) < 0)
+			return 128;
+		init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size);
 	}
 	if (unpack_trees(nr_trees, t, &opts))
 		return 128;
 
-	if (opts.debug_unpack || opts.dry_run)
+	if (opts.internal.debug_unpack || opts.dry_run)
 		return 0; /* do not write the index out */
 
 	/*
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6635f10..891f284 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -6,18 +6,21 @@
 
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "run-command.h"
-#include "exec-cmd.h"
 #include "strvec.h"
 #include "dir.h"
-#include "packfile.h"
 #include "refs.h"
-#include "quote.h"
 #include "config.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
 #include "lockfile.h"
+#include "object-file.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "commit.h"
 #include "diff.h"
 #include "wt-status.h"
@@ -28,6 +31,7 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 #include "reset.h"
+#include "trace2.h"
 #include "hook.h"
 
 static char const * const builtin_rebase_usage[] = {
@@ -54,7 +58,7 @@
 	EMPTY_UNSPECIFIED = -1,
 	EMPTY_DROP,
 	EMPTY_KEEP,
-	EMPTY_ASK
+	EMPTY_STOP
 };
 
 enum action {
@@ -116,13 +120,15 @@
 	struct string_list exec;
 	int allow_empty_message;
 	int rebase_merges, rebase_cousins;
-	char *strategy, *strategy_opts;
+	char *strategy;
+	struct string_list strategy_opts;
 	struct strbuf git_format_patch_opt;
 	int reschedule_failed_exec;
 	int reapply_cherry_picks;
 	int fork_point;
 	int update_refs;
 	int config_autosquash;
+	int config_rebase_merges;
 	int config_update_refs;
 };
 
@@ -139,9 +145,11 @@
 		.reapply_cherry_picks = -1,             \
 		.allow_empty_message = 1,               \
 		.autosquash = -1,                       \
-		.config_autosquash = -1,                \
+		.rebase_merges = -1,                    \
+		.config_rebase_merges = -1,             \
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
+		.strategy_opts = STRING_LIST_INIT_NODUP,\
 	}
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -175,8 +183,8 @@
 		replay.default_strategy = NULL;
 	}
 
-	if (opts->strategy_opts)
-		parse_strategy_opts(&replay, opts->strategy_opts);
+	for (size_t i = 0; i < opts->strategy_opts.nr; i++)
+		strvec_push(&replay.xopts, opts->strategy_opts.items[i].string);
 
 	if (opts->squash_onto) {
 		oidcpy(&replay.squash_onto, opts->squash_onto);
@@ -196,7 +204,7 @@
 	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 		return error_errno(_("could not read '%s'."), todo_file);
 
-	strbuf_stripspace(&todo_list.buf, 1);
+	strbuf_stripspace(&todo_list.buf, comment_line_str);
 	res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
 	if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
 					    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
@@ -218,13 +226,15 @@
 	*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
 			     oid_to_hex(orig_head));
 
-	shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV);
+	shorthead = repo_find_unique_abbrev(the_repository, orig_head,
+					    DEFAULT_ABBREV);
 
 	if (upstream) {
 		const char *shortrev;
 
-		shortrev = find_unique_abbrev(&base_rev->object.oid,
-					      DEFAULT_ABBREV);
+		shortrev = repo_find_unique_abbrev(the_repository,
+						   &base_rev->object.oid,
+						   DEFAULT_ABBREV);
 
 		*shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
 	} else
@@ -361,20 +371,6 @@
 	return ret;
 }
 
-static void imply_merge(struct rebase_options *opts, const char *option);
-static int parse_opt_keep_empty(const struct option *opt, const char *arg,
-				int unset)
-{
-	struct rebase_options *opts = opt->value;
-
-	BUG_ON_OPT_ARG(arg);
-
-	imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
-	opts->keep_empty = !unset;
-	opts->type = REBASE_MERGE;
-	return 0;
-}
-
 static int is_merge(struct rebase_options *opts)
 {
 	return opts->type == REBASE_MERGE;
@@ -482,24 +478,6 @@
 		opts->gpg_sign_opt = xstrdup(buf.buf);
 	}
 
-	if (file_exists(state_dir_path("strategy", opts))) {
-		strbuf_reset(&buf);
-		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
-				   READ_ONELINER_WARN_MISSING))
-			return -1;
-		free(opts->strategy);
-		opts->strategy = xstrdup(buf.buf);
-	}
-
-	if (file_exists(state_dir_path("strategy_opts", opts))) {
-		strbuf_reset(&buf);
-		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
-				   READ_ONELINER_WARN_MISSING))
-			return -1;
-		free(opts->strategy_opts);
-		opts->strategy_opts = xstrdup(buf.buf);
-	}
-
 	strbuf_release(&buf);
 
 	return 0;
@@ -517,12 +495,6 @@
 		write_file(state_dir_path("quiet", opts), "%s", "");
 	if (opts->flags & REBASE_VERBOSE)
 		write_file(state_dir_path("verbose", opts), "%s", "");
-	if (opts->strategy)
-		write_file(state_dir_path("strategy", opts), "%s",
-			   opts->strategy);
-	if (opts->strategy_opts)
-		write_file(state_dir_path("strategy_opts", opts), "%s",
-			   opts->strategy_opts);
 	if (opts->allow_rerere_autoupdate > 0)
 		write_file(state_dir_path("allow_rerere_autoupdate", opts),
 			   "-%s-rerere-autoupdate",
@@ -543,7 +515,7 @@
 	int ret = 0;
 
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	unlink(git_path_auto_merge(the_repository));
+	delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
 	apply_autostash(state_dir_path("autostash", opts));
 	/*
 	 * We ignore errors in 'git maintenance run --auto', since the
@@ -595,18 +567,10 @@
 	return ret;
 }
 
-static const char *resolvemsg =
-N_("Resolve all conflicts manually, mark them as resolved with\n"
-"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
-"You can instead skip this commit: run \"git rebase --skip\".\n"
-"To abort and get back to the state before \"git rebase\", run "
-"\"git rebase --abort\".");
-
 static int run_am(struct rebase_options *opts)
 {
 	struct child_process am = CHILD_PROCESS_INIT;
 	struct child_process format_patch = CHILD_PROCESS_INIT;
-	struct strbuf revisions = STRBUF_INIT;
 	int status;
 	char *rebased_patches;
 
@@ -616,7 +580,7 @@
 		     opts->reflog_action);
 	if (opts->action == ACTION_CONTINUE) {
 		strvec_push(&am.args, "--resolved");
-		strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+		strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
 		if (opts->gpg_sign_opt)
 			strvec_push(&am.args, opts->gpg_sign_opt);
 		status = run_command(&am);
@@ -627,7 +591,7 @@
 	}
 	if (opts->action == ACTION_SKIP) {
 		strvec_push(&am.args, "--skip");
-		strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+		strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
 		status = run_command(&am);
 		if (status)
 			return status;
@@ -639,13 +603,6 @@
 		return run_command(&am);
 	}
 
-	strbuf_addf(&revisions, "%s...%s",
-		    oid_to_hex(opts->root ?
-			       /* this is now equivalent to !opts->upstream */
-			       &opts->onto->object.oid :
-			       &opts->upstream->object.oid),
-		    oid_to_hex(&opts->orig_head->object.oid));
-
 	rebased_patches = xstrdup(git_path("rebased-patches"));
 	format_patch.out = open(rebased_patches,
 				O_WRONLY | O_CREAT | O_TRUNC, 0666);
@@ -653,20 +610,25 @@
 		status = error_errno(_("could not open '%s' for writing"),
 				     rebased_patches);
 		free(rebased_patches);
-		strvec_clear(&am.args);
+		child_process_clear(&am);
 		return status;
 	}
 
 	format_patch.git_cmd = 1;
 	strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
 		     "--full-index", "--cherry-pick", "--right-only",
-		     "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
+		     "--default-prefix", "--no-renames",
 		     "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
 		     "--no-base", NULL);
 	if (opts->git_format_patch_opt.len)
 		strvec_split(&format_patch.args,
 			     opts->git_format_patch_opt.buf);
-	strvec_push(&format_patch.args, revisions.buf);
+	strvec_pushf(&format_patch.args, "%s...%s",
+		     oid_to_hex(opts->root ?
+				/* this is now equivalent to !opts->upstream */
+				&opts->onto->object.oid :
+				&opts->upstream->object.oid),
+		     oid_to_hex(&opts->orig_head->object.oid));
 	if (opts->restrict_revision)
 		strvec_pushf(&format_patch.args, "^%s",
 			     oid_to_hex(&opts->restrict_revision->object.oid));
@@ -676,7 +638,7 @@
 		struct reset_head_opts ropts = { 0 };
 		unlink(rebased_patches);
 		free(rebased_patches);
-		strvec_clear(&am.args);
+		child_process_clear(&am);
 
 		ropts.oid = &opts->orig_head->object.oid;
 		ropts.branch = opts->head_name;
@@ -689,23 +651,21 @@
 			"As a result, git cannot rebase them."),
 		      opts->revisions);
 
-		strbuf_release(&revisions);
 		return status;
 	}
-	strbuf_release(&revisions);
 
 	am.in = open(rebased_patches, O_RDONLY);
 	if (am.in < 0) {
 		status = error_errno(_("could not open '%s' for reading"),
 				     rebased_patches);
 		free(rebased_patches);
-		strvec_clear(&am.args);
+		child_process_clear(&am);
 		return status;
 	}
 
 	strvec_pushv(&am.args, opts->git_am_opts.v);
 	strvec_push(&am.args, "--rebasing");
-	strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+	strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
 	strvec_push(&am.args, "--patch-format=mboxrd");
 	if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
 		strvec_push(&am.args, "--rerere-autoupdate");
@@ -733,11 +693,8 @@
 
 	if (opts->type == REBASE_MERGE) {
 		/* Run sequencer-based rebase */
-		setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
-		if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+		if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT))
 			setenv("GIT_SEQUENCE_EDITOR", ":", 1);
-			opts->autosquash = 0;
-		}
 		if (opts->gpg_sign_opt) {
 			/* remove the leading "-S" */
 			char *tmp = xstrdup(opts->gpg_sign_opt + 2);
@@ -771,7 +728,18 @@
 	return status ? -1 : 0;
 }
 
-static int rebase_config(const char *var, const char *value, void *data)
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+	if (!strcmp("no-rebase-cousins", value))
+		options->rebase_cousins = 0;
+	else if (!strcmp("rebase-cousins", value))
+		options->rebase_cousins = 1;
+	else
+		die(_("Unknown rebase-merges mode: %s"), value);
+}
+
+static int rebase_config(const char *var, const char *value,
+			 const struct config_context *ctx, void *data)
 {
 	struct rebase_options *opts = data;
 
@@ -800,6 +768,17 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.rebasemerges")) {
+		opts->config_rebase_merges = git_parse_maybe_bool(value);
+		if (opts->config_rebase_merges < 0) {
+			opts->config_rebase_merges = 1;
+			parse_rebase_merges_value(opts, value);
+		} else {
+			opts->rebase_cousins = 0;
+		}
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.updaterefs")) {
 		opts->config_update_refs = git_config_bool(var, value);
 		return 0;
@@ -819,7 +798,7 @@
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
-	return git_default_config(var, value, data);
+	return git_default_config(var, value, ctx, data);
 }
 
 static int checkout_up_to_date(struct rebase_options *options)
@@ -851,7 +830,7 @@
 static int is_linear_history(struct commit *from, struct commit *to)
 {
 	while (to && to != from) {
-		parse_commit(to);
+		repo_parse_commit(the_repository, to);
 		if (!to->parents)
 			return 1;
 		if (to->parents->next)
@@ -880,7 +859,8 @@
 	if (!upstream)
 		goto done;
 
-	merge_bases = get_merge_bases(upstream, head);
+	if (repo_get_merge_bases(the_repository, upstream, head, &merge_bases) < 0)
+		exit(128);
 	if (!merge_bases || merge_bases->next)
 		goto done;
 
@@ -899,7 +879,9 @@
 {
 	struct commit_list *merge_bases = NULL;
 
-	merge_bases = get_merge_bases(options->onto, options->orig_head);
+	if (repo_get_merge_bases(the_repository, options->onto,
+				 options->orig_head, &merge_bases) < 0)
+		exit(128);
 	if (!merge_bases || merge_bases->next)
 		oidcpy(branch_base, null_oid());
 	else
@@ -963,10 +945,26 @@
 		return EMPTY_DROP;
 	else if (!strcasecmp(value, "keep"))
 		return EMPTY_KEEP;
-	else if (!strcasecmp(value, "ask"))
-		return EMPTY_ASK;
+	else if (!strcasecmp(value, "stop"))
+		return EMPTY_STOP;
+	else if (!strcasecmp(value, "ask")) {
+		warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+		return EMPTY_STOP;
+	}
 
-	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
+}
+
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+				int unset)
+{
+	struct rebase_options *opts = opt->value;
+
+	BUG_ON_OPT_ARG(arg);
+
+	imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
+	opts->keep_empty = !unset;
+	return 0;
 }
 
 static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
@@ -980,6 +978,28 @@
 	return 0;
 }
 
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+	struct rebase_options *options = opt->value;
+
+	options->rebase_merges = !unset;
+	options->rebase_cousins = 0;
+
+	if (arg) {
+		if (!*arg) {
+			warning(_("--rebase-merges with an empty string "
+				  "argument is deprecated and will stop "
+				  "working in a future version of Git. Use "
+				  "--rebase-merges without an argument "
+				  "instead, which does the same thing."));
+			return 0;
+		}
+		parse_rebase_merges_value(options, arg);
+	}
+
+	return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
 	struct branch *current_branch = branch_get(NULL);
@@ -1035,8 +1055,6 @@
 	struct object_id branch_base;
 	int ignore_whitespace = 0;
 	const char *gpg_sign = NULL;
-	const char *rebase_merges = NULL;
-	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
 	char *keep_base_onto_name = NULL;
@@ -1113,7 +1131,7 @@
 				 "instead of ignoring them"),
 			      1, PARSE_OPT_HIDDEN),
 		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-		OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
+		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
 			       N_("how to handle commits that become empty"),
 			       PARSE_OPT_NONEG, parse_opt_empty),
 		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1137,15 +1155,14 @@
 			   &options.allow_empty_message,
 			   N_("allow rebasing commits with empty messages"),
 			   PARSE_OPT_HIDDEN),
-		{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
-			N_("mode"),
+		OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
 			   N_("strategy"), N_("use the given merge strategy")),
-		OPT_STRING_LIST('X', "strategy-option", &strategy_options,
+		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
 				N_("option"),
 				N_("pass the argument through to the merge "
 				   "strategy")),
@@ -1235,7 +1252,7 @@
 		die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
 
 	if (options.action != ACTION_NONE && !in_progress)
-		die(_("No rebase in progress?"));
+		die(_("no rebase in progress"));
 
 	if (options.action == ACTION_EDIT_TODO && !is_merge(&options))
 		die(_("The --edit-todo action can only be used during "
@@ -1261,7 +1278,7 @@
 		int fd;
 
 		/* Sanity check */
-		if (get_oid("HEAD", &head))
+		if (repo_get_oid(the_repository, "HEAD", &head))
 			die(_("Cannot read HEAD"));
 
 		fd = repo_hold_locked_index(the_repository, &lock_file, 0);
@@ -1374,7 +1391,6 @@
 	if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
 	    (options.action != ACTION_NONE) ||
 	    (options.exec.nr > 0) ||
-	    (options.autosquash == -1 && options.config_autosquash == 1) ||
 	    options.autosquash == 1) {
 		allow_preemptive_ff = 0;
 	}
@@ -1436,17 +1452,6 @@
 	if (options.exec.nr)
 		imply_merge(&options, "--exec");
 
-	if (rebase_merges) {
-		if (!*rebase_merges)
-			; /* default mode; do nothing */
-		else if (!strcmp("rebase-cousins", rebase_merges))
-			options.rebase_cousins = 1;
-		else if (strcmp("no-rebase-cousins", rebase_merges))
-			die(_("Unknown mode: %s"), rebase_merges);
-		options.rebase_merges = 1;
-		imply_merge(&options, "--rebase-merges");
-	}
-
 	if (options.type == REBASE_APPLY) {
 		if (ignore_whitespace)
 			strvec_push(&options.git_am_opts,
@@ -1459,43 +1464,19 @@
 	} else {
 		/* REBASE_MERGE */
 		if (ignore_whitespace) {
-			string_list_append(&strategy_options,
+			string_list_append(&options.strategy_opts,
 					   "ignore-space-change");
 		}
 	}
 
-	if (strategy_options.nr) {
-		int i;
-
-		if (!options.strategy)
-			options.strategy = "ort";
-
-		strbuf_reset(&buf);
-		for (i = 0; i < strategy_options.nr; i++)
-			strbuf_addf(&buf, " --%s",
-				    strategy_options.items[i].string);
-		options.strategy_opts = xstrdup(buf.buf);
-	}
+	if (options.strategy_opts.nr && !options.strategy)
+		options.strategy = "ort";
 
 	if (options.strategy) {
 		options.strategy = xstrdup(options.strategy);
-		switch (options.type) {
-		case REBASE_APPLY:
-			die(_("--strategy requires --merge or --interactive"));
-		case REBASE_MERGE:
-			/* compatible */
-			break;
-		case REBASE_UNSPECIFIED:
-			options.type = REBASE_MERGE;
-			break;
-		default:
-			BUG("unhandled rebase type (%d)", options.type);
-		}
+		imply_merge(&options, "--strategy");
 	}
 
-	if (options.type == REBASE_MERGE)
-		imply_merge(&options, "--merge");
-
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
 
@@ -1512,8 +1493,8 @@
 			if (is_merge(&options))
 				die(_("apply options and merge options "
 					  "cannot be used together"));
-			else if (options.autosquash == -1 && options.config_autosquash == 1)
-				die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
+			else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+				die(_("apply options are incompatible with rebase.rebaseMerges.  Consider adding --no-rebase-merges"));
 			else if (options.update_refs == -1 && options.config_update_refs == 1)
 				die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
 			else
@@ -1526,14 +1507,22 @@
 	options.update_refs = (options.update_refs >= 0) ? options.update_refs :
 			     ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
 
-	if (options.autosquash == 1)
+	if (options.rebase_merges == 1)
+		imply_merge(&options, "--rebase-merges");
+	options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+				((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
+	if (options.autosquash == 1) {
 		imply_merge(&options, "--autosquash");
-	options.autosquash = (options.autosquash >= 0) ? options.autosquash :
-			     ((options.config_autosquash >= 0) ? options.config_autosquash : 0);
+	} else if (options.autosquash == -1) {
+		options.autosquash =
+			options.config_autosquash &&
+			(options.flags & REBASE_INTERACTIVE_EXPLICIT);
+	}
 
 	if (options.type == REBASE_UNSPECIFIED) {
 		if (!strcmp(options.default_backend, "merge"))
-			imply_merge(&options, "--merge");
+			options.type = REBASE_MERGE;
 		else if (!strcmp(options.default_backend, "apply"))
 			options.type = REBASE_APPLY;
 		else
@@ -1559,7 +1548,7 @@
 
 	if (options.empty == EMPTY_UNSPECIFIED) {
 		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-			options.empty = EMPTY_ASK;
+			options.empty = EMPTY_STOP;
 		else if (options.exec.nr > 0)
 			options.empty = EMPTY_KEEP;
 		else
@@ -1680,7 +1669,7 @@
 	} else if (!options.onto_name)
 		options.onto_name = options.upstream_name;
 	if (strstr(options.onto_name, "...")) {
-		if (get_oid_mb(options.onto_name, &branch_base) < 0) {
+		if (repo_get_oid_mb(the_repository, options.onto_name, &branch_base) < 0) {
 			if (keep_base)
 				die(_("'%s': need exactly one merge base with branch"),
 				    options.upstream_name);
@@ -1783,9 +1772,8 @@
 		}
 
 		/* We want color (if set), but no pager */
-		diff_setup(&opts);
-		opts.stat_width = -1; /* use full terminal width */
-		opts.stat_graph_width = -1; /* respect statGraphWidth config */
+		repo_diff_setup(the_repository, &opts);
+		init_diffstat_widths(&opts);
 		opts.output_format |=
 			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
 		opts.detect_rename = DIFF_DETECT_RENAME;
@@ -1850,10 +1838,9 @@
 	free(options.gpg_sign_opt);
 	string_list_clear(&options.exec, 0);
 	free(options.strategy);
-	free(options.strategy_opts);
+	string_list_clear(&options.strategy_opts, 0);
 	strbuf_release(&options.git_format_patch_opt);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
-	string_list_clear(&strategy_options, 0);
 	return !!ret;
 }
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index cd5c7a2..56d8a77 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1,6 +1,10 @@
 #include "builtin.h"
+#include "abspath.h"
 #include "repository.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "pack.h"
 #include "refs.h"
@@ -18,18 +22,23 @@
 #include "connected.h"
 #include "strvec.h"
 #include "version.h"
-#include "tag.h"
 #include "gpg-interface.h"
 #include "sigchain.h"
 #include "fsck.h"
 #include "tmp-objdir.h"
 #include "oidset.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "protocol.h"
 #include "commit-reach.h"
+#include "server-info.h"
+#include "trace.h"
+#include "trace2.h"
 #include "worktree.h"
 #include "shallow.h"
+#include "parse-options.h"
 
 static const char * const receive_pack_usage[] = {
 	N_("git receive-pack <git-dir>"),
@@ -80,7 +89,7 @@
 static struct signature_check sigcheck;
 static const char *push_cert_nonce;
 static const char *cert_nonce_seed;
-static struct string_list hidden_refs = STRING_LIST_INIT_DUP;
+static struct strvec hidden_refs = STRVEC_INIT;
 
 static const char *NONCE_UNSOLICITED = "UNSOLICITED";
 static const char *NONCE_BAD = "BAD";
@@ -129,17 +138,15 @@
 	return DENY_IGNORE;
 }
 
-static int receive_pack_config(const char *var, const char *value, void *cb)
+static int receive_pack_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
+	const char *msg_id;
 	int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
 
 	if (status)
 		return status;
 
-	status = git_gpg_config(var, value, NULL);
-	if (status)
-		return status;
-
 	if (strcmp(var, "receive.denydeletes") == 0) {
 		deny_deletes = git_config_bool(var, value);
 		return 0;
@@ -151,12 +158,12 @@
 	}
 
 	if (strcmp(var, "receive.unpacklimit") == 0) {
-		receive_unpack_limit = git_config_int(var, value);
+		receive_unpack_limit = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (strcmp(var, "transfer.unpacklimit") == 0) {
-		transfer_unpack_limit = git_config_int(var, value);
+		transfer_unpack_limit = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -171,12 +178,14 @@
 		return 0;
 	}
 
-	if (skip_prefix(var, "receive.fsck.", &var)) {
-		if (is_valid_msg_type(var, value))
+	if (skip_prefix(var, "receive.fsck.", &msg_id)) {
+		if (!value)
+			return config_error_nonbool(var);
+		if (is_valid_msg_type(msg_id, value))
 			strbuf_addf(&fsck_msg_types, "%c%s=%s",
-				fsck_msg_types.len ? ',' : '=', var, value);
+				fsck_msg_types.len ? ',' : '=', msg_id, value);
 		else
-			warning("skipping unknown msg id '%s'", var);
+			warning("skipping unknown msg id '%s'", msg_id);
 		return 0;
 	}
 
@@ -224,7 +233,7 @@
 		return git_config_string(&cert_nonce_seed, var, value);
 
 	if (strcmp(var, "receive.certnonceslop") == 0) {
-		nonce_stamp_slop_limit = git_config_ulong(var, value);
+		nonce_stamp_slop_limit = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -239,12 +248,12 @@
 	}
 
 	if (strcmp(var, "receive.keepalive") == 0) {
-		keepalive_in_sec = git_config_int(var, value);
+		keepalive_in_sec = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (strcmp(var, "receive.maxinputsize") == 0) {
-		max_input_size = git_config_int64(var, value);
+		max_input_size = git_config_int64(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -260,7 +269,7 @@
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void show_ref(const char *path, const struct object_id *oid)
@@ -331,7 +340,9 @@
 {
 	static struct oidset seen = OIDSET_INIT;
 
-	for_each_ref(show_ref_cb, &seen);
+	refs_for_each_fullref_in(get_main_ref_store(the_repository), "",
+				 hidden_refs_to_excludes(&hidden_refs),
+				 show_ref_cb, &seen);
 	for_each_alternate_ref(show_one_alternate_ref, &seen);
 	oidset_clear(&seen);
 	if (!sent_capabilities)
@@ -582,21 +593,6 @@
 	return strbuf_detach(&buf, NULL);
 }
 
-static char *find_header(const char *msg, size_t len, const char *key,
-			 const char **next_line)
-{
-	size_t out_len;
-	const char *val = find_header_mem(msg, len, key, &out_len);
-
-	if (!val)
-		return NULL;
-
-	if (next_line)
-		*next_line = val + out_len + 1;
-
-	return xmemdupz(val, out_len);
-}
-
 /*
  * Return zero if a and b are equal up to n bytes and nonzero if they are not.
  * This operation is guaranteed to run in constant time to avoid leaking data.
@@ -611,13 +607,14 @@
 	return res;
 }
 
-static const char *check_nonce(const char *buf, size_t len)
+static const char *check_nonce(const char *buf)
 {
-	char *nonce = find_header(buf, len, "nonce", NULL);
+	size_t noncelen;
+	const char *found = find_commit_header(buf, "nonce", &noncelen);
+	char *nonce = found ? xmemdupz(found, noncelen) : NULL;
 	timestamp_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
-	size_t noncelen;
 
 	if (!nonce) {
 		retval = NONCE_MISSING;
@@ -659,7 +656,6 @@
 		goto leave;
 	}
 
-	noncelen = strlen(nonce);
 	expect = prepare_push_cert_nonce(service_dir, stamp);
 	if (noncelen != strlen(expect)) {
 		/* This is not even the right size. */
@@ -707,35 +703,28 @@
 static int check_cert_push_options(const struct string_list *push_options)
 {
 	const char *buf = push_cert.buf;
-	int len = push_cert.len;
 
-	char *option;
-	const char *next_line;
+	const char *option;
+	size_t optionlen;
 	int options_seen = 0;
 
 	int retval = 1;
 
-	if (!len)
+	if (!*buf)
 		return 1;
 
-	while ((option = find_header(buf, len, "push-option", &next_line))) {
-		len -= (next_line - buf);
-		buf = next_line;
+	while ((option = find_commit_header(buf, "push-option", &optionlen))) {
+		buf = option + optionlen + 1;
 		options_seen++;
 		if (options_seen > push_options->nr
-		    || strcmp(option,
-			      push_options->items[options_seen - 1].string)) {
-			retval = 0;
-			goto leave;
-		}
-		free(option);
+		    || xstrncmpz(push_options->items[options_seen - 1].string,
+				 option, optionlen))
+			return 0;
 	}
 
 	if (options_seen != push_options->nr)
 		retval = 0;
 
-leave:
-	free(option);
 	return retval;
 }
 
@@ -762,7 +751,7 @@
 		check_signature(&sigcheck, push_cert.buf + bogs,
 				push_cert.len - bogs);
 
-		nonce_status = check_nonce(push_cert.buf, bogs);
+		nonce_status = check_nonce(sigcheck.payload);
 	}
 	if (!is_null_oid(&push_cert_oid)) {
 		strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
@@ -1347,7 +1336,7 @@
 {
 	struct object_id oid;
 
-	return !get_oid("HEAD", &oid);
+	return !repo_get_oid(the_repository, "HEAD", &oid);
 }
 
 static const char *push_to_deploy(unsigned char *sha1,
@@ -1463,8 +1452,10 @@
 		find_shared_symref(worktrees, "HEAD", name);
 
 	/* only refs/... are allowed */
-	if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
-		rp_error("refusing to create funny ref '%s' remotely", name);
+	if (!starts_with(name, "refs/") ||
+	    check_refname_format(name + 5, is_null_oid(new_oid) ?
+				 REFNAME_ALLOW_ONELEVEL : 0)) {
+		rp_error("refusing to update funny ref '%s' remotely", name);
 		ret = "funny refname";
 		goto out;
 	}
@@ -1494,7 +1485,7 @@
 		}
 	}
 
-	if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
+	if (!is_null_oid(new_oid) && !repo_has_object_file(the_repository, new_oid)) {
 		error("unpack should have generated %s, "
 		      "but I can't find it!", oid_to_hex(new_oid));
 		ret = "bad pack";
@@ -1535,6 +1526,7 @@
 	    starts_with(name, "refs/heads/")) {
 		struct object *old_object, *new_object;
 		struct commit *old_commit, *new_commit;
+		int ret2;
 
 		old_object = parse_object(the_repository, old_oid);
 		new_object = parse_object(the_repository, new_oid);
@@ -1548,7 +1540,10 @@
 		}
 		old_commit = (struct commit *)old_object;
 		new_commit = (struct commit *)new_object;
-		if (!in_merge_bases(old_commit, new_commit)) {
+		ret2 = repo_in_merge_bases(the_repository, old_commit, new_commit);
+		if (ret2 < 0)
+			exit(128);
+		if (!ret2) {
 			rp_error("denying non-fast-forward %s"
 				 " (you should pull first)", name);
 			ret = "non-fast-forward";
@@ -1681,11 +1676,11 @@
 	rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
 		 " its target '%s' (%s..%s)",
 		 cmd->ref_name,
-		 find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV),
-		 find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV),
+		 repo_find_unique_abbrev(the_repository, &cmd->old_oid, DEFAULT_ABBREV),
+		 repo_find_unique_abbrev(the_repository, &cmd->new_oid, DEFAULT_ABBREV),
 		 dst_cmd->ref_name,
-		 find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV),
-		 find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV));
+		 repo_find_unique_abbrev(the_repository, &dst_cmd->old_oid, DEFAULT_ABBREV),
+		 repo_find_unique_abbrev(the_repository, &dst_cmd->new_oid, DEFAULT_ABBREV));
 
 	cmd->error_string = dst_cmd->error_string =
 		"inconsistent aliased update";
@@ -2089,7 +2084,7 @@
 			const char *feature_list = reader->line + linelen + 1;
 			const char *hash = NULL;
 			const char *client_sid;
-			int len = 0;
+			size_t len = 0;
 			if (parse_feature_request(feature_list, "report-status"))
 				report_status = 1;
 			if (parse_feature_request(feature_list, "report-status-v2"))
@@ -2184,7 +2179,7 @@
 	}
 }
 
-static const char *pack_lockfile;
+static struct tempfile *pack_lockfile;
 
 static void push_header_arg(struct strvec *args, struct pack_header *hdr)
 {
@@ -2251,6 +2246,7 @@
 			return "unpack-objects abnormal exit";
 	} else {
 		char hostname[HOST_NAME_MAX + 1];
+		char *lockfile;
 
 		strvec_pushl(&child.args, "index-pack", "--stdin", NULL);
 		push_header_arg(&child.args, &hdr);
@@ -2280,8 +2276,14 @@
 		status = start_command(&child);
 		if (status)
 			return "index-pack fork failed";
-		pack_lockfile = index_pack_lockfile(child.out, NULL);
+
+		lockfile = index_pack_lockfile(child.out, NULL);
+		if (lockfile) {
+			pack_lockfile = register_tempfile(lockfile);
+			free(lockfile);
+		}
 		close(child.out);
+
 		status = finish_command(&child);
 		if (status)
 			return "index-pack abnormal exit";
@@ -2509,10 +2511,10 @@
 	if (cert_nonce_seed)
 		push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL));
 
-	if (0 <= transfer_unpack_limit)
-		unpack_limit = transfer_unpack_limit;
-	else if (0 <= receive_unpack_limit)
+	if (0 <= receive_unpack_limit)
 		unpack_limit = receive_unpack_limit;
+	else if (0 <= transfer_unpack_limit)
+		unpack_limit = transfer_unpack_limit;
 
 	switch (determine_protocol_version_server()) {
 	case protocol_v2:
@@ -2568,8 +2570,7 @@
 		use_keepalive = KEEPALIVE_ALWAYS;
 		execute_commands(commands, unpack_status, &si,
 				 &push_options);
-		if (pack_lockfile)
-			unlink_or_warn(pack_lockfile);
+		delete_tempfile(&pack_lockfile);
 		sigchain_push(SIGPIPE, SIG_IGN);
 		if (report_status_v2)
 			report_v2(commands, unpack_status);
@@ -2605,7 +2606,7 @@
 		packet_flush(1);
 	oid_array_clear(&shallow);
 	oid_array_clear(&ref);
-	string_list_clear(&hidden_refs, 0);
+	strvec_clear(&hidden_refs);
 	free((void *)push_cert_nonce);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 270681d..060eb33 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -1,13 +1,21 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
+#include "repository.h"
 #include "revision.h"
 #include "reachable.h"
+#include "wildmatch.h"
 #include "worktree.h"
 #include "reflog.h"
+#include "refs.h"
+#include "parse-options.h"
 
 #define BUILTIN_REFLOG_SHOW_USAGE \
 	N_("git reflog [show] [<log-options>] [<ref>]")
 
+#define BUILTIN_REFLOG_LIST_USAGE \
+	N_("git reflog list")
+
 #define BUILTIN_REFLOG_EXPIRE_USAGE \
 	N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
 	   "                  [--rewrite] [--updateref] [--stale-fix]\n" \
@@ -25,6 +33,11 @@
 	NULL,
 };
 
+static const char *const reflog_list_usage[] = {
+	BUILTIN_REFLOG_LIST_USAGE,
+	NULL,
+};
+
 static const char *const reflog_expire_usage[] = {
 	BUILTIN_REFLOG_EXPIRE_USAGE,
 	NULL
@@ -42,6 +55,7 @@
 
 static const char *const reflog_usage[] = {
 	BUILTIN_REFLOG_SHOW_USAGE,
+	BUILTIN_REFLOG_LIST_USAGE,
 	BUILTIN_REFLOG_EXPIRE_USAGE,
 	BUILTIN_REFLOG_DELETE_USAGE,
 	BUILTIN_REFLOG_EXISTS_USAGE,
@@ -56,8 +70,7 @@
 	struct string_list reflogs;
 };
 
-static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
-			  int flags UNUSED, void *cb_data)
+static int collect_reflog(const char *ref, void *cb_data)
 {
 	struct worktree_reflogs *cb = cb_data;
 	struct worktree *worktree = cb->worktree;
@@ -92,8 +105,7 @@
 		reflog_expire_cfg_tail = &reflog_expire_cfg;
 
 	for (ent = reflog_expire_cfg; ent; ent = ent->next)
-		if (!strncmp(ent->pattern, pattern, len) &&
-		    ent->pattern[len] == '\0')
+		if (!xstrncmpz(ent->pattern, pattern, len))
 			return ent;
 
 	FLEX_ALLOC_MEM(ent, pattern, pattern, len);
@@ -106,7 +118,8 @@
 #define EXPIRE_TOTAL   01
 #define EXPIRE_UNREACH 02
 
-static int reflog_expire_config(const char *var, const char *value, void *cb)
+static int reflog_expire_config(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
 	const char *pattern, *key;
 	size_t pattern_len;
@@ -115,7 +128,7 @@
 	struct reflog_expire_cfg *ent;
 
 	if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
-		return git_default_config(var, value, cb);
+		return git_default_config(var, value, ctx, cb);
 
 	if (!strcmp(key, "reflogexpire")) {
 		slot = EXPIRE_TOTAL;
@@ -126,7 +139,7 @@
 		if (git_config_expiry_date(&expire, var, value))
 			return -1;
 	} else
-		return git_default_config(var, value, cb);
+		return git_default_config(var, value, ctx, cb);
 
 	if (!pattern) {
 		switch (slot) {
@@ -234,16 +247,39 @@
 	return cmd_log_reflog(argc, argv, prefix);
 }
 
+static int show_reflog(const char *refname, void *cb_data UNUSED)
+{
+	printf("%s\n", refname);
+	return 0;
+}
+
+static int cmd_reflog_list(int argc, const char **argv, const char *prefix)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	struct ref_store *ref_store;
+
+	argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
+	if (argc)
+		return error(_("%s does not accept arguments: '%s'"),
+			     "list", argv[0]);
+
+	ref_store = get_main_ref_store(the_repository);
+
+	return refs_for_each_reflog(ref_store, show_reflog, NULL);
+}
+
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct cmd_reflog_expire_cb cmd = { 0 };
 	timestamp_t now = time(NULL);
-	int i, status, do_all, all_worktrees = 1;
+	int i, status, do_all, single_worktree = 0;
 	unsigned int flags = 0;
 	int verbose = 0;
 	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
 	const struct option options[] = {
-		OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+		OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
 			EXPIRE_REFLOGS_DRY_RUN),
 		OPT_BIT(0, "rewrite", &flags,
 			N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
@@ -263,7 +299,7 @@
 		OPT_BOOL(0, "stale-fix", &cmd.stalefix,
 			 N_("prune any reflog entries that point to broken commits")),
 		OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
-		OPT_BOOL(1, "single-worktree", &all_worktrees,
+		OPT_BOOL(0, "single-worktree", &single_worktree,
 			 N_("limits processing to reflogs from the current worktree only")),
 		OPT_END()
 	};
@@ -293,7 +329,7 @@
 		struct rev_info revs;
 
 		repo_init_revisions(the_repository, &revs, prefix);
-		revs.do_not_die_on_missing_tree = 1;
+		revs.do_not_die_on_missing_objects = 1;
 		revs.ignore_missing = 1;
 		revs.ignore_missing_links = 1;
 		if (verbose)
@@ -313,7 +349,7 @@
 
 		worktrees = get_worktrees();
 		for (p = worktrees; *p; p++) {
-			if (!all_worktrees && !(*p)->is_current)
+			if (single_worktree && !(*p)->is_current)
 				continue;
 			collected.worktree = *p;
 			refs_for_each_reflog(get_worktree_ref_store(*p),
@@ -363,7 +399,7 @@
 	int verbose = 0;
 
 	const struct option options[] = {
-		OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+		OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
 			EXPIRE_REFLOGS_DRY_RUN),
 		OPT_BIT(0, "rewrite", &flags,
 			N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
@@ -413,6 +449,7 @@
 	parse_opt_subcommand_fn *fn = NULL;
 	struct option options[] = {
 		OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
+		OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
 		OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
 		OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
 		OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index ee338bf..282782e 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -197,6 +197,8 @@
 
 int cmd_remote_ext(int argc, const char **argv, const char *prefix)
 {
+	BUG_ON_NON_EMPTY_PREFIX(prefix);
+
 	if (argc != 3)
 		usage(usage_msg);
 
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
index b2a3980..9020fab 100644
--- a/builtin/remote-fd.c
+++ b/builtin/remote-fd.c
@@ -59,6 +59,8 @@
 	int output_fd = -1;
 	char *end;
 
+	BUG_ON_NON_EMPTY_PREFIX(prefix);
+
 	if (argc != 3)
 		usage(usage_msg);
 
diff --git a/builtin/remote.c b/builtin/remote.c
index 729f6f3..8412d12 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -1,6 +1,8 @@
 #include "builtin.h"
 #include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "path.h"
 #include "transport.h"
 #include "remote.h"
 #include "string-list.h"
@@ -9,7 +11,7 @@
 #include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "strvec.h"
 #include "commit-reach.h"
 #include "progress.h"
@@ -148,7 +150,7 @@
 	else if (!strcmp(arg, "push"))
 		*mirror = MIRROR_PUSH;
 	else
-		return error(_("unknown mirror argument: %s"), arg);
+		return error(_("unknown --mirror argument: %s"), arg);
 	return 0;
 }
 
@@ -166,10 +168,9 @@
 	struct option options[] = {
 		OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")),
 		OPT_SET_INT(0, "tags", &fetch_tags,
-			    N_("import all tags and associated objects when fetching"),
+			    N_("import all tags and associated objects when fetching\n"
+			       "or do not fetch any tag at all (--no-tags)"),
 			    TAGS_SET),
-		OPT_SET_INT(0, NULL, &fetch_tags,
-			    N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET),
 		OPT_STRING_LIST('t', "track", &track, N_("branch"),
 				N_("branch(es) to track")),
 		OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
@@ -267,6 +268,7 @@
 #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
 
 static int config_read_branches(const char *key, const char *value,
+				const struct config_context *ctx UNUSED,
 				void *data UNUSED)
 {
 	const char *orig_key = key;
@@ -443,7 +445,7 @@
 			info->status = PUSH_STATUS_UPTODATE;
 		else if (is_null_oid(&ref->old_oid))
 			info->status = PUSH_STATUS_CREATE;
-		else if (has_object_file(&ref->old_oid) &&
+		else if (repo_has_object_file(the_repository, &ref->old_oid) &&
 			 ref_newer(&ref->new_oid, &ref->old_oid))
 			info->status = PUSH_STATUS_FASTFORWARD;
 		else
@@ -644,17 +646,19 @@
 };
 
 static int config_read_push_default(const char *key, const char *value,
-	void *cb)
+	const struct config_context *ctx, void *cb)
 {
+	const struct key_value_info *kvi = ctx->kvi;
+
 	struct push_default_info* info = cb;
 	if (strcmp(key, "remote.pushdefault") ||
 	    !value || strcmp(value, info->old_name))
 		return 0;
 
-	info->scope = current_config_scope();
+	info->scope = kvi->scope;
 	strbuf_reset(&info->origin);
-	strbuf_addstr(&info->origin, current_config_name());
-	info->linenr = current_config_line();
+	strbuf_addstr(&info->origin, config_origin_type_name(kvi->origin_type));
+	info->linenr = kvi->linenr;
 
 	return 0;
 }
@@ -1493,7 +1497,9 @@
 	return result;
 }
 
-static int get_remote_default(const char *key, const char *value UNUSED, void *priv)
+static int get_remote_default(const char *key, const char *value UNUSED,
+			      const struct config_context *ctx UNUSED,
+			      void *priv)
 {
 	if (strcmp(key, "remotes.default") == 0) {
 		int *found = priv;
diff --git a/builtin/repack.c b/builtin/repack.c
index f649379..15e4ccc 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -1,29 +1,33 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "parse-options.h"
+#include "path.h"
 #include "run-command.h"
-#include "sigchain.h"
+#include "server-info.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "midx.h"
 #include "packfile.h"
 #include "prune-packed.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "promisor-remote.h"
 #include "shallow.h"
 #include "pack.h"
 #include "pack-bitmap.h"
 #include "refs.h"
+#include "list-objects-filter-options.h"
 
 #define ALL_INTO_ONE 1
 #define LOOSEN_UNREACHABLE 2
 #define PACK_CRUFT 4
 
 #define DELETE_PACK 1
-#define CRUFT_PACK 2
+#define RETAIN_PACK 2
 
 static int pack_everything;
 static int delta_base_offset = 1;
@@ -48,14 +52,16 @@
 	const char *window_memory;
 	const char *depth;
 	const char *threads;
-	const char *max_pack_size;
+	unsigned long max_pack_size;
 	int no_reuse_delta;
 	int no_reuse_object;
 	int quiet;
 	int local;
+	struct list_objects_filter_options filter_options;
 };
 
-static int repack_config(const char *var, const char *value, void *cb)
+static int repack_config(const char *var, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
 	struct pack_objects_args *cruft_po_args = cb;
 	if (!strcmp(var, "repack.usedeltabaseoffset")) {
@@ -87,53 +93,102 @@
 		return git_config_string(&cruft_po_args->depth, var, value);
 	if (!strcmp(var, "repack.cruftthreads"))
 		return git_config_string(&cruft_po_args->threads, var, value);
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
-/*
- * Adds all packs hex strings to either fname_nonkept_list or
- * fname_kept_list based on whether each pack has a corresponding
- * .keep file or not.  Packs without a .keep file are not to be kept
- * if we are going to pack everything into one file.
- */
-static void collect_pack_filenames(struct string_list *fname_nonkept_list,
-				   struct string_list *fname_kept_list,
-				   const struct string_list *extra_keep)
+struct existing_packs {
+	struct string_list kept_packs;
+	struct string_list non_kept_packs;
+	struct string_list cruft_packs;
+};
+
+#define EXISTING_PACKS_INIT { \
+	.kept_packs = STRING_LIST_INIT_DUP, \
+	.non_kept_packs = STRING_LIST_INIT_DUP, \
+	.cruft_packs = STRING_LIST_INIT_DUP, \
+}
+
+static int has_existing_non_kept_packs(const struct existing_packs *existing)
 {
-	DIR *dir;
-	struct dirent *e;
-	char *fname;
+	return existing->non_kept_packs.nr || existing->cruft_packs.nr;
+}
 
-	if (!(dir = opendir(packdir)))
-		return;
+static void pack_mark_for_deletion(struct string_list_item *item)
+{
+	item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
+}
 
-	while ((e = readdir(dir)) != NULL) {
-		size_t len;
-		int i;
+static void pack_unmark_for_deletion(struct string_list_item *item)
+{
+	item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
+}
 
-		if (!strip_suffix(e->d_name, ".pack", &len))
+static int pack_is_marked_for_deletion(struct string_list_item *item)
+{
+	return (uintptr_t)item->util & DELETE_PACK;
+}
+
+static void pack_mark_retained(struct string_list_item *item)
+{
+	item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
+}
+
+static int pack_is_retained(struct string_list_item *item)
+{
+	return (uintptr_t)item->util & RETAIN_PACK;
+}
+
+static void mark_packs_for_deletion_1(struct string_list *names,
+				      struct string_list *list)
+{
+	struct string_list_item *item;
+	const int hexsz = the_hash_algo->hexsz;
+
+	for_each_string_list_item(item, list) {
+		char *sha1;
+		size_t len = strlen(item->string);
+		if (len < hexsz)
 			continue;
+		sha1 = item->string + len - hexsz;
 
-		for (i = 0; i < extra_keep->nr; i++)
-			if (!fspathcmp(e->d_name, extra_keep->items[i].string))
-				break;
-
-		fname = xmemdupz(e->d_name, len);
-
-		if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
-		    (file_exists(mkpath("%s/%s.keep", packdir, fname)))) {
-			string_list_append_nodup(fname_kept_list, fname);
-		} else {
-			struct string_list_item *item;
-			item = string_list_append_nodup(fname_nonkept_list,
-							fname);
-			if (file_exists(mkpath("%s/%s.mtimes", packdir, fname)))
-				item->util = (void*)(uintptr_t)CRUFT_PACK;
+		if (pack_is_retained(item)) {
+			pack_unmark_for_deletion(item);
+		} else if (!string_list_has_string(names, sha1)) {
+			/*
+			 * Mark this pack for deletion, which ensures
+			 * that this pack won't be included in a MIDX
+			 * (if `--write-midx` was given) and that we
+			 * will actually delete this pack (if `-d` was
+			 * given).
+			 */
+			pack_mark_for_deletion(item);
 		}
 	}
-	closedir(dir);
+}
 
-	string_list_sort(fname_kept_list);
+static void retain_cruft_pack(struct existing_packs *existing,
+			      struct packed_git *cruft)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list_item *item;
+
+	strbuf_addstr(&buf, pack_basename(cruft));
+	strbuf_strip_suffix(&buf, ".pack");
+
+	item = string_list_lookup(&existing->cruft_packs, buf.buf);
+	if (!item)
+		BUG("could not find cruft pack '%s'", pack_basename(cruft));
+
+	pack_mark_retained(item);
+	strbuf_release(&buf);
+}
+
+static void mark_packs_for_deletion(struct existing_packs *existing,
+				    struct string_list *names)
+
+{
+	mark_packs_for_deletion_1(names, &existing->non_kept_packs);
+	mark_packs_for_deletion_1(names, &existing->cruft_packs);
 }
 
 static void remove_redundant_pack(const char *dir_name, const char *base_name)
@@ -148,6 +203,72 @@
 	strbuf_release(&buf);
 }
 
+static void remove_redundant_packs_1(struct string_list *packs)
+{
+	struct string_list_item *item;
+	for_each_string_list_item(item, packs) {
+		if (!pack_is_marked_for_deletion(item))
+			continue;
+		remove_redundant_pack(packdir, item->string);
+	}
+}
+
+static void remove_redundant_existing_packs(struct existing_packs *existing)
+{
+	remove_redundant_packs_1(&existing->non_kept_packs);
+	remove_redundant_packs_1(&existing->cruft_packs);
+}
+
+static void existing_packs_release(struct existing_packs *existing)
+{
+	string_list_clear(&existing->kept_packs, 0);
+	string_list_clear(&existing->non_kept_packs, 0);
+	string_list_clear(&existing->cruft_packs, 0);
+}
+
+/*
+ * Adds all packs hex strings (pack-$HASH) to either packs->non_kept
+ * or packs->kept based on whether each pack has a corresponding
+ * .keep file or not.  Packs without a .keep file are not to be kept
+ * if we are going to pack everything into one file.
+ */
+static void collect_pack_filenames(struct existing_packs *existing,
+				   const struct string_list *extra_keep)
+{
+	struct packed_git *p;
+	struct strbuf buf = STRBUF_INIT;
+
+	for (p = get_all_packs(the_repository); p; p = p->next) {
+		int i;
+		const char *base;
+
+		if (!p->pack_local)
+			continue;
+
+		base = pack_basename(p);
+
+		for (i = 0; i < extra_keep->nr; i++)
+			if (!fspathcmp(base, extra_keep->items[i].string))
+				break;
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, base);
+		strbuf_strip_suffix(&buf, ".pack");
+
+		if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
+			string_list_append(&existing->kept_packs, buf.buf);
+		else if (p->is_cruft)
+			string_list_append(&existing->cruft_packs, buf.buf);
+		else
+			string_list_append(&existing->non_kept_packs, buf.buf);
+	}
+
+	string_list_sort(&existing->kept_packs);
+	string_list_sort(&existing->non_kept_packs);
+	string_list_sort(&existing->cruft_packs);
+	strbuf_release(&buf);
+}
+
 static void prepare_pack_objects(struct child_process *cmd,
 				 const struct pack_objects_args *args,
 				 const char *out)
@@ -162,7 +283,7 @@
 	if (args->threads)
 		strvec_pushf(&cmd->args, "--threads=%s", args->threads);
 	if (args->max_pack_size)
-		strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
+		strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
 	if (args->no_reuse_delta)
 		strvec_pushf(&cmd->args, "--no-reuse-delta");
 	if (args->no_reuse_object)
@@ -182,8 +303,9 @@
  * Write oid to the given struct child_process's stdin, starting it first if
  * necessary.
  */
-static int write_oid(const struct object_id *oid, struct packed_git *pack,
-		     uint32_t pos, void *data)
+static int write_oid(const struct object_id *oid,
+		     struct packed_git *pack UNUSED,
+		     uint32_t pos UNUSED, void *data)
 {
 	struct child_process *cmd = data;
 
@@ -192,8 +314,9 @@
 			die(_("could not start pack-objects to repack promisor objects"));
 	}
 
-	xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz);
-	xwrite(cmd->in, "\n", 1);
+	if (write_in_full(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
+	    write_in_full(cmd->in, "\n", 1) < 0)
+		die(_("failed to feed promisor objects to pack-objects"));
 	return 0;
 }
 
@@ -234,6 +357,18 @@
 	return data;
 }
 
+static int has_pack_ext(const struct generated_pack_data *data,
+			const char *ext)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(exts); i++) {
+		if (strcmp(exts[i].name, ext))
+			continue;
+		return !!data->tempfiles[i];
+	}
+	BUG("unknown pack extension: '%s'", ext);
+}
+
 static void repack_promisor_objects(const struct pack_objects_args *args,
 				    struct string_list *names)
 {
@@ -299,6 +434,8 @@
 	struct packed_git **pack;
 	uint32_t pack_nr, pack_alloc;
 	uint32_t split;
+
+	int split_factor;
 };
 
 static uint32_t geometry_pack_weight(struct packed_git *p)
@@ -320,36 +457,42 @@
 	return 0;
 }
 
-static void init_pack_geometry(struct pack_geometry **geometry_p,
-			       struct string_list *existing_kept_packs)
+static void init_pack_geometry(struct pack_geometry *geometry,
+			       struct existing_packs *existing,
+			       const struct pack_objects_args *args)
 {
 	struct packed_git *p;
-	struct pack_geometry *geometry;
 	struct strbuf buf = STRBUF_INIT;
 
-	*geometry_p = xcalloc(1, sizeof(struct pack_geometry));
-	geometry = *geometry_p;
-
 	for (p = get_all_packs(the_repository); p; p = p->next) {
+		if (args->local && !p->pack_local)
+			/*
+			 * When asked to only repack local packfiles we skip
+			 * over any packfiles that are borrowed from alternate
+			 * object directories.
+			 */
+			continue;
+
 		if (!pack_kept_objects) {
 			/*
-			 * Any pack that has its pack_keep bit set will appear
-			 * in existing_kept_packs below, but this saves us from
-			 * doing a more expensive check.
+			 * Any pack that has its pack_keep bit set will
+			 * appear in existing->kept_packs below, but
+			 * this saves us from doing a more expensive
+			 * check.
 			 */
 			if (p->pack_keep)
 				continue;
 
 			/*
-			 * The pack may be kept via the --keep-pack option;
-			 * check 'existing_kept_packs' to determine whether to
-			 * ignore it.
+			 * The pack may be kept via the --keep-pack
+			 * option; check 'existing->kept_packs' to
+			 * determine whether to ignore it.
 			 */
 			strbuf_reset(&buf);
 			strbuf_addstr(&buf, pack_basename(p));
 			strbuf_strip_suffix(&buf, ".pack");
 
-			if (string_list_has_string(existing_kept_packs, buf.buf))
+			if (string_list_has_string(&existing->kept_packs, buf.buf))
 				continue;
 		}
 		if (p->is_cruft)
@@ -367,7 +510,7 @@
 	strbuf_release(&buf);
 }
 
-static void split_pack_geometry(struct pack_geometry *geometry, int factor)
+static void split_pack_geometry(struct pack_geometry *geometry)
 {
 	uint32_t i;
 	uint32_t split;
@@ -386,12 +529,14 @@
 		struct packed_git *ours = geometry->pack[i];
 		struct packed_git *prev = geometry->pack[i - 1];
 
-		if (unsigned_mult_overflows(factor, geometry_pack_weight(prev)))
+		if (unsigned_mult_overflows(geometry->split_factor,
+					    geometry_pack_weight(prev)))
 			die(_("pack %s too large to consider in geometric "
 			      "progression"),
 			    prev->pack_name);
 
-		if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev))
+		if (geometry_pack_weight(ours) <
+		    geometry->split_factor * geometry_pack_weight(prev))
 			break;
 	}
 
@@ -426,10 +571,12 @@
 	for (i = split; i < geometry->pack_nr; i++) {
 		struct packed_git *ours = geometry->pack[i];
 
-		if (unsigned_mult_overflows(factor, total_size))
+		if (unsigned_mult_overflows(geometry->split_factor,
+					    total_size))
 			die(_("pack %s too large to roll up"), ours->pack_name);
 
-		if (geometry_pack_weight(ours) < factor * total_size) {
+		if (geometry_pack_weight(ours) <
+		    geometry->split_factor * total_size) {
 			if (unsigned_add_overflows(total_size,
 						   geometry_pack_weight(ours)))
 				die(_("pack %s too large to roll up"),
@@ -444,8 +591,10 @@
 	geometry->split = split;
 }
 
-static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
+static struct packed_git *get_preferred_pack(struct pack_geometry *geometry)
 {
+	uint32_t i;
+
 	if (!geometry) {
 		/*
 		 * No geometry means either an all-into-one repack (in which
@@ -460,18 +609,55 @@
 	}
 	if (geometry->split == geometry->pack_nr)
 		return NULL;
-	return geometry->pack[geometry->pack_nr - 1];
+
+	/*
+	 * The preferred pack is the largest pack above the split line. In
+	 * other words, it is the largest pack that does not get rolled up in
+	 * the geometric repack.
+	 */
+	for (i = geometry->pack_nr; i > geometry->split; i--)
+		/*
+		 * A pack that is not local would never be included in a
+		 * multi-pack index. We thus skip over any non-local packs.
+		 */
+		if (geometry->pack[i - 1]->pack_local)
+			return geometry->pack[i - 1];
+
+	return NULL;
 }
 
-static void clear_pack_geometry(struct pack_geometry *geometry)
+static void geometry_remove_redundant_packs(struct pack_geometry *geometry,
+					    struct string_list *names,
+					    struct existing_packs *existing)
+{
+	struct strbuf buf = STRBUF_INIT;
+	uint32_t i;
+
+	for (i = 0; i < geometry->split; i++) {
+		struct packed_git *p = geometry->pack[i];
+		if (string_list_has_string(names, hash_to_hex(p->hash)))
+			continue;
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, pack_basename(p));
+		strbuf_strip_suffix(&buf, ".pack");
+
+		if ((p->pack_keep) ||
+		    (string_list_has_string(&existing->kept_packs, buf.buf)))
+			continue;
+
+		remove_redundant_pack(packdir, buf.buf);
+	}
+
+	strbuf_release(&buf);
+}
+
+static void free_pack_geometry(struct pack_geometry *geometry)
 {
 	if (!geometry)
 		return;
 
 	free(geometry->pack);
-	geometry->pack_nr = 0;
-	geometry->pack_alloc = 0;
-	geometry->split = 0;
 }
 
 struct midx_snapshot_ref_data {
@@ -537,57 +723,76 @@
 }
 
 static void midx_included_packs(struct string_list *include,
-				struct string_list *existing_nonkept_packs,
-				struct string_list *existing_kept_packs,
+				struct existing_packs *existing,
 				struct string_list *names,
 				struct pack_geometry *geometry)
 {
 	struct string_list_item *item;
 
-	for_each_string_list_item(item, existing_kept_packs)
+	for_each_string_list_item(item, &existing->kept_packs)
 		string_list_insert(include, xstrfmt("%s.idx", item->string));
 	for_each_string_list_item(item, names)
 		string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
-	if (geometry) {
+	if (geometry->split_factor) {
 		struct strbuf buf = STRBUF_INIT;
 		uint32_t i;
 		for (i = geometry->split; i < geometry->pack_nr; i++) {
 			struct packed_git *p = geometry->pack[i];
 
+			/*
+			 * The multi-pack index never refers to packfiles part
+			 * of an alternate object database, so we skip these.
+			 * While git-multi-pack-index(1) would silently ignore
+			 * them anyway, this allows us to skip executing the
+			 * command completely when we have only non-local
+			 * packfiles.
+			 */
+			if (!p->pack_local)
+				continue;
+
 			strbuf_addstr(&buf, pack_basename(p));
 			strbuf_strip_suffix(&buf, ".pack");
 			strbuf_addstr(&buf, ".idx");
 
 			string_list_insert(include, strbuf_detach(&buf, NULL));
 		}
-
-		for_each_string_list_item(item, existing_nonkept_packs) {
-			if (!((uintptr_t)item->util & CRUFT_PACK)) {
-				/*
-				 * no need to check DELETE_PACK, since we're not
-				 * doing an ALL_INTO_ONE repack
-				 */
-				continue;
-			}
-			string_list_insert(include, xstrfmt("%s.idx", item->string));
-		}
 	} else {
-		for_each_string_list_item(item, existing_nonkept_packs) {
-			if ((uintptr_t)item->util & DELETE_PACK)
+		for_each_string_list_item(item, &existing->non_kept_packs) {
+			if (pack_is_marked_for_deletion(item))
 				continue;
 			string_list_insert(include, xstrfmt("%s.idx", item->string));
 		}
 	}
+
+	for_each_string_list_item(item, &existing->cruft_packs) {
+		/*
+		 * When doing a --geometric repack, there is no need to check
+		 * for deleted packs, since we're by definition not doing an
+		 * ALL_INTO_ONE repack (hence no packs will be deleted).
+		 * Otherwise we must check for and exclude any packs which are
+		 * enqueued for deletion.
+		 *
+		 * So we could omit the conditional below in the --geometric
+		 * case, but doing so is unnecessary since no packs are marked
+		 * as pending deletion (since we only call
+		 * `mark_packs_for_deletion()` when doing an all-into-one
+		 * repack).
+		 */
+		if (pack_is_marked_for_deletion(item))
+			continue;
+		string_list_insert(include, xstrfmt("%s.idx", item->string));
+	}
 }
 
 static int write_midx_included_packs(struct string_list *include,
 				     struct pack_geometry *geometry,
+				     struct string_list *names,
 				     const char *refs_snapshot,
 				     int show_progress, int write_bitmaps)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
 	struct string_list_item *item;
-	struct packed_git *largest = get_largest_active_pack(geometry);
+	struct packed_git *preferred = get_preferred_pack(geometry);
 	FILE *in;
 	int ret;
 
@@ -608,9 +813,41 @@
 	if (write_bitmaps)
 		strvec_push(&cmd.args, "--bitmap");
 
-	if (largest)
+	if (preferred)
 		strvec_pushf(&cmd.args, "--preferred-pack=%s",
-			     pack_basename(largest));
+			     pack_basename(preferred));
+	else if (names->nr) {
+		/* The largest pack was repacked, meaning that either
+		 * one or two packs exist depending on whether the
+		 * repository has a cruft pack or not.
+		 *
+		 * Select the non-cruft one as preferred to encourage
+		 * pack-reuse among packs containing reachable objects
+		 * over unreachable ones.
+		 *
+		 * (Note we could write multiple packs here if
+		 * `--max-pack-size` was given, but any one of them
+		 * will suffice, so pick the first one.)
+		 */
+		for_each_string_list_item(item, names) {
+			struct generated_pack_data *data = item->util;
+			if (has_pack_ext(data, ".mtimes"))
+				continue;
+
+			strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack",
+				     item->string);
+			break;
+		}
+	} else {
+		/*
+		 * No packs were kept, and no packs were written. The
+		 * only thing remaining are .keep packs (unless
+		 * --pack-kept-objects was given).
+		 *
+		 * Set the `--preferred-pack` arbitrarily here.
+		 */
+		;
+	}
 
 	if (refs_snapshot)
 		strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
@@ -656,18 +893,163 @@
 	strbuf_release(&path);
 }
 
+static int finish_pack_objects_cmd(struct child_process *cmd,
+				   struct string_list *names,
+				   int local)
+{
+	FILE *out;
+	struct strbuf line = STRBUF_INIT;
+
+	out = xfdopen(cmd->out, "r");
+	while (strbuf_getline_lf(&line, out) != EOF) {
+		struct string_list_item *item;
+
+		if (line.len != the_hash_algo->hexsz)
+			die(_("repack: Expecting full hex object ID lines only "
+			      "from pack-objects."));
+		/*
+		 * Avoid putting packs written outside of the repository in the
+		 * list of names.
+		 */
+		if (local) {
+			item = string_list_append(names, line.buf);
+			item->util = populate_pack_exts(line.buf);
+		}
+	}
+	fclose(out);
+
+	strbuf_release(&line);
+
+	return finish_command(cmd);
+}
+
+static int write_filtered_pack(const struct pack_objects_args *args,
+			       const char *destination,
+			       const char *pack_prefix,
+			       struct existing_packs *existing,
+			       struct string_list *names)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	struct string_list_item *item;
+	FILE *in;
+	int ret;
+	const char *caret;
+	const char *scratch;
+	int local = skip_prefix(destination, packdir, &scratch);
+
+	prepare_pack_objects(&cmd, args, destination);
+
+	strvec_push(&cmd.args, "--stdin-packs");
+
+	if (!pack_kept_objects)
+		strvec_push(&cmd.args, "--honor-pack-keep");
+	for_each_string_list_item(item, &existing->kept_packs)
+		strvec_pushf(&cmd.args, "--keep-pack=%s", item->string);
+
+	cmd.in = -1;
+
+	ret = start_command(&cmd);
+	if (ret)
+		return ret;
+
+	/*
+	 * Here 'names' contains only the pack(s) that were just
+	 * written, which is exactly the packs we want to keep. Also
+	 * 'existing_kept_packs' already contains the packs in
+	 * 'keep_pack_list'.
+	 */
+	in = xfdopen(cmd.in, "w");
+	for_each_string_list_item(item, names)
+		fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
+	for_each_string_list_item(item, &existing->non_kept_packs)
+		fprintf(in, "%s.pack\n", item->string);
+	for_each_string_list_item(item, &existing->cruft_packs)
+		fprintf(in, "%s.pack\n", item->string);
+	caret = pack_kept_objects ? "" : "^";
+	for_each_string_list_item(item, &existing->kept_packs)
+		fprintf(in, "%s%s.pack\n", caret, item->string);
+	fclose(in);
+
+	return finish_pack_objects_cmd(&cmd, names, local);
+}
+
+static int existing_cruft_pack_cmp(const void *va, const void *vb)
+{
+	struct packed_git *a = *(struct packed_git **)va;
+	struct packed_git *b = *(struct packed_git **)vb;
+
+	if (a->pack_size < b->pack_size)
+		return -1;
+	if (a->pack_size > b->pack_size)
+		return 1;
+	return 0;
+}
+
+static void collapse_small_cruft_packs(FILE *in, size_t max_size,
+				       struct existing_packs *existing)
+{
+	struct packed_git **existing_cruft, *p;
+	struct strbuf buf = STRBUF_INIT;
+	size_t total_size = 0;
+	size_t existing_cruft_nr = 0;
+	size_t i;
+
+	ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr);
+
+	for (p = get_all_packs(the_repository); p; p = p->next) {
+		if (!(p->is_cruft && p->pack_local))
+			continue;
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, pack_basename(p));
+		strbuf_strip_suffix(&buf, ".pack");
+
+		if (!string_list_has_string(&existing->cruft_packs, buf.buf))
+			continue;
+
+		if (existing_cruft_nr >= existing->cruft_packs.nr)
+			BUG("too many cruft packs (found %"PRIuMAX", but knew "
+			    "of %"PRIuMAX")",
+			    (uintmax_t)existing_cruft_nr + 1,
+			    (uintmax_t)existing->cruft_packs.nr);
+		existing_cruft[existing_cruft_nr++] = p;
+	}
+
+	QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp);
+
+	for (i = 0; i < existing_cruft_nr; i++) {
+		size_t proposed;
+
+		p = existing_cruft[i];
+		proposed = st_add(total_size, p->pack_size);
+
+		if (proposed <= max_size) {
+			total_size = proposed;
+			fprintf(in, "-%s\n", pack_basename(p));
+		} else {
+			retain_cruft_pack(existing, p);
+			fprintf(in, "%s\n", pack_basename(p));
+		}
+	}
+
+	for (i = 0; i < existing->non_kept_packs.nr; i++)
+		fprintf(in, "-%s.pack\n",
+			existing->non_kept_packs.items[i].string);
+
+	strbuf_release(&buf);
+	free(existing_cruft);
+}
+
 static int write_cruft_pack(const struct pack_objects_args *args,
 			    const char *destination,
 			    const char *pack_prefix,
 			    const char *cruft_expiration,
 			    struct string_list *names,
-			    struct string_list *existing_packs,
-			    struct string_list *existing_kept_packs)
+			    struct existing_packs *existing)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
-	struct strbuf line = STRBUF_INIT;
 	struct string_list_item *item;
-	FILE *in, *out;
+	FILE *in;
 	int ret;
 	const char *scratch;
 	int local = skip_prefix(destination, packdir, &scratch);
@@ -681,7 +1063,6 @@
 
 	strvec_push(&cmd.args, "--honor-pack-keep");
 	strvec_push(&cmd.args, "--non-empty");
-	strvec_push(&cmd.args, "--max-pack-size=0");
 
 	cmd.in = -1;
 
@@ -705,33 +1086,30 @@
 	in = xfdopen(cmd.in, "w");
 	for_each_string_list_item(item, names)
 		fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
-	for_each_string_list_item(item, existing_packs)
-		fprintf(in, "-%s.pack\n", item->string);
-	for_each_string_list_item(item, existing_kept_packs)
+	if (args->max_pack_size && !cruft_expiration) {
+		collapse_small_cruft_packs(in, args->max_pack_size, existing);
+	} else {
+		for_each_string_list_item(item, &existing->non_kept_packs)
+			fprintf(in, "-%s.pack\n", item->string);
+		for_each_string_list_item(item, &existing->cruft_packs)
+			fprintf(in, "-%s.pack\n", item->string);
+	}
+	for_each_string_list_item(item, &existing->kept_packs)
 		fprintf(in, "%s.pack\n", item->string);
 	fclose(in);
 
-	out = xfdopen(cmd.out, "r");
-	while (strbuf_getline_lf(&line, out) != EOF) {
-		struct string_list_item *item;
+	return finish_pack_objects_cmd(&cmd, names, local);
+}
 
-		if (line.len != the_hash_algo->hexsz)
-			die(_("repack: Expecting full hex object ID lines only "
-			      "from pack-objects."));
-		/*
-		 * avoid putting packs written outside of the repository in the
-		 * list of names
-		 */
-		if (local) {
-			item = string_list_append(names, line.buf);
-			item->util = populate_pack_exts(line.buf);
-		}
-	}
-	fclose(out);
-
-	strbuf_release(&line);
-
-	return finish_command(&cmd);
+static const char *find_pack_prefix(const char *packdir, const char *packtmp)
+{
+	const char *pack_prefix;
+	if (!skip_prefix(packtmp, packdir, &pack_prefix))
+		die(_("pack prefix %s does not begin with objdir %s"),
+		    packtmp, packdir);
+	if (*pack_prefix == '/')
+		pack_prefix++;
+	return pack_prefix;
 }
 
 int cmd_repack(int argc, const char **argv, const char *prefix)
@@ -739,13 +1117,10 @@
 	struct child_process cmd = CHILD_PROCESS_INIT;
 	struct string_list_item *item;
 	struct string_list names = STRING_LIST_INIT_DUP;
-	struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
-	struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
-	struct pack_geometry *geometry = NULL;
-	struct strbuf line = STRBUF_INIT;
+	struct existing_packs existing = EXISTING_PACKS_INIT;
+	struct pack_geometry geometry = { 0 };
 	struct tempfile *refs_snapshot = NULL;
 	int i, ext, ret;
-	FILE *out;
 	int show_progress;
 
 	/* variables to be filled by option parsing */
@@ -755,10 +1130,10 @@
 	struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
 	struct pack_objects_args po_args = {NULL};
 	struct pack_objects_args cruft_po_args = {NULL};
-	int geometric_factor = 0;
 	int write_midx = 0;
 	const char *cruft_expiration = NULL;
 	const char *expire_to = NULL;
+	const char *filter_to = NULL;
 
 	struct option builtin_repack_options[] = {
 		OPT_BIT('a', NULL, &pack_everything,
@@ -770,7 +1145,9 @@
 				N_("same as -a, pack unreachable cruft objects separately"),
 				   PACK_CRUFT),
 		OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
-				N_("with -C, expire objects older than this")),
+				N_("with --cruft, expire objects older than this")),
+		OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size,
+				N_("with --cruft, limit the size of new cruft packs")),
 		OPT_BOOL('d', NULL, &delete_redundant,
 				N_("remove redundant packs, and run git-prune-packed")),
 		OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
@@ -798,21 +1175,26 @@
 				N_("limits the maximum delta depth")),
 		OPT_STRING(0, "threads", &po_args.threads, N_("n"),
 				N_("limits the maximum number of threads")),
-		OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"),
+		OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size,
 				N_("maximum size of each packfile")),
+		OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options),
 		OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
 				N_("repack objects in packs marked with .keep")),
 		OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
 				N_("do not repack this pack")),
-		OPT_INTEGER('g', "geometric", &geometric_factor,
+		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_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"),
+			   N_("pack prefix to store a pack containing filtered out objects")),
 		OPT_END()
 	};
 
+	list_objects_filter_init(&po_args.filter_options);
+
 	git_config(repack_config, &cruft_po_args);
 
 	argc = parse_options(argc, argv, prefix, builtin_repack_options,
@@ -821,19 +1203,13 @@
 	if (delete_redundant && repository_format_precious_objects)
 		die(_("cannot delete packs in a precious-objects repo"));
 
-	if (keep_unreachable &&
-	    (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
-		die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
+	die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A",
+				  keep_unreachable, "-k/--keep-unreachable",
+				  pack_everything & PACK_CRUFT, "--cruft");
 
-	if (pack_everything & PACK_CRUFT) {
+	if (pack_everything & PACK_CRUFT)
 		pack_everything |= ALL_INTO_ONE;
 
-		if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))
-			die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A");
-		if (keep_unreachable)
-			die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k");
-	}
-
 	if (write_bitmaps < 0) {
 		if (!write_midx &&
 		    (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
@@ -849,6 +1225,18 @@
 	if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
 		die(_(incremental_bitmap_conflict_error));
 
+	if (write_bitmaps && po_args.local && has_alt_odb(the_repository)) {
+		/*
+		 * When asked to do a local repack, but we have
+		 * packfiles that are inherited from an alternate, then
+		 * we cannot guarantee that the multi-pack-index would
+		 * have full coverage of all objects. We thus disable
+		 * writing bitmaps in that case.
+		 */
+		warning(_("disabling bitmap writing, as some objects are not being packed"));
+		write_bitmaps = 0;
+	}
+
 	if (write_midx && write_bitmaps) {
 		struct strbuf path = STRBUF_INIT;
 
@@ -865,14 +1253,13 @@
 	packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid());
 	packtmp = mkpathdup("%s/%s", packdir, packtmp_name);
 
-	collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
-			       &keep_pack_list);
+	collect_pack_filenames(&existing, &keep_pack_list);
 
-	if (geometric_factor) {
+	if (geometry.split_factor) {
 		if (pack_everything)
 			die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
-		init_pack_geometry(&geometry, &existing_kept_packs);
-		split_pack_geometry(geometry, geometric_factor);
+		init_pack_geometry(&geometry, &existing, &po_args);
+		split_pack_geometry(&geometry);
 	}
 
 	prepare_pack_objects(&cmd, &po_args, packtmp);
@@ -886,7 +1273,7 @@
 		strvec_pushf(&cmd.args, "--keep-pack=%s",
 			     keep_pack_list.items[i].string);
 	strvec_push(&cmd.args, "--non-empty");
-	if (!geometry) {
+	if (!geometry.split_factor) {
 		/*
 		 * We need to grab all reachable objects, including those that
 		 * are reachable from reflogs and the index.
@@ -901,7 +1288,7 @@
 		strvec_push(&cmd.args, "--reflog");
 		strvec_push(&cmd.args, "--indexed-objects");
 	}
-	if (has_promisor_remote())
+	if (repo_has_promisor_remote(the_repository))
 		strvec_push(&cmd.args, "--exclude-promisor-objects");
 	if (!write_midx) {
 		if (write_bitmaps > 0)
@@ -915,7 +1302,8 @@
 	if (pack_everything & ALL_INTO_ONE) {
 		repack_promisor_objects(&po_args, &names);
 
-		if (existing_nonkept_packs.nr && delete_redundant &&
+		if (has_existing_non_kept_packs(&existing) &&
+		    delete_redundant &&
 		    !(pack_everything & PACK_CRUFT)) {
 			for_each_string_list_item(item, &names) {
 				strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
@@ -933,7 +1321,7 @@
 				strvec_push(&cmd.args, "--pack-loose-unreachable");
 			}
 		}
-	} else if (geometry) {
+	} else if (geometry.split_factor) {
 		strvec_push(&cmd.args, "--stdin-packs");
 		strvec_push(&cmd.args, "--unpacked");
 	} else {
@@ -941,7 +1329,13 @@
 		strvec_push(&cmd.args, "--incremental");
 	}
 
-	if (geometry)
+	if (po_args.filter_options.choice)
+		strvec_pushf(&cmd.args, "--filter=%s",
+			     expand_list_objects_filter_spec(&po_args.filter_options));
+	else if (filter_to)
+		die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter");
+
+	if (geometry.split_factor)
 		cmd.in = -1;
 	else
 		cmd.no_stdin = 1;
@@ -950,32 +1344,21 @@
 	if (ret)
 		goto cleanup;
 
-	if (geometry) {
+	if (geometry.split_factor) {
 		FILE *in = xfdopen(cmd.in, "w");
 		/*
 		 * The resulting pack should contain all objects in packs that
 		 * are going to be rolled up, but exclude objects in packs which
 		 * are being left alone.
 		 */
-		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 = 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]));
 		fclose(in);
 	}
 
-	out = xfdopen(cmd.out, "r");
-	while (strbuf_getline_lf(&line, out) != EOF) {
-		struct string_list_item *item;
-
-		if (line.len != the_hash_algo->hexsz)
-			die(_("repack: Expecting full hex object ID lines only from pack-objects."));
-		item = string_list_append(&names, line.buf);
-		item->util = populate_pack_exts(item->string);
-	}
-	strbuf_release(&line);
-	fclose(out);
-	ret = finish_command(&cmd);
+	ret = finish_pack_objects_cmd(&cmd, &names, 1);
 	if (ret)
 		goto cleanup;
 
@@ -983,12 +1366,7 @@
 		printf_ln(_("Nothing new to pack."));
 
 	if (pack_everything & PACK_CRUFT) {
-		const char *pack_prefix;
-		if (!skip_prefix(packtmp, packdir, &pack_prefix))
-			die(_("pack prefix %s does not begin with objdir %s"),
-			    packtmp, packdir);
-		if (*pack_prefix == '/')
-			pack_prefix++;
+		const char *pack_prefix = find_pack_prefix(packdir, packtmp);
 
 		if (!cruft_po_args.window)
 			cruft_po_args.window = po_args.window;
@@ -998,14 +1376,15 @@
 			cruft_po_args.depth = po_args.depth;
 		if (!cruft_po_args.threads)
 			cruft_po_args.threads = po_args.threads;
+		if (!cruft_po_args.max_pack_size)
+			cruft_po_args.max_pack_size = po_args.max_pack_size;
 
 		cruft_po_args.local = po_args.local;
 		cruft_po_args.quiet = po_args.quiet;
 
 		ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix,
 				       cruft_expiration, &names,
-				       &existing_nonkept_packs,
-				       &existing_kept_packs);
+				       &existing);
 		if (ret)
 			goto cleanup;
 
@@ -1036,13 +1415,25 @@
 					       pack_prefix,
 					       NULL,
 					       &names,
-					       &existing_nonkept_packs,
-					       &existing_kept_packs);
+					       &existing);
 			if (ret)
 				goto cleanup;
 		}
 	}
 
+	if (po_args.filter_options.choice) {
+		if (!filter_to)
+			filter_to = packtmp;
+
+		ret = write_filtered_pack(&po_args,
+					  filter_to,
+					  find_pack_prefix(packdir, packtmp),
+					  &existing,
+					  &names);
+		if (ret)
+			goto cleanup;
+	}
+
 	string_list_sort(&names);
 
 	close_object_store(the_repository->objects);
@@ -1081,31 +1472,14 @@
 	}
 	/* End of pack replacement. */
 
-	if (delete_redundant && pack_everything & ALL_INTO_ONE) {
-		const int hexsz = the_hash_algo->hexsz;
-		for_each_string_list_item(item, &existing_nonkept_packs) {
-			char *sha1;
-			size_t len = strlen(item->string);
-			if (len < hexsz)
-				continue;
-			sha1 = item->string + len - hexsz;
-			/*
-			 * Mark this pack for deletion, which ensures that this
-			 * pack won't be included in a MIDX (if `--write-midx`
-			 * was given) and that we will actually delete this pack
-			 * (if `-d` was given).
-			 */
-			if (!string_list_has_string(&names, sha1))
-				item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK);
-		}
-	}
+	if (delete_redundant && pack_everything & ALL_INTO_ONE)
+		mark_packs_for_deletion(&existing, &names);
 
 	if (write_midx) {
 		struct string_list include = STRING_LIST_INIT_NODUP;
-		midx_included_packs(&include, &existing_nonkept_packs,
-				    &existing_kept_packs, &names, geometry);
+		midx_included_packs(&include, &existing, &names, &geometry);
 
-		ret = write_midx_included_packs(&include, geometry,
+		ret = write_midx_included_packs(&include, &geometry, &names,
 						refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
 						show_progress, write_bitmaps > 0);
 
@@ -1122,35 +1496,11 @@
 
 	if (delete_redundant) {
 		int opts = 0;
-		for_each_string_list_item(item, &existing_nonkept_packs) {
-			if (!((uintptr_t)item->util & DELETE_PACK))
-				continue;
-			remove_redundant_pack(packdir, item->string);
-		}
+		remove_redundant_existing_packs(&existing);
 
-		if (geometry) {
-			struct strbuf buf = STRBUF_INIT;
-
-			uint32_t i;
-			for (i = 0; i < geometry->split; i++) {
-				struct packed_git *p = geometry->pack[i];
-				if (string_list_has_string(&names,
-							   hash_to_hex(p->hash)))
-					continue;
-
-				strbuf_reset(&buf);
-				strbuf_addstr(&buf, pack_basename(p));
-				strbuf_strip_suffix(&buf, ".pack");
-
-				if ((p->pack_keep) ||
-				    (string_list_has_string(&existing_kept_packs,
-							    buf.buf)))
-					continue;
-
-				remove_redundant_pack(packdir, buf.buf);
-			}
-			strbuf_release(&buf);
-		}
+		if (geometry.split_factor)
+			geometry_remove_redundant_packs(&geometry, &names,
+							&existing);
 		if (show_progress)
 			opts |= PRUNE_PACKED_VERBOSE;
 		prune_packed_objects(opts);
@@ -1174,9 +1524,9 @@
 
 cleanup:
 	string_list_clear(&names, 1);
-	string_list_clear(&existing_nonkept_packs, 0);
-	string_list_clear(&existing_kept_packs, 0);
-	clear_pack_geometry(geometry);
+	existing_packs_release(&existing);
+	free_pack_geometry(&geometry);
+	list_objects_filter_release(&po_args.filter_options);
 
 	return ret;
 }
diff --git a/builtin/replace.c b/builtin/replace.c
index a29e911..da59600 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -8,15 +8,23 @@
  * git-tag.sh and mktag.c by Linus Torvalds.
  */
 
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "path.h"
 #include "run-command.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "replace-object.h"
 #include "repository.h"
 #include "tag.h"
+#include "wildmatch.h"
 
 static const char * const git_replace_usage[] = {
 	N_("git replace [-f] <object> <replacement>"),
@@ -41,7 +49,7 @@
 
 static int show_reference(struct repository *r, const char *refname,
 			  const struct object_id *oid,
-			  int flag, void *cb_data)
+			  int flag UNUSED, void *cb_data)
 {
 	struct show_data *data = cb_data;
 
@@ -54,7 +62,7 @@
 			struct object_id object;
 			enum object_type obj_type, repl_type;
 
-			if (get_oid(refname, &object))
+			if (repo_get_oid(r, refname, &object))
 				return error(_("failed to resolve '%s' as a valid ref"), refname);
 
 			obj_type = oid_object_info(r, &object, NULL);
@@ -112,7 +120,7 @@
 	base_len = ref.len;
 
 	for (p = argv; *p; p++) {
-		if (get_oid(*p, &oid)) {
+		if (repo_get_oid(the_repository, *p, &oid)) {
 			error("failed to resolve '%s' as a valid ref", *p);
 			had_error = 1;
 			continue;
@@ -206,10 +214,10 @@
 {
 	struct object_id object, repl;
 
-	if (get_oid(object_ref, &object))
+	if (repo_get_oid(the_repository, object_ref, &object))
 		return error(_("failed to resolve '%s' as a valid ref"),
 			     object_ref);
-	if (get_oid(replace_ref, &repl))
+	if (repo_get_oid(the_repository, replace_ref, &repl))
 		return error(_("failed to resolve '%s' as a valid ref"),
 			     replace_ref);
 
@@ -320,7 +328,7 @@
 	struct object_id old_oid, new_oid, prev;
 	struct strbuf ref = STRBUF_INIT;
 
-	if (get_oid(object_ref, &old_oid) < 0)
+	if (repo_get_oid(the_repository, object_ref, &old_oid) < 0)
 		return error(_("not a valid object name: '%s'"), object_ref);
 
 	type = oid_object_info(the_repository, &old_oid, NULL);
@@ -375,7 +383,7 @@
 		struct object_id oid;
 		struct commit *commit;
 
-		if (get_oid(argv[i], &oid) < 0) {
+		if (repo_get_oid(the_repository, argv[i], &oid) < 0) {
 			strbuf_release(&new_parents);
 			return error(_("not a valid object name: '%s'"),
 				     argv[i]);
@@ -401,7 +409,7 @@
 	const char **argv;
 };
 
-static int check_one_mergetag(struct commit *commit,
+static int check_one_mergetag(struct commit *commit UNUSED,
 			       struct commit_extra_header *extra,
 			       void *data)
 {
@@ -422,7 +430,7 @@
 	/* iterate over new parents */
 	for (i = 1; i < mergetag_data->argc; i++) {
 		struct object_id oid;
-		if (get_oid(mergetag_data->argv[i], &oid) < 0)
+		if (repo_get_oid(the_repository, mergetag_data->argv[i], &oid) < 0)
 			return error(_("not a valid object name: '%s'"),
 				     mergetag_data->argv[i]);
 		if (oideq(get_tagged_oid(tag), &oid))
@@ -452,15 +460,15 @@
 	const char *buffer;
 	unsigned long size;
 
-	if (get_oid(old_ref, &old_oid) < 0)
+	if (repo_get_oid(the_repository, old_ref, &old_oid) < 0)
 		return error(_("not a valid object name: '%s'"), old_ref);
 	commit = lookup_commit_reference(the_repository, &old_oid);
 	if (!commit)
 		return error(_("could not parse %s"), old_ref);
 
-	buffer = get_commit_buffer(commit, &size);
+	buffer = repo_get_commit_buffer(the_repository, commit, &size);
 	strbuf_add(&buf, buffer, size);
-	unuse_commit_buffer(commit, buffer);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 
 	if (replace_parents(&buf, argc - 1, &argv[1]) < 0) {
 		strbuf_release(&buf);
@@ -559,7 +567,7 @@
 		OPT_END()
 	};
 
-	read_replace_refs = 0;
+	disable_replace_refs();
 	git_config(git_default_config, NULL);
 
 	argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
diff --git a/builtin/replay.c b/builtin/replay.c
new file mode 100644
index 0000000..6bc4b47
--- /dev/null
+++ b/builtin/replay.c
@@ -0,0 +1,446 @@
+/*
+ * "git replay" builtin command
+ */
+
+#define USE_THE_INDEX_VARIABLE
+#include "git-compat-util.h"
+
+#include "builtin.h"
+#include "environment.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "merge-ort.h"
+#include "object-name.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "revision.h"
+#include "strmap.h"
+#include <oidset.h>
+#include <tree.h>
+
+static const char *short_commit_name(struct commit *commit)
+{
+	return repo_find_unique_abbrev(the_repository, &commit->object.oid,
+				       DEFAULT_ABBREV);
+}
+
+static struct commit *peel_committish(const char *name)
+{
+	struct object *obj;
+	struct object_id oid;
+
+	if (repo_get_oid(the_repository, name, &oid))
+		return NULL;
+	obj = parse_object(the_repository, &oid);
+	return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj,
+						  OBJ_COMMIT);
+}
+
+static char *get_author(const char *message)
+{
+	size_t len;
+	const char *a;
+
+	a = find_commit_header(message, "author", &len);
+	if (a)
+		return xmemdupz(a, len);
+
+	return NULL;
+}
+
+static struct commit *create_commit(struct tree *tree,
+				    struct commit *based_on,
+				    struct commit *parent)
+{
+	struct object_id ret;
+	struct object *obj;
+	struct commit_list *parents = NULL;
+	char *author;
+	char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
+	struct commit_extra_header *extra;
+	struct strbuf msg = STRBUF_INIT;
+	const char *out_enc = get_commit_output_encoding();
+	const char *message = repo_logmsg_reencode(the_repository, based_on,
+						   NULL, out_enc);
+	const char *orig_message = NULL;
+	const char *exclude_gpgsig[] = { "gpgsig", NULL };
+
+	commit_list_insert(parent, &parents);
+	extra = read_commit_extra_headers(based_on, exclude_gpgsig);
+	find_commit_subject(message, &orig_message);
+	strbuf_addstr(&msg, orig_message);
+	author = get_author(message);
+	reset_ident_date();
+	if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
+				 &ret, author, NULL, sign_commit, extra)) {
+		error(_("failed to write commit object"));
+		return NULL;
+	}
+	free(author);
+	strbuf_release(&msg);
+
+	obj = parse_object(the_repository, &ret);
+	return (struct commit *)obj;
+}
+
+struct ref_info {
+	struct commit *onto;
+	struct strset positive_refs;
+	struct strset negative_refs;
+	int positive_refexprs;
+	int negative_refexprs;
+};
+
+static void get_ref_information(struct rev_cmdline_info *cmd_info,
+				struct ref_info *ref_info)
+{
+	int i;
+
+	ref_info->onto = NULL;
+	strset_init(&ref_info->positive_refs);
+	strset_init(&ref_info->negative_refs);
+	ref_info->positive_refexprs = 0;
+	ref_info->negative_refexprs = 0;
+
+	/*
+	 * When the user specifies e.g.
+	 *   git replay origin/main..mybranch
+	 *   git replay ^origin/next mybranch1 mybranch2
+	 * we want to be able to determine where to replay the commits.  In
+	 * these examples, the branches are probably based on an old version
+	 * of either origin/main or origin/next, so we want to replay on the
+	 * newest version of that branch.  In contrast we would want to error
+	 * out if they ran
+	 *   git replay ^origin/master ^origin/next mybranch
+	 *   git replay mybranch~2..mybranch
+	 * the first of those because there's no unique base to choose, and
+	 * the second because they'd likely just be replaying commits on top
+	 * of the same commit and not making any difference.
+	 */
+	for (i = 0; i < cmd_info->nr; i++) {
+		struct rev_cmdline_entry *e = cmd_info->rev + i;
+		struct object_id oid;
+		const char *refexpr = e->name;
+		char *fullname = NULL;
+		int can_uniquely_dwim = 1;
+
+		if (*refexpr == '^')
+			refexpr++;
+		if (repo_dwim_ref(the_repository, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1)
+			can_uniquely_dwim = 0;
+
+		if (e->flags & BOTTOM) {
+			if (can_uniquely_dwim)
+				strset_add(&ref_info->negative_refs, fullname);
+			if (!ref_info->negative_refexprs)
+				ref_info->onto = lookup_commit_reference_gently(the_repository,
+										&e->item->oid, 1);
+			ref_info->negative_refexprs++;
+		} else {
+			if (can_uniquely_dwim)
+				strset_add(&ref_info->positive_refs, fullname);
+			ref_info->positive_refexprs++;
+		}
+
+		free(fullname);
+	}
+}
+
+static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
+				  const char *onto_name,
+				  const char **advance_name,
+				  struct commit **onto,
+				  struct strset **update_refs)
+{
+	struct ref_info rinfo;
+
+	get_ref_information(cmd_info, &rinfo);
+	if (!rinfo.positive_refexprs)
+		die(_("need some commits to replay"));
+	if (onto_name && *advance_name)
+		die(_("--onto and --advance are incompatible"));
+	else if (onto_name) {
+		*onto = peel_committish(onto_name);
+		if (rinfo.positive_refexprs <
+		    strset_get_size(&rinfo.positive_refs))
+			die(_("all positive revisions given must be references"));
+	} else if (*advance_name) {
+		struct object_id oid;
+		char *fullname = NULL;
+
+		*onto = peel_committish(*advance_name);
+		if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name),
+			     &oid, &fullname, 0) == 1) {
+			*advance_name = fullname;
+		} else {
+			die(_("argument to --advance must be a reference"));
+		}
+		if (rinfo.positive_refexprs > 1)
+			die(_("cannot advance target with multiple sources because ordering would be ill-defined"));
+	} else {
+		int positive_refs_complete = (
+			rinfo.positive_refexprs ==
+			strset_get_size(&rinfo.positive_refs));
+		int negative_refs_complete = (
+			rinfo.negative_refexprs ==
+			strset_get_size(&rinfo.negative_refs));
+		/*
+		 * We need either positive_refs_complete or
+		 * negative_refs_complete, but not both.
+		 */
+		if (rinfo.negative_refexprs > 0 &&
+		    positive_refs_complete == negative_refs_complete)
+			die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+		if (negative_refs_complete) {
+			struct hashmap_iter iter;
+			struct strmap_entry *entry;
+
+			if (rinfo.negative_refexprs == 0)
+				die(_("all positive revisions given must be references"));
+			else if (rinfo.negative_refexprs > 1)
+				die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+			else if (rinfo.positive_refexprs > 1)
+				die(_("cannot advance target with multiple source branches because ordering would be ill-defined"));
+
+			/* Only one entry, but we have to loop to get it */
+			strset_for_each_entry(&rinfo.negative_refs,
+					      &iter, entry) {
+				*advance_name = entry->key;
+			}
+		} else { /* positive_refs_complete */
+			if (rinfo.negative_refexprs > 1)
+				die(_("cannot implicitly determine correct base for --onto"));
+			if (rinfo.negative_refexprs == 1)
+				*onto = rinfo.onto;
+		}
+	}
+	if (!*advance_name) {
+		*update_refs = xcalloc(1, sizeof(**update_refs));
+		**update_refs = rinfo.positive_refs;
+		memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
+	}
+	strset_clear(&rinfo.negative_refs);
+	strset_clear(&rinfo.positive_refs);
+}
+
+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);
+	if (pos == kh_end(replayed_commits))
+		return fallback;
+	return kh_value(replayed_commits, pos);
+}
+
+static struct commit *pick_regular_commit(struct commit *pickme,
+					  kh_oid_map_t *replayed_commits,
+					  struct commit *onto,
+					  struct merge_options *merge_opt,
+					  struct merge_result *result)
+{
+	struct commit *base, *replayed_base;
+	struct tree *pickme_tree, *base_tree;
+
+	base = pickme->parents->item;
+	replayed_base = mapped_commit(replayed_commits, base, onto);
+
+	result->tree = repo_get_commit_tree(the_repository, replayed_base);
+	pickme_tree = repo_get_commit_tree(the_repository, pickme);
+	base_tree = repo_get_commit_tree(the_repository, base);
+
+	merge_opt->branch1 = short_commit_name(replayed_base);
+	merge_opt->branch2 = short_commit_name(pickme);
+	merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+
+	merge_incore_nonrecursive(merge_opt,
+				  base_tree,
+				  result->tree,
+				  pickme_tree,
+				  result);
+
+	free((char*)merge_opt->ancestor);
+	merge_opt->ancestor = NULL;
+	if (!result->clean)
+		return NULL;
+	return create_commit(result->tree, pickme, replayed_base);
+}
+
+int cmd_replay(int argc, const char **argv, const char *prefix)
+{
+	const char *advance_name = NULL;
+	struct commit *onto = NULL;
+	const char *onto_name = NULL;
+	int contained = 0;
+
+	struct rev_info revs;
+	struct commit *last_commit = NULL;
+	struct commit *commit;
+	struct merge_options merge_opt;
+	struct merge_result result;
+	struct strset *update_refs = NULL;
+	kh_oid_map_t *replayed_commits;
+	int ret = 0;
+
+	const char * const replay_usage[] = {
+		N_("(EXPERIMENTAL!) git replay "
+		   "([--contained] --onto <newbase> | --advance <branch>) "
+		   "<revision-range>..."),
+		NULL
+	};
+	struct option replay_options[] = {
+		OPT_STRING(0, "advance", &advance_name,
+			   N_("branch"),
+			   N_("make replay advance given branch")),
+		OPT_STRING(0, "onto", &onto_name,
+			   N_("revision"),
+			   N_("replay onto given commit")),
+		OPT_BOOL(0, "contained", &contained,
+			 N_("advance all branches contained in revision-range")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, replay_options, replay_usage,
+			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+	if (!onto_name && !advance_name) {
+		error(_("option --onto or --advance is mandatory"));
+		usage_with_options(replay_usage, replay_options);
+	}
+
+	if (advance_name && contained)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--advance", "--contained");
+
+	repo_init_revisions(the_repository, &revs, prefix);
+
+	/*
+	 * Set desired values for rev walking options here. If they
+	 * are changed by some user specified option in setup_revisions()
+	 * below, we will detect that below and then warn.
+	 *
+	 * TODO: In the future we might want to either die(), or allow
+	 * some options changing these values if we think they could
+	 * be useful.
+	 */
+	revs.reverse = 1;
+	revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+	revs.topo_order = 1;
+	revs.simplify_history = 0;
+
+	argc = setup_revisions(argc, argv, &revs, NULL);
+	if (argc > 1) {
+		ret = error(_("unrecognized argument: %s"), argv[1]);
+		goto cleanup;
+	}
+
+	/*
+	 * Detect and warn if we override some user specified rev
+	 * walking options.
+	 */
+	if (revs.reverse != 1) {
+		warning(_("some rev walking options will be overridden as "
+			  "'%s' bit in 'struct rev_info' will be forced"),
+			"reverse");
+		revs.reverse = 1;
+	}
+	if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) {
+		warning(_("some rev walking options will be overridden as "
+			  "'%s' bit in 'struct rev_info' will be forced"),
+			"sort_order");
+		revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+	}
+	if (revs.topo_order != 1) {
+		warning(_("some rev walking options will be overridden as "
+			  "'%s' bit in 'struct rev_info' will be forced"),
+			"topo_order");
+		revs.topo_order = 1;
+	}
+	if (revs.simplify_history != 0) {
+		warning(_("some rev walking options will be overridden as "
+			  "'%s' bit in 'struct rev_info' will be forced"),
+			"simplify_history");
+		revs.simplify_history = 0;
+	}
+
+	determine_replay_mode(&revs.cmdline, onto_name, &advance_name,
+			      &onto, &update_refs);
+
+	if (!onto) /* FIXME: Should handle replaying down to root commit */
+		die("Replaying down to root commit is not supported yet!");
+
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
+
+	init_merge_options(&merge_opt, the_repository);
+	memset(&result, 0, sizeof(result));
+	merge_opt.show_rename_progress = 0;
+	last_commit = onto;
+	replayed_commits = kh_init_oid_map();
+	while ((commit = get_revision(&revs))) {
+		const struct name_decoration *decoration;
+		khint_t pos;
+		int hr;
+
+		if (!commit->parents)
+			die(_("replaying down to root commit is not supported yet!"));
+		if (commit->parents->next)
+			die(_("replaying merge commits is not supported yet!"));
+
+		last_commit = pick_regular_commit(commit, replayed_commits, onto,
+						  &merge_opt, &result);
+		if (!last_commit)
+			break;
+
+		/* Record commit -> last_commit mapping */
+		pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr);
+		if (hr == 0)
+			BUG("Duplicate rewritten commit: %s\n",
+			    oid_to_hex(&commit->object.oid));
+		kh_value(replayed_commits, pos) = last_commit;
+
+		/* Update any necessary branches */
+		if (advance_name)
+			continue;
+		decoration = get_name_decoration(&commit->object);
+		if (!decoration)
+			continue;
+		while (decoration) {
+			if (decoration->type == DECORATION_REF_LOCAL &&
+			    (contained || strset_contains(update_refs,
+							  decoration->name))) {
+				printf("update %s %s %s\n",
+				       decoration->name,
+				       oid_to_hex(&last_commit->object.oid),
+				       oid_to_hex(&commit->object.oid));
+			}
+			decoration = decoration->next;
+		}
+	}
+
+	/* In --advance mode, advance the target ref */
+	if (result.clean == 1 && advance_name) {
+		printf("update %s %s %s\n",
+		       advance_name,
+		       oid_to_hex(&last_commit->object.oid),
+		       oid_to_hex(&onto->object.oid));
+	}
+
+	merge_finalize(&merge_opt, &result);
+	kh_destroy_oid_map(replayed_commits);
+	if (update_refs) {
+		strset_clear(update_refs);
+		free(update_refs);
+	}
+	ret = result.clean;
+
+cleanup:
+	release_revisions(&revs);
+
+	/* Return */
+	if (ret < 0)
+		exit(128);
+	return ret ? 0 : 1;
+}
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 94ffb8c..b2efc6f 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -1,8 +1,8 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
-#include "dir.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff/xdiff.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 0697fa8..1d62ff6 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -9,22 +9,30 @@
  */
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
-#include "tag.h"
 #include "object.h"
 #include "pretty.h"
-#include "run-command.h"
 #include "refs.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "tree.h"
 #include "branch.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "unpack-trees.h"
 #include "cache-tree.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
+#include "trace.h"
+#include "trace2.h"
 #include "dir.h"
 #include "add-interactive.h"
 
@@ -89,7 +97,7 @@
 
 	if (reset_type == KEEP) {
 		struct object_id head_oid;
-		if (get_oid("HEAD", &head_oid))
+		if (repo_get_oid(the_repository, "HEAD", &head_oid))
 			return error(_("You do not have a valid HEAD."));
 		if (!fill_tree_descriptor(the_repository, desc + nr, &head_oid))
 			return error(_("Failed to find tree of HEAD."));
@@ -108,6 +116,10 @@
 
 	if (reset_type == MIXED || reset_type == HARD) {
 		tree = parse_tree_indirect(oid);
+		if (!tree) {
+			error(_("unable to read tree (%s)"), oid_to_hex(oid));
+			goto out;
+		}
 		prime_cache_tree(the_repository, the_repository->index, tree);
 	}
 
@@ -124,7 +136,7 @@
 	struct strbuf buf = STRBUF_INIT;
 
 	printf(_("HEAD is now at %s"),
-		find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+		repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
 
 	pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
 	if (buf.len > 0)
@@ -260,8 +272,8 @@
 		 * has to be unambiguous. If there is a single argument, it
 		 * can not be a tree
 		 */
-		else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) ||
-			 (argv[1] && !get_oid_treeish(argv[0], &unused))) {
+		else if ((!argv[1] && !repo_get_oid_committish(the_repository, argv[0], &unused)) ||
+			 (argv[1] && !repo_get_oid_treeish(the_repository, argv[0], &unused))) {
 			/*
 			 * Ok, argv[0] looks like a commit/tree; it should not
 			 * be a filename.
@@ -273,7 +285,9 @@
 			verify_filename(prefix, argv[0], 1);
 		}
 	}
-	*rev_ret = rev;
+
+	/* treat '@' as a shortcut for 'HEAD' */
+	*rev_ret = !strcmp("@", rev) ? "HEAD" : rev;
 
 	parse_pathspec(pathspec, 0,
 		       PATHSPEC_PREFER_FULL |
@@ -288,9 +302,9 @@
 	struct object_id *orig = NULL, oid_orig,
 		*old_orig = NULL, oid_old_orig;
 
-	if (!get_oid("ORIG_HEAD", &oid_old_orig))
+	if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig))
 		old_orig = &oid_old_orig;
-	if (!get_oid("HEAD", &oid_orig)) {
+	if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
 		orig = &oid_orig;
 		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
 		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
@@ -304,12 +318,13 @@
 	return update_ref_status;
 }
 
-static int git_reset_config(const char *var, const char *value, void *cb)
+static int git_reset_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "submodule.recurse"))
 		return git_default_submodule_config(var, value, cb);
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
@@ -317,7 +332,8 @@
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
 	int no_refresh = 0;
 	int patch_mode = 0, pathspec_file_nul = 0, unborn;
-	const char *rev, *pathspec_from_file = NULL;
+	const char *rev;
+	char *pathspec_from_file = NULL;
 	struct object_id oid;
 	struct pathspec pathspec;
 	int intent_to_add = 0;
@@ -325,18 +341,25 @@
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "no-refresh", &no_refresh,
 				N_("skip refreshing the index after reset")),
-		OPT_SET_INT(0, "mixed", &reset_type,
-						N_("reset HEAD and index"), MIXED),
-		OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
-		OPT_SET_INT(0, "hard", &reset_type,
-				N_("reset HEAD, index and working tree"), HARD),
-		OPT_SET_INT(0, "merge", &reset_type,
-				N_("reset HEAD, index and working tree"), MERGE),
-		OPT_SET_INT(0, "keep", &reset_type,
-				N_("reset HEAD but keep local changes"), KEEP),
+		OPT_SET_INT_F(0, "mixed", &reset_type,
+			      N_("reset HEAD and index"),
+			      MIXED, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "soft", &reset_type,
+			      N_("reset only HEAD"),
+			      SOFT, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "hard", &reset_type,
+			      N_("reset HEAD, index and working tree"),
+			      HARD, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "merge", &reset_type,
+			      N_("reset HEAD, index and working tree"),
+			      MERGE, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "keep", &reset_type,
+			      N_("reset HEAD but keep local changes"),
+			      KEEP, PARSE_OPT_NONEG),
 		OPT_CALLBACK_F(0, "recurse-submodules", NULL,
-			    "reset", "control recursive updating of submodules",
-			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
+			       "reset", "control recursive updating of submodules",
+			       PARSE_OPT_OPTARG,
+			       option_parse_recurse_submodules_worktree_updater),
 		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
 		OPT_BOOL('N', "intent-to-add", &intent_to_add,
 				N_("record only the fact that removed paths will be added later")),
@@ -365,13 +388,14 @@
 		die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
 	}
 
-	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
+	unborn = !strcmp(rev, "HEAD") && repo_get_oid(the_repository, "HEAD",
+						      &oid);
 	if (unborn) {
 		/* reset on unborn branch: treat as reset to empty tree */
 		oidcpy(&oid, the_hash_algo->empty_tree);
 	} else if (!pathspec.nr && !patch_mode) {
 		struct commit *commit;
-		if (get_oid_committish(rev, &oid))
+		if (repo_get_oid_committish(the_repository, rev, &oid))
 			die(_("Failed to resolve '%s' as a valid revision."), rev);
 		commit = lookup_commit_reference(the_repository, &oid);
 		if (!commit)
@@ -379,7 +403,7 @@
 		oidcpy(&oid, &commit->object.oid);
 	} else {
 		struct tree *tree;
-		if (get_oid_treeish(rev, &oid))
+		if (repo_get_oid_treeish(the_repository, rev, &oid))
 			die(_("Failed to resolve '%s' as a valid tree."), rev);
 		tree = parse_tree_indirect(&oid);
 		if (!tree)
@@ -464,7 +488,8 @@
 			char *ref = NULL;
 			int err;
 
-			dwim_ref(rev, strlen(rev), &dummy, &ref, 0);
+			repo_dwim_ref(the_repository, rev, strlen(rev),
+				      &dummy, &ref, 0);
 			if (ref && !starts_with(ref, "refs/"))
 				FREE_AND_NULL(ref);
 
@@ -495,5 +520,6 @@
 
 cleanup:
 	clear_pathspec(&pathspec);
+	free(pathspec_from_file);
 	return update_ref_status;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index d42db0b..7780372 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -1,16 +1,18 @@
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "object.h"
-#include "object-store.h"
-#include "pack.h"
+#include "object-name.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "pack-bitmap.h"
-#include "builtin.h"
 #include "log-tree.h"
 #include "graph.h"
 #include "bisect.h"
@@ -38,7 +40,7 @@
 "    --tags\n"
 "    --remotes\n"
 "    --stdin\n"
-"    --exclude-hidden=[receive|uploadpack]\n"
+"    --exclude-hidden=[fetch|receive|uploadpack]\n"
 "    --quiet\n"
 "  ordering output:\n"
 "    --topo-order\n"
@@ -96,7 +98,48 @@
 	return size;
 }
 
-static void finish_commit(struct commit *commit);
+static inline void finish_object__ma(struct object *obj)
+{
+	/*
+	 * Whether or not we try to dynamically fetch missing objects
+	 * from the server, we currently DO NOT have the object.  We
+	 * can either print, allow (ignore), or conditionally allow
+	 * (ignore) them.
+	 */
+	switch (arg_missing_action) {
+	case MA_ERROR:
+		die("missing %s object '%s'",
+		    type_name(obj->type), oid_to_hex(&obj->oid));
+		return;
+
+	case MA_ALLOW_ANY:
+		return;
+
+	case MA_PRINT:
+		oidset_insert(&missing_objects, &obj->oid);
+		return;
+
+	case MA_ALLOW_PROMISOR:
+		if (is_promisor_object(&obj->oid))
+			return;
+		die("unexpected missing %s object '%s'",
+		    type_name(obj->type), oid_to_hex(&obj->oid));
+		return;
+
+	default:
+		BUG("unhandled missing_action");
+		return;
+	}
+}
+
+static void finish_commit(struct commit *commit)
+{
+	free_commit_list(commit->parents);
+	commit->parents = NULL;
+	free_commit_buffer(the_repository->parsed_objects,
+			   commit);
+}
+
 static void show_commit(struct commit *commit, void *data)
 {
 	struct rev_list_info *info = data;
@@ -104,6 +147,12 @@
 
 	display_progress(progress, ++progress_counter);
 
+	if (revs->do_not_die_on_missing_objects &&
+	    oidset_contains(&revs->missing_commits, &commit->object.oid)) {
+		finish_object__ma(&commit->object);
+		return;
+	}
+
 	if (show_disk_usage)
 		total_disk_usage += get_object_disk_usage(&commit->object);
 
@@ -134,7 +183,7 @@
 		if (!revs->graph)
 			fputs(get_revision_mark(revs, commit), stdout);
 		if (revs->abbrev_commit && revs->abbrev)
-			fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
+			fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, revs->abbrev),
 			      stdout);
 		else
 			fputs(oid_to_hex(&commit->object.oid), stdout);
@@ -170,6 +219,7 @@
 		ctx.fmt = revs->commit_format;
 		ctx.output_encoding = get_log_output_encoding();
 		ctx.color = revs->diffopt.use_color;
+		ctx.rev = revs;
 		pretty_print_commit(&ctx, commit, &buf);
 		if (buf.len) {
 			if (revs->commit_format != CMIT_FMT_ONELINE)
@@ -215,49 +265,8 @@
 	finish_commit(commit);
 }
 
-static void finish_commit(struct commit *commit)
-{
-	free_commit_list(commit->parents);
-	commit->parents = NULL;
-	free_commit_buffer(the_repository->parsed_objects,
-			   commit);
-}
-
-static inline void finish_object__ma(struct object *obj)
-{
-	/*
-	 * Whether or not we try to dynamically fetch missing objects
-	 * from the server, we currently DO NOT have the object.  We
-	 * can either print, allow (ignore), or conditionally allow
-	 * (ignore) them.
-	 */
-	switch (arg_missing_action) {
-	case MA_ERROR:
-		die("missing %s object '%s'",
-		    type_name(obj->type), oid_to_hex(&obj->oid));
-		return;
-
-	case MA_ALLOW_ANY:
-		return;
-
-	case MA_PRINT:
-		oidset_insert(&missing_objects, &obj->oid);
-		return;
-
-	case MA_ALLOW_PROMISOR:
-		if (is_promisor_object(&obj->oid))
-			return;
-		die("unexpected missing %s object '%s'",
-		    type_name(obj->type), oid_to_hex(&obj->oid));
-		return;
-
-	default:
-		BUG("unhandled missing_action");
-		return;
-	}
-}
-
-static int finish_object(struct object *obj, const char *name, void *cb_data)
+static int finish_object(struct object *obj, const char *name UNUSED,
+			 void *cb_data)
 {
 	struct rev_list_info *info = cb_data;
 	if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
@@ -362,11 +371,11 @@
 
 static int show_object_fast(
 	const struct object_id *oid,
-	enum object_type type,
-	int exclude,
-	uint32_t name_hash,
-	struct packed_git *found_pack,
-	off_t found_offset)
+	enum object_type type UNUSED,
+	int exclude UNUSED,
+	uint32_t name_hash UNUSED,
+	struct packed_git *found_pack UNUSED,
+	off_t found_offset UNUSED)
 {
 	fprintf(stdout, "%s\n", oid_to_hex(oid));
 	return 1;
@@ -537,6 +546,18 @@
 	 *
 	 * Let "--missing" to conditionally set fetch_if_missing.
 	 */
+	/*
+	 * NEEDSWORK: These loops that attempt to find presence of
+	 * options without understanding that the options they are
+	 * skipping are broken (e.g., it would not know "--grep
+	 * --exclude-promisor-objects" is not triggering
+	 * "--exclude-promisor-objects" option).  We really need
+	 * setup_revisions() to have a mechanism to allow and disallow
+	 * some sets of options for different commands (like rev-list,
+	 * replay, etc). Such a mechanism should do an early parsing
+	 * of options and be able to manage the `--missing=...` and
+	 * `--exclude-promisor-objects` options below.
+	 */
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		if (!strcmp(arg, "--exclude-promisor-objects")) {
@@ -556,7 +577,7 @@
 	}
 
 	if (arg_missing_action)
-		revs.do_not_die_on_missing_tree = 1;
+		revs.do_not_die_on_missing_objects = 1;
 
 	argc = setup_revisions(argc, argv, &revs, &s_r_opt);
 
@@ -745,8 +766,12 @@
 
 	if (arg_print_omitted)
 		oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
-	if (arg_missing_action == MA_PRINT)
+	if (arg_missing_action == MA_PRINT) {
 		oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+		/* Add missing tips */
+		oidset_insert_from_set(&missing_objects, &revs.missing_commits);
+		oidset_clear(&revs.missing_commits);
+	}
 
 	traverse_commit_list_filtered(
 		&revs, show_commit, show_object, &info,
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index e67999e..624182e 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -4,19 +4,28 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
+#include "abspath.h"
 #include "config.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "refs.h"
 #include "quote.h"
-#include "builtin.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "diff.h"
+#include "read-cache-ll.h"
 #include "revision.h"
+#include "setup.h"
 #include "split-index.h"
 #include "submodule.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "object-file-convert.h"
 
 #define DO_REVS		1
 #define DO_NOREV	2
@@ -136,7 +145,9 @@
 			struct object_id discard;
 			char *full;
 
-			switch (dwim_ref(name, strlen(name), &discard, &full, 0)) {
+			switch (repo_dwim_ref(the_repository, name,
+					      strlen(name), &discard, &full,
+					      0)) {
 			case 0:
 				/*
 				 * Not found -- not a ref.  We could
@@ -147,9 +158,12 @@
 				 */
 				break;
 			case 1: /* happy */
-				if (abbrev_ref)
+				if (abbrev_ref) {
+					char *old = full;
 					full = shorten_unambiguous_ref(full,
 						abbrev_ref_strict);
+					free(old);
+				}
 				show_with_type(type, full);
 				break;
 			default: /* ambiguous */
@@ -162,7 +176,8 @@
 		}
 	}
 	else if (abbrev)
-		show_with_type(type, find_unique_abbrev(oid, abbrev));
+		show_with_type(type,
+			       repo_find_unique_abbrev(the_repository, oid, abbrev));
 	else
 		show_with_type(type, oid_to_hex(oid));
 }
@@ -187,7 +202,7 @@
 		struct object_id oid;
 
 		def = NULL;
-		if (!get_oid(s, &oid)) {
+		if (!repo_get_oid(the_repository, s, &oid)) {
 			show_rev(NORMAL, &oid, s);
 			return 1;
 		}
@@ -211,7 +226,7 @@
 	return 0;
 }
 
-static int show_abbrev(const struct object_id *oid, void *cb_data)
+static int show_abbrev(const struct object_id *oid, void *cb_data UNUSED)
 {
 	show_rev(NORMAL, oid, NULL);
 	return 0;
@@ -279,11 +294,11 @@
 		return 0;
 	}
 
-	if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) {
+	if (!repo_get_oid_committish(the_repository, start, &start_oid) && !repo_get_oid_committish(the_repository, end, &end_oid)) {
 		show_rev(NORMAL, &end_oid, end);
 		show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
 		if (symmetric) {
-			struct commit_list *exclude;
+			struct commit_list *exclude = NULL;
 			struct commit *a, *b;
 			a = lookup_commit_reference(the_repository, &start_oid);
 			b = lookup_commit_reference(the_repository, &end_oid);
@@ -291,7 +306,8 @@
 				*dotdot = '.';
 				return 0;
 			}
-			exclude = get_merge_bases(a, b);
+			if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0)
+				exit(128);
 			while (exclude) {
 				struct commit *commit = pop_commit(&exclude);
 				show_rev(REVERSED, &commit->object.oid, NULL);
@@ -337,7 +353,7 @@
 		return 0;
 
 	*dotdot = 0;
-	if (get_oid_committish(arg, &oid) ||
+	if (repo_get_oid_committish(the_repository, arg, &oid) ||
 	    !(commit = lookup_commit_reference(the_repository, &oid))) {
 		*dotdot = '^';
 		return 0;
@@ -661,6 +677,8 @@
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
 	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+	const struct git_hash_algo *output_algo = NULL;
+	const struct git_hash_algo *compat = NULL;
 	int did_repo_setup = 0;
 	int has_dashdash = 0;
 	int output_prefix = 0;
@@ -732,6 +750,7 @@
 
 			prepare_repo_settings(the_repository);
 			the_repository->settings.command_requires_full_index = 0;
+			compat = the_repository->compat_hash_algo;
 		}
 
 		if (!strcmp(arg, "--")) {
@@ -819,6 +838,22 @@
 				flags |= GET_OID_QUIETLY;
 				continue;
 			}
+			if (opt_with_value(arg, "--output-object-format", &arg)) {
+				if (!arg)
+					die(_("no object format specified"));
+				if (!strcmp(arg, the_hash_algo->name) ||
+				    !strcmp(arg, "storage")) {
+					flags |= GET_OID_HASH_ANY;
+					output_algo = the_hash_algo;
+					continue;
+				}
+				else if (compat && !strcmp(arg, compat->name)) {
+					flags |= GET_OID_HASH_ANY;
+					output_algo = compat;
+					continue;
+				}
+				else die(_("unsupported object format: %s"), arg);
+			}
 			if (opt_with_value(arg, "--short", &arg)) {
 				filter &= ~(DO_FLAGS|DO_NOREV);
 				verify = 1;
@@ -868,7 +903,8 @@
 				continue;
 			}
 			if (skip_prefix(arg, "--disambiguate=", &arg)) {
-				for_each_abbrev(arg, show_abbrev, NULL);
+				repo_for_each_abbrev(the_repository, arg, the_hash_algo,
+						     show_abbrev, NULL);
 				continue;
 			}
 			if (!strcmp(arg, "--bisect")) {
@@ -878,13 +914,15 @@
 			}
 			if (opt_with_value(arg, "--branches", &arg)) {
 				if (ref_excludes.hidden_refs_configured)
-					return error(_("--exclude-hidden cannot be used together with --branches"));
+					return error(_("options '%s' and '%s' cannot be used together"),
+						     "--exclude-hidden", "--branches");
 				handle_ref_opt(arg, "refs/heads/");
 				continue;
 			}
 			if (opt_with_value(arg, "--tags", &arg)) {
 				if (ref_excludes.hidden_refs_configured)
-					return error(_("--exclude-hidden cannot be used together with --tags"));
+					return error(_("options '%s' and '%s' cannot be used together"),
+						     "--exclude-hidden", "--tags");
 				handle_ref_opt(arg, "refs/tags/");
 				continue;
 			}
@@ -894,7 +932,8 @@
 			}
 			if (opt_with_value(arg, "--remotes", &arg)) {
 				if (ref_excludes.hidden_refs_configured)
-					return error(_("--exclude-hidden cannot be used together with --remotes"));
+					return error(_("options '%s' and '%s' cannot be used together"),
+						     "--exclude-hidden", "--remotes");
 				handle_ref_opt(arg, "refs/remotes/");
 				continue;
 			}
@@ -1044,6 +1083,10 @@
 				puts(the_hash_algo->name);
 				continue;
 			}
+			if (!strcmp(arg, "--show-ref-format")) {
+				puts(ref_storage_format_to_name(the_repository->ref_storage_format));
+				continue;
+			}
 			if (!strcmp(arg, "--end-of-options")) {
 				seen_end_of_options = 1;
 				if (filter & (DO_FLAGS | DO_REVS))
@@ -1068,6 +1111,9 @@
 		}
 		if (!get_oid_with_context(the_repository, name,
 					  flags, &oid, &unused)) {
+			if (output_algo)
+				repo_oid_to_algop(the_repository, &oid,
+						  output_algo, &oid);
 			if (verify)
 				revs_count++;
 			else
diff --git a/builtin/revert.c b/builtin/revert.c
index 77d2035..53935d2 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1,11 +1,11 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
 #include "builtin.h"
 #include "parse-options.h"
 #include "diff.h"
+#include "gettext.h"
+#include "repository.h"
 #include "revision.h"
 #include "rerere.h"
-#include "dir.h"
 #include "sequencer.h"
 #include "branch.h"
 
@@ -43,17 +43,28 @@
 	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
-static int option_parse_x(const struct option *opt,
-			  const char *arg, int unset)
+enum empty_action {
+	EMPTY_COMMIT_UNSPECIFIED = -1,
+	STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
 {
-	struct replay_opts **opts_ptr = opt->value;
-	struct replay_opts *opts = *opts_ptr;
+	int *opt_value = opt->value;
 
-	if (unset)
-		return 0;
+	BUG_ON_OPT_NEG(unset);
 
-	ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
-	opts->xopts[opts->xopts_nr++] = xstrdup(arg);
+	if (!strcmp(arg, "stop"))
+		*opt_value = STOP_ON_EMPTY_COMMIT;
+	else if (!strcmp(arg, "drop"))
+		*opt_value = DROP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "keep"))
+		*opt_value = KEEP_EMPTY_COMMIT;
+	else
+		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
 	return 0;
 }
 
@@ -93,11 +104,13 @@
 		die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
 }
 
-static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
+static int run_sequencer(int argc, const char **argv, const char *prefix,
+			 struct replay_opts *opts)
 {
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	const char *cleanup_arg = NULL;
+	enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -113,8 +126,8 @@
 			     N_("select mainline parent"), option_parse_m),
 		OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
 		OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
-		OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
-			N_("option for merge strategy"), option_parse_x),
+		OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"),
+			N_("option for merge strategy")),
 		{ OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
 		  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 		OPT_END()
@@ -127,7 +140,10 @@
 			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+				       N_("how to handle commits that become empty"),
+				       PARSE_OPT_NONEG, parse_opt_empty),
 			OPT_END(),
 		};
 		options = parse_options_concat(options, cp_extra);
@@ -140,13 +156,18 @@
 		options = parse_options_concat(options, cp_extra);
 	}
 
-	argc = parse_options(argc, argv, NULL, options, usage_str,
+	argc = parse_options(argc, argv, prefix, options, usage_str,
 			PARSE_OPT_KEEP_ARGV0 |
 			PARSE_OPT_KEEP_UNKNOWN_OPT);
 
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
+	if (opts->action == REPLAY_PICK) {
+		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+	}
+
 	/* implies allow_empty */
 	if (opts->keep_redundant_commits)
 		opts->allow_empty = 1;
@@ -175,11 +196,13 @@
 				"--signoff", opts->signoff,
 				"--mainline", opts->mainline,
 				"--strategy", opts->strategy ? 1 : 0,
-				"--strategy-option", opts->xopts ? 1 : 0,
+				"--strategy-option", opts->xopts.nr ? 1 : 0,
 				"-x", opts->record_origin,
 				"--ff", opts->allow_ff,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+				"--keep-redundant-commits", opts->keep_redundant_commits,
+				"--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
 				NULL);
 	}
 
@@ -245,7 +268,7 @@
 
 	opts.action = REPLAY_REVERT;
 	sequencer_init_config(&opts);
-	res = run_sequencer(argc, argv, &opts);
+	res = run_sequencer(argc, argv, prefix, &opts);
 	if (res < 0)
 		die(_("revert failed"));
 	replay_opts_release(&opts);
@@ -259,7 +282,7 @@
 
 	opts.action = REPLAY_PICK;
 	sequencer_init_config(&opts);
-	res = run_sequencer(argc, argv, &opts);
+	res = run_sequencer(argc, argv, prefix, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
 	replay_opts_release(&opts);
diff --git a/builtin/rm.c b/builtin/rm.c
index 8844f90..fd130ce 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -9,10 +9,16 @@
 #include "config.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "cache-tree.h"
+#include "gettext.h"
+#include "hash.h"
 #include "tree-walk.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "read-cache.h"
+#include "repository.h"
 #include "string-list.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
 #include "pathspec.h"
 
@@ -370,7 +376,7 @@
 	 */
 	if (!force) {
 		struct object_id oid;
-		if (get_oid("HEAD", &oid))
+		if (repo_get_oid(the_repository, "HEAD", &oid))
 			oidclr(&oid);
 		if (check_local_mod(&oid, index_only))
 			exit(1);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 4c5d125..3df9eaa 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -1,20 +1,18 @@
 #include "builtin.h"
 #include "config.h"
-#include "commit.h"
-#include "refs.h"
+#include "hex.h"
 #include "pkt-line.h"
-#include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
 #include "quote.h"
 #include "transport.h"
-#include "version.h"
 #include "oid-array.h"
-#include "gpg-interface.h"
 #include "gettext.h"
 #include "protocol.h"
+#include "parse-options.h"
+#include "write-or-die.h"
 
 static const char * const send_pack_usage[] = {
 	N_("git send-pack [--mirror] [--dry-run] [--force]\n"
@@ -128,29 +126,25 @@
 	strbuf_release(&buf);
 }
 
-static int send_pack_config(const char *k, const char *v, void *cb)
+static int send_pack_config(const char *k, const char *v,
+			    const struct config_context *ctx, void *cb)
 {
-	git_gpg_config(k, v, NULL);
-
 	if (!strcmp(k, "push.gpgsign")) {
-		const char *value;
-		if (!git_config_get_value("push.gpgsign", &value)) {
-			switch (git_parse_maybe_bool(value)) {
-			case 0:
-				args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
-				break;
-			case 1:
-				args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
-				break;
-			default:
-				if (value && !strcasecmp(value, "if-asked"))
-					args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
-				else
-					return error(_("invalid value for '%s'"), k);
-			}
+		switch (git_parse_maybe_bool(v)) {
+		case 0:
+			args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
+			break;
+		case 1:
+			args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
+			break;
+		default:
+			if (!strcasecmp(v, "if-asked"))
+				args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
+			else
+				return error(_("invalid value for '%s'"), k);
 		}
 	}
-	return git_default_config(k, v, cb);
+	return git_default_config(k, v, ctx, cb);
 }
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
@@ -206,7 +200,7 @@
 		OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
 		OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
 		OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
-		OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+		OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"),
 		  N_("require old value of ref to be at this value"),
 		  PARSE_OPT_OPTARG, parseopt_push_cas_option),
 		OPT_BOOL(0, TRANS_OPT_FORCE_IF_INCLUDES, &force_if_includes,
@@ -275,7 +269,7 @@
 		fd[0] = 0;
 		fd[1] = 1;
 	} else {
-		conn = git_connect(fd, dest, receivepack,
+		conn = git_connect(fd, dest, "git-receive-pack", receivepack,
 			args.verbose ? CONNECT_VERBOSE : 0);
 	}
 
@@ -339,6 +333,7 @@
 	}
 
 	if (!ret && !transport_refs_pushed(remote_refs))
+		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
 	return ret;
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 27a8716..3c7cd2d 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -1,12 +1,15 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
 #include "string-list.h"
+#include "repository.h"
 #include "revision.h"
 #include "utf8.h"
 #include "mailmap.h"
+#include "setup.h"
 #include "shortlog.h"
 #include "parse-options.h"
 #include "trailer.h"
@@ -176,10 +179,11 @@
 		return;
 
 	/*
-	 * Using format_commit_message("%B") would be simpler here, but
+	 * Using repo_format_commit_message("%B") would be simpler here, but
 	 * this saves us copying the message.
 	 */
-	commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding);
+	commit_buffer = repo_logmsg_reencode(the_repository, commit, NULL,
+					     ctx->output_encoding);
 	body = strstr(commit_buffer, "\n\n");
 	if (!body)
 		return;
@@ -202,7 +206,7 @@
 	trailer_iterator_release(&iter);
 
 	strbuf_release(&ident);
-	unuse_commit_buffer(commit, commit_buffer);
+	repo_unuse_commit_buffer(the_repository, commit, commit_buffer);
 }
 
 static int shortlog_needs_dedup(const struct shortlog *log)
@@ -222,7 +226,8 @@
 	for_each_string_list_item(item, &log->format) {
 		strbuf_reset(&buf);
 
-		format_commit_message(commit, item->string, &buf, ctx);
+		repo_format_commit_message(the_repository, commit,
+					   item->string, &buf, ctx);
 
 		if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf))
 			insert_one_record(log, buf.buf, oneline);
@@ -240,7 +245,6 @@
 
 	ctx.fmt = CMIT_FMT_USERFORMAT;
 	ctx.abbrev = log->abbrev;
-	ctx.print_email_subject = 1;
 	ctx.date_mode = log->date_mode;
 	ctx.output_encoding = get_log_output_encoding();
 
@@ -248,7 +252,8 @@
 		if (log->user_format)
 			pretty_print_commit(&ctx, commit, &oneline);
 		else
-			format_commit_message(commit, "%s", &oneline, &ctx);
+			repo_format_commit_message(the_repository, commit,
+						   "%s", &oneline, &ctx);
 	}
 	oneline_str = oneline.len ? oneline.buf : "<none>";
 
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 358ac3e..b01ec76 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -1,14 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "pretty.h"
 #include "refs.h"
-#include "builtin.h"
 #include "color.h"
 #include "strvec.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "dir.h"
 #include "commit-slab.h"
 #include "date.h"
+#include "wildmatch.h"
 
 static const char* show_branch_usage[] = {
     N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
@@ -240,7 +246,7 @@
 			parents = parents->next;
 			if ((this_flag & flags) == flags)
 				continue;
-			parse_commit(p);
+			repo_parse_commit(the_repository, p);
 			if (mark_seen(p, seen_p) && !still_interesting)
 				extra--;
 			p->object.flags |= flags;
@@ -312,8 +318,8 @@
 		}
 		else
 			printf("[%s] ",
-			       find_unique_abbrev(&commit->object.oid,
-						  DEFAULT_ABBREV));
+			       repo_find_unique_abbrev(the_repository, &commit->object.oid,
+						       DEFAULT_ABBREV));
 	}
 	puts(pretty_str);
 	strbuf_release(&pretty);
@@ -414,7 +420,7 @@
 	/* If both heads/foo and tags/foo exists, get_sha1 would
 	 * get confused.
 	 */
-	if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+	if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
 		ofs = 5;
 	return append_ref(refname + ofs, oid, 0);
 }
@@ -429,7 +435,7 @@
 	/* If both heads/foo and tags/foo exists, get_sha1 would
 	 * get confused.
 	 */
-	if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid))
+	if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
 		ofs = 5;
 	return append_ref(refname + ofs, oid, 0);
 }
@@ -533,7 +539,7 @@
 static void append_one_rev(const char *av)
 {
 	struct object_id revkey;
-	if (!get_oid(av, &revkey)) {
+	if (!repo_get_oid(the_repository, av, &revkey)) {
 		append_ref(av, &revkey, 0);
 		return;
 	}
@@ -553,7 +559,8 @@
 	die("bad sha1 reference %s", av);
 }
 
-static int git_show_branch_config(const char *var, const char *value, void *cb)
+static int git_show_branch_config(const char *var, const char *value,
+				  const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "showbranch.default")) {
 		if (!value)
@@ -573,7 +580,10 @@
 		return 0;
 	}
 
-	return git_color_default_config(var, value, cb);
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
@@ -639,7 +649,7 @@
 	int with_current_branch = 0;
 	int head_at = -1;
 	int topics = 0;
-	int dense = 1;
+	int sparse = 0;
 	const char *reflog_base = NULL;
 	struct option builtin_show_branch_options[] = {
 		OPT_BOOL('a', "all", &all_heads,
@@ -661,17 +671,17 @@
 			 N_("show possible merge bases")),
 		OPT_BOOL(0, "independent", &independent,
 			    N_("show refs unreachable from any other ref")),
-		OPT_SET_INT(0, "topo-order", &sort_order,
-			    N_("show commits in topological order"),
-			    REV_SORT_IN_GRAPH_ORDER),
+		OPT_SET_INT_F(0, "topo-order", &sort_order,
+			      N_("show commits in topological order"),
+			      REV_SORT_IN_GRAPH_ORDER, PARSE_OPT_NONEG),
 		OPT_BOOL(0, "topics", &topics,
 			 N_("show only commits not on the first branch")),
-		OPT_SET_INT(0, "sparse", &dense,
-			    N_("show merges reachable from only one tip"), 0),
-		OPT_SET_INT(0, "date-order", &sort_order,
-			    N_("topologically sort, maintaining date order "
-			       "where possible"),
-			    REV_SORT_BY_COMMIT_DATE),
+		OPT_SET_INT(0, "sparse", &sparse,
+			    N_("show merges reachable from only one tip"), 1),
+		OPT_SET_INT_F(0, "date-order", &sort_order,
+			      N_("topologically sort, maintaining date order "
+				 "where possible"),
+			      REV_SORT_BY_COMMIT_DATE, PARSE_OPT_NONEG),
 		OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"),
 			    N_("show <n> most recent ref-log entries starting at "
 			       "base"),
@@ -746,7 +756,8 @@
 			die(Q_("only %d entry can be shown at one time.",
 			       "only %d entries can be shown at one time.",
 			       MAX_REVS), MAX_REVS);
-		if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0))
+		if (!repo_dwim_ref(the_repository, *av, strlen(*av), &oid,
+				   &ref, 0))
 			die(_("no such ref %s"), *av);
 
 		/* Has the base been specified? */
@@ -836,13 +847,13 @@
 			die(Q_("cannot handle more than %d rev.",
 			       "cannot handle more than %d revs.",
 			       MAX_REVS), MAX_REVS);
-		if (get_oid(ref_name[num_rev], &revkey))
+		if (repo_get_oid(the_repository, ref_name[num_rev], &revkey))
 			die(_("'%s' is not a valid ref."), ref_name[num_rev]);
 		commit = lookup_commit_reference(the_repository, &revkey);
 		if (!commit)
 			die(_("cannot find commit %s (%s)"),
 			    ref_name[num_rev], oid_to_hex(&revkey));
-		parse_commit(commit);
+		repo_parse_commit(the_repository, commit);
 		mark_seen(commit, &seen);
 
 		/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
@@ -929,7 +940,7 @@
 			    !is_merge_point &&
 			    (this_flag & (1u << REV_SHIFT)))
 				continue;
-			if (dense && is_merge &&
+			if (!sparse && is_merge &&
 			    omit_in_dense(commit, rev, num_rev))
 				continue;
 			for (i = 0; i < num_rev; i++) {
diff --git a/builtin/show-index.c b/builtin/show-index.c
index 0e0b9fb..540dc3d 100644
--- a/builtin/show-index.c
+++ b/builtin/show-index.c
@@ -1,7 +1,10 @@
 #include "builtin.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "pack.h"
 #include "parse-options.h"
+#include "repository.h"
 
 static const char *const show_index_usage[] = {
 	"git show-index [--object-format=<hash-algorithm>]",
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 3af6a53..1c15421 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -1,62 +1,79 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
-#include "refs.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "refs/refs-internal.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "object.h"
-#include "tag.h"
 #include "string-list.h"
 #include "parse-options.h"
 
 static const char * const show_ref_usage[] = {
-	N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+	N_("git show-ref [--head] [-d | --dereference]\n"
 	   "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 	   "             [--heads] [--] [<pattern>...]"),
+	N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+	   "             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+	   "             [--] [<ref>...]"),
 	N_("git show-ref --exclude-existing[=<pattern>]"),
+	N_("git show-ref --exists <ref>"),
 	NULL
 };
 
-static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
-	   quiet, hash_only, abbrev, exclude_arg;
-static const char **pattern;
-static const char *exclude_existing_arg;
+struct show_one_options {
+	int quiet;
+	int hash_only;
+	int abbrev;
+	int deref_tags;
+};
 
-static void show_one(const char *refname, const struct object_id *oid)
+static void show_one(const struct show_one_options *opts,
+		     const char *refname, const struct object_id *oid)
 {
 	const char *hex;
 	struct object_id peeled;
 
-	if (!has_object_file(oid))
+	if (!repo_has_object_file(the_repository, oid))
 		die("git show-ref: bad ref %s (%s)", refname,
 		    oid_to_hex(oid));
 
-	if (quiet)
+	if (opts->quiet)
 		return;
 
-	hex = find_unique_abbrev(oid, abbrev);
-	if (hash_only)
+	hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+	if (opts->hash_only)
 		printf("%s\n", hex);
 	else
 		printf("%s %s\n", hex, refname);
 
-	if (!deref_tags)
+	if (!opts->deref_tags)
 		return;
 
 	if (!peel_iterated_oid(oid, &peeled)) {
-		hex = find_unique_abbrev(&peeled, abbrev);
+		hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
 		printf("%s %s^{}\n", hex, refname);
 	}
 }
 
+struct show_ref_data {
+	const struct show_one_options *show_one_opts;
+	const char **patterns;
+	int found_match;
+	int show_head;
+};
+
 static int show_ref(const char *refname, const struct object_id *oid,
-		    int flag UNUSED, void *cbdata UNUSED)
+		    int flag UNUSED, void *cbdata)
 {
-	if (show_head && !strcmp(refname, "HEAD"))
+	struct show_ref_data *data = cbdata;
+
+	if (data->show_head && !strcmp(refname, "HEAD"))
 		goto match;
 
-	if (pattern) {
+	if (data->patterns) {
 		int reflen = strlen(refname);
-		const char **p = pattern, *m;
+		const char **p = data->patterns, *m;
 		while ((m = *p++) != NULL) {
 			int len = strlen(m);
 			if (len > reflen)
@@ -72,9 +89,9 @@
 	}
 
 match:
-	found_match++;
+	data->found_match++;
 
-	show_one(refname, oid);
+	show_one(data->show_one_opts, refname, oid);
 
 	return 0;
 }
@@ -88,6 +105,15 @@
 	return 0;
 }
 
+struct exclude_existing_options {
+	/*
+	 * We need an explicit `enabled` field because it is perfectly valid
+	 * for `pattern` to be `NULL` even if `--exclude-existing` was given.
+	 */
+	int enabled;
+	const char *pattern;
+};
+
 /*
  * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
  * and
@@ -97,11 +123,11 @@
  * (4) ignore if refname is a ref that exists in the local repository;
  * (5) otherwise output the line.
  */
-static int exclude_existing(const char *match)
+static int cmd_show_ref__exclude_existing(const struct exclude_existing_options *opts)
 {
-	static struct string_list existing_refs = STRING_LIST_INIT_DUP;
+	struct string_list existing_refs = STRING_LIST_INIT_DUP;
 	char buf[1024];
-	int matchlen = match ? strlen(match) : 0;
+	int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
 
 	for_each_ref(add_existing, &existing_refs);
 	while (fgets(buf, sizeof(buf), stdin)) {
@@ -117,11 +143,11 @@
 		for (ref = buf + len; buf < ref; ref--)
 			if (isspace(ref[-1]))
 				break;
-		if (match) {
+		if (opts->pattern) {
 			int reflen = buf + len - ref;
-			if (reflen < matchlen)
+			if (reflen < patternlen)
 				continue;
-			if (strncmp(ref, match, matchlen))
+			if (strncmp(ref, opts->pattern, patternlen))
 				continue;
 		}
 		if (check_refname_format(ref, 0)) {
@@ -132,97 +158,172 @@
 			printf("%s\n", buf);
 		}
 	}
+
+	string_list_clear(&existing_refs, 0);
 	return 0;
 }
 
+static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
+				const char **refs)
+{
+	if (!refs || !*refs)
+		die("--verify requires a reference");
+
+	while (*refs) {
+		struct object_id oid;
+
+		if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
+		    !read_ref(*refs, &oid)) {
+			show_one(show_one_opts, *refs, &oid);
+		}
+		else if (!show_one_opts->quiet)
+			die("'%s' - not a valid ref", *refs);
+		else
+			return 1;
+		refs++;
+	}
+
+	return 0;
+}
+
+struct patterns_options {
+	int show_head;
+	int heads_only;
+	int tags_only;
+};
+
+static int cmd_show_ref__patterns(const struct patterns_options *opts,
+				  const struct show_one_options *show_one_opts,
+				  const char **patterns)
+{
+	struct show_ref_data show_ref_data = {
+		.show_one_opts = show_one_opts,
+		.show_head = opts->show_head,
+	};
+
+	if (patterns && *patterns)
+		show_ref_data.patterns = patterns;
+
+	if (opts->show_head)
+		head_ref(show_ref, &show_ref_data);
+	if (opts->heads_only || opts->tags_only) {
+		if (opts->heads_only)
+			for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
+		if (opts->tags_only)
+			for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
+	} else {
+		for_each_ref(show_ref, &show_ref_data);
+	}
+	if (!show_ref_data.found_match)
+		return 1;
+
+	return 0;
+}
+
+static int cmd_show_ref__exists(const char **refs)
+{
+	struct strbuf unused_referent = STRBUF_INIT;
+	struct object_id unused_oid;
+	unsigned int unused_type;
+	int failure_errno = 0;
+	const char *ref;
+	int ret = 0;
+
+	if (!refs || !*refs)
+		die("--exists requires a reference");
+	ref = *refs++;
+	if (*refs)
+		die("--exists requires exactly one reference");
+
+	if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
+			      &unused_oid, &unused_referent, &unused_type,
+			      &failure_errno)) {
+		if (failure_errno == ENOENT || failure_errno == EISDIR) {
+			error(_("reference does not exist"));
+			ret = 2;
+		} else {
+			errno = failure_errno;
+			error_errno(_("failed to look up reference"));
+			ret = 1;
+		}
+
+		goto out;
+	}
+
+out:
+	strbuf_release(&unused_referent);
+	return ret;
+}
+
 static int hash_callback(const struct option *opt, const char *arg, int unset)
 {
-	hash_only = 1;
+	struct show_one_options *opts = opt->value;
+	struct option abbrev_opt = *opt;
+
+	opts->hash_only = 1;
 	/* Use full length SHA1 if no argument */
 	if (!arg)
 		return 0;
-	return parse_opt_abbrev_cb(opt, arg, unset);
+
+	abbrev_opt.value = &opts->abbrev;
+	return parse_opt_abbrev_cb(&abbrev_opt, arg, unset);
 }
 
 static int exclude_existing_callback(const struct option *opt, const char *arg,
 				     int unset)
 {
+	struct exclude_existing_options *opts = opt->value;
 	BUG_ON_OPT_NEG(unset);
-	exclude_arg = 1;
-	*(const char **)opt->value = arg;
+	opts->enabled = 1;
+	opts->pattern = arg;
 	return 0;
 }
 
-static const struct option show_ref_options[] = {
-	OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
-	OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
-	OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
-		    "requires exact ref path")),
-	OPT_HIDDEN_BOOL('h', NULL, &show_head,
-			N_("show the HEAD reference, even if it would be filtered out")),
-	OPT_BOOL(0, "head", &show_head,
-	  N_("show the HEAD reference, even if it would be filtered out")),
-	OPT_BOOL('d', "dereference", &deref_tags,
-		    N_("dereference tags into object IDs")),
-	OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"),
-		       N_("only show SHA1 hash using <n> digits"),
-		       PARSE_OPT_OPTARG, &hash_callback),
-	OPT__ABBREV(&abbrev),
-	OPT__QUIET(&quiet,
-		   N_("do not print results to stdout (useful with --verify)")),
-	OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg,
-		       N_("pattern"), N_("show refs from stdin that aren't in local repository"),
-		       PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
-	OPT_END()
-};
-
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
+	struct exclude_existing_options exclude_existing_opts = {0};
+	struct patterns_options patterns_opts = {0};
+	struct show_one_options show_one_opts = {0};
+	int verify = 0, exists = 0;
+	const struct option show_ref_options[] = {
+		OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")),
+		OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")),
+		OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")),
+		OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
+			    "requires exact ref path")),
+		OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head,
+				N_("show the HEAD reference, even if it would be filtered out")),
+		OPT_BOOL(0, "head", &patterns_opts.show_head,
+		  N_("show the HEAD reference, even if it would be filtered out")),
+		OPT_BOOL('d', "dereference", &show_one_opts.deref_tags,
+			    N_("dereference tags into object IDs")),
+		OPT_CALLBACK_F('s', "hash", &show_one_opts, N_("n"),
+			       N_("only show SHA1 hash using <n> digits"),
+			       PARSE_OPT_OPTARG, &hash_callback),
+		OPT__ABBREV(&show_one_opts.abbrev),
+		OPT__QUIET(&show_one_opts.quiet,
+			   N_("do not print results to stdout (useful with --verify)")),
+		OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_opts,
+			       N_("pattern"), N_("show refs from stdin that aren't in local repository"),
+			       PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
+		OPT_END()
+	};
+
 	git_config(git_default_config, NULL);
 
 	argc = parse_options(argc, argv, prefix, show_ref_options,
 			     show_ref_usage, 0);
 
-	if (exclude_arg)
-		return exclude_existing(exclude_existing_arg);
+	die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing",
+				  verify, "--verify",
+				  exists, "--exists");
 
-	pattern = argv;
-	if (!*pattern)
-		pattern = NULL;
-
-	if (verify) {
-		if (!pattern)
-			die("--verify requires a reference");
-		while (*pattern) {
-			struct object_id oid;
-
-			if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) &&
-			    !read_ref(*pattern, &oid)) {
-				show_one(*pattern, &oid);
-			}
-			else if (!quiet)
-				die("'%s' - not a valid ref", *pattern);
-			else
-				return 1;
-			pattern++;
-		}
-		return 0;
-	}
-
-	if (show_head)
-		head_ref(show_ref, NULL);
-	if (heads_only || tags_only) {
-		if (heads_only)
-			for_each_fullref_in("refs/heads/", show_ref, NULL);
-		if (tags_only)
-			for_each_fullref_in("refs/tags/", show_ref, NULL);
-	} else {
-		for_each_ref(show_ref, NULL);
-	}
-	if (!found_match) {
-		if (verify && !quiet)
-			die("No match");
-		return 1;
-	}
-	return 0;
+	if (exclude_existing_opts.enabled)
+		return cmd_show_ref__exclude_existing(&exclude_existing_opts);
+	else if (verify)
+		return cmd_show_ref__verify(&show_one_opts, argv);
+	else if (exists)
+		return cmd_show_ref__exists(argv);
+	else
+		return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv);
 }
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index c373815..0f52e25 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -1,26 +1,26 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-file.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "pathspec.h"
 #include "repository.h"
-#include "run-command.h"
 #include "strbuf.h"
 #include "string-list.h"
-#include "cache-tree.h"
 #include "lockfile.h"
-#include "resolve-undo.h"
 #include "unpack-trees.h"
-#include "wt-status.h"
 #include "quote.h"
+#include "setup.h"
 #include "sparse-index.h"
 #include "worktree.h"
 
 static const char *empty_base = "";
 
 static char const * const builtin_sparse_checkout_usage[] = {
-	N_("git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"),
+	N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"),
 	NULL
 };
 
@@ -57,6 +57,7 @@
 	char *sparse_filename;
 	int res;
 
+	setup_work_tree();
 	if (!core_apply_sparse_checkout)
 		die(_("this worktree is not sparse"));
 
@@ -217,16 +218,14 @@
 	o.head_idx = -1;
 	o.src_index = r->index;
 	o.dst_index = r->index;
-	index_state_init(&o.result, r);
 	o.skip_sparse_checkout = 0;
-	o.pl = pl;
 
 	setup_work_tree();
 
 	repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
 
 	setup_unpack_trees_porcelain(&o, "sparse-checkout");
-	result = update_sparsity(&o);
+	result = update_sparsity(&o, pl);
 	clear_unpack_trees_porcelain(&o);
 
 	if (result == UPDATE_SPARSITY_WARNINGS)
@@ -383,13 +382,7 @@
 	return 0;
 }
 
-static int update_modes(int *cone_mode, int *sparse_index)
-{
-	int mode, record_mode;
-
-	/* Determine if we need to record the mode; ensure sparse checkout on */
-	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
-
+static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
 	/* If not specified, use previous definition of cone mode */
 	if (*cone_mode == -1 && core_apply_sparse_checkout)
 		*cone_mode = core_sparse_checkout_cone;
@@ -397,12 +390,21 @@
 	/* Set cone/non-cone mode appropriately */
 	core_apply_sparse_checkout = 1;
 	if (*cone_mode == 1 || *cone_mode == -1) {
-		mode = MODE_CONE_PATTERNS;
 		core_sparse_checkout_cone = 1;
-	} else {
-		mode = MODE_ALL_PATTERNS;
-		core_sparse_checkout_cone = 0;
+		return MODE_CONE_PATTERNS;
 	}
+	core_sparse_checkout_cone = 0;
+	return MODE_ALL_PATTERNS;
+}
+
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+	int mode, record_mode;
+
+	/* Determine if we need to record the mode; ensure sparse checkout on */
+	record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+	mode = update_cone_mode(cone_mode);
 	if (record_mode && set_config(mode))
 		return 1;
 
@@ -448,6 +450,7 @@
 		OPT_END(),
 	};
 
+	setup_work_tree();
 	repo_read_index(the_repository);
 
 	init_opts.cone_mode = -1;
@@ -471,7 +474,7 @@
 		return update_working_directory(NULL);
 	}
 
-	if (get_oid("HEAD", &oid)) {
+	if (repo_get_oid(the_repository, "HEAD", &oid)) {
 		FILE *fp;
 
 		/* assume we are in a fresh repo, but update the sparse-checkout file */
@@ -545,7 +548,7 @@
 
 static void add_patterns_from_input(struct pattern_list *pl,
 				    int argc, const char **argv,
-				    int use_stdin)
+				    FILE *file)
 {
 	int i;
 	if (core_sparse_checkout_cone) {
@@ -555,9 +558,9 @@
 		hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
 		pl->use_cone_patterns = 1;
 
-		if (use_stdin) {
+		if (file) {
 			struct strbuf unquoted = STRBUF_INIT;
-			while (!strbuf_getline(&line, stdin)) {
+			while (!strbuf_getline(&line, file)) {
 				if (line.buf[0] == '"') {
 					strbuf_reset(&unquoted);
 					if (unquote_c_style(&unquoted, line.buf, NULL))
@@ -579,10 +582,10 @@
 			}
 		}
 	} else {
-		if (use_stdin) {
+		if (file) {
 			struct strbuf line = STRBUF_INIT;
 
-			while (!strbuf_getline(&line, stdin)) {
+			while (!strbuf_getline(&line, file)) {
 				size_t len;
 				char *buf = strbuf_detach(&line, &len);
 				add_pattern(buf, empty_base, 0, pl, 0);
@@ -609,7 +612,8 @@
 	struct pattern_list existing;
 	char *sparse_filename = get_sparse_checkout_filename();
 
-	add_patterns_from_input(pl, argc, argv, use_stdin);
+	add_patterns_from_input(pl, argc, argv,
+				use_stdin ? stdin : NULL);
 
 	memset(&existing, 0, sizeof(existing));
 	existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -646,7 +650,7 @@
 					   pl, NULL, 0))
 		die(_("unable to load existing sparse-checkout patterns"));
 	free(sparse_filename);
-	add_patterns_from_input(pl, argc, argv, use_stdin);
+	add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL);
 }
 
 static int modify_pattern_list(int argc, const char **argv, int use_stdin,
@@ -665,7 +669,8 @@
 		break;
 
 	case REPLACE:
-		add_patterns_from_input(pl, argc, argv, use_stdin);
+		add_patterns_from_input(pl, argc, argv,
+					use_stdin ? stdin : NULL);
 		break;
 	}
 
@@ -760,6 +765,7 @@
 		OPT_END(),
 	};
 
+	setup_work_tree();
 	if (!core_apply_sparse_checkout)
 		die(_("no sparse-checkout to add to"));
 
@@ -767,8 +773,7 @@
 
 	argc = parse_options(argc, argv, prefix,
 			     builtin_sparse_checkout_add_options,
-			     builtin_sparse_checkout_add_usage,
-			     PARSE_OPT_KEEP_UNKNOWN_OPT);
+			     builtin_sparse_checkout_add_usage, 0);
 
 	sanitize_paths(argc, argv, prefix, add_opts.skip_checks);
 
@@ -806,6 +811,7 @@
 		OPT_END(),
 	};
 
+	setup_work_tree();
 	repo_read_index(the_repository);
 
 	set_opts.cone_mode = -1;
@@ -813,8 +819,7 @@
 
 	argc = parse_options(argc, argv, prefix,
 			     builtin_sparse_checkout_set_options,
-			     builtin_sparse_checkout_set_usage,
-			     PARSE_OPT_KEEP_UNKNOWN_OPT);
+			     builtin_sparse_checkout_set_usage, 0);
 
 	if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
 		return 1;
@@ -824,7 +829,7 @@
 	 * non-cone mode, if nothing is specified, manually select just the
 	 * top-level directory (much as 'init' would do).
 	 */
-	if (!core_sparse_checkout_cone && argc == 0) {
+	if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) {
 		argv = default_patterns;
 		argc = default_patterns_nr;
 	} else {
@@ -855,6 +860,7 @@
 		OPT_END(),
 	};
 
+	setup_work_tree();
 	if (!core_apply_sparse_checkout)
 		die(_("must be in a sparse-checkout to reapply sparsity patterns"));
 
@@ -898,6 +904,7 @@
 	 * forcibly return to a dense checkout regardless of initial state.
 	 */
 
+	setup_work_tree();
 	argc = parse_options(argc, argv, prefix,
 			     builtin_sparse_checkout_disable_options,
 			     builtin_sparse_checkout_disable_usage, 0);
@@ -923,6 +930,90 @@
 	return set_config(MODE_NO_PATTERNS);
 }
 
+static char const * const builtin_sparse_checkout_check_rules_usage[] = {
+	N_("git sparse-checkout check-rules [-z] [--skip-checks]"
+	   "[--[no-]cone] [--rules-file <file>]"),
+	NULL
+};
+
+static struct sparse_checkout_check_rules_opts {
+	int cone_mode;
+	int null_termination;
+	char *rules_file;
+} check_rules_opts;
+
+static int check_rules(struct pattern_list *pl, int null_terminated) {
+	struct strbuf line = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	char *path;
+	int line_terminator = null_terminated ? 0 : '\n';
+	strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul
+		: strbuf_getline;
+	the_repository->index->sparse_checkout_patterns = pl;
+	while (!getline_fn(&line, stdin)) {
+		path = line.buf;
+		if (!null_terminated && line.buf[0] == '"') {
+			strbuf_reset(&unquoted);
+			if (unquote_c_style(&unquoted, line.buf, NULL))
+				die(_("unable to unquote C-style string '%s'"),
+					line.buf);
+
+			path = unquoted.buf;
+		}
+
+		if (path_in_sparse_checkout(path, the_repository->index))
+			write_name_quoted(path, stdout, line_terminator);
+	}
+	strbuf_release(&line);
+	strbuf_release(&unquoted);
+
+	return 0;
+}
+
+static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix)
+{
+	static struct option builtin_sparse_checkout_check_rules_options[] = {
+		OPT_BOOL('z', NULL, &check_rules_opts.null_termination,
+			 N_("terminate input and output files by a NUL character")),
+		OPT_BOOL(0, "cone", &check_rules_opts.cone_mode,
+			 N_("when used with --rules-file interpret patterns as cone mode patterns")),
+		OPT_FILENAME(0, "rules-file", &check_rules_opts.rules_file,
+			 N_("use patterns in <file> instead of the current ones.")),
+		OPT_END(),
+	};
+
+	FILE *fp;
+	int ret;
+	struct pattern_list pl = {0};
+	char *sparse_filename;
+	check_rules_opts.cone_mode = -1;
+
+	argc = parse_options(argc, argv, prefix,
+			     builtin_sparse_checkout_check_rules_options,
+			     builtin_sparse_checkout_check_rules_usage, 0);
+
+	if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0)
+		check_rules_opts.cone_mode = 1;
+
+	update_cone_mode(&check_rules_opts.cone_mode);
+	pl.use_cone_patterns = core_sparse_checkout_cone;
+	if (check_rules_opts.rules_file) {
+		fp = xfopen(check_rules_opts.rules_file, "r");
+		add_patterns_from_input(&pl, argc, argv, fp);
+		fclose(fp);
+	} else {
+		sparse_filename = get_sparse_checkout_filename();
+		if (add_patterns_from_file_to_list(sparse_filename, "", 0, &pl,
+						   NULL, 0))
+			die(_("unable to load existing sparse-checkout patterns"));
+		free(sparse_filename);
+	}
+
+	ret = check_rules(&pl, check_rules_opts.null_termination);
+	clear_pattern_list(&pl);
+	return ret;
+}
+
 int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
 {
 	parse_opt_subcommand_fn *fn = NULL;
@@ -933,6 +1024,7 @@
 		OPT_SUBCOMMAND("add", &fn, sparse_checkout_add),
 		OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply),
 		OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable),
+		OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules),
 		OPT_END(),
 	};
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 3a4f9fd..062be1f 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,6 +1,12 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "lockfile.h"
@@ -12,11 +18,14 @@
 #include "run-command.h"
 #include "dir.h"
 #include "entry.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "rerere.h"
 #include "revision.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "log-tree.h"
 #include "diffcore.h"
-#include "exec-cmd.h"
 #include "reflog.h"
 #include "add-interactive.h"
 
@@ -201,7 +210,7 @@
 
 	revision = info->revision.buf;
 
-	if (get_oid(revision, &info->w_commit))
+	if (repo_get_oid(the_repository, revision, &info->w_commit))
 		return error(_("%s is not a valid reference"), revision);
 
 	assert_stash_like(info, revision);
@@ -211,7 +220,8 @@
 	end_of_rev = strchrnul(revision, '@');
 	strbuf_add(&symbolic, revision, end_of_rev - revision);
 
-	ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0);
+	ret = repo_dwim_ref(the_repository, symbolic.buf, symbolic.len,
+			    &dummy, &expanded_ref, 0);
 	strbuf_release(&symbolic);
 	switch (ret) {
 	case 0: /* Not found, but valid ref */
@@ -231,7 +241,7 @@
 static int do_clear_stash(void)
 {
 	struct object_id obj;
-	if (get_oid(ref_stash, &obj))
+	if (repo_get_oid(the_repository, ref_stash, &obj))
 		return 0;
 
 	return delete_ref(NULL, ref_stash, &obj, 0);
@@ -274,7 +284,7 @@
 	if (parse_tree(tree))
 		return -1;
 
-	init_tree_desc(t, tree->buffer, tree->size);
+	init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
 
 	opts.head_idx = 1;
 	opts.src_index = &the_index;
@@ -351,7 +361,7 @@
 }
 
 static void add_diff_to_buf(struct diff_queue_struct *q,
-			    struct diff_options *options,
+			    struct diff_options *options UNUSED,
 			    void *data)
 {
 	int i;
@@ -427,7 +437,7 @@
 	 * to the index before a merge was run) and the current index
 	 * (reflecting the changes brought in by the merge).
 	 */
-	diff_setup(&diff_opts);
+	repo_diff_setup(the_repository, &diff_opts);
 	diff_opts.flags.recursive = 1;
 	diff_opts.detect_rename = 0;
 	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -510,7 +520,7 @@
 	repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-		die(_("Unable to write index."));
+		die(_("could not write index"));
 }
 
 static int do_apply_stash(const char *prefix, struct stash_info *info,
@@ -527,7 +537,7 @@
 	repo_read_index_preload(the_repository, NULL, 0);
 	if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
 					 NULL, NULL, NULL))
-		return -1;
+		return error(_("could not write index"));
 
 	if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
 				NULL))
@@ -600,7 +610,7 @@
 		ret = error(_("could not write index"));
 
 	if (ret) {
-		rerere(0);
+		repo_rerere(the_repository, 0);
 
 		if (index)
 			fprintf_ln(stderr, _("Index was not unstashed."));
@@ -830,7 +840,8 @@
 static int show_patch;
 static int show_include_untracked;
 
-static int git_stash_config(const char *var, const char *value, void *cb)
+static int git_stash_config(const char *var, const char *value,
+			    const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "stash.showstat")) {
 		show_stat = git_config_bool(var, value);
@@ -844,7 +855,7 @@
 		show_include_untracked = git_config_bool(var, value);
 		return 0;
 	}
-	return git_diff_basic_config(var, value, cb);
+	return git_diff_basic_config(var, value, ctx, cb);
 }
 
 static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
@@ -859,7 +870,8 @@
 		tree[i] = parse_tree_indirect(oid[i]);
 		if (parse_tree(tree[i]) < 0)
 			die(_("failed to parse tree"));
-		init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
+		init_tree_desc(&tree_desc[i], &tree[i]->object.oid,
+			       tree[i]->buffer, tree[i]->size);
 	}
 
 	unpack_tree_opt.head_idx = -1;
@@ -900,7 +912,7 @@
 
 	init_diff_ui_defaults();
 	git_config(git_diff_ui_config, NULL);
-	init_revisions(&rev, prefix);
+	repo_init_revisions(the_repository, &rev, prefix);
 
 	argc = parse_options(argc, argv, prefix, options, git_stash_show_usage,
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
@@ -961,7 +973,7 @@
 	}
 	log_tree_diff_flush(&rev);
 
-	ret = diff_result_code(&rev.diffopt, 0);
+	ret = diff_result_code(&rev.diffopt);
 cleanup:
 	strvec_clear(&stash_args);
 	free_stash_info(&info);
@@ -977,6 +989,12 @@
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
 			  int quiet)
 {
+	struct stash_info info;
+	char revision[GIT_MAX_HEXSZ];
+
+	oid_to_hex_r(revision, w_commit);
+	assert_stash_like(&info, revision);
+
 	if (!stash_msg)
 		stash_msg = "Created via \"git stash store\".";
 
@@ -1077,19 +1095,18 @@
  */
 static int check_changes_tracked_files(const struct pathspec *ps)
 {
-	int result;
 	struct rev_info rev;
 	struct object_id dummy;
 	int ret = 0;
 
 	/* No initial commit. */
-	if (get_oid("HEAD", &dummy))
+	if (repo_get_oid(the_repository, "HEAD", &dummy))
 		return -1;
 
 	if (repo_read_index(the_repository) < 0)
 		return -1;
 
-	init_revisions(&rev, NULL);
+	repo_init_revisions(the_repository, &rev, NULL);
 	copy_pathspec(&rev.prune_data, ps);
 
 	rev.diffopt.flags.quick = 1;
@@ -1099,14 +1116,14 @@
 	add_head_to_pending(&rev);
 	diff_setup_done(&rev.diffopt);
 
-	result = run_diff_index(&rev, 1);
-	if (diff_result_code(&rev.diffopt, result)) {
+	run_diff_index(&rev, DIFF_INDEX_CACHED);
+	if (diff_result_code(&rev.diffopt)) {
 		ret = 1;
 		goto done;
 	}
 
-	result = run_diff_files(&rev, 0);
-	if (diff_result_code(&rev.diffopt, result)) {
+	run_diff_files(&rev, 0);
+	if (diff_result_code(&rev.diffopt)) {
 		ret = 1;
 		goto done;
 	}
@@ -1276,7 +1293,7 @@
 	struct strbuf diff_output = STRBUF_INIT;
 	struct index_state istate = INDEX_STATE_INIT(the_repository);
 
-	init_revisions(&rev, NULL);
+	repo_init_revisions(the_repository, &rev, NULL);
 	copy_pathspec(&rev.prune_data, ps);
 
 	set_alternate_index_output(stash_index_path.buf);
@@ -1297,10 +1314,7 @@
 
 	add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
 			   "");
-	if (run_diff_index(&rev, 0)) {
-		ret = -1;
-		goto done;
-	}
+	run_diff_index(&rev, 0);
 
 	cp_upd_index.git_cmd = 1;
 	strvec_pushl(&cp_upd_index.args, "update-index",
@@ -1351,11 +1365,11 @@
 	repo_read_index_preload(the_repository, NULL, 0);
 	if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
 					 NULL, NULL, NULL) < 0) {
-		ret = -1;
+		ret = error(_("could not write index"));
 		goto done;
 	}
 
-	if (get_oid("HEAD", &info->b_commit)) {
+	if (repo_get_oid(the_repository, "HEAD", &info->b_commit)) {
 		if (!quiet)
 			fprintf_ln(stderr, _("You do not have "
 					     "the initial commit yet"));
@@ -1373,8 +1387,9 @@
 	branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
 	if (flags & REF_ISSYMREF)
 		skip_prefix(branch_ref, "refs/heads/", &branch_name);
-	head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
-					     DEFAULT_ABBREV);
+	head_short_sha1 = repo_find_unique_abbrev(the_repository,
+						  &head_commit->object.oid,
+						  DEFAULT_ABBREV);
 	strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
 	pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
 
@@ -1465,7 +1480,7 @@
 	return ret;
 }
 
-static int create_stash(int argc, const char **argv, const char *prefix)
+static int create_stash(int argc, const char **argv, const char *prefix UNUSED)
 {
 	int ret;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
@@ -1541,7 +1556,7 @@
 
 	if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
 					 NULL, NULL, NULL)) {
-		ret = -1;
+		ret = error(_("could not write index"));
 		goto done;
 	}
 
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 1e34cf2..e5626e5 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -1,8 +1,11 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "write-or-die.h"
 
 static void comment_lines(struct strbuf *buf)
 {
@@ -10,7 +13,7 @@
 	size_t len;
 
 	msg = strbuf_detach(buf, &len);
-	strbuf_add_commented_lines(buf, msg, len);
+	strbuf_add_commented_lines(buf, msg, len, comment_line_str);
 	free(msg);
 }
 
@@ -55,7 +58,8 @@
 		die_errno("could not read the input");
 
 	if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
-		strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
+		strbuf_stripspace(&buf,
+			  mode == STRIP_COMMENTS ? comment_line_str : NULL);
 	else
 		comment_lines(&buf);
 
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4c173d8..e4e18ad 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,12 +1,20 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
-#include "cache.h"
 #include "config.h"
 #include "parse-options.h"
 #include "quote.h"
+#include "path.h"
 #include "pathspec.h"
+#include "preload-index.h"
 #include "dir.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule.h"
 #include "submodule-config.h"
 #include "string-list.h"
@@ -14,11 +22,12 @@
 #include "remote.h"
 #include "refs.h"
 #include "refspec.h"
-#include "connect.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "diff.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "advice.h"
 #include "branch.h"
 #include "list-objects-filter-options.h"
@@ -557,7 +566,7 @@
 	 * If there are no path args and submodule.active is set then,
 	 * by default, only initialize 'active' modules.
 	 */
-	if (!argc && git_config_get_value_multi("submodule.active"))
+	if (!argc && !git_config_get("submodule.active"))
 		module_list_active(&list);
 
 	info.prefix = prefix;
@@ -619,7 +628,6 @@
 	char *displaypath;
 	struct strvec diff_files_args = STRVEC_INIT;
 	struct rev_info rev = REV_INFO_INIT;
-	int diff_files_result;
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
 	struct setup_revision_opt opt = {
@@ -659,9 +667,9 @@
 	repo_init_revisions(the_repository, &rev, NULL);
 	rev.abbrev = 0;
 	setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt);
-	diff_files_result = run_diff_files(&rev, 0);
+	run_diff_files(&rev, 0);
 
-	if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+	if (!diff_result_code(&rev.diffopt)) {
 		print_status(flags, ' ', path, ce_oid,
 			     displaypath);
 	} else if (!(flags & OPT_CACHED)) {
@@ -1108,7 +1116,7 @@
 		strvec_pushv(&diff_args, info->argv);
 
 	git_config(git_diff_basic_config, NULL);
-	init_revisions(&rev, info->prefix);
+	repo_init_revisions(the_repository, &rev, info->prefix);
 	rev.abbrev = 0;
 	precompose_argv_prefix(diff_args.nr, diff_args.v, NULL);
 	setup_revisions(diff_args.nr, diff_args.v, &rev, &opt);
@@ -1131,7 +1139,7 @@
 	}
 
 	if (diff_cmd == DIFF_INDEX)
-		run_diff_index(&rev, info->cached);
+		run_diff_index(&rev, info->cached ? DIFF_INDEX_CACHED : 0);
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
@@ -1174,7 +1182,7 @@
 	if (!summary_limit)
 		return 0;
 
-	if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
+	if (!repo_get_oid(the_repository, argc ? argv[0] : "HEAD", &head_oid)) {
 		if (argc) {
 			argv++;
 			argc--;
@@ -1187,7 +1195,7 @@
 			argc--;
 		}
 	} else {
-		if (get_oid("HEAD", &head_oid))
+		if (repo_get_oid(the_repository, "HEAD", &head_oid))
 			die(_("could not fetch a revision for HEAD"));
 	}
 
@@ -1275,7 +1283,7 @@
 	submodule_to_gitdir(&sb, path);
 	strbuf_addstr(&sb, "/config");
 
-	if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+	if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
 		die(_("failed to update remote for submodule '%s'"),
 		      path);
 
@@ -2016,14 +2024,17 @@
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "submodule.%s.url", sub->name);
 	if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
-		if (starts_with_dot_slash(sub->url) ||
-		    starts_with_dot_dot_slash(sub->url)) {
+		if (sub->url && (starts_with_dot_slash(sub->url) ||
+				 starts_with_dot_dot_slash(sub->url))) {
 			url = resolve_relative_url(sub->url, NULL, 0);
 			need_free_url = 1;
 		} else
 			url = sub->url;
 	}
 
+	if (!url)
+		die(_("cannot clone submodule '%s' without a URL"), sub->name);
+
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/.git", ce->name);
 	needs_cloning = !file_exists(sb.buf);
@@ -2132,9 +2143,9 @@
 	return 0;
 }
 
-static int update_clone_start_failure(struct strbuf *err,
+static int update_clone_start_failure(struct strbuf *err UNUSED,
 				      void *suc_cb,
-				      void *idx_task_cb)
+				      void *idx_task_cb UNUSED)
 {
 	struct submodule_update_clone *suc = suc_cb;
 
@@ -2181,12 +2192,13 @@
 }
 
 static int git_update_clone_config(const char *var, const char *value,
+				   const struct config_context *ctx,
 				   void *cb)
 {
 	int *max_jobs = cb;
 
 	if (!strcmp(var, "submodule.fetchjobs"))
-		*max_jobs = parse_submodule_fetchjobs(var, value);
+		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
 	return 0;
 }
 
@@ -2743,7 +2755,7 @@
 		 * If there are no path args and submodule.active is set then,
 		 * by default, only initialize 'active' modules.
 		 */
-		if (!argc && git_config_get_value_multi("submodule.active"))
+		if (!argc && !git_config_get("submodule.active"))
 			module_list_active(&list);
 
 		info.prefix = opt.prefix;
@@ -2764,7 +2776,7 @@
 	return ret;
 }
 
-static int push_check(int argc, const char **argv, const char *prefix)
+static int push_check(int argc, const char **argv, const char *prefix UNUSED)
 {
 	struct remote *remote;
 	const char *superproject_head;
@@ -2876,7 +2888,7 @@
 
 static int module_set_url(int argc, const char **argv, const char *prefix)
 {
-	int quiet = 0;
+	int quiet = 0, ret;
 	const char *newurl;
 	const char *path;
 	char *config_name;
@@ -2888,20 +2900,29 @@
 		N_("git submodule set-url [--quiet] <path> <newurl>"),
 		NULL
 	};
+	const struct submodule *sub;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1]))
 		usage_with_options(usage, options);
 
-	config_name = xstrfmt("submodule.%s.url", path);
+	sub = submodule_from_path(the_repository, null_oid(), path);
 
-	config_set_in_gitmodules_file_gently(config_name, newurl);
-	sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0);
+	if (!sub)
+		die(_("no submodule mapping found in .gitmodules for path '%s'"),
+		    path);
+
+	config_name = xstrfmt("submodule.%s.url", sub->name);
+	ret = config_set_in_gitmodules_file_gently(config_name, newurl);
+
+	if (!ret) {
+		repo_read_gitmodules(the_repository, 0);
+		sync_submodule(sub->path, prefix, NULL, quiet ? OPT_QUIET : 0);
+	}
 
 	free(config_name);
-
-	return 0;
+	return !!ret;
 }
 
 static int module_set_branch(int argc, const char **argv, const char *prefix)
@@ -2928,6 +2949,7 @@
 		N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"),
 		NULL
 	};
+	const struct submodule *sub;
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
@@ -2940,7 +2962,13 @@
 	if (argc != 1 || !(path = argv[0]))
 		usage_with_options(usage, options);
 
-	config_name = xstrfmt("submodule.%s.branch", path);
+	sub = submodule_from_path(the_repository, null_oid(), path);
+
+	if (!sub)
+		die(_("no submodule mapping found in .gitmodules for path '%s'"),
+		    path);
+
+	config_name = xstrfmt("submodule.%s.branch", sub->name);
 	ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);
 
 	free(config_name);
@@ -3140,7 +3168,6 @@
 static void configure_added_submodule(struct add_data *add_data)
 {
 	char *key;
-	const char *val;
 	struct child_process add_submod = CHILD_PROCESS_INIT;
 	struct child_process add_gitmodules = CHILD_PROCESS_INIT;
 
@@ -3185,7 +3212,7 @@
 	 * is_submodule_active(), since that function needs to find
 	 * out the value of "submodule.active" again anyway.
 	 */
-	if (!git_config_get_string_tmp("submodule.active", &val)) {
+	if (!git_config_get("submodule.active")) {
 		/*
 		 * If the submodule being added isn't already covered by the
 		 * current configured pathspec, set the submodule's active flag
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index e00768a..c9defe4 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -1,8 +1,9 @@
 #include "builtin.h"
 #include "config.h"
-#include "cache.h"
+#include "gettext.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "strbuf.h"
 
 static const char * const git_symbolic_ref_usage[] = {
 	N_("git symbolic-ref [-m <reason>] <name> <ref>"),
diff --git a/builtin/tag.c b/builtin/tag.c
index d428c45..9a33cb5 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -6,13 +6,18 @@
  * Based on git-tag.sh and mktag.c by Linus Torvalds.
  */
 
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "advice.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "tag.h"
-#include "run-command.h"
 #include "parse-options.h"
 #include "diff.h"
 #include "revision.h"
@@ -21,6 +26,8 @@
 #include "column.h"
 #include "ref-filter.h"
 #include "date.h"
+#include "write-or-die.h"
+#include "object-file-convert.h"
 
 static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
@@ -41,13 +48,7 @@
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
 		     struct ref_format *format)
 {
-	struct ref_array array;
-	struct strbuf output = STRBUF_INIT;
-	struct strbuf err = STRBUF_INIT;
 	char *to_free = NULL;
-	int i;
-
-	memset(&array, 0, sizeof(array));
 
 	if (filter->lines == -1)
 		filter->lines = 0;
@@ -65,21 +66,8 @@
 	if (verify_ref_format(format))
 		die(_("unable to parse format string"));
 	filter->with_commit_tag_algo = 1;
-	filter_refs(&array, filter, FILTER_REFS_TAGS);
-	ref_array_sort(sorting, &array);
+	filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format);
 
-	for (i = 0; i < array.nr; i++) {
-		strbuf_reset(&output);
-		strbuf_reset(&err);
-		if (format_ref_array_item(array.items[i], format, &output, &err))
-			die("%s", err.buf);
-		fwrite(output.buf, 1, output.len, stdout);
-		putchar('\n');
-	}
-
-	strbuf_release(&err);
-	strbuf_release(&output);
-	ref_array_clear(&array);
 	free(to_free);
 
 	return 0;
@@ -111,7 +99,7 @@
 	return had_error;
 }
 
-static int collect_tags(const char *name, const char *ref,
+static int collect_tags(const char *name UNUSED, const char *ref,
 			const struct object_id *oid, void *cb_data)
 {
 	struct string_list *ref_list = cb_data;
@@ -137,7 +125,7 @@
 		if (!ref_exists(name))
 			printf(_("Deleted tag '%s' (was %s)\n"),
 				item->string + 10,
-				find_unique_abbrev(oid, DEFAULT_ABBREV));
+				repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
 
 		free(oid);
 	}
@@ -145,7 +133,7 @@
 	return result;
 }
 
-static int verify_tag(const char *name, const char *ref,
+static int verify_tag(const char *name, const char *ref UNUSED,
 		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
@@ -164,24 +152,57 @@
 	return 0;
 }
 
-static int do_sign(struct strbuf *buffer)
+static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
+		   struct object_id *compat_oid_buf)
 {
-	return sign_buffer(buffer, buffer, get_signing_key());
+	const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+	struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+	struct strbuf compat_buf = STRBUF_INIT;
+	const char *keyid = get_signing_key();
+	int ret = -1;
+
+	if (sign_buffer(buffer, &sig, keyid))
+		return -1;
+
+	if (compat) {
+		const struct git_hash_algo *algo = the_repository->hash_algo;
+
+		if (convert_object_file(&compat_buf, algo, compat,
+					buffer->buf, buffer->len, OBJ_TAG, 1))
+			goto out;
+		if (sign_buffer(&compat_buf, &compat_sig, keyid))
+			goto out;
+		add_header_signature(&compat_buf, &sig, algo);
+		strbuf_addbuf(&compat_buf, &compat_sig);
+		hash_object_file(compat, compat_buf.buf, compat_buf.len,
+				 OBJ_TAG, compat_oid_buf);
+		*compat_oid = compat_oid_buf;
+	}
+
+	if (compat_sig.len)
+		add_header_signature(buffer, &compat_sig, compat);
+
+	strbuf_addbuf(buffer, &sig);
+	ret = 0;
+out:
+	strbuf_release(&sig);
+	strbuf_release(&compat_sig);
+	strbuf_release(&compat_buf);
+	return ret;
 }
 
 static const char tag_template[] =
 	N_("\nWrite a message for tag:\n  %s\n"
-	"Lines starting with '%c' will be ignored.\n");
+	"Lines starting with '%s' will be ignored.\n");
 
 static const char tag_template_nocleanup[] =
 	N_("\nWrite a message for tag:\n  %s\n"
-	"Lines starting with '%c' will be kept; you may remove them"
+	"Lines starting with '%s' will be kept; you may remove them"
 	" yourself if you want to.\n");
 
-static int git_tag_config(const char *var, const char *value, void *cb)
+static int git_tag_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
-	int status;
-
 	if (!strcmp(var, "tag.gpgsign")) {
 		config_sign_tag = git_config_bool(var, value);
 		return 0;
@@ -194,9 +215,6 @@
 		return 0;
 	}
 
-	status = git_gpg_config(var, value, cb);
-	if (status)
-		return status;
 	if (!strcmp(var, "tag.forcesignannotated")) {
 		force_sign_annotate = git_config_bool(var, value);
 		return 0;
@@ -204,7 +222,11 @@
 
 	if (starts_with(var, "column."))
 		return git_column_config(var, value, "tag", &colopts);
-	return git_color_default_config(var, value, cb);
+
+	if (git_color_config(var, value, cb) < 0)
+		return -1;
+
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void write_tag_body(int fd, const struct object_id *oid)
@@ -215,7 +237,7 @@
 	struct strbuf payload = STRBUF_INIT;
 	struct strbuf signature = STRBUF_INIT;
 
-	orig = buf = read_object_file(oid, &type, &size);
+	orig = buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf)
 		return;
 	if (parse_signature(buf, size, &payload, &signature)) {
@@ -239,9 +261,11 @@
 
 static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
 {
-	if (sign && do_sign(buf) < 0)
+	struct object_id *compat_oid = NULL, compat_oid_buf;
+	if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0)
 		return error(_("unable to sign the tag"));
-	if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
+	if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result,
+				    compat_oid, 0) < 0)
 		return error(_("unable to write tag file"));
 	return 0;
 }
@@ -266,11 +290,10 @@
 static void create_tag(const struct object_id *object, const char *object_ref,
 		       const char *tag,
 		       struct strbuf *buf, struct create_tag_options *opt,
-		       struct object_id *prev, struct object_id *result)
+		       struct object_id *prev, struct object_id *result, char *path)
 {
 	enum object_type type;
 	struct strbuf header = STRBUF_INIT;
-	char *path = NULL;
 
 	type = oid_object_info(the_repository, object, NULL);
 	if (type <= OBJ_NONE)
@@ -294,7 +317,6 @@
 		int fd;
 
 		/* write the template message before editing: */
-		path = git_pathdup("TAG_EDITMSG");
 		fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
 		if (opt->message_given) {
@@ -306,9 +328,11 @@
 			struct strbuf buf = STRBUF_INIT;
 			strbuf_addch(&buf, '\n');
 			if (opt->cleanup_mode == CLEANUP_ALL)
-				strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
+				strbuf_commented_addf(&buf, comment_line_str,
+				      _(tag_template), tag, comment_line_str);
 			else
-				strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
+				strbuf_commented_addf(&buf, comment_line_str,
+				      _(tag_template_nocleanup), tag, comment_line_str);
 			write_or_die(fd, buf.buf, buf.len);
 			strbuf_release(&buf);
 		}
@@ -322,7 +346,8 @@
 	}
 
 	if (opt->cleanup_mode != CLEANUP_NONE)
-		strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+		strbuf_stripspace(buf,
+		  opt->cleanup_mode == CLEANUP_ALL ? comment_line_str : NULL);
 
 	if (!opt->message_given && !buf->len)
 		die(_("no tag message?"));
@@ -336,10 +361,6 @@
 				path);
 		exit(128);
 	}
-	if (path) {
-		unlink_or_warn(path);
-		free(path);
-	}
 }
 
 static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
@@ -366,7 +387,7 @@
 		strbuf_addstr(sb, "object of unknown type");
 		break;
 	case OBJ_COMMIT:
-		if ((buf = read_object_file(oid, &type, &size))) {
+		if ((buf = repo_read_object_file(the_repository, oid, &type, &size))) {
 			subject_len = find_commit_subject(buf, &subject_start);
 			strbuf_insert(sb, sb->len, subject_start, subject_len);
 		} else {
@@ -433,11 +454,12 @@
 	int create_reflog = 0;
 	int annotate = 0, force = 0;
 	int cmdmode = 0, create_tag_object = 0;
-	const char *msgfile = NULL, *keyid = NULL;
+	char *msgfile = NULL;
+	const char *keyid = NULL;
 	struct msg_arg msg = { .buf = STRBUF_INIT };
 	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
-	struct ref_filter filter;
+	struct ref_filter filter = REF_FILTER_INIT;
 	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
@@ -473,6 +495,8 @@
 		OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
 		OPT_MERGED(&filter, N_("print only tags that are merged")),
 		OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+		OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
+			N_("do not output a newline after empty formatted refs")),
 		OPT_REF_SORT(&sorting_options),
 		{
 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
@@ -487,13 +511,19 @@
 	};
 	int ret = 0;
 	const char *only_in_list = NULL;
+	char *path = NULL;
 
 	setup_ref_filter_porcelain_msg();
 
+	/*
+	 * Try to set sort keys from config. If config does not set any,
+	 * fall back on default (refname) sorting.
+	 */
 	git_config(git_tag_config, &sorting_options);
+	if (!sorting_options.nr)
+		string_list_append(&sorting_options, "refname");
 
 	memset(&opt, 0, sizeof(opt));
-	memset(&filter, 0, sizeof(filter));
 	filter.lines = -1;
 	opt.sign = -1;
 
@@ -537,7 +567,8 @@
 			struct column_options copts;
 			memset(&copts, 0, sizeof(copts));
 			copts.padding = 2;
-			run_column_filter(colopts, &copts);
+			if (run_column_filter(colopts, &copts))
+				die(_("could not start 'git column'"));
 		}
 		filter.name_patterns = argv;
 		ret = list_tags(&filter, sorting, &format);
@@ -593,7 +624,7 @@
 	if (argc > 2)
 		die(_("too many arguments"));
 
-	if (get_oid(object_ref, &object))
+	if (repo_get_oid(the_repository, object_ref, &object))
 		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 
 	if (strbuf_check_tag_ref(&ref, tag))
@@ -621,7 +652,9 @@
 	if (create_tag_object) {
 		if (force_sign_annotate && !annotate)
 			opt.sign = 1;
-		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
+		path = git_pathdup("TAG_EDITMSG");
+		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
+			   path);
 	}
 
 	transaction = ref_transaction_begin(&err);
@@ -629,19 +662,30 @@
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
-	    ref_transaction_commit(transaction, &err))
+	    ref_transaction_commit(transaction, &err)) {
+		if (path)
+			fprintf(stderr,
+				_("The tag message has been left in %s\n"),
+				path);
 		die("%s", err.buf);
+	}
+	if (path) {
+		unlink_or_warn(path);
+		free(path);
+	}
 	ref_transaction_free(transaction);
 	if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
 		printf(_("Updated tag '%s' (was %s)\n"), tag,
-		       find_unique_abbrev(&prev, DEFAULT_ABBREV));
+		       repo_find_unique_abbrev(the_repository, &prev, DEFAULT_ABBREV));
 
 cleanup:
 	ref_sorting_release(sorting);
+	ref_filter_clear(&filter);
 	strbuf_release(&buf);
 	strbuf_release(&ref);
 	strbuf_release(&reflog_msg);
 	strbuf_release(&msg.buf);
 	strbuf_release(&err);
+	free(msgfile);
 	return ret;
 }
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 88de32b..c129e2b 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -1,6 +1,8 @@
 #include "builtin.h"
 #include "config.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 
 static char *create_temp_file(struct object_id *oid)
 {
@@ -10,7 +12,7 @@
 	unsigned long size;
 	int fd;
 
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf || type != OBJ_BLOB)
 		die("unable to read blob object %s", oid_to_hex(oid));
 
@@ -23,13 +25,13 @@
 	return path;
 }
 
-int cmd_unpack_file(int argc, const char **argv, const char *prefix)
+int cmd_unpack_file(int argc, const char **argv, const char *prefix UNUSED)
 {
 	struct object_id oid;
 
 	if (argc != 2 || !strcmp(argv[1], "-h"))
 		usage("git unpack-file <blob>");
-	if (get_oid(argv[1], &oid))
+	if (repo_get_oid(the_repository, argv[1], &oid))
 		die("Not a valid object name %s", argv[1]);
 
 	git_config(git_default_config, NULL);
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 43789b8..f1c85a0 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -1,16 +1,17 @@
 #include "builtin.h"
-#include "cache.h"
 #include "bulk-checkin.h"
 #include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "gettext.h"
+#include "git-zlib.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "object.h"
 #include "delta.h"
 #include "pack.h"
 #include "blob.h"
-#include "commit.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
+#include "replace-object.h"
+#include "strbuf.h"
 #include "progress.h"
 #include "decorate.h"
 #include "fsck.h"
@@ -210,7 +211,8 @@
  * Verify its reachability and validity recursively and write it out.
  */
 static int check_object(struct object *obj, enum object_type type,
-			void *data, struct fsck_options *options)
+			void *data UNUSED,
+			struct fsck_options *options UNUSED)
 {
 	struct obj_buffer *obj_buf;
 
@@ -442,7 +444,7 @@
 		delta_data = get_data(delta_size);
 		if (!delta_data)
 			return;
-		if (has_object_file(&base_oid))
+		if (repo_has_object_file(the_repository, &base_oid))
 			; /* Ok we have this one */
 		else if (resolve_against_held(nr, &base_oid,
 					      delta_data, delta_size))
@@ -508,7 +510,8 @@
 	if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
 		return;
 
-	base = read_object_file(&base_oid, &type, &base_size);
+	base = repo_read_object_file(the_repository, &base_oid, &type,
+				     &base_size);
 	if (!base) {
 		error("failed to read delta-pack base object %s",
 		      oid_to_hex(&base_oid));
@@ -598,12 +601,13 @@
 		die("unresolved deltas left after unpacking");
 }
 
-int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
 {
 	int i;
 	struct object_id oid;
+	git_hash_ctx tmp_ctx;
 
-	read_replace_refs = 0;
+	disable_replace_refs();
 
 	git_config(git_default_config, NULL);
 
@@ -662,7 +666,9 @@
 	the_hash_algo->init_fn(&ctx);
 	unpack_all();
 	the_hash_algo->update_fn(&ctx, buffer, offset);
-	the_hash_algo->final_oid_fn(&oid, &ctx);
+	the_hash_algo->init_fn(&tmp_ctx);
+	the_hash_algo->clone_fn(&tmp_ctx, &ctx);
+	the_hash_algo->final_oid_fn(&oid, &tmp_ctx);
 	if (strict) {
 		write_rest();
 		if (fsck_finish(&fsck_options))
@@ -673,13 +679,7 @@
 	use(the_hash_algo->rawsz);
 
 	/* Write the last part of the buffer to stdout */
-	while (len) {
-		int ret = xwrite(1, buffer + offset, len);
-		if (ret <= 0)
-			break;
-		len -= ret;
-		offset += ret;
-	}
+	write_in_full(1, buffer + offset, len);
 
 	/* All done */
 	return has_errors;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index bf38885..7bcaa14 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -4,21 +4,31 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
 #include "bulk-checkin.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
-#include "builtin.h"
+#include "object-file.h"
 #include "refs.h"
 #include "resolve-undo.h"
 #include "parse-options.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "read-cache.h"
+#include "repository.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "split-index.h"
+#include "symlinks.h"
 #include "fsmonitor.h"
+#include "write-or-die.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -599,9 +609,6 @@
 	NULL
 };
 
-static struct object_id head_oid;
-static struct object_id merge_head_oid;
-
 static struct cache_entry *read_one_ent(const char *which,
 					struct object_id *ent, const char *path,
 					int namelen, int stage)
@@ -632,84 +639,17 @@
 
 static int unresolve_one(const char *path)
 {
-	int namelen = strlen(path);
-	int pos;
-	int ret = 0;
-	struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
+	struct string_list_item *item;
+	int res = 0;
 
-	/* See if there is such entry in the index. */
-	pos = index_name_pos(&the_index, path, namelen);
-	if (0 <= pos) {
-		/* already merged */
-		pos = unmerge_index_entry_at(&the_index, pos);
-		if (pos < the_index.cache_nr) {
-			const struct cache_entry *ce = the_index.cache[pos];
-			if (ce_stage(ce) &&
-			    ce_namelen(ce) == namelen &&
-			    !memcmp(ce->name, path, namelen))
-				return 0;
-		}
-		/* no resolve-undo information; fall back */
-	} else {
-		/* If there isn't, either it is unmerged, or
-		 * resolved as "removed" by mistake.  We do not
-		 * want to do anything in the former case.
-		 */
-		pos = -pos-1;
-		if (pos < the_index.cache_nr) {
-			const struct cache_entry *ce = the_index.cache[pos];
-			if (ce_namelen(ce) == namelen &&
-			    !memcmp(ce->name, path, namelen)) {
-				fprintf(stderr,
-					"%s: skipping still unmerged path.\n",
-					path);
-				goto free_return;
-			}
-		}
-	}
-
-	/* Grab blobs from given path from HEAD and MERGE_HEAD,
-	 * stuff HEAD version in stage #2,
-	 * stuff MERGE_HEAD version in stage #3.
-	 */
-	ce_2 = read_one_ent("our", &head_oid, path, namelen, 2);
-	ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3);
-
-	if (!ce_2 || !ce_3) {
-		ret = -1;
-		goto free_return;
-	}
-	if (oideq(&ce_2->oid, &ce_3->oid) &&
-	    ce_2->ce_mode == ce_3->ce_mode) {
-		fprintf(stderr, "%s: identical in both, skipping.\n",
-			path);
-		goto free_return;
-	}
-
-	remove_file_from_index(&the_index, path);
-	if (add_index_entry(&the_index, ce_2, ADD_CACHE_OK_TO_ADD)) {
-		error("%s: cannot add our version to the index.", path);
-		ret = -1;
-		goto free_return;
-	}
-	if (!add_index_entry(&the_index, ce_3, ADD_CACHE_OK_TO_ADD))
-		return 0;
-	error("%s: cannot add their version to the index.", path);
-	ret = -1;
- free_return:
-	discard_cache_entry(ce_2);
-	discard_cache_entry(ce_3);
-	return ret;
-}
-
-static void read_head_pointers(void)
-{
-	if (read_ref("HEAD", &head_oid))
-		die("No HEAD -- no initial commit yet?");
-	if (read_ref("MERGE_HEAD", &merge_head_oid)) {
-		fprintf(stderr, "Not in the middle of a merge.\n");
-		exit(0);
-	}
+	if (!the_index.resolve_undo)
+		return res;
+	item = string_list_lookup(the_index.resolve_undo, path);
+	if (!item)
+		return res; /* no resolve-undo record for the path */
+	res = unmerge_index_entry(&the_index, path, item->util, 0);
+	FREE_AND_NULL(item->util);
+	return res;
 }
 
 static int do_unresolve(int ac, const char **av,
@@ -718,11 +658,6 @@
 	int i;
 	int err = 0;
 
-	/* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
-	 * are not doing a merge, so exit with success status.
-	 */
-	read_head_pointers();
-
 	for (i = 1; i < ac; i++) {
 		const char *arg = av[i];
 		char *p = prefix_path(prefix, prefix_length, arg);
@@ -741,6 +676,7 @@
 	int pos;
 	int has_head = 1;
 	struct pathspec pathspec;
+	struct object_id head_oid;
 
 	parse_pathspec(&pathspec, 0,
 		       PATHSPEC_PREFER_CWD,
@@ -846,7 +782,7 @@
 	return 0;
 }
 
-static int resolve_undo_clear_callback(const struct option *opt,
+static int resolve_undo_clear_callback(const struct option *opt UNUSED,
 				const char *arg, int unset)
 {
 	BUG_ON_OPT_NEG(unset);
@@ -880,7 +816,7 @@
 }
 
 static enum parse_opt_result cacheinfo_callback(
-	struct parse_opt_ctx_t *ctx, const struct option *opt,
+	struct parse_opt_ctx_t *ctx, const struct option *opt UNUSED,
 	const char *arg, int unset)
 {
 	struct object_id oid;
@@ -1080,6 +1016,8 @@
 			resolve_undo_clear_callback),
 		OPT_INTEGER(0, "index-version", &preferred_index_format,
 			N_("write index in this format")),
+		OPT_SET_INT(0, "show-index-version", &preferred_index_format,
+			    N_("report on-disk index format version"), -1),
 		OPT_BOOL(0, "split-index", &split_index,
 			N_("enable or disable split index")),
 		OPT_BOOL(0, "untracked-cache", &untracked_cache,
@@ -1172,15 +1110,20 @@
 
 	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
 	if (preferred_index_format) {
-		if (preferred_index_format < INDEX_FORMAT_LB ||
-		    INDEX_FORMAT_UB < preferred_index_format)
+		if (preferred_index_format < 0) {
+			printf(_("%d\n"), the_index.version);
+		} else if (preferred_index_format < INDEX_FORMAT_LB ||
+			   INDEX_FORMAT_UB < preferred_index_format) {
 			die("index-version %d not in range: %d..%d",
 			    preferred_index_format,
 			    INDEX_FORMAT_LB, INDEX_FORMAT_UB);
-
-		if (the_index.version != preferred_index_format)
-			the_index.cache_changed |= SOMETHING_CHANGED;
-		the_index.version = preferred_index_format;
+		} else {
+			if (the_index.version != preferred_index_format)
+				the_index.cache_changed |= SOMETHING_CHANGED;
+			report(_("index-version: was %d, set to %d"),
+			       the_index.version, preferred_index_format);
+			the_index.version = preferred_index_format;
+		}
 	}
 
 	if (read_from_stdin) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index a84e7b4..e46afbc 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -1,14 +1,16 @@
-#include "cache.h"
-#include "config.h"
-#include "refs.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
+#include "hash.h"
+#include "refs.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "quote.h"
-#include "strvec.h"
+#include "repository.h"
 
 static const char * const git_update_ref_usage[] = {
-	N_("git update-ref [<options>] -d <refname> [<old-val>]"),
-	N_("git update-ref [<options>]    <refname> <new-val> [<old-val>]"),
+	N_("git update-ref [<options>] -d <refname> [<old-oid>]"),
+	N_("git update-ref [<options>]    <refname> <new-oid> [<old-oid>]"),
 	N_("git update-ref [<options>] --stdin [-z]"),
 	NULL
 };
@@ -75,14 +77,14 @@
 }
 
 /*
- * The value being parsed is <oldvalue> (as opposed to <newvalue>; the
+ * The value being parsed is <old-oid> (as opposed to <new-oid>; the
  * difference affects which error messages are generated):
  */
 #define PARSE_SHA1_OLD 0x01
 
 /*
  * For backwards compatibility, accept an empty string for update's
- * <newvalue> in binary mode to be equivalent to specifying zeros.
+ * <new-oid> in binary mode to be equivalent to specifying zeros.
  */
 #define PARSE_SHA1_ALLOW_EMPTY 0x02
 
@@ -116,7 +118,7 @@
 		(*next)++;
 		*next = parse_arg(*next, &arg);
 		if (arg.len) {
-			if (get_oid(arg.buf, oid))
+			if (repo_get_oid(the_repository, arg.buf, oid))
 				goto invalid;
 		} else {
 			/* Without -z, an empty value means all zeros: */
@@ -134,11 +136,11 @@
 		*next += arg.len;
 
 		if (arg.len) {
-			if (get_oid(arg.buf, oid))
+			if (repo_get_oid(the_repository, arg.buf, oid))
 				goto invalid;
 		} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
 			/* With -z, treat an empty value as all zeros: */
-			warning("%s %s: missing <newvalue>, treating as zero",
+			warning("%s %s: missing <new-oid>, treating as zero",
 				command, refname);
 			oidclr(oid);
 		} else {
@@ -156,14 +158,14 @@
 
  invalid:
 	die(flags & PARSE_SHA1_OLD ?
-	    "%s %s: invalid <oldvalue>: %s" :
-	    "%s %s: invalid <newvalue>: %s",
+	    "%s %s: invalid <old-oid>: %s" :
+	    "%s %s: invalid <new-oid>: %s",
 	    command, refname, arg.buf);
 
  eof:
 	die(flags & PARSE_SHA1_OLD ?
-	    "%s %s: unexpected end of input when reading <oldvalue>" :
-	    "%s %s: unexpected end of input when reading <newvalue>",
+	    "%s %s: unexpected end of input when reading <old-oid>" :
+	    "%s %s: unexpected end of input when reading <new-oid>",
 	    command, refname);
 }
 
@@ -192,7 +194,7 @@
 
 	if (parse_next_oid(&next, end, &new_oid, "update", refname,
 			   PARSE_SHA1_ALLOW_EMPTY))
-		die("update %s: missing <newvalue>", refname);
+		die("update %s: missing <new-oid>", refname);
 
 	have_old = !parse_next_oid(&next, end, &old_oid, "update", refname,
 				   PARSE_SHA1_OLD);
@@ -223,10 +225,10 @@
 		die("create: missing <ref>");
 
 	if (parse_next_oid(&next, end, &new_oid, "create", refname, 0))
-		die("create %s: missing <newvalue>", refname);
+		die("create %s: missing <new-oid>", refname);
 
 	if (is_null_oid(&new_oid))
-		die("create %s: zero <newvalue>", refname);
+		die("create %s: zero <new-oid>", refname);
 
 	if (*next != line_termination)
 		die("create %s: extra input: %s", refname, next);
@@ -258,7 +260,7 @@
 		have_old = 0;
 	} else {
 		if (is_null_oid(&old_oid))
-			die("delete %s: zero <oldvalue>", refname);
+			die("delete %s: zero <old-oid>", refname);
 		have_old = 1;
 	}
 
@@ -308,8 +310,8 @@
 	fflush(stdout);
 }
 
-static void parse_cmd_option(struct ref_transaction *transaction,
-			     const char *next, const char *end)
+static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
+			     const char *next, const char *end UNUSED)
 {
 	const char *rest;
 	if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
@@ -318,8 +320,8 @@
 		die("option unknown: %s", next);
 }
 
-static void parse_cmd_start(struct ref_transaction *transaction,
-			    const char *next, const char *end)
+static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
+			    const char *next, const char *end UNUSED)
 {
 	if (*next != line_termination)
 		die("start: extra input: %s", next);
@@ -327,7 +329,7 @@
 }
 
 static void parse_cmd_prepare(struct ref_transaction *transaction,
-			      const char *next, const char *end)
+			      const char *next, const char *end UNUSED)
 {
 	struct strbuf error = STRBUF_INIT;
 	if (*next != line_termination)
@@ -338,7 +340,7 @@
 }
 
 static void parse_cmd_abort(struct ref_transaction *transaction,
-			    const char *next, const char *end)
+			    const char *next, const char *end UNUSED)
 {
 	struct strbuf error = STRBUF_INIT;
 	if (*next != line_termination)
@@ -349,7 +351,7 @@
 }
 
 static void parse_cmd_commit(struct ref_transaction *transaction,
-			     const char *next, const char *end)
+			     const char *next, const char *end UNUSED)
 {
 	struct strbuf error = STRBUF_INIT;
 	if (*next != line_termination)
@@ -549,7 +551,7 @@
 		refname = argv[0];
 		value = argv[1];
 		oldval = argv[2];
-		if (get_oid(value, &oid))
+		if (repo_get_oid(the_repository, value, &oid))
 			die("%s: not a valid SHA1", value);
 	}
 
@@ -560,7 +562,7 @@
 			 * must not already exist:
 			 */
 			oidclr(&oldoid);
-		else if (get_oid(oldval, &oldoid))
+		else if (repo_get_oid(the_repository, oldval, &oldoid))
 			die("%s: not a valid old SHA1", oldval);
 	}
 
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index d2239c9..1dc3971 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -1,7 +1,8 @@
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
 #include "parse-options.h"
+#include "server-info.h"
 
 static const char * const update_server_info_usage[] = {
 	"git update-server-info [-f | --force]",
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 945ee2b..1b09e5e 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -1,11 +1,12 @@
 /*
  * Copyright (c) 2006 Franck Bui-Huu
  */
-#include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "path.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "repository.h"
 #include "run-command.h"
 #include "strvec.h"
 
@@ -79,6 +80,8 @@
 {
 	struct child_process writer = CHILD_PROCESS_INIT;
 
+	BUG_ON_NON_EMPTY_PREFIX(prefix);
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(upload_archive_usage);
 
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 25b69da..15afb97 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -1,11 +1,14 @@
-#include "cache.h"
 #include "builtin.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "pkt-line.h"
 #include "parse-options.h"
+#include "path.h"
 #include "protocol.h"
+#include "replace-object.h"
 #include "upload-pack.h"
 #include "serve.h"
+#include "commit.h"
 
 static const char * const upload_pack_usage[] = {
 	N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
@@ -34,7 +37,8 @@
 	};
 
 	packet_trace_identity("upload-pack");
-	read_replace_refs = 0;
+	disable_replace_refs();
+	save_commit_buffer = 0;
 
 	argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
diff --git a/builtin/var.c b/builtin/var.c
index a80c1df..cf55672 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -4,57 +4,188 @@
  * Copyright (C) Eric Biederman, 2005
  */
 #include "builtin.h"
+#include "attr.h"
 #include "config.h"
+#include "editor.h"
+#include "ident.h"
+#include "pager.h"
 #include "refs.h"
+#include "path.h"
+#include "strbuf.h"
 
 static const char var_usage[] = "git var (-l | <variable>)";
 
-static const char *editor(int flag)
+static char *committer(int ident_flag)
 {
-	return git_editor();
+	return xstrdup_or_null(git_committer_info(ident_flag));
 }
 
-static const char *sequence_editor(int flag)
+static char *author(int ident_flag)
 {
-	return git_sequence_editor();
+	return xstrdup_or_null(git_author_info(ident_flag));
 }
 
-static const char *pager(int flag)
+static char *editor(int ident_flag UNUSED)
+{
+	return xstrdup_or_null(git_editor());
+}
+
+static char *sequence_editor(int ident_flag UNUSED)
+{
+	return xstrdup_or_null(git_sequence_editor());
+}
+
+static char *pager(int ident_flag UNUSED)
 {
 	const char *pgm = git_pager(1);
 
 	if (!pgm)
 		pgm = "cat";
-	return pgm;
+	return xstrdup(pgm);
 }
 
-static const char *default_branch(int flag)
+static char *default_branch(int ident_flag UNUSED)
 {
-	return git_default_branch_name(1);
+	return xstrdup_or_null(git_default_branch_name(1));
+}
+
+static char *shell_path(int ident_flag UNUSED)
+{
+	return xstrdup(SHELL_PATH);
+}
+
+static char *git_attr_val_system(int ident_flag UNUSED)
+{
+	if (git_attr_system_is_enabled()) {
+		char *file = xstrdup(git_attr_system_file());
+		normalize_path_copy(file, file);
+		return file;
+	}
+	return NULL;
+}
+
+static char *git_attr_val_global(int ident_flag UNUSED)
+{
+	char *file = xstrdup_or_null(git_attr_global_file());
+	if (file) {
+		normalize_path_copy(file, file);
+		return file;
+	}
+	return NULL;
+}
+
+static char *git_config_val_system(int ident_flag UNUSED)
+{
+	if (git_config_system()) {
+		char *file = git_system_config();
+		normalize_path_copy(file, file);
+		return file;
+	}
+	return NULL;
+}
+
+static char *git_config_val_global(int ident_flag UNUSED)
+{
+	struct strbuf buf = STRBUF_INIT;
+	char *user, *xdg;
+	size_t unused;
+
+	git_global_config_paths(&user, &xdg);
+	if (xdg && *xdg) {
+		normalize_path_copy(xdg, xdg);
+		strbuf_addf(&buf, "%s\n", xdg);
+	}
+	if (user && *user) {
+		normalize_path_copy(user, user);
+		strbuf_addf(&buf, "%s\n", user);
+	}
+	free(xdg);
+	free(user);
+	strbuf_trim_trailing_newline(&buf);
+	if (buf.len == 0) {
+		strbuf_release(&buf);
+		return NULL;
+	}
+	return strbuf_detach(&buf, &unused);
 }
 
 struct git_var {
 	const char *name;
-	const char *(*read)(int);
+	char *(*read)(int);
+	int multivalued;
 };
 static struct git_var git_vars[] = {
-	{ "GIT_COMMITTER_IDENT", git_committer_info },
-	{ "GIT_AUTHOR_IDENT",   git_author_info },
-	{ "GIT_EDITOR", editor },
-	{ "GIT_SEQUENCE_EDITOR", sequence_editor },
-	{ "GIT_PAGER", pager },
-	{ "GIT_DEFAULT_BRANCH", default_branch },
-	{ "", NULL },
+	{
+		.name = "GIT_COMMITTER_IDENT",
+		.read = committer,
+	},
+	{
+		.name = "GIT_AUTHOR_IDENT",
+		.read = author,
+	},
+	{
+		.name = "GIT_EDITOR",
+		.read = editor,
+	},
+	{
+		.name = "GIT_SEQUENCE_EDITOR",
+		.read = sequence_editor,
+	},
+	{
+		.name = "GIT_PAGER",
+		.read = pager,
+	},
+	{
+		.name = "GIT_DEFAULT_BRANCH",
+		.read = default_branch,
+	},
+	{
+		.name = "GIT_SHELL_PATH",
+		.read = shell_path,
+	},
+	{
+		.name = "GIT_ATTR_SYSTEM",
+		.read = git_attr_val_system,
+	},
+	{
+		.name = "GIT_ATTR_GLOBAL",
+		.read = git_attr_val_global,
+	},
+	{
+		.name = "GIT_CONFIG_SYSTEM",
+		.read = git_config_val_system,
+	},
+	{
+		.name = "GIT_CONFIG_GLOBAL",
+		.read = git_config_val_global,
+		.multivalued = 1,
+	},
+	{
+		.name = "",
+		.read = NULL,
+	},
 };
 
 static void list_vars(void)
 {
 	struct git_var *ptr;
-	const char *val;
+	char *val;
 
 	for (ptr = git_vars; ptr->read; ptr++)
-		if ((val = ptr->read(0)))
-			printf("%s=%s\n", ptr->name, val);
+		if ((val = ptr->read(0))) {
+			if (ptr->multivalued && *val) {
+				struct string_list list = STRING_LIST_INIT_DUP;
+				int i;
+
+				string_list_split(&list, val, '\n', -1);
+				for (i = 0; i < list.nr; i++)
+					printf("%s=%s\n", ptr->name, list.items[i].string);
+				string_list_clear(&list, 0);
+			} else {
+				printf("%s=%s\n", ptr->name, val);
+			}
+			free(val);
+		}
 }
 
 static const struct git_var *get_git_var(const char *var)
@@ -68,19 +199,20 @@
 	return NULL;
 }
 
-static int show_config(const char *var, const char *value, void *cb)
+static int show_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (value)
 		printf("%s=%s\n", var, value);
 	else
 		printf("%s\n", var);
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
-int cmd_var(int argc, const char **argv, const char *prefix)
+int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
 {
 	const struct git_var *git_var;
-	const char *val;
+	char *val;
 
 	if (argc != 2)
 		usage(var_usage);
@@ -101,6 +233,7 @@
 		return 1;
 
 	printf("%s\n", val);
+	free(val);
 
 	return 0;
 }
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 3ebad32..0d2b9ae 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -5,13 +5,12 @@
  *
  * Based on git-verify-tag
  */
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
-#include "object-store.h"
+#include "config.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "repository.h"
 #include "commit.h"
-#include "run-command.h"
 #include "parse-options.h"
 #include "gpg-interface.h"
 
@@ -39,7 +38,7 @@
 	struct object_id oid;
 	struct object *obj;
 
-	if (get_oid(name, &oid))
+	if (repo_get_oid(the_repository, name, &oid))
 		return error("commit '%s' not found.", name);
 
 	obj = parse_object(the_repository, &oid);
@@ -52,14 +51,6 @@
 	return run_gpg_verify((struct commit *)obj, flags);
 }
 
-static int git_verify_commit_config(const char *var, const char *value, void *cb)
-{
-	int status = git_gpg_config(var, value, cb);
-	if (status)
-		return status;
-	return git_default_config(var, value, cb);
-}
-
 int cmd_verify_commit(int argc, const char **argv, const char *prefix)
 {
 	int i = 1, verbose = 0, had_error = 0;
@@ -70,7 +61,7 @@
 		OPT_END()
 	};
 
-	git_config(git_verify_commit_config, NULL);
+	git_config(git_default_config, NULL);
 
 	argc = parse_options(argc, argv, prefix, verify_commit_options,
 			     verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index 27d6f75..011dddd 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -1,8 +1,9 @@
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "gettext.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "strbuf.h"
 
 #define VERIFY_PACK_VERBOSE 01
 #define VERIFY_PACK_STAT_ONLY 02
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 2175669..c731e2f 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -5,11 +5,11 @@
  *
  * Based on git-verify-tag.sh
  */
-#include "cache.h"
-#include "config.h"
 #include "builtin.h"
+#include "config.h"
+#include "gettext.h"
 #include "tag.h"
-#include "run-command.h"
+#include "object-name.h"
 #include "parse-options.h"
 #include "gpg-interface.h"
 #include "ref-filter.h"
@@ -19,14 +19,6 @@
 		NULL
 };
 
-static int git_verify_tag_config(const char *var, const char *value, void *cb)
-{
-	int status = git_gpg_config(var, value, cb);
-	if (status)
-		return status;
-	return git_default_config(var, value, cb);
-}
-
 int cmd_verify_tag(int argc, const char **argv, const char *prefix)
 {
 	int i = 1, verbose = 0, had_error = 0;
@@ -39,7 +31,7 @@
 		OPT_END()
 	};
 
-	git_config(git_verify_tag_config, NULL);
+	git_config(git_default_config, NULL);
 
 	argc = parse_options(argc, argv, prefix, verify_tag_options,
 			     verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
@@ -60,7 +52,7 @@
 		struct object_id oid;
 		const char *name = argv[i++];
 
-		if (get_oid(name, &oid)) {
+		if (repo_get_oid(the_repository, name, &oid)) {
 			had_error = !!error("tag '%s' not found.", name);
 			continue;
 		}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 254283a..7c6c725 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -1,12 +1,23 @@
-#include "cache.h"
+#include "builtin.h"
+#include "abspath.h"
+#include "advice.h"
 #include "checkout.h"
 #include "config.h"
-#include "builtin.h"
+#include "copy.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-name.h"
 #include "parse-options.h"
+#include "path.h"
 #include "strvec.h"
 #include "branch.h"
+#include "read-cache-ll.h"
 #include "refs.h"
+#include "remote.h"
+#include "repository.h"
 #include "run-command.h"
 #include "hook.h"
 #include "sigchain.h"
@@ -17,7 +28,8 @@
 
 #define BUILTIN_WORKTREE_ADD_USAGE \
 	N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \
-	   "                 [-b <new-branch>] <path> [<commit-ish>]")
+	   "                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]")
+
 #define BUILTIN_WORKTREE_LIST_USAGE \
 	N_("git worktree list [-v | --porcelain [-z]]")
 #define BUILTIN_WORKTREE_LOCK_USAGE \
@@ -33,6 +45,23 @@
 #define BUILTIN_WORKTREE_UNLOCK_USAGE \
 	N_("git worktree unlock <worktree>")
 
+#define WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT \
+	_("No possible source branch, inferring '--orphan'")
+
+#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
+	_("If you meant to create a worktree containing a new unborn branch\n" \
+	"(branch with no commits) for this repository, you can do so\n" \
+	"using the --orphan flag:\n" \
+	"\n" \
+	"    git worktree add --orphan -b %s %s\n")
+
+#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
+	_("If you meant to create a worktree containing a new unborn branch\n" \
+	"(branch with no commits) for this repository, you can do so\n" \
+	"using the --orphan flag:\n" \
+	"\n" \
+	"    git worktree add --orphan %s\n")
+
 static const char * const git_worktree_usage[] = {
 	BUILTIN_WORKTREE_ADD_USAGE,
 	BUILTIN_WORKTREE_LIST_USAGE,
@@ -90,6 +119,7 @@
 	int detach;
 	int quiet;
 	int checkout;
+	int orphan;
 	const char *keep_locked;
 };
 
@@ -98,14 +128,15 @@
 static int guess_remote;
 static timestamp_t expire;
 
-static int git_worktree_config(const char *var, const char *value, void *cb)
+static int git_worktree_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "worktree.guessremote")) {
 		guess_remote = git_config_bool(var, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int delete_git_dir(const char *id)
@@ -319,7 +350,6 @@
 
 	if (file_exists(from_file)) {
 		struct config_set cs = { { 0 } };
-		const char *core_worktree;
 		int bare;
 
 		if (safe_create_leading_directories(to_file) ||
@@ -335,12 +365,12 @@
 		if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
 			bare &&
 			git_config_set_multivar_in_file_gently(
-				to_file, "core.bare", NULL, "true", 0))
+				to_file, "core.bare", NULL, "true", NULL, 0))
 			error(_("failed to unset '%s' in '%s'"),
 				"core.bare", to_file);
-		if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
+		if (!git_configset_get(&cs, "core.worktree") &&
 			git_config_set_in_file_gently(to_file,
-							"core.worktree", NULL))
+							"core.worktree", NULL, NULL))
 			error(_("failed to unset '%s' in '%s'"),
 				"core.worktree", to_file);
 
@@ -364,13 +394,28 @@
 	return run_command(&cp);
 }
 
+static int make_worktree_orphan(const char * ref, const struct add_opts *opts,
+				struct strvec *child_env)
+{
+	struct strbuf symref = STRBUF_INIT;
+	struct child_process cp = CHILD_PROCESS_INIT;
+
+	validate_new_branchname(ref, &symref, 0);
+	strvec_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL);
+	if (opts->quiet)
+		strvec_push(&cp.args, "--quiet");
+	strvec_pushv(&cp.env, child_env->v);
+	strbuf_release(&symref);
+	cp.git_cmd = 1;
+	return run_command(&cp);
+}
+
 static int add_worktree(const char *path, const char *refname,
 			const struct add_opts *opts)
 {
 	struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
 	struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
 	const char *name;
-	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strvec child_env = STRVEC_INIT;
 	unsigned int counter = 0;
 	int len, ret;
@@ -378,7 +423,8 @@
 	struct commit *commit = NULL;
 	int is_branch = 0;
 	struct strbuf sb_name = STRBUF_INIT;
-	struct worktree **worktrees;
+	struct worktree **worktrees, *wt = NULL;
+	struct ref_store *wt_refs;
 
 	worktrees = get_worktrees();
 	check_candidate_path(path, opts->force, worktrees, "add");
@@ -393,7 +439,7 @@
 			die_if_checked_out(symref.buf, 0);
 	}
 	commit = lookup_commit_reference_by_name(refname);
-	if (!commit)
+	if (!commit && !opts->orphan)
 		die(_("invalid reference: %s"), refname);
 
 	name = worktree_basename(path, &len);
@@ -449,21 +495,33 @@
 	strbuf_realpath(&realpath, get_git_common_dir(), 1);
 	write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
 		   realpath.buf, name);
-	/*
-	 * This is to keep resolve_ref() happy. We need a valid HEAD
-	 * or is_git_directory() will reject the directory. Any value which
-	 * looks like an object ID will do since it will be immediately
-	 * replaced by the symbolic-ref or update-ref invocation in the new
-	 * worktree.
-	 */
-	strbuf_reset(&sb);
-	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-	write_file(sb.buf, "%s", oid_to_hex(null_oid()));
 	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
 	write_file(sb.buf, "../..");
 
 	/*
+	 * Set up the ref store of the worktree and create the HEAD reference.
+	 */
+	wt = get_linked_worktree(name, 1);
+	if (!wt) {
+		ret = error(_("could not find created worktree '%s'"), name);
+		goto done;
+	}
+	wt_refs = get_worktree_ref_store(wt);
+
+	ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+	if (ret)
+		goto done;
+
+	if (!is_branch && commit)
+		ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
+				      NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+	else
+		ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+	if (ret)
+		goto done;
+
+	/*
 	 * If the current worktree has sparse-checkout enabled, then copy
 	 * the sparse-checkout patterns from the current worktree.
 	 */
@@ -475,26 +533,14 @@
 	 * values from the current worktree into the new one, that way the
 	 * new worktree behaves the same as this one.
 	 */
-	if (repository_format_worktree_config)
+	if (the_repository->repository_format_worktree_config)
 		copy_filtered_worktree_config(sb_repo.buf);
 
 	strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
 	strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
-	cp.git_cmd = 1;
 
-	if (!is_branch)
-		strvec_pushl(&cp.args, "update-ref", "HEAD",
-			     oid_to_hex(&commit->object.oid), NULL);
-	else {
-		strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
-			     symref.buf, NULL);
-		if (opts->quiet)
-			strvec_push(&cp.args, "--quiet");
-	}
-
-	strvec_pushv(&cp.env, child_env.v);
-	ret = run_command(&cp);
-	if (ret)
+	if (opts->orphan &&
+	    (ret = make_worktree_orphan(refname, opts, &child_env)))
 		goto done;
 
 	if (opts->checkout &&
@@ -516,7 +562,7 @@
 	 * Hook failure does not warrant worktree deletion, so run hook after
 	 * is_junk is cleared, but do return appropriate code when hook fails.
 	 */
-	if (!ret && opts->checkout) {
+	if (!ret && opts->checkout && !opts->orphan) {
 		struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
 
 		strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
@@ -537,6 +583,7 @@
 	strbuf_release(&sb_git);
 	strbuf_release(&sb_name);
 	strbuf_release(&realpath);
+	free_worktree(wt);
 	return ret;
 }
 
@@ -552,7 +599,7 @@
 		else
 			fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
 				  new_branch,
-				  find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+				  repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
 	} else if (new_branch) {
 		fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
 	} else {
@@ -564,14 +611,131 @@
 		else {
 			struct commit *commit = lookup_commit_reference_by_name(branch);
 			if (!commit)
-				die(_("invalid reference: %s"), branch);
+				BUG(_("unreachable: invalid reference: %s"), branch);
 			fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
-				  find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
+				  repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
 		}
 		strbuf_release(&s);
 	}
 }
 
+/**
+ * Callback to short circuit iteration over refs on the first reference
+ * corresponding to a valid oid.
+ *
+ * Returns 0 on failure and non-zero on success.
+ */
+static int first_valid_ref(const char *refname UNUSED,
+			   const struct object_id *oid UNUSED,
+			   int flags UNUSED,
+			   void *cb_data UNUSED)
+{
+	return 1;
+}
+
+/**
+ * Verifies HEAD and determines whether there exist any valid local references.
+ *
+ * - Checks whether HEAD points to a valid reference.
+ *
+ * - Checks whether any valid local branches exist.
+ *
+ * - Emits a warning if there exist any valid branches but HEAD does not point
+ *   to a valid reference.
+ *
+ * Returns 1 if any of the previous checks are true, otherwise returns 0.
+ */
+static int can_use_local_refs(const struct add_opts *opts)
+{
+	if (head_ref(first_valid_ref, NULL)) {
+		return 1;
+	} else if (for_each_branch_ref(first_valid_ref, NULL)) {
+		if (!opts->quiet) {
+			struct strbuf path = STRBUF_INIT;
+			struct strbuf contents = STRBUF_INIT;
+
+			strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+			strbuf_addstr(&path, "/HEAD");
+			strbuf_read_file(&contents, path.buf, 64);
+			strbuf_stripspace(&contents, NULL);
+			strbuf_strip_suffix(&contents, "\n");
+
+			warning(_("HEAD points to an invalid (or orphaned) reference.\n"
+				  "HEAD path: '%s'\n"
+				  "HEAD contents: '%s'"),
+				  path.buf, contents.buf);
+			strbuf_release(&path);
+			strbuf_release(&contents);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * Reports whether the necessary flags were set and whether the repository has
+ * remote references to attempt DWIM tracking of upstream branches.
+ *
+ * 1. Checks that `--guess-remote` was used or `worktree.guessRemote = true`.
+ *
+ * 2. Checks whether any valid remote branches exist.
+ *
+ * 3. Checks that there exists at least one remote and emits a warning/error
+ *    if both checks 1. and 2. are false (can be bypassed with `--force`).
+ *
+ * Returns 1 if checks 1. and 2. are true, otherwise 0.
+ */
+static int can_use_remote_refs(const struct add_opts *opts)
+{
+	if (!guess_remote) {
+		return 0;
+	} else if (for_each_remote_ref(first_valid_ref, NULL)) {
+		return 1;
+	} else if (!opts->force && remote_get(NULL)) {
+		die(_("No local or remote refs exist despite at least one remote\n"
+		      "present, stopping; use 'add -f' to override or fetch a remote first"));
+	}
+	return 0;
+}
+
+/**
+ * Determines whether `--orphan` should be inferred in the evaluation of
+ * `worktree add path/` or `worktree add -b branch path/` and emits an error
+ * if the supplied arguments would produce an illegal combination when the
+ * `--orphan` flag is included.
+ *
+ * `opts` and `opt_track` contain the other options & flags supplied to the
+ * command.
+ *
+ * remote determines whether to check `can_use_remote_refs()` or not. This
+ * is primarily to differentiate between the basic `add` DWIM and `add -b`.
+ *
+ * Returns 1 when inferring `--orphan`, 0 otherwise, and emits an error when
+ * `--orphan` is inferred but doing so produces an illegal combination of
+ * options and flags. Additionally produces an error when remote refs are
+ * checked and the repo is in a state that looks like the user added a remote
+ * but forgot to fetch (and did not override the warning with -f).
+ */
+static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
+{
+	if (can_use_local_refs(opts)) {
+		return 0;
+	} else if (remote && can_use_remote_refs(opts)) {
+		return 0;
+	} else if (!opts->quiet) {
+		fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
+	}
+
+	if (opt_track) {
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--orphan", "--track");
+	} else if (!opts->checkout) {
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--orphan", "--no-checkout");
+	}
+	return 1;
+}
+
 static const char *dwim_branch(const char *path, const char **new_branch)
 {
 	int n;
@@ -608,6 +772,7 @@
 	const char *opt_track = NULL;
 	const char *lock_reason = NULL;
 	int keep_locked = 0;
+	int used_new_branch_options;
 	struct option options[] = {
 		OPT__FORCE(&opts.force,
 			   N_("checkout <branch> even if already checked out in other worktree"),
@@ -616,6 +781,7 @@
 			   N_("create a new branch")),
 		OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
 			   N_("create or reset a branch")),
+		OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn branch")),
 		OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
 		OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 		OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
@@ -636,6 +802,18 @@
 	ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
 	if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
 		die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
+	if (opts.detach && opts.orphan)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--orphan", "--detach");
+	if (opts.orphan && opt_track)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--orphan", "--track");
+	if (opts.orphan && !opts.checkout)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--orphan", "--no-checkout");
+	if (opts.orphan && ac == 2)
+		die(_("option '%s' and commit-ish cannot be used together"),
+		    "--orphan");
 	if (lock_reason && !keep_locked)
 		die(_("the option '%s' requires '%s'"), "--reason", "--lock");
 	if (lock_reason)
@@ -648,6 +826,7 @@
 
 	path = prefix_filename(prefix, av[0]);
 	branch = ac < 2 ? "HEAD" : av[1];
+	used_new_branch_options = new_branch || new_branch_force;
 
 	if (!strcmp(branch, "-"))
 		branch = "@{-1}";
@@ -664,13 +843,28 @@
 		strbuf_release(&symref);
 	}
 
-	if (ac < 2 && !new_branch && !opts.detach) {
+	if (opts.orphan && !new_branch) {
+		int n;
+		const char *s = worktree_basename(path, &n);
+		new_branch = xstrndup(s, n);
+	} else if (opts.orphan) {
+		; /* no-op */
+	} else if (opts.detach) {
+		/* Check HEAD */
+		if (!strcmp(branch, "HEAD"))
+			can_use_local_refs(&opts);
+	} else if (ac < 2 && new_branch) {
+		/* DWIM: Infer --orphan when repo has no refs. */
+		opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
+	} else if (ac < 2) {
+		/* DWIM: Guess branch name from path. */
 		const char *s = dwim_branch(path, &new_branch);
 		if (s)
 			branch = s;
-	}
 
-	if (ac == 2 && !new_branch && !opts.detach) {
+		/* DWIM: Infer --orphan when repo has no refs. */
+		opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
+	} else if (ac == 2) {
 		struct object_id oid;
 		struct commit *commit;
 		const char *remote;
@@ -683,11 +877,31 @@
 				branch = remote;
 			}
 		}
+
+		if (!strcmp(branch, "HEAD"))
+			can_use_local_refs(&opts);
+
 	}
+
+	if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
+		int attempt_hint = !opts.quiet && (ac < 2);
+		if (attempt_hint && used_new_branch_options) {
+			advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+				WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,
+				new_branch, path);
+		} else if (attempt_hint) {
+			advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+				WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);
+		}
+		die(_("invalid reference: %s"), branch);
+	}
+
 	if (!opts.quiet)
 		print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
 
-	if (new_branch) {
+	if (opts.orphan) {
+		branch = new_branch;
+	} else if (new_branch) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		cp.git_cmd = 1;
 		strvec_push(&cp.args, "branch");
@@ -756,7 +970,7 @@
 		strbuf_addstr(&sb, "(bare)");
 	else {
 		strbuf_addf(&sb, "%-*s ", abbrev_len,
-				find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
+				repo_find_unique_abbrev(the_repository, &wt->head_oid, DEFAULT_ABBREV));
 		if (wt->is_detached)
 			strbuf_addstr(&sb, "(detached HEAD)");
 		else if (wt->head_ref) {
@@ -793,7 +1007,7 @@
 
 		if (path_len > *maxlen)
 			*maxlen = path_len;
-		sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
+		sha1_len = strlen(repo_find_unique_abbrev(the_repository, &wt[i]->head_oid, *abbrev));
 		if (sha1_len > *abbrev)
 			*abbrev = sha1_len;
 	}
@@ -1192,5 +1406,9 @@
 		prefix = "";
 
 	ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0);
+
+	prepare_repo_settings(the_repository);
+	the_repository->settings.command_requires_full_index = 0;
+
 	return fn(ac, av, prefix);
 }
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 0780103..66e83d0 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -5,11 +5,14 @@
  */
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
-#include "cache.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tree.h"
 #include "cache-tree.h"
 #include "parse-options.h"
+#include "repository.h"
 
 static const char * const write_tree_usage[] = {
 	N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"),
@@ -38,6 +41,9 @@
 	argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
 			     write_tree_usage, 0);
 
+	prepare_repo_settings(the_repository);
+	the_repository->settings.command_requires_full_index = 0;
+
 	ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
 				  tree_prefix);
 	switch (ret) {
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 855b68e..eb46b88 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -1,17 +1,20 @@
 /*
  * Copyright (c) 2011, Google Inc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "bulk-checkin.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "repository.h"
 #include "csum-file.h"
 #include "pack.h"
 #include "strbuf.h"
-#include "string-list.h"
 #include "tmp-objdir.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 
 static int odb_transaction_nesting;
 
@@ -124,7 +127,7 @@
 	int i;
 
 	/* The object may already exist in the repository */
-	if (has_object_file(oid))
+	if (repo_has_object_file(the_repository, oid))
 		return 1;
 
 	/* Might want to keep the list sorted */
@@ -151,10 +154,10 @@
  * status before calling us just in case we ask it to call us again
  * with a new pack.
  */
-static int stream_to_pack(struct bulk_checkin_packfile *state,
-			  git_hash_ctx *ctx, off_t *already_hashed_to,
-			  int fd, size_t size, enum object_type type,
-			  const char *path, unsigned flags)
+static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
+			       git_hash_ctx *ctx, off_t *already_hashed_to,
+			       int fd, size_t size, const char *path,
+			       unsigned flags)
 {
 	git_zstream s;
 	unsigned char ibuf[16384];
@@ -166,7 +169,7 @@
 
 	git_deflate_init(&s, pack_compression_level);
 
-	hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), type, size);
+	hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), OBJ_BLOB, size);
 	s.next_out = obuf + hdrlen;
 	s.avail_out = sizeof(obuf) - hdrlen;
 
@@ -243,11 +246,10 @@
 		die_errno("unable to write pack header");
 }
 
-static int deflate_to_pack(struct bulk_checkin_packfile *state,
-			   struct object_id *result_oid,
-			   int fd, size_t size,
-			   enum object_type type, const char *path,
-			   unsigned flags)
+static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,
+				struct object_id *result_oid,
+				int fd, size_t size,
+				const char *path, unsigned flags)
 {
 	off_t seekback, already_hashed_to;
 	git_hash_ctx ctx;
@@ -261,9 +263,10 @@
 		return error("cannot find the current offset");
 
 	header_len = format_object_header((char *)obuf, sizeof(obuf),
-					  type, size);
+					  OBJ_BLOB, size);
 	the_hash_algo->init_fn(&ctx);
 	the_hash_algo->update_fn(&ctx, obuf, header_len);
+	the_hash_algo->init_fn(&checkpoint.ctx);
 
 	/* Note: idx is non-NULL when we are writing */
 	if ((flags & HASH_WRITE_OBJECT) != 0)
@@ -278,8 +281,8 @@
 			idx->offset = state->offset;
 			crc32_begin(state->f);
 		}
-		if (!stream_to_pack(state, &ctx, &already_hashed_to,
-				    fd, size, type, path, flags))
+		if (!stream_blob_to_pack(state, &ctx, &already_hashed_to,
+					 fd, size, path, flags))
 			break;
 		/*
 		 * Writing this object to the current pack will make
@@ -346,12 +349,12 @@
 	}
 }
 
-int index_bulk_checkin(struct object_id *oid,
-		       int fd, size_t size, enum object_type type,
-		       const char *path, unsigned flags)
+int index_blob_bulk_checkin(struct object_id *oid,
+			    int fd, size_t size,
+			    const char *path, unsigned flags)
 {
-	int status = deflate_to_pack(&bulk_checkin_packfile, oid, fd, size, type,
-				     path, flags);
+	int status = deflate_blob_to_pack(&bulk_checkin_packfile, oid, fd, size,
+					  path, flags);
 	if (!odb_transaction_nesting)
 		flush_bulk_checkin_packfile(&bulk_checkin_packfile);
 	return status;
diff --git a/bulk-checkin.h b/bulk-checkin.h
index 8281b9c..aa7286a 100644
--- a/bulk-checkin.h
+++ b/bulk-checkin.h
@@ -4,14 +4,14 @@
 #ifndef BULK_CHECKIN_H
 #define BULK_CHECKIN_H
 
-#include "cache.h"
+#include "object.h"
 
 void prepare_loose_object_bulk_checkin(void);
 void fsync_loose_object_bulk_checkin(int fd, const char *filename);
 
-int index_bulk_checkin(struct object_id *oid,
-		       int fd, size_t size, enum object_type type,
-		       const char *path, unsigned flags);
+int index_blob_bulk_checkin(struct object_id *oid,
+			    int fd, size_t size,
+			    const char *path, unsigned flags);
 
 /*
  * Tell the object database to optimize for adding
diff --git a/bundle-uri.c b/bundle-uri.c
index 8a3c39c..ca32050 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -1,7 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "bundle-uri.h"
 #include "bundle.h"
-#include "object-store.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
 #include "refs.h"
 #include "run-command.h"
 #include "hashmap.h"
@@ -17,7 +19,7 @@
 	{ BUNDLE_HEURISTIC_CREATIONTOKEN, "creationToken" },
 };
 
-static int compare_bundles(const void *hashmap_cmp_fn_data,
+static int compare_bundles(const void *hashmap_cmp_fn_data UNUSED,
 			   const struct hashmap_entry *he1,
 			   const struct hashmap_entry *he2,
 			   const void *id)
@@ -42,7 +44,7 @@
 }
 
 static int clear_remote_bundle_info(struct remote_bundle_info *bundle,
-				    void *data)
+				    void *data UNUSED)
 {
 	FREE_AND_NULL(bundle->id);
 	FREE_AND_NULL(bundle->uri);
@@ -221,7 +223,9 @@
 	return 0;
 }
 
-static int config_to_bundle_list(const char *key, const char *value, void *data)
+static int config_to_bundle_list(const char *key, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *data)
 {
 	struct bundle_list *list = data;
 	return bundle_list_update(key, value, list);
@@ -250,6 +254,7 @@
 	}
 	result = git_config_from_file_with_options(config_to_bundle_list,
 						   filename, list,
+						   CONFIG_SCOPE_UNKNOWN,
 						   &opts);
 
 	if (!result && list->mode == BUNDLE_MODE_NONE) {
@@ -773,7 +778,7 @@
 	return 0;
 }
 
-static int unlink_bundle(struct remote_bundle_info *info, void *data)
+static int unlink_bundle(struct remote_bundle_info *info, void *data UNUSED)
 {
 	if (info->file)
 		unlink_or_warn(info->file);
@@ -792,6 +797,15 @@
 
 	init_bundle_list(&list);
 
+	/*
+	 * Do not fetch an empty bundle URI. An empty bundle URI
+	 * could signal that a configured bundle URI has been disabled.
+	 */
+	if (!*uri) {
+		result = 0;
+		goto cleanup;
+	}
+
 	/* If a bundle is added to this global list, then it is required. */
 	list.mode = BUNDLE_MODE_ALL;
 
@@ -859,7 +873,9 @@
 	return advertise_bundle_uri;
 }
 
-static int config_to_packet_line(const char *key, const char *value, void *data)
+static int config_to_packet_line(const char *key, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *data)
 {
 	struct packet_reader *writer = data;
 
@@ -884,7 +900,7 @@
 	 * Read all "bundle.*" config lines to the client as key=value
 	 * packet lines.
 	 */
-	git_config(config_to_packet_line, &writer);
+	repo_config(r, config_to_packet_line, &writer);
 
 	packet_writer_flush(&writer);
 
diff --git a/bundle.c b/bundle.c
index 6ab6cd7..a9744da 100644
--- a/bundle.c
+++ b/bundle.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "lockfile.h"
 #include "bundle.h"
-#include "object-store.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "repository.h"
 #include "object.h"
 #include "commit.h"
@@ -13,6 +16,7 @@
 #include "strvec.h"
 #include "list-objects-filter-options.h"
 #include "connected.h"
+#include "write-or-die.h"
 
 static const char v2_bundle_signature[] = "# v2 git bundle\n";
 static const char v3_bundle_signature[] = "# v3 git bundle\n";
@@ -267,10 +271,10 @@
 			list_refs(r, 0, NULL);
 		}
 
-		printf_ln("The bundle uses this hash algorithm: %s",
+		printf_ln(_("The bundle uses this hash algorithm: %s"),
 			  header->hash_algo->name);
 		if (header->filter.choice)
-			printf_ln("The bundle uses this filter: %s",
+			printf_ln(_("The bundle uses this filter: %s"),
 				  list_objects_filter_spec(&header->filter));
 	}
 cleanup:
@@ -293,7 +297,7 @@
 	if (revs->max_age == -1 && revs->min_age == -1)
 		goto out;
 
-	buf = read_object_file(&tag->oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &tag->oid, &type, &size);
 	if (!buf)
 		goto out;
 	line = memmem(buf, size, "\ntagger ", 8);
@@ -382,7 +386,8 @@
 
 		if (e->item->flags & UNINTERESTING)
 			continue;
-		if (dwim_ref(e->name, strlen(e->name), &oid, &ref, 0) != 1)
+		if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
+				  &oid, &ref, 0) != 1)
 			goto skip_write_ref;
 		if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
 			flag = 0;
diff --git a/bundle.h b/bundle.h
index 9f2bd73..021adbd 100644
--- a/bundle.h
+++ b/bundle.h
@@ -2,7 +2,6 @@
 #define BUNDLE_H
 
 #include "strvec.h"
-#include "cache.h"
 #include "string-list.h"
 #include "list-objects-filter-options.h"
 
diff --git a/cache-tree.c b/cache-tree.c
index 88c2c04..387c0a3 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -1,13 +1,18 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "bulk-checkin.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "read-cache-ll.h"
 #include "replace-object.h"
 #include "promisor-remote.h"
-#include "sparse-index.h"
+#include "trace.h"
+#include "trace2.h"
 
 #ifndef DEBUG_CACHE_TREE
 #define DEBUG_CACHE_TREE 0
@@ -229,7 +234,7 @@
 	int i;
 	if (!it)
 		return 0;
-	if (it->entry_count < 0 || !has_object_file(&it->oid))
+	if (it->entry_count < 0 || !repo_has_object_file(the_repository, &it->oid))
 		return 0;
 	for (i = 0; i < it->subtree_nr; i++) {
 		if (!cache_tree_fully_valid(it->down[i]->cache_tree))
@@ -240,7 +245,7 @@
 
 static int must_check_existence(const struct cache_entry *ce)
 {
-	return !(has_promisor_remote() && ce_skip_worktree(ce));
+	return !(repo_has_promisor_remote(the_repository) && ce_skip_worktree(ce));
 }
 
 static int update_one(struct cache_tree *it,
@@ -280,7 +285,7 @@
 		}
 	}
 
-	if (0 <= it->entry_count && has_object_file(&it->oid))
+	if (0 <= it->entry_count && repo_has_object_file(the_repository, &it->oid))
 		return it->entry_count;
 
 	/*
@@ -386,7 +391,7 @@
 		ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
 			!must_check_existence(ce);
 		if (is_null_oid(oid) ||
-		    (!ce_missing_ok && !has_object_file(oid))) {
+		    (!ce_missing_ok && !repo_has_object_file(the_repository, oid))) {
 			strbuf_release(&buffer);
 			if (expected_missing)
 				return -1;
@@ -434,7 +439,7 @@
 		struct object_id oid;
 		hash_object_file(the_hash_algo, buffer.buf, buffer.len,
 				 OBJ_TREE, &oid);
-		if (has_object_file_with_flags(&oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
+		if (repo_has_object_file_with_flags(the_repository, &oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
 			oidcpy(&it->oid, &oid);
 		else
 			to_invalidate = 1;
@@ -442,7 +447,7 @@
 		hash_object_file(the_hash_algo, buffer.buf, buffer.len,
 				 OBJ_TREE, &it->oid);
 	} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
-					   &it->oid, flags & WRITE_TREE_SILENT
+					   &it->oid, NULL, flags & WRITE_TREE_SILENT
 					   ? HASH_SILENT : 0)) {
 		strbuf_release(&buffer);
 		return -1;
@@ -470,7 +475,7 @@
 	if (!istate->cache_tree)
 		istate->cache_tree = cache_tree();
 
-	if (!(flags & WRITE_TREE_MISSING_OK) && has_promisor_remote())
+	if (!(flags & WRITE_TREE_MISSING_OK) && repo_has_promisor_remote(the_repository))
 		prefetch_cache_entries(istate, must_check_existence);
 
 	trace_performance_enter();
@@ -764,7 +769,7 @@
 
 	oidcpy(&it->oid, &tree->object.oid);
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 	cnt = 0;
 	while (tree_entry(&desc, &entry)) {
 		if (!S_ISDIR(entry.mode))
@@ -773,8 +778,8 @@
 			struct cache_tree_sub *sub;
 			struct tree *subtree = lookup_tree(r, &entry.oid);
 
-			if (!subtree->object.parsed)
-				parse_tree(subtree);
+			if (parse_tree(subtree) < 0)
+				exit(128);
 			sub = cache_tree_sub(it, entry.path);
 			sub->cache_tree = cache_tree();
 
@@ -814,14 +819,14 @@
 {
 	struct strbuf tree_path = STRBUF_INIT;
 
-	trace2_region_enter("cache-tree", "prime_cache_tree", the_repository);
+	trace2_region_enter("cache-tree", "prime_cache_tree", r);
 	cache_tree_free(&istate->cache_tree);
 	istate->cache_tree = cache_tree();
 
 	prime_cache_tree_rec(r, istate->cache_tree, tree, &tree_path);
 	strbuf_release(&tree_path);
 	istate->cache_changed |= CACHE_TREE_CHANGED;
-	trace2_region_leave("cache-tree", "prime_cache_tree", the_repository);
+	trace2_region_leave("cache-tree", "prime_cache_tree", r);
 }
 
 /*
diff --git a/cache-tree.h b/cache-tree.h
index bd97caa..faae88b 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -1,7 +1,6 @@
 #ifndef CACHE_TREE_H
 #define CACHE_TREE_H
 
-#include "cache.h"
 #include "tree.h"
 #include "tree-walk.h"
 
diff --git a/cache.h b/cache.h
deleted file mode 100644
index 1278990..0000000
--- a/cache.h
+++ /dev/null
@@ -1,1950 +0,0 @@
-#ifndef CACHE_H
-#define CACHE_H
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "hashmap.h"
-#include "list.h"
-#include "advice.h"
-#include "gettext.h"
-#include "convert.h"
-#include "trace.h"
-#include "trace2.h"
-#include "string-list.h"
-#include "pack-revindex.h"
-#include "hash.h"
-#include "path.h"
-#include "oid-array.h"
-#include "repository.h"
-#include "mem-pool.h"
-
-typedef struct git_zstream {
-	z_stream z;
-	unsigned long avail_in;
-	unsigned long avail_out;
-	unsigned long total_in;
-	unsigned long total_out;
-	unsigned char *next_in;
-	unsigned char *next_out;
-} git_zstream;
-
-void git_inflate_init(git_zstream *);
-void git_inflate_init_gzip_only(git_zstream *);
-void git_inflate_end(git_zstream *);
-int git_inflate(git_zstream *, int flush);
-
-void git_deflate_init(git_zstream *, int level);
-void git_deflate_init_gzip(git_zstream *, int level);
-void git_deflate_init_raw(git_zstream *, int level);
-void git_deflate_end(git_zstream *);
-int git_deflate_abort(git_zstream *);
-int git_deflate_end_gently(git_zstream *);
-int git_deflate(git_zstream *, int flush);
-unsigned long git_deflate_bound(git_zstream *, unsigned long);
-
-#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
-#define DTYPE(de)	((de)->d_type)
-#else
-#undef DT_UNKNOWN
-#undef DT_DIR
-#undef DT_REG
-#undef DT_LNK
-#define DT_UNKNOWN	0
-#define DT_DIR		1
-#define DT_REG		2
-#define DT_LNK		3
-#define DTYPE(de)	DT_UNKNOWN
-#endif
-
-/* unknown mode (impossible combination S_IFIFO|S_IFCHR) */
-#define S_IFINVALID     0030000
-
-/*
- * A "directory link" is a link to another git directory.
- *
- * The value 0160000 is not normally a valid mode, and
- * also just happens to be S_IFDIR + S_IFLNK
- */
-#define S_IFGITLINK	0160000
-#define S_ISGITLINK(m)	(((m) & S_IFMT) == S_IFGITLINK)
-
-/*
- * Some mode bits are also used internally for computations.
- *
- * They *must* not overlap with any valid modes, and they *must* not be emitted
- * to outside world - i.e. appear on disk or network. In other words, it's just
- * temporary fields, which we internally use, but they have to stay in-house.
- *
- * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
- *   codebase mode is `unsigned int` which is assumed to be at least 32 bits )
- */
-
-/* used internally in tree-diff */
-#define S_DIFFTREE_IFXMIN_NEQ	0x80000000
-
-
-/*
- * Intensive research over the course of many years has shown that
- * port 9418 is totally unused by anything else. Or
- *
- *	Your search - "port 9418" - did not match any documents.
- *
- * as www.google.com puts it.
- *
- * This port has been properly assigned for git use by IANA:
- * git (Assigned-9418) [I06-050728-0001].
- *
- *	git  9418/tcp   git pack transfer service
- *	git  9418/udp   git pack transfer service
- *
- * with Linus Torvalds <torvalds@osdl.org> as the point of
- * contact. September 2005.
- *
- * See http://www.iana.org/assignments/port-numbers
- */
-#define DEFAULT_GIT_PORT 9418
-
-/*
- * Basic data structures for the directory cache
- */
-
-#define CACHE_SIGNATURE 0x44495243	/* "DIRC" */
-struct cache_header {
-	uint32_t hdr_signature;
-	uint32_t hdr_version;
-	uint32_t hdr_entries;
-};
-
-#define INDEX_FORMAT_LB 2
-#define INDEX_FORMAT_UB 4
-
-/*
- * The "cache_time" is just the low 32 bits of the
- * time. It doesn't matter if it overflows - we only
- * check it for equality in the 32 bits we save.
- */
-struct cache_time {
-	uint32_t sec;
-	uint32_t nsec;
-};
-
-struct stat_data {
-	struct cache_time sd_ctime;
-	struct cache_time sd_mtime;
-	unsigned int sd_dev;
-	unsigned int sd_ino;
-	unsigned int sd_uid;
-	unsigned int sd_gid;
-	unsigned int sd_size;
-};
-
-struct cache_entry {
-	struct hashmap_entry ent;
-	struct stat_data ce_stat_data;
-	unsigned int ce_mode;
-	unsigned int ce_flags;
-	unsigned int mem_pool_allocated;
-	unsigned int ce_namelen;
-	unsigned int index;	/* for link extension */
-	struct object_id oid;
-	char name[FLEX_ARRAY]; /* more */
-};
-
-#define CE_STAGEMASK (0x3000)
-#define CE_EXTENDED  (0x4000)
-#define CE_VALID     (0x8000)
-#define CE_STAGESHIFT 12
-
-/*
- * Range 0xFFFF0FFF in ce_flags is divided into
- * two parts: in-memory flags and on-disk ones.
- * Flags in CE_EXTENDED_FLAGS will get saved on-disk
- * if you want to save a new flag, add it in
- * CE_EXTENDED_FLAGS
- *
- * In-memory only flags
- */
-#define CE_UPDATE            (1 << 16)
-#define CE_REMOVE            (1 << 17)
-#define CE_UPTODATE          (1 << 18)
-#define CE_ADDED             (1 << 19)
-
-#define CE_HASHED            (1 << 20)
-#define CE_FSMONITOR_VALID   (1 << 21)
-#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
-#define CE_CONFLICTED        (1 << 23)
-
-#define CE_UNPACKED          (1 << 24)
-#define CE_NEW_SKIP_WORKTREE (1 << 25)
-
-/* used to temporarily mark paths matched by pathspecs */
-#define CE_MATCHED           (1 << 26)
-
-#define CE_UPDATE_IN_BASE    (1 << 27)
-#define CE_STRIP_NAME        (1 << 28)
-
-/*
- * Extended on-disk flags
- */
-#define CE_INTENT_TO_ADD     (1 << 29)
-#define CE_SKIP_WORKTREE     (1 << 30)
-/* CE_EXTENDED2 is for future extension */
-#define CE_EXTENDED2         (1U << 31)
-
-#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
-
-/*
- * Safeguard to avoid saving wrong flags:
- *  - CE_EXTENDED2 won't get saved until its semantic is known
- *  - Bits in 0x0000FFFF have been saved in ce_flags already
- *  - Bits in 0x003F0000 are currently in-memory flags
- */
-#if CE_EXTENDED_FLAGS & 0x803FFFFF
-#error "CE_EXTENDED_FLAGS out of range"
-#endif
-
-#define S_ISSPARSEDIR(m) ((m) == S_IFDIR)
-
-/* Forward structure decls */
-struct pathspec;
-struct child_process;
-struct tree;
-
-/*
- * Copy the sha1 and stat state of a cache entry from one to
- * another. But we never change the name, or the hash state!
- */
-static inline void copy_cache_entry(struct cache_entry *dst,
-				    const struct cache_entry *src)
-{
-	unsigned int state = dst->ce_flags & CE_HASHED;
-	int mem_pool_allocated = dst->mem_pool_allocated;
-
-	/* Don't copy hash chain and name */
-	memcpy(&dst->ce_stat_data, &src->ce_stat_data,
-			offsetof(struct cache_entry, name) -
-			offsetof(struct cache_entry, ce_stat_data));
-
-	/* Restore the hash state */
-	dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
-
-	/* Restore the mem_pool_allocated flag */
-	dst->mem_pool_allocated = mem_pool_allocated;
-}
-
-static inline unsigned create_ce_flags(unsigned stage)
-{
-	return (stage << CE_STAGESHIFT);
-}
-
-#define ce_namelen(ce) ((ce)->ce_namelen)
-#define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
-#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
-#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
-#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
-#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
-
-#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
-static inline unsigned int create_ce_mode(unsigned int mode)
-{
-	if (S_ISLNK(mode))
-		return S_IFLNK;
-	if (S_ISSPARSEDIR(mode))
-		return S_IFDIR;
-	if (S_ISDIR(mode) || S_ISGITLINK(mode))
-		return S_IFGITLINK;
-	return S_IFREG | ce_permissions(mode);
-}
-static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
-					     unsigned int mode)
-{
-	extern int trust_executable_bit, has_symlinks;
-	if (!has_symlinks && S_ISREG(mode) &&
-	    ce && S_ISLNK(ce->ce_mode))
-		return ce->ce_mode;
-	if (!trust_executable_bit && S_ISREG(mode)) {
-		if (ce && S_ISREG(ce->ce_mode))
-			return ce->ce_mode;
-		return create_ce_mode(0666);
-	}
-	return create_ce_mode(mode);
-}
-static inline int ce_to_dtype(const struct cache_entry *ce)
-{
-	unsigned ce_mode = ntohl(ce->ce_mode);
-	if (S_ISREG(ce_mode))
-		return DT_REG;
-	else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
-		return DT_DIR;
-	else if (S_ISLNK(ce_mode))
-		return DT_LNK;
-	else
-		return DT_UNKNOWN;
-}
-static inline unsigned int canon_mode(unsigned int mode)
-{
-	if (S_ISREG(mode))
-		return S_IFREG | ce_permissions(mode);
-	if (S_ISLNK(mode))
-		return S_IFLNK;
-	if (S_ISDIR(mode))
-		return S_IFDIR;
-	return S_IFGITLINK;
-}
-
-#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
-
-#define SOMETHING_CHANGED	(1 << 0) /* unclassified changes go here */
-#define CE_ENTRY_CHANGED	(1 << 1)
-#define CE_ENTRY_REMOVED	(1 << 2)
-#define CE_ENTRY_ADDED		(1 << 3)
-#define RESOLVE_UNDO_CHANGED	(1 << 4)
-#define CACHE_TREE_CHANGED	(1 << 5)
-#define SPLIT_INDEX_ORDERED	(1 << 6)
-#define UNTRACKED_CHANGED	(1 << 7)
-#define FSMONITOR_CHANGED	(1 << 8)
-
-struct split_index;
-struct untracked_cache;
-struct progress;
-struct pattern_list;
-
-enum sparse_index_mode {
-	/*
-	 * There are no sparse directories in the index at all.
-	 *
-	 * Repositories that don't use cone-mode sparse-checkout will
-	 * always have their indexes in this mode.
-	 */
-	INDEX_EXPANDED = 0,
-
-	/*
-	 * The index has already been collapsed to sparse directories
-	 * whereever possible.
-	 */
-	INDEX_COLLAPSED,
-
-	/*
-	 * The sparse directories that exist are outside the
-	 * sparse-checkout boundary, but it is possible that some file
-	 * entries could collapse to sparse directory entries.
-	 */
-	INDEX_PARTIALLY_SPARSE,
-};
-
-struct index_state {
-	struct cache_entry **cache;
-	unsigned int version;
-	unsigned int cache_nr, cache_alloc, cache_changed;
-	struct string_list *resolve_undo;
-	struct cache_tree *cache_tree;
-	struct split_index *split_index;
-	struct cache_time timestamp;
-	unsigned name_hash_initialized : 1,
-		 initialized : 1,
-		 drop_cache_tree : 1,
-		 updated_workdir : 1,
-		 updated_skipworktree : 1,
-		 fsmonitor_has_run_once : 1;
-	enum sparse_index_mode sparse_index;
-	struct hashmap name_hash;
-	struct hashmap dir_hash;
-	struct object_id oid;
-	struct untracked_cache *untracked;
-	char *fsmonitor_last_update;
-	struct ewah_bitmap *fsmonitor_dirty;
-	struct mem_pool *ce_mem_pool;
-	struct progress *progress;
-	struct repository *repo;
-	struct pattern_list *sparse_checkout_patterns;
-};
-
-/**
- * A "struct index_state istate" must be initialized with
- * INDEX_STATE_INIT or the corresponding index_state_init().
- *
- * If the variable won't be used again, use release_index() to free()
- * its resources. If it needs to be used again use discard_index(),
- * which does the same thing, but will use use index_state_init() at
- * the end. The discard_index() will use its own "istate->repo" as the
- * "r" argument to index_state_init() in that case.
- */
-#define INDEX_STATE_INIT(r) { \
-	.repo = (r), \
-}
-void index_state_init(struct index_state *istate, struct repository *r);
-void release_index(struct index_state *istate);
-
-/* Name hashing */
-int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
-void add_name_hash(struct index_state *istate, struct cache_entry *ce);
-void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
-void free_name_hash(struct index_state *istate);
-
-/* Cache entry creation and cleanup */
-
-/*
- * Create cache_entry intended for use in the specified index. Caller
- * is responsible for discarding the cache_entry with
- * `discard_cache_entry`.
- */
-struct cache_entry *make_cache_entry(struct index_state *istate,
-				     unsigned int mode,
-				     const struct object_id *oid,
-				     const char *path,
-				     int stage,
-				     unsigned int refresh_options);
-
-struct cache_entry *make_empty_cache_entry(struct index_state *istate,
-					   size_t name_len);
-
-/*
- * Create a cache_entry that is not intended to be added to an index. If
- * `ce_mem_pool` is not NULL, the entry is allocated within the given memory
- * pool. Caller is responsible for discarding "loose" entries with
- * `discard_cache_entry()` and the memory pool with
- * `mem_pool_discard(ce_mem_pool, should_validate_cache_entries())`.
- */
-struct cache_entry *make_transient_cache_entry(unsigned int mode,
-					       const struct object_id *oid,
-					       const char *path,
-					       int stage,
-					       struct mem_pool *ce_mem_pool);
-
-struct cache_entry *make_empty_transient_cache_entry(size_t len,
-						     struct mem_pool *ce_mem_pool);
-
-/*
- * Discard cache entry.
- */
-void discard_cache_entry(struct cache_entry *ce);
-
-/*
- * Check configuration if we should perform extra validation on cache
- * entries.
- */
-int should_validate_cache_entries(void);
-
-/*
- * Duplicate a cache_entry. Allocate memory for the new entry from a
- * memory_pool. Takes into account cache_entry fields that are meant
- * for managing the underlying memory allocation of the cache_entry.
- */
-struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
-
-/*
- * Validate the cache entries in the index.  This is an internal
- * consistency check that the cache_entry structs are allocated from
- * the expected memory pool.
- */
-void validate_cache_entries(const struct index_state *istate);
-
-/*
- * Bulk prefetch all missing cache entries that are not GITLINKs and that match
- * the given predicate. This function should only be called if
- * has_promisor_remote() returns true.
- */
-typedef int (*must_prefetch_predicate)(const struct cache_entry *);
-void prefetch_cache_entries(const struct index_state *istate,
-			    must_prefetch_predicate must_prefetch);
-
-#ifdef USE_THE_INDEX_VARIABLE
-extern struct index_state the_index;
-#endif
-
-#define TYPE_BITS 3
-
-/*
- * Values in this enum (except those outside the 3 bit range) are part
- * of pack file format. See gitformat-pack(5) for more information.
- */
-enum object_type {
-	OBJ_BAD = -1,
-	OBJ_NONE = 0,
-	OBJ_COMMIT = 1,
-	OBJ_TREE = 2,
-	OBJ_BLOB = 3,
-	OBJ_TAG = 4,
-	/* 5 for future expansion */
-	OBJ_OFS_DELTA = 6,
-	OBJ_REF_DELTA = 7,
-	OBJ_ANY,
-	OBJ_MAX
-};
-
-static inline enum object_type object_type(unsigned int mode)
-{
-	return S_ISDIR(mode) ? OBJ_TREE :
-		S_ISGITLINK(mode) ? OBJ_COMMIT :
-		OBJ_BLOB;
-}
-
-/* Double-check local_repo_env below if you add to this list. */
-#define GIT_DIR_ENVIRONMENT "GIT_DIR"
-#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
-#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
-#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
-#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
-#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
-#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
-#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
-#define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
-#define GIT_SHALLOW_FILE_ENVIRONMENT "GIT_SHALLOW_FILE"
-#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
-#define CONFIG_ENVIRONMENT "GIT_CONFIG"
-#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
-#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
-#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
-#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
-#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
-#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
-#define GITATTRIBUTES_FILE ".gitattributes"
-#define INFOATTRIBUTES_FILE "info/attributes"
-#define ATTRIBUTE_MACRO_PREFIX "[attr]"
-#define GITMODULES_FILE ".gitmodules"
-#define GITMODULES_INDEX ":.gitmodules"
-#define GITMODULES_HEAD "HEAD:.gitmodules"
-#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
-#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
-#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
-#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
-#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
-#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
-#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
-#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
-#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
-#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
-#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
-#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
-
-/*
- * Environment variable used in handshaking the wire protocol.
- * Contains a colon ':' separated list of keys with optional values
- * 'key[=value]'.  Presence of unknown keys and values must be
- * ignored.
- */
-#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
-/* HTTP header used to handshake the wire protocol */
-#define GIT_PROTOCOL_HEADER "Git-Protocol"
-
-/*
- * This environment variable is expected to contain a boolean indicating
- * whether we should or should not treat:
- *
- *   GIT_DIR=foo.git git ...
- *
- * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
- * of this, but we use it internally to communicate to sub-processes that we
- * are in a bare repo. If not set, defaults to true.
- */
-#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
-
-/*
- * Repository-local GIT_* environment variables; these will be cleared
- * when git spawns a sub-process that runs inside another repository.
- * The array is NULL-terminated, which makes it easy to pass in the "env"
- * parameter of a run-command invocation, or to do a simple walk.
- */
-extern const char * const local_repo_env[];
-
-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).
- */
-int have_git_dir(void);
-
-extern int is_bare_repository_cfg;
-int is_bare_repository(void);
-int is_inside_git_dir(void);
-extern char *git_work_tree_cfg;
-int is_inside_work_tree(void);
-const char *get_git_dir(void);
-const char *get_git_common_dir(void);
-const char *get_object_directory(void);
-char *get_index_file(void);
-char *get_graft_file(struct repository *r);
-void set_git_dir(const char *path, int make_realpath);
-int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
-int get_common_dir(struct strbuf *sb, const char *gitdir);
-const char *get_git_namespace(void);
-const char *strip_namespace(const char *namespaced_ref);
-const char *get_git_work_tree(void);
-
-/*
- * Return true if the given path is a git directory; note that this _just_
- * looks at the directory itself. If you want to know whether "foo/.git"
- * is a repository, you must feed that path, not just "foo".
- */
-int is_git_directory(const char *path);
-
-/*
- * Return 1 if the given path is the root of a git repository or
- * submodule, else 0. Will not return 1 for bare repositories with the
- * exception of creating a bare repository in "foo/.git" and calling
- * is_git_repository("foo").
- *
- * If we run into read errors, we err on the side of saying "yes, it is",
- * as we usually consider sub-repos precious, and would prefer to err on the
- * side of not disrupting or deleting them.
- */
-int is_nonbare_repository_dir(struct strbuf *path);
-
-#define READ_GITFILE_ERR_STAT_FAILED 1
-#define READ_GITFILE_ERR_NOT_A_FILE 2
-#define READ_GITFILE_ERR_OPEN_FAILED 3
-#define READ_GITFILE_ERR_READ_FAILED 4
-#define READ_GITFILE_ERR_INVALID_FORMAT 5
-#define READ_GITFILE_ERR_NO_PATH 6
-#define READ_GITFILE_ERR_NOT_A_REPO 7
-#define READ_GITFILE_ERR_TOO_LARGE 8
-void read_gitfile_error_die(int error_code, const char *path, const char *dir);
-const char *read_gitfile_gently(const char *path, int *return_error_code);
-#define read_gitfile(path) read_gitfile_gently((path), NULL)
-const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
-#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
-
-void set_git_work_tree(const char *tree);
-
-#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
-
-void setup_work_tree(void);
-/*
- * Find the commondir and gitdir of the repository that contains the current
- * working directory, without changing the working directory or other global
- * state. The result is appended to commondir and gitdir.  If the discovered
- * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
- * both have the same result appended to the buffer.  The return value is
- * either 0 upon success and non-zero if no repository was found.
- */
-int discover_git_directory(struct strbuf *commondir,
-			   struct strbuf *gitdir);
-const char *setup_git_directory_gently(int *);
-const char *setup_git_directory(void);
-char *prefix_path(const char *prefix, int len, const char *path);
-char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
-
-/*
- * Concatenate "prefix" (if len is non-zero) and "path", with no
- * connecting characters (so "prefix" should end with a "/").
- * Unlike prefix_path, this should be used if the named file does
- * not have to interact with index entry; i.e. name of a random file
- * on the filesystem.
- *
- * The return value is always a newly allocated string (even if the
- * prefix was empty).
- */
-char *prefix_filename(const char *prefix, const char *path);
-
-int check_filename(const char *prefix, const char *name);
-void verify_filename(const char *prefix,
-		     const char *name,
-		     int diagnose_misspelt_rev);
-void verify_non_filename(const char *prefix, const char *name);
-int path_inside_repo(const char *prefix, const char *path);
-
-#define INIT_DB_QUIET 0x0001
-#define INIT_DB_EXIST_OK 0x0002
-
-int init_db(const char *git_dir, const char *real_git_dir,
-	    const char *template_dir, int hash_algo,
-	    const char *initial_branch, unsigned int flags);
-void initialize_repository_version(int hash_algo, int reinit);
-
-void sanitize_stdfds(void);
-int daemonize(void);
-
-#define alloc_nr(x) (((x)+16)*3/2)
-
-/**
- * Dynamically growing an array using realloc() is error prone and boring.
- *
- * Define your array with:
- *
- * - a pointer (`item`) that points at the array, initialized to `NULL`
- *   (although please name the variable based on its contents, not on its
- *   type);
- *
- * - an integer variable (`alloc`) that keeps track of how big the current
- *   allocation is, initialized to `0`;
- *
- * - another integer variable (`nr`) to keep track of how many elements the
- *   array currently has, initialized to `0`.
- *
- * Then before adding `n`th element to the item, call `ALLOC_GROW(item, n,
- * alloc)`.  This ensures that the array can hold at least `n` elements by
- * calling `realloc(3)` and adjusting `alloc` variable.
- *
- * ------------
- * sometype *item;
- * size_t nr;
- * size_t alloc
- *
- * for (i = 0; i < nr; i++)
- * 	if (we like item[i] already)
- * 		return;
- *
- * // we did not like any existing one, so add one
- * ALLOC_GROW(item, nr + 1, alloc);
- * item[nr++] = value you like;
- * ------------
- *
- * You are responsible for updating the `nr` variable.
- *
- * If you need to specify the number of elements to allocate explicitly
- * then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
- *
- * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
- * added niceties.
- *
- * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
- */
-#define ALLOC_GROW(x, nr, alloc) \
-	do { \
-		if ((nr) > alloc) { \
-			if (alloc_nr(alloc) < (nr)) \
-				alloc = (nr); \
-			else \
-				alloc = alloc_nr(alloc); \
-			REALLOC_ARRAY(x, alloc); \
-		} \
-	} while (0)
-
-/*
- * Similar to ALLOC_GROW but handles updating of the nr value and
- * zeroing the bytes of the newly-grown array elements.
- *
- * DO NOT USE any expression with side-effect for any of the
- * arguments.
- */
-#define ALLOC_GROW_BY(x, nr, increase, alloc) \
-	do { \
-		if (increase) { \
-			size_t new_nr = nr + (increase); \
-			if (new_nr < nr) \
-				BUG("negative growth in ALLOC_GROW_BY"); \
-			ALLOC_GROW(x, new_nr, alloc); \
-			memset((x) + nr, 0, sizeof(*(x)) * (increase)); \
-			nr = new_nr; \
-		} \
-	} while (0)
-
-/* Initialize and use the cache information */
-struct lock_file;
-void preload_index(struct index_state *index,
-		   const struct pathspec *pathspec,
-		   unsigned int refresh_flags);
-int do_read_index(struct index_state *istate, const char *path,
-		  int must_exist); /* for testting only! */
-int read_index_from(struct index_state *, const char *path,
-		    const char *gitdir);
-int is_index_unborn(struct index_state *);
-
-void ensure_full_index(struct index_state *istate);
-
-/* For use with `write_locked_index()`. */
-#define COMMIT_LOCK		(1 << 0)
-#define SKIP_IF_UNCHANGED	(1 << 1)
-
-/*
- * Write the index while holding an already-taken lock. Close the lock,
- * and if `COMMIT_LOCK` is given, commit it.
- *
- * Unless a split index is in use, write the index into the lockfile.
- *
- * With a split index, write the shared index to a temporary file,
- * adjust its permissions and rename it into place, then write the
- * split index to the lockfile. If the temporary file for the shared
- * index cannot be created, fall back to the behavior described in
- * the previous paragraph.
- *
- * With `COMMIT_LOCK`, the lock is always committed or rolled back.
- * Without it, the lock is closed, but neither committed nor rolled
- * back.
- *
- * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
- * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
- */
-int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
-
-void discard_index(struct index_state *);
-void move_index_extensions(struct index_state *dst, struct index_state *src);
-int unmerged_index(const struct index_state *);
-
-/**
- * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
- * compares istate to HEAD.  If tree is NULL and on an unborn branch,
- * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
- * provided, the space-separated list of files that differ will be appended
- * to it.
- */
-int repo_index_has_changes(struct repository *repo,
-			   struct tree *tree,
-			   struct strbuf *sb);
-
-int verify_path(const char *path, unsigned mode);
-int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
-int index_dir_exists(struct index_state *istate, const char *name, int namelen);
-void adjust_dirname_case(struct index_state *istate, char *name);
-struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
-
-/*
- * Searches for an entry defined by name and namelen in the given index.
- * If the return value is positive (including 0) it is the position of an
- * exact match. If the return value is negative, the negated value minus 1
- * is the position where the entry would be inserted.
- * Example: The current index consists of these files and its stages:
- *
- *   b#0, d#0, f#1, f#3
- *
- * index_name_pos(&index, "a", 1) -> -1
- * index_name_pos(&index, "b", 1) ->  0
- * index_name_pos(&index, "c", 1) -> -2
- * index_name_pos(&index, "d", 1) ->  1
- * index_name_pos(&index, "e", 1) -> -3
- * index_name_pos(&index, "f", 1) -> -3
- * index_name_pos(&index, "g", 1) -> -5
- */
-int index_name_pos(struct index_state *, const char *name, int namelen);
-
-/*
- * Like index_name_pos, returns the position of an entry of the given name in
- * the index if one exists, otherwise returns a negative value where the negated
- * value minus 1 is the position where the index entry would be inserted. Unlike
- * index_name_pos, however, a sparse index is not expanded to find an entry
- * inside a sparse directory.
- */
-int index_name_pos_sparse(struct index_state *, const char *name, int namelen);
-
-/*
- * Determines whether an entry with the given name exists within the
- * given index. The return value is 1 if an exact match is found, otherwise
- * it is 0. Note that, unlike index_name_pos, this function does not expand
- * the index if it is sparse. If an item exists within the full index but it
- * is contained within a sparse directory (and not in the sparse index), 0 is
- * returned.
- */
-int index_entry_exists(struct index_state *, const char *name, int namelen);
-
-/*
- * Some functions return the negative complement of an insert position when a
- * precise match was not found but a position was found where the entry would
- * need to be inserted. This helper protects that logic from any integer
- * underflow.
- */
-static inline int index_pos_to_insert_pos(uintmax_t pos)
-{
-	if (pos > INT_MAX)
-		die("overflow: -1 - %"PRIuMAX, pos);
-	return -1 - (int)pos;
-}
-
-#define ADD_CACHE_OK_TO_ADD 1		/* Ok to add */
-#define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
-#define ADD_CACHE_SKIP_DFCHECK 4	/* Ok to skip DF conflict checks */
-#define ADD_CACHE_JUST_APPEND 8		/* Append only */
-#define ADD_CACHE_NEW_ONLY 16		/* Do not replace existing ones */
-#define ADD_CACHE_KEEP_CACHE_TREE 32	/* Do not invalidate cache-tree */
-#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
-int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
-void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
-
-/* Remove entry, return true if there are more entries to go. */
-int remove_index_entry_at(struct index_state *, int pos);
-
-void remove_marked_cache_entries(struct index_state *istate, int invalidate);
-int remove_file_from_index(struct index_state *, const char *path);
-#define ADD_CACHE_VERBOSE 1
-#define ADD_CACHE_PRETEND 2
-#define ADD_CACHE_IGNORE_ERRORS	4
-#define ADD_CACHE_IGNORE_REMOVAL 8
-#define ADD_CACHE_INTENT 16
-/*
- * These two are used to add the contents of the file at path
- * to the index, marking the working tree up-to-date by storing
- * the cached stat info in the resulting cache entry.  A caller
- * that has already run lstat(2) on the path can call
- * add_to_index(), and all others can call add_file_to_index();
- * the latter will do necessary lstat(2) internally before
- * calling the former.
- */
-int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
-int add_file_to_index(struct index_state *, const char *path, int flags);
-
-int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
-int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
-void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
-int index_name_is_other(struct index_state *, const char *, int);
-void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
-
-/* do stat comparison even if CE_VALID is true */
-#define CE_MATCH_IGNORE_VALID		01
-/* do not check the contents but report dirty on racily-clean entries */
-#define CE_MATCH_RACY_IS_DIRTY		02
-/* do stat comparison even if CE_SKIP_WORKTREE is true */
-#define CE_MATCH_IGNORE_SKIP_WORKTREE	04
-/* ignore non-existent files during stat update  */
-#define CE_MATCH_IGNORE_MISSING		0x08
-/* enable stat refresh */
-#define CE_MATCH_REFRESH		0x10
-/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
-#define CE_MATCH_IGNORE_FSMONITOR 0X20
-int is_racy_timestamp(const struct index_state *istate,
-		      const struct cache_entry *ce);
-int has_racy_timestamp(struct index_state *istate);
-int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-
-#define HASH_WRITE_OBJECT 1
-#define HASH_FORMAT_CHECK 2
-#define HASH_RENORMALIZE  4
-#define HASH_SILENT 8
-int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
-
-/*
- * Record to sd the data from st that we use to check whether a file
- * might have changed.
- */
-void fill_stat_data(struct stat_data *sd, struct stat *st);
-
-/*
- * Return 0 if st is consistent with a file not having been changed
- * since sd was filled.  If there are differences, return a
- * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
- * INODE_CHANGED, and DATA_CHANGED.
- */
-int match_stat_data(const struct stat_data *sd, struct stat *st);
-int match_stat_data_racy(const struct index_state *istate,
-			 const struct stat_data *sd, struct stat *st);
-
-void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
-
-#define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
-#define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
-#define REFRESH_QUIET                    (1 << 2) /* be quiet about it */
-#define REFRESH_IGNORE_MISSING           (1 << 3) /* ignore non-existent */
-#define REFRESH_IGNORE_SUBMODULES        (1 << 4) /* ignore submodules */
-#define REFRESH_IN_PORCELAIN             (1 << 5) /* user friendly output, not "needs update" */
-#define REFRESH_PROGRESS                 (1 << 6) /* show progress bar if stderr is tty */
-#define REFRESH_IGNORE_SKIP_WORKTREE     (1 << 7) /* ignore skip_worktree entries */
-int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
-/*
- * Refresh the index and write it to disk.
- *
- * 'refresh_flags' is passed directly to 'refresh_index()', while
- * 'COMMIT_LOCK | write_flags' is passed to 'write_locked_index()', so
- * the lockfile is always either committed or rolled back.
- *
- * If 'gentle' is passed, errors locking the index are ignored.
- *
- * Return 1 if refreshing the index returns an error, -1 if writing
- * the index to disk fails, 0 on success.
- *
- * Note that if refreshing the index returns an error, we still write
- * out the index (unless locking fails).
- */
-int repo_refresh_and_write_index(struct repository*, unsigned int refresh_flags, unsigned int write_flags, int gentle, const struct pathspec *, char *seen, const char *header_msg);
-
-struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
-
-void set_alternate_index_output(const char *);
-
-extern int verify_index_checksum;
-extern int verify_ce_order;
-
-/* Environment bits from configuration mechanism */
-extern int trust_executable_bit;
-extern int trust_ctime;
-extern int check_stat;
-extern int quote_path_fully;
-extern int has_symlinks;
-extern int minimum_abbrev, default_abbrev;
-extern int ignore_case;
-extern int assume_unchanged;
-extern int prefer_symlink_refs;
-extern int warn_ambiguous_refs;
-extern int warn_on_object_refname_ambiguity;
-extern char *apply_default_whitespace;
-extern char *apply_default_ignorewhitespace;
-extern const char *git_attributes_file;
-extern const char *git_hooks_path;
-extern int zlib_compression_level;
-extern int pack_compression_level;
-extern size_t packed_git_window_size;
-extern size_t packed_git_limit;
-extern size_t delta_base_cache_limit;
-extern unsigned long big_file_threshold;
-extern unsigned long pack_size_limit_cfg;
-
-/*
- * Accessors for the core.sharedrepository config which lazy-load the value
- * from the config (if not already set). The "reset" function can be
- * used to unset "set" or cached value, meaning that the value will be loaded
- * fresh from the config file on the next call to get_shared_repository().
- */
-void set_shared_repository(int value);
-int get_shared_repository(void);
-void reset_shared_repository(void);
-
-/*
- * Do replace refs need to be checked this run?  This variable is
- * initialized to true unless --no-replace-object is used or
- * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
- * commands that do not want replace references to be active.
- */
-extern int read_replace_refs;
-
-/*
- * These values are used to help identify parts of a repository to fsync.
- * FSYNC_COMPONENT_NONE identifies data that will not be a persistent part of the
- * repository and so shouldn't be fsynced.
- */
-enum fsync_component {
-	FSYNC_COMPONENT_NONE,
-	FSYNC_COMPONENT_LOOSE_OBJECT		= 1 << 0,
-	FSYNC_COMPONENT_PACK			= 1 << 1,
-	FSYNC_COMPONENT_PACK_METADATA		= 1 << 2,
-	FSYNC_COMPONENT_COMMIT_GRAPH		= 1 << 3,
-	FSYNC_COMPONENT_INDEX			= 1 << 4,
-	FSYNC_COMPONENT_REFERENCE		= 1 << 5,
-};
-
-#define FSYNC_COMPONENTS_OBJECTS (FSYNC_COMPONENT_LOOSE_OBJECT | \
-				  FSYNC_COMPONENT_PACK)
-
-#define FSYNC_COMPONENTS_DERIVED_METADATA (FSYNC_COMPONENT_PACK_METADATA | \
-					   FSYNC_COMPONENT_COMMIT_GRAPH)
-
-#define FSYNC_COMPONENTS_DEFAULT ((FSYNC_COMPONENTS_OBJECTS | \
-				   FSYNC_COMPONENTS_DERIVED_METADATA) & \
-				  ~FSYNC_COMPONENT_LOOSE_OBJECT)
-
-#define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS | \
-				    FSYNC_COMPONENT_REFERENCE)
-
-#define FSYNC_COMPONENTS_ADDED (FSYNC_COMPONENTS_COMMITTED | \
-				FSYNC_COMPONENT_INDEX)
-
-#define FSYNC_COMPONENTS_ALL (FSYNC_COMPONENT_LOOSE_OBJECT | \
-			      FSYNC_COMPONENT_PACK | \
-			      FSYNC_COMPONENT_PACK_METADATA | \
-			      FSYNC_COMPONENT_COMMIT_GRAPH | \
-			      FSYNC_COMPONENT_INDEX | \
-			      FSYNC_COMPONENT_REFERENCE)
-
-#ifndef FSYNC_COMPONENTS_PLATFORM_DEFAULT
-#define FSYNC_COMPONENTS_PLATFORM_DEFAULT FSYNC_COMPONENTS_DEFAULT
-#endif
-
-/*
- * A bitmask indicating which components of the repo should be fsynced.
- */
-extern enum fsync_component fsync_components;
-extern int fsync_object_files;
-extern int use_fsync;
-
-enum fsync_method {
-	FSYNC_METHOD_FSYNC,
-	FSYNC_METHOD_WRITEOUT_ONLY,
-	FSYNC_METHOD_BATCH,
-};
-
-extern enum fsync_method fsync_method;
-extern int core_preload_index;
-extern int precomposed_unicode;
-extern int protect_hfs;
-extern int protect_ntfs;
-
-extern int core_apply_sparse_checkout;
-extern int core_sparse_checkout_cone;
-extern int sparse_expect_files_outside_of_patterns;
-
-/*
- * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
- */
-int use_optional_locks(void);
-
-/*
- * The character that begins a commented line in user-editable file
- * that is subject to stripspace.
- */
-extern char comment_line_char;
-extern int auto_comment_line_char;
-
-enum log_refs_config {
-	LOG_REFS_UNSET = -1,
-	LOG_REFS_NONE = 0,
-	LOG_REFS_NORMAL,
-	LOG_REFS_ALWAYS
-};
-extern enum log_refs_config log_all_ref_updates;
-
-enum rebase_setup_type {
-	AUTOREBASE_NEVER = 0,
-	AUTOREBASE_LOCAL,
-	AUTOREBASE_REMOTE,
-	AUTOREBASE_ALWAYS
-};
-
-enum push_default_type {
-	PUSH_DEFAULT_NOTHING = 0,
-	PUSH_DEFAULT_MATCHING,
-	PUSH_DEFAULT_SIMPLE,
-	PUSH_DEFAULT_UPSTREAM,
-	PUSH_DEFAULT_CURRENT,
-	PUSH_DEFAULT_UNSPECIFIED
-};
-
-extern enum rebase_setup_type autorebase;
-extern enum push_default_type push_default;
-
-enum object_creation_mode {
-	OBJECT_CREATION_USES_HARDLINKS = 0,
-	OBJECT_CREATION_USES_RENAMES = 1
-};
-
-extern enum object_creation_mode object_creation_mode;
-
-extern char *notes_ref_name;
-
-extern int grafts_replace_parents;
-
-/*
- * GIT_REPO_VERSION is the version we write by default. The
- * _READ variant is the highest number we know how to
- * handle.
- */
-#define GIT_REPO_VERSION 0
-#define GIT_REPO_VERSION_READ 1
-extern int repository_format_precious_objects;
-extern int repository_format_worktree_config;
-
-/*
- * You _have_ to initialize a `struct repository_format` using
- * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
- */
-struct repository_format {
-	int version;
-	int precious_objects;
-	char *partial_clone; /* value of extensions.partialclone */
-	int worktree_config;
-	int is_bare;
-	int hash_algo;
-	int sparse_index;
-	char *work_tree;
-	struct string_list unknown_extensions;
-	struct string_list v1_only_extensions;
-};
-
-/*
- * Always use this to initialize a `struct repository_format`
- * to a well-defined, default state before calling
- * `read_repository()`.
- */
-#define REPOSITORY_FORMAT_INIT \
-{ \
-	.version = -1, \
-	.is_bare = -1, \
-	.hash_algo = GIT_HASH_SHA1, \
-	.unknown_extensions = STRING_LIST_INIT_DUP, \
-	.v1_only_extensions = STRING_LIST_INIT_DUP, \
-}
-
-/*
- * Read the repository format characteristics from the config file "path" into
- * "format" struct. Returns the numeric version. On error, or if no version is
- * found in the configuration, -1 is returned, format->version is set to -1,
- * and all other fields in the struct are set to the default configuration
- * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
- * REPOSITORY_FORMAT_INIT before calling this function.
- */
-int read_repository_format(struct repository_format *format, const char *path);
-
-/*
- * Free the memory held onto by `format`, but not the struct itself.
- * (No need to use this after `read_repository_format()` fails.)
- */
-void clear_repository_format(struct repository_format *format);
-
-/*
- * Verify that the repository described by repository_format is something we
- * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
- * any errors encountered.
- */
-int verify_repository_format(const struct repository_format *format,
-			     struct strbuf *err);
-
-/*
- * Check the repository format version in the path found in get_git_dir(),
- * 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);
-
-#define MTIME_CHANGED	0x0001
-#define CTIME_CHANGED	0x0002
-#define OWNER_CHANGED	0x0004
-#define MODE_CHANGED    0x0008
-#define INODE_CHANGED   0x0010
-#define DATA_CHANGED    0x0020
-#define TYPE_CHANGED    0x0040
-
-/*
- * Return an abbreviated sha1 unique within this repository's object database.
- * The result will be at least `len` characters long, and will be NUL
- * terminated.
- *
- * The non-`_r` version returns a static buffer which remains valid until 4
- * more calls to find_unique_abbrev are made.
- *
- * The `_r` variant writes to a buffer supplied by the caller, which must be at
- * least `GIT_MAX_HEXSZ + 1` bytes. The return value is the number of bytes
- * written (excluding the NUL terminator).
- *
- * Note that while this version avoids the static buffer, it is not fully
- * reentrant, as it calls into other non-reentrant git code.
- */
-const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
-#define find_unique_abbrev(oid, len) repo_find_unique_abbrev(the_repository, oid, len)
-int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
-#define find_unique_abbrev_r(hex, oid, len) repo_find_unique_abbrev_r(the_repository, hex, oid, len)
-
-/* set default permissions by passing mode arguments to open(2) */
-int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
-int git_mkstemp_mode(char *pattern, int mode);
-
-/*
- * NOTE NOTE NOTE!!
- *
- * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
- * not be changed. Old repositories have core.sharedrepository written in
- * numeric format, and therefore these values are preserved for compatibility
- * reasons.
- */
-enum sharedrepo {
-	PERM_UMASK          = 0,
-	OLD_PERM_GROUP      = 1,
-	OLD_PERM_EVERYBODY  = 2,
-	PERM_GROUP          = 0660,
-	PERM_EVERYBODY      = 0664
-};
-int git_config_perm(const char *var, const char *value);
-int adjust_shared_perm(const char *path);
-
-/*
- * Create the directory containing the named path, using care to be
- * somewhat safe against races. Return one of the scld_error values to
- * indicate success/failure. On error, set errno to describe the
- * problem.
- *
- * SCLD_VANISHED indicates that one of the ancestor directories of the
- * path existed at one point during the function call and then
- * suddenly vanished, probably because another process pruned the
- * directory while we were working.  To be robust against this kind of
- * race, callers might want to try invoking the function again when it
- * returns SCLD_VANISHED.
- *
- * safe_create_leading_directories() temporarily changes path while it
- * is working but restores it before returning.
- * safe_create_leading_directories_const() doesn't modify path, even
- * temporarily. Both these variants adjust the permissions of the
- * created directories to honor core.sharedRepository, so they are best
- * suited for files inside the git dir. For working tree files, use
- * safe_create_leading_directories_no_share() instead, as it ignores
- * the core.sharedRepository setting.
- */
-enum scld_error {
-	SCLD_OK = 0,
-	SCLD_FAILED = -1,
-	SCLD_PERMS = -2,
-	SCLD_EXISTS = -3,
-	SCLD_VANISHED = -4
-};
-enum scld_error safe_create_leading_directories(char *path);
-enum scld_error safe_create_leading_directories_const(const char *path);
-enum scld_error safe_create_leading_directories_no_share(char *path);
-
-int mkdir_in_gitdir(const char *path);
-char *interpolate_path(const char *path, int real_home);
-/* NEEDSWORK: remove this synonym once in-flight topics have migrated */
-#define expand_user_path interpolate_path
-const char *enter_repo(const char *path, int strict);
-static inline int is_absolute_path(const char *path)
-{
-	return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
-}
-int is_directory(const char *);
-char *strbuf_realpath(struct strbuf *resolved, const char *path,
-		      int die_on_error);
-char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
-				int die_on_error);
-char *real_pathdup(const char *path, int die_on_error);
-const char *absolute_path(const char *path);
-char *absolute_pathdup(const char *path);
-const char *remove_leading_path(const char *in, const char *prefix);
-const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
-int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
-int normalize_path_copy(char *dst, const char *src);
-int longest_ancestor_length(const char *path, struct string_list *prefixes);
-char *strip_path_suffix(const char *path, const char *suffix);
-int daemon_avoid_alias(const char *path);
-
-/*
- * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
- * details.
- */
-int is_ntfs_dotgit(const char *name);
-int is_ntfs_dotgitmodules(const char *name);
-int is_ntfs_dotgitignore(const char *name);
-int is_ntfs_dotgitattributes(const char *name);
-int is_ntfs_dotmailmap(const char *name);
-
-/*
- * Returns true iff "str" could be confused as a command-line option when
- * passed to a sub-program like "ssh". Note that this has nothing to do with
- * shell-quoting, which should be handled separately; we're assuming here that
- * the string makes it verbatim to the sub-program.
- */
-int looks_like_command_line_option(const char *str);
-
-/**
- * Return a newly allocated string with the evaluation of
- * "$XDG_CONFIG_HOME/$subdir/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
- * "$HOME/.config/$subdir/$filename". Return NULL upon error.
- */
-char *xdg_config_home_for(const char *subdir, const char *filename);
-
-/**
- * Return a newly allocated string with the evaluation of
- * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
- * "$HOME/.config/git/$filename". Return NULL upon error.
- */
-char *xdg_config_home(const char *filename);
-
-/**
- * Return a newly allocated string with the evaluation of
- * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
- * "$HOME/.cache/git/$filename". Return NULL upon error.
- */
-char *xdg_cache_home(const char *filename);
-
-int git_open_cloexec(const char *name, int flags);
-#define git_open(name) git_open_cloexec(name, O_RDONLY)
-
-/**
- * unpack_loose_header() initializes the data stream needed to unpack
- * a loose object header.
- *
- * Returns:
- *
- * - ULHR_OK on success
- * - ULHR_BAD on error
- * - ULHR_TOO_LONG if the header was too long
- *
- * It will only parse up to MAX_HEADER_LEN bytes unless an optional
- * "hdrbuf" argument is non-NULL. This is intended for use with
- * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
- * reporting. The full header will be extracted to "hdrbuf" for use
- * with parse_loose_header(), ULHR_TOO_LONG will still be returned
- * from this function to indicate that the header was too long.
- */
-enum unpack_loose_header_result {
-	ULHR_OK,
-	ULHR_BAD,
-	ULHR_TOO_LONG,
-};
-enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
-						    unsigned char *map,
-						    unsigned long mapsize,
-						    void *buffer,
-						    unsigned long bufsiz,
-						    struct strbuf *hdrbuf);
-
-/**
- * parse_loose_header() parses the starting "<type> <len>\0" of an
- * object. If it doesn't follow that format -1 is returned. To check
- * the validity of the <type> populate the "typep" in the "struct
- * object_info". It will be OBJ_BAD if the object type is unknown. The
- * parsed <len> can be retrieved via "oi->sizep", and from there
- * passed to unpack_loose_rest().
- */
-struct object_info;
-int parse_loose_header(const char *hdr, struct object_info *oi);
-
-/**
- * With in-core object data in "buf", rehash it to make sure the
- * object name actually matches "oid" to detect object corruption.
- *
- * A negative value indicates an error, usually that the OID is not
- * what we expected, but it might also indicate another error.
- */
-int check_object_signature(struct repository *r, const struct object_id *oid,
-			   void *map, unsigned long size,
-			   enum object_type type);
-
-/**
- * A streaming version of check_object_signature().
- * Try reading the object named with "oid" using
- * the streaming interface and rehash it to do the same.
- */
-int stream_object_signature(struct repository *r, const struct object_id *oid);
-
-int finalize_object_file(const char *tmpfile, const char *filename);
-
-/* Helper to check and "touch" a file */
-int check_and_freshen_file(const char *fn, int freshen);
-
-extern const signed char hexval_table[256];
-static inline unsigned int hexval(unsigned char c)
-{
-	return hexval_table[c];
-}
-
-/*
- * Convert two consecutive hexadecimal digits into a char.  Return a
- * negative value on error.  Don't run over the end of short strings.
- */
-static inline int hex2chr(const char *s)
-{
-	unsigned int val = hexval(s[0]);
-	return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
-}
-
-/* Convert to/from hex/sha1 representation */
-#define MINIMUM_ABBREV minimum_abbrev
-#define DEFAULT_ABBREV default_abbrev
-
-/* used when the code does not know or care what the default abbrev is */
-#define FALLBACK_DEFAULT_ABBREV 7
-
-struct object_context {
-	unsigned short mode;
-	/*
-	 * symlink_path is only used by get_tree_entry_follow_symlinks,
-	 * and only for symlinks that point outside the repository.
-	 */
-	struct strbuf symlink_path;
-	/*
-	 * If GET_OID_RECORD_PATH is set, this will record path (if any)
-	 * found when resolving the name. The caller is responsible for
-	 * releasing the memory.
-	 */
-	char *path;
-};
-
-#define GET_OID_QUIETLY           01
-#define GET_OID_COMMIT            02
-#define GET_OID_COMMITTISH        04
-#define GET_OID_TREE             010
-#define GET_OID_TREEISH          020
-#define GET_OID_BLOB             040
-#define GET_OID_FOLLOW_SYMLINKS 0100
-#define GET_OID_RECORD_PATH     0200
-#define GET_OID_ONLY_TO_DIE    04000
-#define GET_OID_REQUIRE_PATH  010000
-
-#define GET_OID_DISAMBIGUATORS \
-	(GET_OID_COMMIT | GET_OID_COMMITTISH | \
-	GET_OID_TREE | GET_OID_TREEISH | \
-	GET_OID_BLOB)
-
-enum get_oid_result {
-	FOUND = 0,
-	MISSING_OBJECT = -1, /* The requested object is missing */
-	SHORT_NAME_AMBIGUOUS = -2,
-	/* The following only apply when symlinks are followed */
-	DANGLING_SYMLINK = -4, /*
-				* The initial symlink is there, but
-				* (transitively) points to a missing
-				* in-tree file
-				*/
-	SYMLINK_LOOP = -5,
-	NOT_DIR = -6, /*
-		       * Somewhere along the symlink chain, a path is
-		       * requested which contains a file as a
-		       * non-final element.
-		       */
-};
-
-int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
-__attribute__((format (printf, 2, 3)))
-int get_oidf(struct object_id *oid, const char *fmt, ...);
-int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid);
-int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid);
-void maybe_die_on_misspelt_object_name(struct repository *repo,
-				       const char *name,
-				       const char *prefix);
-enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
-					 unsigned flags, struct object_id *oid,
-					 struct object_context *oc);
-
-#define get_oid(str, oid)		repo_get_oid(the_repository, str, oid)
-#define get_oid_commit(str, oid)	repo_get_oid_commit(the_repository, str, oid)
-#define get_oid_committish(str, oid)	repo_get_oid_committish(the_repository, str, oid)
-#define get_oid_tree(str, oid)		repo_get_oid_tree(the_repository, str, oid)
-#define get_oid_treeish(str, oid)	repo_get_oid_treeish(the_repository, str, oid)
-#define get_oid_blob(str, oid)		repo_get_oid_blob(the_repository, str, oid)
-#define get_oid_mb(str, oid) 		repo_get_oid_mb(the_repository, str, oid)
-
-typedef int each_abbrev_fn(const struct object_id *oid, void *);
-int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
-#define for_each_abbrev(prefix, fn, data) repo_for_each_abbrev(the_repository, prefix, fn, data)
-
-int set_disambiguate_hint_config(const char *var, const char *value);
-
-/*
- * Try to read a SHA1 in hexadecimal format from the 40 characters
- * starting at hex.  Write the 20-byte result to sha1 in binary form.
- * Return 0 on success.  Reading stops if a NUL is encountered in the
- * input, so it is safe to pass this function an arbitrary
- * null-terminated string.
- */
-int get_sha1_hex(const char *hex, unsigned char *sha1);
-int get_oid_hex(const char *hex, struct object_id *sha1);
-
-/* Like get_oid_hex, but for an arbitrary hash algorithm. */
-int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
-
-/*
- * Read `len` pairs of hexadecimal digits from `hex` and write the
- * values to `binary` as `len` bytes. Return 0 on success, or -1 if
- * the input does not consist of hex digits).
- */
-int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
-
-/*
- * Convert a binary hash in "unsigned char []" or an object name in
- * "struct object_id *" to its hex equivalent. The `_r` variant is reentrant,
- * and writes the NUL-terminated output to the buffer `out`, which must be at
- * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
- * convenience.
- *
- * The non-`_r` variant returns a static buffer, but uses a ring of 4
- * buffers, making it safe to make multiple calls for a single statement, like:
- *
- *   printf("%s -> %s", hash_to_hex(one), hash_to_hex(two));
- *   printf("%s -> %s", oid_to_hex(one), oid_to_hex(two));
- */
-char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
-char *oid_to_hex_r(char *out, const struct object_id *oid);
-char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);	/* static buffer result! */
-char *hash_to_hex(const unsigned char *hash);						/* same static buffer */
-char *oid_to_hex(const struct object_id *oid);						/* same static buffer */
-
-/*
- * Parse a 40-character hexadecimal object ID starting from hex, updating the
- * pointer specified by end when parsing stops.  The resulting object ID is
- * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
- * other invalid character.  end is only updated on success; otherwise, it is
- * unmodified.
- */
-int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
-
-/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
-int parse_oid_hex_algop(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
- * a hex value for any algorithm. The algorithm is detected based on the length
- * and the algorithm in use is returned. If this is not a hex object ID in any
- * algorithm, returns GIT_HASH_UNKNOWN.
- */
-int get_oid_hex_any(const char *hex, struct object_id *oid);
-int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
-
-/*
- * This reads short-hand syntax that not only evaluates to a commit
- * object name, but also can act as if the end user spelled the name
- * of the branch from the command line.
- *
- * - "@{-N}" finds the name of the Nth previous branch we were on, and
- *   places the name of the branch in the given buf and returns the
- *   number of characters parsed if successful.
- *
- * - "<branch>@{upstream}" finds the name of the other ref that
- *   <branch> is configured to merge with (missing <branch> defaults
- *   to the current branch), and places the name of the branch in the
- *   given buf and returns the number of characters parsed if
- *   successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
- */
-#define INTERPRET_BRANCH_LOCAL (1<<0)
-#define INTERPRET_BRANCH_REMOTE (1<<1)
-#define INTERPRET_BRANCH_HEAD (1<<2)
-struct interpret_branch_name_options {
-	/*
-	 * If "allowed" is non-zero, it is a treated as a bitfield of allowable
-	 * expansions: local branches ("refs/heads/"), remote branches
-	 * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
-	 * allowed, even ones to refs outside of those namespaces.
-	 */
-	unsigned allowed;
-
-	/*
-	 * If ^{upstream} or ^{push} (or equivalent) is requested, and the
-	 * branch in question does not have such a reference, return -1 instead
-	 * of die()-ing.
-	 */
-	unsigned nonfatal_dangling_mark : 1;
-};
-int repo_interpret_branch_name(struct repository *r,
-			       const char *str, int len,
-			       struct strbuf *buf,
-			       const struct interpret_branch_name_options *options);
-#define interpret_branch_name(str, len, buf, options) \
-	repo_interpret_branch_name(the_repository, str, len, buf, options)
-
-int validate_headref(const char *ref);
-
-int base_name_compare(const char *name1, size_t len1, int mode1,
-		      const char *name2, size_t len2, int mode2);
-int df_name_compare(const char *name1, size_t len1, int mode1,
-		    const char *name2, size_t len2, int mode2);
-int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
-
-void *read_object_with_reference(struct repository *r,
-				 const struct object_id *oid,
-				 enum object_type required_type,
-				 unsigned long *size,
-				 struct object_id *oid_ret);
-
-struct object *repo_peel_to_type(struct repository *r,
-				 const char *name, int namelen,
-				 struct object *o, enum object_type);
-#define peel_to_type(name, namelen, obj, type) \
-	repo_peel_to_type(the_repository, name, namelen, obj, type)
-
-#define IDENT_STRICT	       1
-#define IDENT_NO_DATE	       2
-#define IDENT_NO_NAME	       4
-
-enum want_ident {
-	WANT_BLANK_IDENT,
-	WANT_AUTHOR_IDENT,
-	WANT_COMMITTER_IDENT
-};
-
-const char *git_author_info(int);
-const char *git_committer_info(int);
-const char *fmt_ident(const char *name, const char *email,
-		      enum want_ident whose_ident,
-		      const char *date_str, int);
-const char *fmt_name(enum want_ident);
-const char *ident_default_name(void);
-const char *ident_default_email(void);
-const char *git_editor(void);
-const char *git_sequence_editor(void);
-const char *git_pager(int stdout_is_tty);
-int is_terminal_dumb(void);
-int git_ident_config(const char *, const char *, void *);
-/*
- * Prepare an ident to fall back on if the user didn't configure it.
- */
-void prepare_fallback_ident(const char *name, const char *email);
-void reset_ident_date(void);
-
-struct ident_split {
-	const char *name_begin;
-	const char *name_end;
-	const char *mail_begin;
-	const char *mail_end;
-	const char *date_begin;
-	const char *date_end;
-	const char *tz_begin;
-	const char *tz_end;
-};
-/*
- * Signals an success with 0, but time part of the result may be NULL
- * if the input lacks timestamp and zone
- */
-int split_ident_line(struct ident_split *, const char *, int);
-
-/*
- * Given a commit or tag object buffer and the commit or tag headers, replaces
- * the idents in the headers with their canonical versions using the mailmap mechanism.
- */
-void apply_mailmap_to_header(struct strbuf *, const char **, struct string_list *);
-
-/*
- * Compare split idents for equality or strict ordering. Note that we
- * compare only the ident part of the line, ignoring any timestamp.
- *
- * Because there are two fields, we must choose one as the primary key; we
- * currently arbitrarily pick the email.
- */
-int ident_cmp(const struct ident_split *, const struct ident_split *);
-
-struct cache_def {
-	struct strbuf path;
-	int flags;
-	int track_flags;
-	int prefix_len_stat_func;
-};
-#define CACHE_DEF_INIT { \
-	.path = STRBUF_INIT, \
-}
-static inline void cache_def_clear(struct cache_def *cache)
-{
-	strbuf_release(&cache->path);
-}
-
-int has_symlink_leading_path(const char *name, int len);
-int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
-int check_leading_path(const char *name, int len, int warn_on_lstat_err);
-int has_dirs_only_path(const char *name, int len, int prefix_len);
-void invalidate_lstat_cache(void);
-void schedule_dir_for_removal(const char *name, int len);
-void remove_scheduled_dirs(void);
-
-struct pack_window {
-	struct pack_window *next;
-	unsigned char *base;
-	off_t offset;
-	size_t len;
-	unsigned int last_used;
-	unsigned int inuse_cnt;
-};
-
-struct pack_entry {
-	off_t offset;
-	struct packed_git *p;
-};
-
-/*
- * Create a temporary file rooted in the object database directory, or
- * die on failure. The filename is taken from "pattern", which should have the
- * usual "XXXXXX" trailer, and the resulting filename is written into the
- * "template" buffer. Returns the open descriptor.
- */
-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
-
-/*
- * Create a pack .keep file named "name" (which should generally be the output
- * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
- * error.
- */
-int odb_pack_keep(const char *name);
-
-/*
- * Set this to 0 to prevent oid_object_info_extended() from fetching missing
- * blobs. This has a difference only if extensions.partialClone is set.
- *
- * Its default value is 1.
- */
-extern int fetch_if_missing;
-
-/* Dumb servers support */
-int update_server_info(int);
-
-const char *get_log_output_encoding(void);
-const char *get_commit_output_encoding(void);
-
-int committer_ident_sufficiently_given(void);
-int author_ident_sufficiently_given(void);
-
-extern const char *git_commit_encoding;
-extern const char *git_log_output_encoding;
-extern const char *git_mailmap_file;
-extern const char *git_mailmap_blob;
-
-/* IO helper functions */
-void maybe_flush_or_die(FILE *, const char *);
-__attribute__((format (printf, 2, 3)))
-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);
-
-#define COPY_READ_ERROR (-2)
-#define COPY_WRITE_ERROR (-3)
-int copy_fd(int ifd, int ofd);
-int copy_file(const char *dst, const char *src, int mode);
-int copy_file_with_time(const char *dst, const char *src, int mode);
-
-void write_or_die(int fd, const void *buf, size_t count);
-void fsync_or_die(int fd, const char *);
-int fsync_component(enum fsync_component component, int fd);
-void fsync_component_or_die(enum fsync_component component, int fd, const char *msg);
-
-static inline int batch_fsync_enabled(enum fsync_component component)
-{
-	return (fsync_components & component) && (fsync_method == FSYNC_METHOD_BATCH);
-}
-
-ssize_t read_in_full(int fd, void *buf, size_t count);
-ssize_t write_in_full(int fd, const void *buf, size_t count);
-ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
-
-static inline ssize_t write_str_in_full(int fd, const char *str)
-{
-	return write_in_full(fd, str, strlen(str));
-}
-
-/**
- * Open (and truncate) the file at path, write the contents of buf to it,
- * and close it. Dies if any errors are encountered.
- */
-void write_file_buf(const char *path, const char *buf, size_t len);
-
-/**
- * Like write_file_buf(), but format the contents into a buffer first.
- * Additionally, write_file() will append a newline if one is not already
- * present, making it convenient to write text files:
- *
- *   write_file(path, "counter: %d", ctr);
- */
-__attribute__((format (printf, 2, 3)))
-void write_file(const char *path, const char *fmt, ...);
-
-/* pager.c */
-void setup_pager(void);
-int pager_in_use(void);
-extern int pager_use_color;
-int term_columns(void);
-void term_clear_line(void);
-int decimal_width(uintmax_t);
-int check_pager_config(const char *cmd);
-void prepare_pager_args(struct child_process *, const char *pager);
-
-extern const char *editor_program;
-extern const char *askpass_program;
-extern const char *excludes_file;
-
-/* base85 */
-int decode_85(char *dst, const char *line, int linelen);
-void encode_85(char *buf, const unsigned char *data, int bytes);
-
-/* pkt-line.c */
-void packet_trace_identity(const char *prog);
-
-/* add */
-/*
- * return 0 if success, 1 - if addition of a file failed and
- * ADD_FILES_IGNORE_ERRORS was specified in flags
- */
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
-
-/* diff.c */
-extern int diff_auto_refresh_index;
-
-/* match-trees.c */
-void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
-void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
-
-/*
- * whitespace rules.
- * used by both diff and apply
- * last two digits are tab width
- */
-#define WS_BLANK_AT_EOL         0100
-#define WS_SPACE_BEFORE_TAB     0200
-#define WS_INDENT_WITH_NON_TAB  0400
-#define WS_CR_AT_EOL           01000
-#define WS_BLANK_AT_EOF        02000
-#define WS_TAB_IN_INDENT       04000
-#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
-#define WS_TAB_WIDTH_MASK        077
-/* All WS_* -- when extended, adapt diff.c emit_symbol */
-#define WS_RULE_MASK           07777
-extern unsigned whitespace_rule_cfg;
-unsigned whitespace_rule(struct index_state *, const char *);
-unsigned parse_whitespace_rule(const char *);
-unsigned ws_check(const char *line, int len, unsigned ws_rule);
-void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
-char *whitespace_error_string(unsigned ws);
-void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-int ws_blank_line(const char *line, int len);
-#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
-
-/* ls-files */
-void overlay_tree_on_index(struct index_state *istate,
-			   const char *tree_name, const char *prefix);
-
-/* setup.c */
-struct startup_info {
-	int have_repository;
-	const char *prefix;
-	const char *original_cwd;
-};
-extern struct startup_info *startup_info;
-extern const char *tmp_original_cwd;
-
-/* merge.c */
-struct commit_list;
-int try_merge_command(struct repository *r,
-		const char *strategy, size_t xopts_nr,
-		const char **xopts, struct commit_list *common,
-		const char *head_arg, struct commit_list *remotes);
-int checkout_fast_forward(struct repository *r,
-			  const struct object_id *from,
-			  const struct object_id *to,
-			  int overwrite_ignore);
-
-
-int sane_execvp(const char *file, char *const argv[]);
-
-/*
- * A struct to encapsulate the concept of whether a file has changed
- * since we last checked it. This uses criteria similar to those used
- * for the index.
- */
-struct stat_validity {
-	struct stat_data *sd;
-};
-
-void stat_validity_clear(struct stat_validity *sv);
-
-/*
- * Returns 1 if the path is a regular file (or a symlink to a regular
- * file) and matches the saved stat_validity, 0 otherwise.  A missing
- * or inaccessible file is considered a match if the struct was just
- * initialized, or if the previous update found an inaccessible file.
- */
-int stat_validity_check(struct stat_validity *sv, const char *path);
-
-/*
- * Update the stat_validity from a file opened at descriptor fd. If
- * the file is missing, inaccessible, or not a regular file, then
- * future calls to stat_validity_check will match iff one of those
- * conditions continues to be true.
- */
-void stat_validity_update(struct stat_validity *sv, int fd);
-
-int versioncmp(const char *s1, const char *s2);
-
-/*
- * Create a directory and (if share is nonzero) adjust its permissions
- * according to the shared_repository setting. Only use this for
- * directories under $GIT_DIR.  Don't use it for working tree
- * directories.
- */
-void safe_create_dir(const char *dir, int share);
-
-/*
- * Should we print an ellipsis after an abbreviated SHA-1 value
- * when doing diff-raw output or indicating a detached HEAD?
- */
-int print_sha1_ellipsis(void);
-
-/* Return 1 if the file is empty or does not exists, 0 otherwise. */
-int is_empty_or_missing_file(const char *filename);
-
-#endif /* CACHE_H */
diff --git a/cbtree.c b/cbtree.c
index 336e46d..c1cc30a 100644
--- a/cbtree.c
+++ b/cbtree.c
@@ -4,6 +4,7 @@
  * Based on Adam Langley's adaptation of Dan Bernstein's public domain code
  * git clone https://github.com/agl/critbit.git
  */
+#include "git-compat-util.h"
 #include "cbtree.h"
 
 static struct cb_node *cb_node_of(const void *p)
diff --git a/cbtree.h b/cbtree.h
index 0be14fb..43193ab 100644
--- a/cbtree.h
+++ b/cbtree.h
@@ -14,8 +14,6 @@
 #ifndef CBTREE_H
 #define CBTREE_H
 
-#include "git-compat-util.h"
-
 struct cb_node;
 struct cb_node {
 	struct cb_node *child[2];
diff --git a/chdir-notify.c b/chdir-notify.c
index 5f7f2c2..0d7bc04 100644
--- a/chdir-notify.c
+++ b/chdir-notify.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "chdir-notify.h"
 #include "list.h"
+#include "path.h"
 #include "strbuf.h"
+#include "trace.h"
 
 struct chdir_notify_entry {
 	const char *name;
diff --git a/checkout.c b/checkout.c
index 2e39dae..4256e71 100644
--- a/checkout.c
+++ b/checkout.c
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "object-name.h"
 #include "remote.h"
 #include "refspec.h"
+#include "repository.h"
 #include "checkout.h"
 #include "config.h"
+#include "strbuf.h"
 
 struct tracking_name_data {
 	/* const */ char *src_ref;
@@ -23,7 +26,7 @@
 	memset(&query, 0, sizeof(struct refspec_item));
 	query.src = cb->src_ref;
 	if (remote_find_tracking(remote, &query) ||
-	    get_oid(query.dst, cb->dst_oid)) {
+	    repo_get_oid(the_repository, query.dst, cb->dst_oid)) {
 		free(query.dst);
 		return 0;
 	}
diff --git a/checkout.h b/checkout.h
index 1152133..3c514a5 100644
--- a/checkout.h
+++ b/checkout.h
@@ -1,7 +1,7 @@
 #ifndef CHECKOUT_H
 #define CHECKOUT_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 /*
  * Check if the branch name uniquely matches a branch name on a remote
diff --git a/chunk-format.c b/chunk-format.c
index 0275b74..cdc7f39 100644
--- a/chunk-format.c
+++ b/chunk-format.c
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "chunk-format.h"
 #include "csum-file.h"
+#include "gettext.h"
+#include "hash.h"
+#include "trace2.h"
 
 /*
  * When writing a chunk-based file format, collect the chunks in
@@ -99,7 +102,8 @@
 			   const unsigned char *mfile,
 			   size_t mfile_size,
 			   uint64_t toc_offset,
-			   int toc_length)
+			   int toc_length,
+			   unsigned expected_alignment)
 {
 	int i;
 	uint32_t chunk_id;
@@ -117,6 +121,11 @@
 			error(_("terminating chunk id appears earlier than expected"));
 			return 1;
 		}
+		if (chunk_offset % expected_alignment != 0) {
+			error(_("chunk id %"PRIx32" not %d-byte aligned"),
+			      chunk_id, expected_alignment);
+			return 1;
+		}
 
 		table_of_contents += CHUNK_TOC_ENTRY_SIZE;
 		next_chunk_offset = get_be64(table_of_contents + 4);
@@ -151,20 +160,28 @@
 	return 0;
 }
 
+struct pair_chunk_data {
+	const unsigned char **p;
+	size_t *size;
+};
+
 static int pair_chunk_fn(const unsigned char *chunk_start,
 			 size_t chunk_size,
 			 void *data)
 {
-	const unsigned char **p = data;
-	*p = chunk_start;
+	struct pair_chunk_data *pcd = data;
+	*pcd->p = chunk_start;
+	*pcd->size = chunk_size;
 	return 0;
 }
 
 int pair_chunk(struct chunkfile *cf,
 	       uint32_t chunk_id,
-	       const unsigned char **p)
+	       const unsigned char **p,
+	       size_t *size)
 {
-	return read_chunk(cf, chunk_id, pair_chunk_fn, p);
+	struct pair_chunk_data pcd = { .p = p, .size = size };
+	return read_chunk(cf, chunk_id, pair_chunk_fn, &pcd);
 }
 
 int read_chunk(struct chunkfile *cf,
diff --git a/chunk-format.h b/chunk-format.h
index 7885aa0..14b7618 100644
--- a/chunk-format.h
+++ b/chunk-format.h
@@ -1,8 +1,7 @@
 #ifndef CHUNK_FORMAT_H
 #define CHUNK_FORMAT_H
 
-#include "git-compat-util.h"
-#include "hash.h"
+#include "hash-ll.h"
 
 struct hashfile;
 struct chunkfile;
@@ -37,20 +36,23 @@
 			   const unsigned char *mfile,
 			   size_t mfile_size,
 			   uint64_t toc_offset,
-			   int toc_length);
+			   int toc_length,
+			   unsigned expected_alignment);
 
 #define CHUNK_NOT_FOUND (-2)
 
 /*
  * Find 'chunk_id' in the given chunkfile and assign the
  * given pointer to the position in the mmap'd file where
- * that chunk begins.
+ * that chunk begins. Likewise the "size" parameter is filled
+ * with the size of the chunk.
  *
  * Returns CHUNK_NOT_FOUND if the chunk does not exist.
  */
 int pair_chunk(struct chunkfile *cf,
 	       uint32_t chunk_id,
-	       const unsigned char **p);
+	       const unsigned char **p,
+	       size_t *size);
 
 typedef int (*chunk_read_fn)(const unsigned char *chunk_start,
 			     size_t chunk_size, void *data);
diff --git a/ci/config/README b/ci/config/README
new file mode 100644
index 0000000..8de3a04
--- /dev/null
+++ b/ci/config/README
@@ -0,0 +1,14 @@
+You can configure some aspects of the GitHub Actions-based CI on a
+per-repository basis by setting "variables" and "secrets" from with the
+GitHub web interface. These can be found at:
+
+  https://github.com/<user>/git/settings/secrets/actions
+
+The following variables can be used:
+
+ - CI_BRANCHES
+
+   By default, CI is run when any branch is pushed. If this variable is
+   non-empty, then only the branches it lists will run CI. Branch names
+   should be separated by spaces, and should use their shortened form
+   (e.g., "main", not "refs/heads/main").
diff --git a/ci/config/allow-ref.sample b/ci/config/allow-ref.sample
deleted file mode 100755
index af0e076..0000000
--- a/ci/config/allow-ref.sample
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-#
-# Sample script for enabling/disabling GitHub Actions CI runs on
-# particular refs. By default, CI is run for all branches pushed to
-# GitHub. You can override this by dropping the ".sample" from the script,
-# editing it, committing, and pushing the result to the "ci-config" branch of
-# your repository:
-#
-#   git checkout -b ci-config
-#   cp allow-ref.sample allow-ref
-#   $EDITOR allow-ref
-#   git add allow-ref
-#   git commit -am "implement my ci preferences"
-#   git push
-#
-# This script will then be run when any refs are pushed to that repository. It
-# gets the fully qualified refname as the first argument, and should exit with
-# success only for refs for which you want to run CI.
-
-case "$1" in
-# allow one-off tests by pushing to "for-ci" or "for-ci/mybranch"
-refs/heads/for-ci*) true ;;
-# always build your integration branch
-refs/heads/my-integration-branch) true ;;
-# don't build any other branches or tags
-*) false ;;
-esac
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 4f40753..b4e22de 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -37,15 +37,13 @@
 	test -z "$BREW_INSTALL_PACKAGES" ||
 	brew install $BREW_INSTALL_PACKAGES
 	brew link --force gettext
-	mkdir -p $HOME/bin
-	(
-		cd $HOME/bin
+
+	mkdir -p "$P4_PATH"
+	pushd "$P4_PATH"
 		wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
 		tar -xf helix-core-server.tgz &&
 		sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true
-	)
-	PATH="$PATH:${HOME}/bin"
-	export PATH
+	popd
 
 	if test -n "$CC_PACKAGE"
 	then
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
index 78b7e32..eb2c9e1 100755
--- a/ci/install-docker-dependencies.sh
+++ b/ci/install-docker-dependencies.sh
@@ -3,6 +3,10 @@
 # Install dependencies required to build and test Git inside container
 #
 
+. ${0%/*}/lib.sh
+
+begin_group "Install dependencies"
+
 case "$jobname" in
 linux32)
 	linux32 --32bit i386 sh -c '
@@ -12,11 +16,31 @@
 	'
 	;;
 linux-musl)
-	apk add --update build-base curl-dev openssl-dev expat-dev gettext \
-		pcre2-dev python3 musl-libintl perl-utils ncurses >/dev/null
+	apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
+		pcre2-dev python3 musl-libintl perl-utils ncurses \
+		apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
+		bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
+	;;
+linux-*|StaticAnalysis)
+	# Required so that apt doesn't wait for user input on certain packages.
+	export DEBIAN_FRONTEND=noninteractive
+
+	apt update -q &&
+	apt install -q -y sudo git make language-pack-is libsvn-perl apache2 libssl-dev \
+		libcurl4-openssl-dev libexpat-dev tcl tk gettext zlib1g-dev \
+		perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
+		libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
+		apache2 cvs cvsps gnupg libcgi-pm-perl subversion
+
+	if test "$jobname" = StaticAnalysis
+	then
+		apt install -q -y coccinelle
+	fi
 	;;
 pedantic)
 	dnf -yq update >/dev/null &&
 	dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
 	;;
 esac
+
+end_group "Install dependencies"
diff --git a/ci/lib.sh b/ci/lib.sh
index db7105e..0a73fc7 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -1,16 +1,7 @@
 # Library of functions shared by all CI scripts
 
-if test true != "$GITHUB_ACTIONS"
+if test true = "$GITHUB_ACTIONS"
 then
-	begin_group () { :; }
-	end_group () { :; }
-
-	group () {
-		shift
-		"$@"
-	}
-	set -x
-else
 	begin_group () {
 		need_to_end_group=t
 		echo "::group::$1" >&2
@@ -23,27 +14,50 @@
 		need_to_end_group=
 		echo '::endgroup::' >&2
 	}
-	trap end_group EXIT
-
-	group () {
-		set +x
-		begin_group "$1"
-		shift
-		# work around `dash` not supporting `set -o pipefail`
-		(
-			"$@" 2>&1
-			echo $? >exit.status
-		) |
-		sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
-		res=$(cat exit.status)
-		rm exit.status
-		end_group
-		return $res
+elif test true = "$GITLAB_CI"
+then
+	begin_group () {
+		need_to_end_group=t
+		printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K$1\n"
+		trap "end_group '$1'" EXIT
+		set -x
 	}
 
-	begin_group "CI setup"
+	end_group () {
+		test -n "$need_to_end_group" || return 0
+		set +x
+		need_to_end_group=
+		printf "\e[0Ksection_end:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K\n"
+		trap - EXIT
+	}
+else
+	begin_group () { :; }
+	end_group () { :; }
+
+	set -x
 fi
 
+group () {
+	group="$1"
+	shift
+	begin_group "$group"
+
+	# work around `dash` not supporting `set -o pipefail`
+	(
+		"$@" 2>&1
+		echo $? >exit.status
+	) |
+	sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
+	res=$(cat exit.status)
+	rm exit.status
+
+	end_group "$group"
+	return $res
+}
+
+begin_group "CI setup"
+trap "end_group 'CI setup'" EXIT
+
 # Set 'exit on error' for all CI scripts to let the caller know that
 # something went wrong.
 #
@@ -71,10 +85,32 @@
 	fi
 }
 
+# Check whether we can use the path passed via the first argument as Git
+# repository.
+is_usable_git_repository () {
+	# We require Git in our PATH, otherwise we cannot access repositories
+	# at all.
+	if ! command -v git >/dev/null
+	then
+		return 1
+	fi
+
+	# And the target directory needs to be a proper Git repository.
+	if ! git -C "$1" rev-parse 2>/dev/null
+	then
+		return 1
+	fi
+}
+
 # Save some info about the current commit's tree, so we can skip the build
 # job if we encounter the same tree again and can provide a useful info
 # message.
 save_good_tree () {
+	if ! is_usable_git_repository .
+	then
+		return
+	fi
+
 	echo "$(git rev-parse $CI_COMMIT^{tree}) $CI_COMMIT $CI_JOB_NUMBER $CI_JOB_ID" >>"$good_trees_file"
 	# limit the file size
 	tail -1000 "$good_trees_file" >"$good_trees_file".tmp
@@ -90,6 +126,11 @@
 		return
 	fi
 
+	if ! is_usable_git_repository .
+	then
+		return
+	fi
+
 	if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
 	then
 		# Haven't seen this tree yet, or no cached good trees file yet.
@@ -121,6 +162,11 @@
 }
 
 check_unignored_build_artifacts () {
+	if ! is_usable_git_repository .
+	then
+		return
+	fi
+
 	! git ls-files --other --exclude-standard --error-unmatch \
 		-- ':/*' 2>/dev/null ||
 	{
@@ -133,6 +179,26 @@
 	return 1
 }
 
+create_failed_test_artifacts () {
+	mkdir -p t/failed-test-artifacts
+
+	for test_exit in t/test-results/*.exit
+	do
+		test 0 != "$(cat "$test_exit")" || continue
+
+		test_name="${test_exit%.exit}"
+		test_name="${test_name##*/}"
+		printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
+		echo "The full logs are in the 'print test failures' step below."
+		echo "See also the 'failed-tests-*' artifacts attached to this run."
+		cat "t/test-results/$test_name.markup"
+
+		trash_dir="t/trash directory.$test_name"
+		cp "t/test-results/$test_name.out" t/failed-test-artifacts/
+		tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+	done
+}
+
 # GitHub Action doesn't set TERM, which is required by tput
 export TERM=${TERM:-dumb}
 
@@ -156,11 +222,8 @@
 	# among *all* phases)
 	cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
 
-	export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
-	export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml"
-	MAKEFLAGS="$MAKEFLAGS --jobs=10"
-	test windows_nt != "$CI_OS_NAME" ||
-	GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+	GIT_TEST_OPTS="--write-junit-xml"
+	JOBS=10
 elif test true = "$GITHUB_ACTIONS"
 then
 	CI_TYPE=github-actions
@@ -173,40 +236,70 @@
 	CC="${CC_PACKAGE:-${CC:-gcc}}"
 	DONT_SKIP_TAGS=t
 	handle_failed_tests () {
-		mkdir -p t/failed-test-artifacts
 		echo "FAILED_TEST_ARTIFACTS=t/failed-test-artifacts" >>$GITHUB_ENV
-
-		for test_exit in t/test-results/*.exit
-		do
-			test 0 != "$(cat "$test_exit")" || continue
-
-			test_name="${test_exit%.exit}"
-			test_name="${test_name##*/}"
-			printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
-			echo "The full logs are in the 'print test failures' step below."
-			echo "See also the 'failed-tests-*' artifacts attached to this run."
-			cat "t/test-results/$test_name.markup"
-
-			trash_dir="t/trash directory.$test_name"
-			cp "t/test-results/$test_name.out" t/failed-test-artifacts/
-			tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
-		done
+		create_failed_test_artifacts
 		return 1
 	}
 
 	cache_dir="$HOME/none"
 
-	export GIT_PROVE_OPTS="--timer --jobs 10"
-	export GIT_TEST_OPTS="--verbose-log -x --github-workflow-markup"
-	MAKEFLAGS="$MAKEFLAGS --jobs=10"
-	test windows != "$CI_OS_NAME" ||
-	GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+	GIT_TEST_OPTS="--github-workflow-markup"
+	JOBS=10
+elif test true = "$GITLAB_CI"
+then
+	CI_TYPE=gitlab-ci
+	CI_BRANCH="$CI_COMMIT_REF_NAME"
+	CI_COMMIT="$CI_COMMIT_SHA"
+	case "$CI_JOB_IMAGE" in
+	macos-*)
+		# GitLab CI has Python installed via multiple package managers,
+		# most notably via asdf and Homebrew. Ensure that our builds
+		# pick up the Homebrew one by prepending it to our PATH as the
+		# asdf one breaks tests.
+		export PATH="$(brew --prefix)/bin:$PATH"
+
+		CI_OS_NAME=osx
+		;;
+	alpine:*|fedora:*|ubuntu:*)
+		CI_OS_NAME=linux;;
+	*)
+		echo "Could not identify OS image" >&2
+		env >&2
+		exit 1
+		;;
+	esac
+	CI_REPO_SLUG="$CI_PROJECT_PATH"
+	CI_JOB_ID="$CI_JOB_ID"
+	CC="${CC_PACKAGE:-${CC:-gcc}}"
+	DONT_SKIP_TAGS=t
+	handle_failed_tests () {
+		create_failed_test_artifacts
+		return 1
+	}
+
+	cache_dir="$HOME/none"
+
+	runs_on_pool=$(echo "$CI_JOB_IMAGE" | tr : -)
+	JOBS=$(nproc)
 else
 	echo "Could not identify CI type" >&2
 	env >&2
 	exit 1
 fi
 
+MAKEFLAGS="$MAKEFLAGS --jobs=$JOBS"
+GIT_PROVE_OPTS="--timer --jobs $JOBS"
+
+GIT_TEST_OPTS="$GIT_TEST_OPTS --verbose-log -x"
+case "$CI_OS_NAME" in
+windows|windows_nt)
+	GIT_TEST_OPTS="$GIT_TEST_OPTS --no-chain-lint --no-bin-wrappers"
+	;;
+esac
+
+export GIT_TEST_OPTS
+export GIT_PROVE_OPTS
+
 good_trees_file="$cache_dir/good-trees"
 
 mkdir -p "$cache_dir"
@@ -253,13 +346,14 @@
 	export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
 	;;
 macos-*)
-	if [ "$jobname" = osx-gcc ]
+	MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
+	if [ "$jobname" != osx-gcc ]
 	then
-		MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
-	else
-		MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
 		MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
 	fi
+
+	P4_PATH="$HOME/custom/p4"
+	export PATH="$P4_PATH:$PATH"
 	;;
 esac
 
@@ -273,20 +367,19 @@
 	MAKEFLAGS="$MAKEFLAGS NO_REGEX=Yes ICONV_OMITS_BOM=Yes"
 	MAKEFLAGS="$MAKEFLAGS GIT_TEST_UTF8_LOCALE=C.UTF-8"
 	;;
-linux-leaks)
+linux-leaks|linux-reftable-leaks)
 	export SANITIZE=leak
 	export GIT_TEST_PASSING_SANITIZE_LEAK=true
 	export GIT_TEST_SANITIZE_LEAK_LOG=true
 	;;
-linux-asan)
-	export SANITIZE=address
-	;;
-linux-ubsan)
-	export SANITIZE=undefined
+linux-asan-ubsan)
+	export SANITIZE=address,undefined
+	export NO_SVN_TESTS=LetsSaveSomeTime
+	MAKEFLAGS="$MAKEFLAGS NO_PYTHON=YepBecauseP4FlakesTooOften"
 	;;
 esac
 
 MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
 
-end_group
+end_group "CI setup"
 set -x
diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh
index 57277ee..b1f80ae 100755
--- a/ci/print-test-failures.sh
+++ b/ci/print-test-failures.sh
@@ -8,7 +8,7 @@
 # Tracing executed commands would produce too much noise in the loop below.
 set +x
 
-cd t/
+cd "${TEST_OUTPUT_DIRECTORY:-t/}"
 
 if ! ls test-results/*.exit >/dev/null 2>/dev/null
 then
@@ -51,6 +51,12 @@
 			tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
 			continue
 			;;
+		gitlab-ci)
+			mkdir -p failed-test-artifacts
+			cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/
+			tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+			continue
+			;;
 		*)
 			echo "Unhandled CI type: $CI_TYPE" >&2
 			exit 1
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
new file mode 100755
index 0000000..a51076d
--- /dev/null
+++ b/ci/run-build-and-minimal-fuzzers.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# Build and test Git's fuzzers
+#
+
+. ${0%/*}/lib.sh
+
+group "Build fuzzers" make \
+	CC=clang \
+	CXX=clang++ \
+	CFLAGS="-fsanitize=fuzzer-no-link,address" \
+	LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
+	fuzz-all
+
+for fuzzer in commit-graph config date pack-headers pack-idx ; do
+	begin_group "fuzz-$fuzzer"
+	./oss-fuzz/fuzz-$fuzzer -verbosity=0 -runs=1 || exit 1
+	end_group "fuzz-$fuzzer"
+done
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index b098e10..c192bd6 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -27,8 +27,9 @@
 	export GIT_TEST_MULTI_PACK_INDEX=1
 	export GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1
 	export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-	export GIT_TEST_WRITE_REV_INDEX=1
+	export GIT_TEST_NO_WRITE_REV_INDEX=1
 	export GIT_TEST_CHECKOUT_WORKERS=2
+	export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
 	;;
 linux-clang)
 	export GIT_TEST_DEFAULT_HASH=sha1
@@ -36,6 +37,9 @@
 linux-sha256)
 	export GIT_TEST_DEFAULT_HASH=sha256
 	;;
+linux-reftable|linux-reftable-leaks|osx-reftable)
+	export GIT_TEST_DEFAULT_REF_FORMAT=reftable
+	;;
 pedantic)
 	# Don't run the tests; we only care about whether Git can be
 	# built.
@@ -49,6 +53,8 @@
 then
 	group "Run tests" make test ||
 	handle_failed_tests
+	group "Run unit tests" \
+		make DEFAULT_UNIT_TEST_TARGET=unit-tests-prove unit-tests
 fi
 check_unignored_build_artifacts
 
diff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh
index a3c6795..ae80943 100755
--- a/ci/run-test-slice.sh
+++ b/ci/run-test-slice.sh
@@ -15,4 +15,9 @@
 	tr '\n' ' ')" ||
 handle_failed_tests
 
+# We only have one unit test at the moment, so run it in the first slice
+if [ "$1" == "0" ] ; then
+	group "Run unit tests" make --quiet -C t unit-tests-prove
+fi
+
 check_unignored_build_artifacts
diff --git a/color.c b/color.c
index f05d8a8..f663c06 100644
--- a/color.c
+++ b/color.c
@@ -1,6 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "color.h"
+#include "editor.h"
+#include "gettext.h"
+#include "hex-ll.h"
+#include "pager.h"
+#include "strbuf.h"
 
 static int git_use_color_default = GIT_COLOR_AUTO;
 int color_stdout_is_tty = -1;
@@ -425,14 +430,6 @@
 	return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
-	if (git_color_config(var, value, cb) < 0)
-		return -1;
-
-	return git_default_config(var, value, cb);
-}
-
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
 {
 	if (*color)
diff --git a/color.h b/color.h
index cfc8f84..bb28343 100644
--- a/color.h
+++ b/color.h
@@ -88,12 +88,8 @@
  */
 extern int color_stdout_is_tty;
 
-/*
- * Use the first one if you need only color config; the second is a convenience
- * if you are just going to change to git_default_config, too.
- */
+/* Parse color config. */
 int git_color_config(const char *var, const char *value, void *cb);
-int git_color_default_config(const char *var, const char *value, void *cb);
 
 /*
  * Parse a config option, which can be a boolean or one of
diff --git a/column.c b/column.c
index fbf8863..50bbccc 100644
--- a/column.c
+++ b/column.c
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "column.h"
 #include "string-list.h"
+#include "pager.h"
 #include "parse-options.h"
 #include "run-command.h"
 #include "utf8.h"
@@ -181,6 +182,8 @@
 {
 	struct column_options nopts;
 
+	if (opts && (0 > opts->padding))
+		BUG("padding must be non-negative");
 	if (!list->nr)
 		return;
 	assert((colopts & COL_ENABLE_MASK) != COL_AUTO);
@@ -360,6 +363,8 @@
 {
 	struct strvec *argv;
 
+	if (opts && (0 > opts->padding))
+		BUG("padding must be non-negative");
 	if (fd_out != -1)
 		return -1;
 
diff --git a/combine-diff.c b/combine-diff.c
index 1a39b5d..d6d6fa1 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1,14 +1,18 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
 #include "commit.h"
-#include "blob.h"
+#include "convert.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "environment.h"
+#include "hex.h"
+#include "object-name.h"
 #include "quote.h"
 #include "xdiff-interface.h"
 #include "xdiff/xmacros.h"
 #include "log-tree.h"
 #include "refs.h"
+#include "tree.h"
 #include "userdiff.h"
 #include "oid-array.h"
 #include "revision.h"
@@ -332,7 +336,9 @@
 		*size = fill_textconv(r, textconv, df, &blob);
 		free_filespec(df);
 	} else {
-		blob = read_object_file(oid, &type, size);
+		blob = repo_read_object_file(r, oid, &type, size);
+		if (!blob)
+			die(_("unable to read %s"), oid_to_hex(oid));
 		if (type != OBJ_BLOB)
 			die("object '%s' is not a blob!", oid_to_hex(oid));
 	}
@@ -948,11 +954,11 @@
 			 "", elem->path, line_prefix, c_meta, c_reset);
 	printf("%s%sindex ", line_prefix, c_meta);
 	for (i = 0; i < num_parent; i++) {
-		abb = find_unique_abbrev(&elem->parent[i].oid,
-					 abbrev);
+		abb = repo_find_unique_abbrev(the_repository,
+					      &elem->parent[i].oid, abbrev);
 		printf("%s%s", i ? "," : "", abb);
 	}
-	abb = find_unique_abbrev(&elem->oid, abbrev);
+	abb = repo_find_unique_abbrev(the_repository, &elem->oid, abbrev);
 	printf("..%s%s\n", abb, c_reset);
 
 	if (mode_differs) {
diff --git a/command-list.txt b/command-list.txt
index 54b2a50..c4cd0f3 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -160,6 +160,7 @@
 git-remote                              ancillarymanipulators           complete
 git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
+git-replay                              plumbingmanipulators
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           history
diff --git a/commit-graph.c b/commit-graph.c
index c11b59f..45417d7 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1,15 +1,19 @@
 #include "git-compat-util.h"
 #include "config.h"
+#include "csum-file.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
-#include "pack.h"
 #include "packfile.h"
 #include "commit.h"
 #include "object.h"
 #include "refs.h"
-#include "revision.h"
 #include "hash-lookup.h"
 #include "commit-graph.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "path.h"
 #include "alloc.h"
 #include "hashmap.h"
 #include "replace-object.h"
@@ -19,6 +23,7 @@
 #include "shallow.h"
 #include "json-writer.h"
 #include "trace2.h"
+#include "tree.h"
 #include "chunk-format.h"
 
 void git_test_write_commit_graph_or_die(void)
@@ -116,11 +121,19 @@
 	struct commit_graph_data *data =
 		commit_graph_data_slab_peek(&commit_graph_data_slab, c);
 
-	if (!data)
-		return GENERATION_NUMBER_INFINITY;
-	else if (data->graph_pos == COMMIT_NOT_FROM_GRAPH)
-		return GENERATION_NUMBER_INFINITY;
+	if (data && data->generation)
+		return data->generation;
 
+	return GENERATION_NUMBER_INFINITY;
+}
+
+static timestamp_t commit_graph_generation_from_graph(const struct commit *c)
+{
+	struct commit_graph_data *data =
+		commit_graph_data_slab_peek(&commit_graph_data_slab, c);
+
+	if (!data || data->graph_pos == COMMIT_NOT_FROM_GRAPH)
+		return GENERATION_NUMBER_INFINITY;
 	return data->generation;
 }
 
@@ -200,14 +213,12 @@
 	return g;
 }
 
-extern int read_replace_refs;
-
 static int commit_graph_compatible(struct repository *r)
 {
 	if (!r->gitdir)
 		return 0;
 
-	if (read_replace_refs) {
+	if (replace_refs_enabled(r)) {
 		prepare_replace_object(r);
 		if (hashmap_get_size(&r->objects->replace_map->map))
 			return 0;
@@ -263,31 +274,25 @@
 	return ret;
 }
 
-static int verify_commit_graph_lite(struct commit_graph *g)
+static int graph_read_oid_fanout(const unsigned char *chunk_start,
+				 size_t chunk_size, void *data)
 {
-	/*
-	 * Basic validation shared between parse_commit_graph()
-	 * which'll be called every time the graph is used, and the
-	 * much more expensive verify_commit_graph() used by
-	 * "commit-graph verify".
-	 *
-	 * There should only be very basic checks here to ensure that
-	 * we don't e.g. segfault in fill_commit_in_graph(), but
-	 * because this is a very hot codepath nothing that e.g. loops
-	 * over g->num_commits, or runs a checksum on the commit-graph
-	 * itself.
-	 */
-	if (!g->chunk_oid_fanout) {
-		error("commit-graph is missing the OID Fanout chunk");
-		return 1;
-	}
-	if (!g->chunk_oid_lookup) {
-		error("commit-graph is missing the OID Lookup chunk");
-		return 1;
-	}
-	if (!g->chunk_commit_data) {
-		error("commit-graph is missing the Commit Data chunk");
-		return 1;
+	struct commit_graph *g = data;
+	int i;
+
+	if (chunk_size != 256 * sizeof(uint32_t))
+		return error(_("commit-graph oid fanout chunk is wrong size"));
+	g->chunk_oid_fanout = (const uint32_t *)chunk_start;
+	g->num_commits = ntohl(g->chunk_oid_fanout[255]);
+
+	for (i = 0; i < 255; i++) {
+		uint32_t oid_fanout1 = ntohl(g->chunk_oid_fanout[i]);
+		uint32_t oid_fanout2 = ntohl(g->chunk_oid_fanout[i + 1]);
+
+		if (oid_fanout1 > oid_fanout2) {
+			error(_("commit-graph fanout values out of order"));
+			return 1;
+		}
 	}
 
 	return 0;
@@ -298,7 +303,40 @@
 {
 	struct commit_graph *g = data;
 	g->chunk_oid_lookup = chunk_start;
-	g->num_commits = chunk_size / g->hash_len;
+	if (chunk_size / g->hash_len != g->num_commits)
+		return error(_("commit-graph OID lookup chunk is the wrong size"));
+	return 0;
+}
+
+static int graph_read_commit_data(const unsigned char *chunk_start,
+				  size_t chunk_size, void *data)
+{
+	struct commit_graph *g = data;
+	if (chunk_size / GRAPH_DATA_WIDTH != g->num_commits)
+		return error(_("commit-graph commit data chunk is wrong size"));
+	g->chunk_commit_data = chunk_start;
+	return 0;
+}
+
+static int graph_read_generation_data(const unsigned char *chunk_start,
+				      size_t chunk_size, void *data)
+{
+	struct commit_graph *g = data;
+	if (chunk_size / sizeof(uint32_t) != g->num_commits)
+		return error(_("commit-graph generations chunk is wrong size"));
+	g->chunk_generation_data = chunk_start;
+	return 0;
+}
+
+static int graph_read_bloom_index(const unsigned char *chunk_start,
+				  size_t chunk_size, void *data)
+{
+	struct commit_graph *g = data;
+	if (chunk_size / 4 != g->num_commits) {
+		warning(_("commit-graph changed-path index chunk is too small"));
+		return -1;
+	}
+	g->chunk_bloom_indexes = chunk_start;
 	return 0;
 }
 
@@ -307,7 +345,17 @@
 {
 	struct commit_graph *g = data;
 	uint32_t hash_version;
+
+	if (chunk_size < BLOOMDATA_CHUNK_HEADER_SIZE) {
+		warning(_("ignoring too-small changed-path chunk"
+			" (%"PRIuMAX" < %"PRIuMAX") in commit-graph file"),
+			(uintmax_t)chunk_size,
+			(uintmax_t)BLOOMDATA_CHUNK_HEADER_SIZE);
+		return -1;
+	}
+
 	g->chunk_bloom_data = chunk_start;
+	g->chunk_bloom_data_size = chunk_size;
 	hash_version = get_be32(chunk_start);
 
 	if (hash_version != 1)
@@ -379,29 +427,41 @@
 	cf = init_chunkfile(NULL);
 
 	if (read_table_of_contents(cf, graph->data, graph_size,
-				   GRAPH_HEADER_SIZE, graph->num_chunks))
+				   GRAPH_HEADER_SIZE, graph->num_chunks, 1))
 		goto free_and_return;
 
-	pair_chunk(cf, GRAPH_CHUNKID_OIDFANOUT,
-		   (const unsigned char **)&graph->chunk_oid_fanout);
-	read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph);
-	pair_chunk(cf, GRAPH_CHUNKID_DATA, &graph->chunk_commit_data);
-	pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges);
-	pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs);
+	if (read_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, graph_read_oid_fanout, graph)) {
+		error(_("commit-graph required OID fanout chunk missing or corrupted"));
+		goto free_and_return;
+	}
+	if (read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph)) {
+		error(_("commit-graph required OID lookup chunk missing or corrupted"));
+		goto free_and_return;
+	}
+	if (read_chunk(cf, GRAPH_CHUNKID_DATA, graph_read_commit_data, graph)) {
+		error(_("commit-graph required commit data chunk missing or corrupted"));
+		goto free_and_return;
+	}
+
+	pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges,
+		   &graph->chunk_extra_edges_size);
+	pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs,
+		   &graph->chunk_base_graphs_size);
 
 	if (s->commit_graph_generation_version >= 2) {
-		pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
-			&graph->chunk_generation_data);
+		read_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
+			   graph_read_generation_data, graph);
 		pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
-			&graph->chunk_generation_data_overflow);
+			   &graph->chunk_generation_data_overflow,
+			   &graph->chunk_generation_data_overflow_size);
 
 		if (graph->chunk_generation_data)
 			graph->read_generation_data = 1;
 	}
 
 	if (s->commit_graph_read_changed_paths) {
-		pair_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
-			   &graph->chunk_bloom_indexes);
+		read_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
+			   graph_read_bloom_index, graph);
 		read_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
 			   graph_read_bloom_data, graph);
 	}
@@ -417,9 +477,6 @@
 
 	oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);
 
-	if (verify_commit_graph_lite(graph))
-		goto free_and_return;
-
 	free_chunkfile(cf);
 	return graph;
 
@@ -461,6 +518,31 @@
 	return g;
 }
 
+/*
+ * returns 1 if and only if all graphs in the chain have
+ * corrected commit dates stored in the generation_data chunk.
+ */
+static int validate_mixed_generation_chain(struct commit_graph *g)
+{
+	int read_generation_data = 1;
+	struct commit_graph *p = g;
+
+	while (read_generation_data && p) {
+		read_generation_data = p->read_generation_data;
+		p = p->base_graph;
+	}
+
+	if (read_generation_data)
+		return 1;
+
+	while (g) {
+		g->read_generation_data = 0;
+		g = g->base_graph;
+	}
+
+	return 0;
+}
+
 static int add_graph_to_chain(struct commit_graph *g,
 			      struct commit_graph *chain,
 			      struct object_id *oids,
@@ -473,12 +555,17 @@
 		return 0;
 	}
 
+	if (g->chunk_base_graphs_size / g->hash_len < n) {
+		warning(_("commit-graph base graphs chunk is too small"));
+		return 0;
+	}
+
 	while (n) {
 		n--;
 
 		if (!cur_g ||
 		    !oideq(&oids[n], &cur_g->oid) ||
-		    !hasheq(oids[n].hash, g->chunk_base_graphs + g->hash_len * n)) {
+		    !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n))) {
 			warning(_("commit-graph chain does not match"));
 			return 0;
 		}
@@ -486,39 +573,56 @@
 		cur_g = cur_g->base_graph;
 	}
 
-	g->base_graph = chain;
-
-	if (chain)
+	if (chain) {
+		if (unsigned_add_overflows(chain->num_commits,
+					   chain->num_commits_in_base)) {
+			warning(_("commit count in base graph too high: %"PRIuMAX),
+				(uintmax_t)chain->num_commits_in_base);
+			return 0;
+		}
 		g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base;
+	}
+
+	g->base_graph = chain;
 
 	return 1;
 }
 
-static struct commit_graph *load_commit_graph_chain(struct repository *r,
-						    struct object_directory *odb)
+int open_commit_graph_chain(const char *chain_file,
+			    int *fd, struct stat *st)
+{
+	*fd = git_open(chain_file);
+	if (*fd < 0)
+		return 0;
+	if (fstat(*fd, st)) {
+		close(*fd);
+		return 0;
+	}
+	if (st->st_size < the_hash_algo->hexsz) {
+		close(*fd);
+		if (!st->st_size) {
+			/* treat empty files the same as missing */
+			errno = ENOENT;
+		} else {
+			warning(_("commit-graph chain file too small"));
+			errno = EINVAL;
+		}
+		return 0;
+	}
+	return 1;
+}
+
+struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
+						   int fd, struct stat *st,
+						   int *incomplete_chain)
 {
 	struct commit_graph *graph_chain = NULL;
 	struct strbuf line = STRBUF_INIT;
-	struct stat st;
 	struct object_id *oids;
 	int i = 0, valid = 1, count;
-	char *chain_name = get_commit_graph_chain_filename(odb);
-	FILE *fp;
-	int stat_res;
+	FILE *fp = xfdopen(fd, "r");
 
-	fp = fopen(chain_name, "r");
-	stat_res = stat(chain_name, &st);
-	free(chain_name);
-
-	if (!fp)
-		return NULL;
-	if (stat_res ||
-	    st.st_size <= the_hash_algo->hexsz) {
-		fclose(fp);
-		return NULL;
-	}
-
-	count = st.st_size / (the_hash_algo->hexsz + 1);
+	count = st->st_size / (the_hash_algo->hexsz + 1);
 	CALLOC_ARRAY(oids, count);
 
 	prepare_alt_odb(r);
@@ -547,6 +651,8 @@
 				if (add_graph_to_chain(g, graph_chain, oids, i)) {
 					graph_chain = g;
 					valid = 1;
+				} else {
+					free_commit_graph(g);
 				}
 
 				break;
@@ -559,36 +665,32 @@
 		}
 	}
 
+	validate_mixed_generation_chain(graph_chain);
+
 	free(oids);
 	fclose(fp);
 	strbuf_release(&line);
 
+	*incomplete_chain = !valid;
 	return graph_chain;
 }
 
-/*
- * returns 1 if and only if all graphs in the chain have
- * corrected commit dates stored in the generation_data chunk.
- */
-static int validate_mixed_generation_chain(struct commit_graph *g)
+static struct commit_graph *load_commit_graph_chain(struct repository *r,
+						    struct object_directory *odb)
 {
-	int read_generation_data = 1;
-	struct commit_graph *p = g;
+	char *chain_file = get_commit_graph_chain_filename(odb);
+	struct stat st;
+	int fd;
+	struct commit_graph *g = NULL;
 
-	while (read_generation_data && p) {
-		read_generation_data = p->read_generation_data;
-		p = p->base_graph;
+	if (open_commit_graph_chain(chain_file, &fd, &st)) {
+		int incomplete;
+		/* ownership of fd is taken over by load function */
+		g = load_commit_graph_chain_fd_st(r, fd, &st, &incomplete);
 	}
 
-	if (read_generation_data)
-		return 1;
-
-	while (g) {
-		g->read_generation_data = 0;
-		g = g->base_graph;
-	}
-
-	return 0;
+	free(chain_file);
+	return g;
 }
 
 struct commit_graph *read_commit_graph_one(struct repository *r,
@@ -599,8 +701,6 @@
 	if (!g)
 		g = load_commit_graph_chain(r, odb);
 
-	validate_mixed_generation_chain(g);
-
 	return g;
 }
 
@@ -704,19 +804,13 @@
 	return NULL;
 }
 
-static void close_commit_graph_one(struct commit_graph *g)
+void close_commit_graph(struct raw_object_store *o)
 {
-	if (!g)
+	if (!o->commit_graph)
 		return;
 
 	clear_commit_graph_data_slab(&commit_graph_data_slab);
-	close_commit_graph_one(g->base_graph);
-	free_commit_graph(g);
-}
-
-void close_commit_graph(struct raw_object_store *o)
-{
-	close_commit_graph_one(o->commit_graph);
+	free_commit_graph(o->commit_graph);
 	o->commit_graph = NULL;
 }
 
@@ -743,7 +837,7 @@
 
 	lex_index = pos - g->num_commits_in_base;
 
-	oidread(oid, g->chunk_oid_lookup + g->hash_len * lex_index);
+	oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index));
 }
 
 static struct commit_list **insert_parent_or_die(struct repository *r,
@@ -779,7 +873,7 @@
 		die(_("invalid commit position. commit-graph is likely corrupt"));
 
 	lex_index = pos - g->num_commits_in_base;
-	commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
+	commit_data = g->chunk_commit_data + st_mult(GRAPH_DATA_WIDTH, lex_index);
 
 	graph_data = commit_graph_data_at(item);
 	graph_data->graph_pos = pos;
@@ -789,14 +883,17 @@
 	item->date = (timestamp_t)((date_high << 32) | date_low);
 
 	if (g->read_generation_data) {
-		offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
+		offset = (timestamp_t)get_be32(g->chunk_generation_data + st_mult(sizeof(uint32_t), lex_index));
 
 		if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) {
 			if (!g->chunk_generation_data_overflow)
 				die(_("commit-graph requires overflow generation data but has none"));
 
 			offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
-			graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
+			if (g->chunk_generation_data_overflow_size / sizeof(uint64_t) <= offset_pos)
+				die(_("commit-graph overflow generation data is too small"));
+			graph_data->generation = item->date +
+				get_be64(g->chunk_generation_data_overflow + sizeof(uint64_t) * offset_pos);
 		} else
 			graph_data->generation = item->date + offset;
 	} else
@@ -816,7 +913,7 @@
 				struct commit_graph *g, uint32_t pos)
 {
 	uint32_t edge_value;
-	uint32_t *parent_data_ptr;
+	uint32_t parent_data_pos;
 	struct commit_list **pptr;
 	const unsigned char *commit_data;
 	uint32_t lex_index;
@@ -827,7 +924,7 @@
 	fill_commit_graph_info(item, g, pos);
 
 	lex_index = pos - g->num_commits_in_base;
-	commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
+	commit_data = g->chunk_commit_data + st_mult(g->hash_len + 16, lex_index);
 
 	item->object.parsed = 1;
 
@@ -848,14 +945,21 @@
 		return 1;
 	}
 
-	parent_data_ptr = (uint32_t*)(g->chunk_extra_edges +
-			  4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK));
+	parent_data_pos = edge_value & GRAPH_EDGE_LAST_MASK;
 	do {
-		edge_value = get_be32(parent_data_ptr);
+		if (g->chunk_extra_edges_size / sizeof(uint32_t) <= parent_data_pos) {
+			error(_("commit-graph extra-edges pointer out of bounds"));
+			free_commit_list(item->parents);
+			item->parents = NULL;
+			item->object.parsed = 0;
+			return 0;
+		}
+		edge_value = get_be32(g->chunk_extra_edges +
+				      sizeof(uint32_t) * parent_data_pos);
 		pptr = insert_parent_or_die(r, g,
 					    edge_value & GRAPH_EDGE_LAST_MASK,
 					    pptr);
-		parent_data_ptr++;
+		parent_data_pos++;
 	} while (!(edge_value & GRAPH_LAST_EDGE));
 
 	return 1;
@@ -898,14 +1002,18 @@
 
 struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
 {
+	static int commit_graph_paranoia = -1;
 	struct commit *commit;
 	uint32_t pos;
 
+	if (commit_graph_paranoia == -1)
+		commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
+
 	if (!prepare_commit_graph(repo))
 		return NULL;
 	if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos))
 		return NULL;
-	if (!has_object(repo, id, 0))
+	if (commit_graph_paranoia && !has_object(repo, id, 0))
 		return NULL;
 
 	commit = lookup_commit(repo, id);
@@ -969,7 +1077,7 @@
 		g = g->base_graph;
 
 	commit_data = g->chunk_commit_data +
-			GRAPH_DATA_WIDTH * (graph_pos - g->num_commits_in_base);
+			st_mult(GRAPH_DATA_WIDTH, graph_pos - g->num_commits_in_base);
 
 	oidread(&oid, commit_data);
 	set_commit_tree(c, lookup_tree(r, &oid));
@@ -1446,24 +1554,52 @@
 	stop_progress(&ctx->progress);
 }
 
-static void compute_topological_levels(struct write_commit_graph_context *ctx)
+struct compute_generation_info {
+	struct repository *r;
+	struct packed_commit_list *commits;
+	struct progress *progress;
+	int progress_cnt;
+
+	timestamp_t (*get_generation)(struct commit *c, void *data);
+	void (*set_generation)(struct commit *c, timestamp_t gen, void *data);
+	void *data;
+};
+
+static timestamp_t compute_generation_from_max(struct commit *c,
+					       timestamp_t max_gen,
+					       int generation_version)
+{
+	switch (generation_version) {
+	case 1: /* topological levels */
+		if (max_gen > GENERATION_NUMBER_V1_MAX - 1)
+			max_gen = GENERATION_NUMBER_V1_MAX - 1;
+		return max_gen + 1;
+
+	case 2: /* corrected commit date */
+		if (c->date && c->date > max_gen)
+			max_gen = c->date - 1;
+		return max_gen + 1;
+
+	default:
+		BUG("attempting unimplemented version");
+	}
+}
+
+static void compute_reachable_generation_numbers(
+			struct compute_generation_info *info,
+			int generation_version)
 {
 	int i;
 	struct commit_list *list = NULL;
 
-	if (ctx->report_progress)
-		ctx->progress = start_delayed_progress(
-					_("Computing commit graph topological levels"),
-					ctx->commits.nr);
-	for (i = 0; i < ctx->commits.nr; i++) {
-		struct commit *c = ctx->commits.list[i];
-		uint32_t level;
+	for (i = 0; i < info->commits->nr; i++) {
+		struct commit *c = info->commits->list[i];
+		timestamp_t gen;
+		repo_parse_commit(info->r, c);
+		gen = info->get_generation(c, info->data);
+		display_progress(info->progress, info->progress_cnt + 1);
 
-		repo_parse_commit(ctx->r, c);
-		level = *topo_level_slab_at(ctx->topo_levels, c);
-
-		display_progress(ctx->progress, i + 1);
-		if (level != GENERATION_NUMBER_ZERO)
+		if (gen != GENERATION_NUMBER_ZERO && gen != GENERATION_NUMBER_INFINITY)
 			continue;
 
 		commit_list_insert(c, &list);
@@ -1471,41 +1607,92 @@
 			struct commit *current = list->item;
 			struct commit_list *parent;
 			int all_parents_computed = 1;
-			uint32_t max_level = 0;
+			uint32_t max_gen = 0;
 
 			for (parent = current->parents; parent; parent = parent->next) {
-				repo_parse_commit(ctx->r, parent->item);
-				level = *topo_level_slab_at(ctx->topo_levels, parent->item);
+				repo_parse_commit(info->r, parent->item);
+				gen = info->get_generation(parent->item, info->data);
 
-				if (level == GENERATION_NUMBER_ZERO) {
+				if (gen == GENERATION_NUMBER_ZERO) {
 					all_parents_computed = 0;
 					commit_list_insert(parent->item, &list);
 					break;
 				}
 
-				if (level > max_level)
-					max_level = level;
+				if (gen > max_gen)
+					max_gen = gen;
 			}
 
 			if (all_parents_computed) {
 				pop_commit(&list);
-
-				if (max_level > GENERATION_NUMBER_V1_MAX - 1)
-					max_level = GENERATION_NUMBER_V1_MAX - 1;
-				*topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
+				gen = compute_generation_from_max(
+						current, max_gen,
+						generation_version);
+				info->set_generation(current, gen, info->data);
 			}
 		}
 	}
+}
+
+static timestamp_t get_topo_level(struct commit *c, void *data)
+{
+	struct write_commit_graph_context *ctx = data;
+	return *topo_level_slab_at(ctx->topo_levels, c);
+}
+
+static void set_topo_level(struct commit *c, timestamp_t t, void *data)
+{
+	struct write_commit_graph_context *ctx = data;
+	*topo_level_slab_at(ctx->topo_levels, c) = (uint32_t)t;
+}
+
+static void compute_topological_levels(struct write_commit_graph_context *ctx)
+{
+	struct compute_generation_info info = {
+		.r = ctx->r,
+		.commits = &ctx->commits,
+		.get_generation = get_topo_level,
+		.set_generation = set_topo_level,
+		.data = ctx,
+	};
+
+	if (ctx->report_progress)
+		info.progress = ctx->progress
+			      = start_delayed_progress(
+					_("Computing commit graph topological levels"),
+					ctx->commits.nr);
+
+	compute_reachable_generation_numbers(&info, 1);
+
 	stop_progress(&ctx->progress);
 }
 
+static timestamp_t get_generation_from_graph_data(struct commit *c,
+						  void *data UNUSED)
+{
+	return commit_graph_data_at(c)->generation;
+}
+
+static void set_generation_v2(struct commit *c, timestamp_t t,
+			      void *data UNUSED)
+{
+	struct commit_graph_data *g = commit_graph_data_at(c);
+	g->generation = t;
+}
+
 static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
 	int i;
-	struct commit_list *list = NULL;
+	struct compute_generation_info info = {
+		.r = ctx->r,
+		.commits = &ctx->commits,
+		.get_generation = get_generation_from_graph_data,
+		.set_generation = set_generation_v2,
+	};
 
 	if (ctx->report_progress)
-		ctx->progress = start_delayed_progress(
+		info.progress = ctx->progress
+			      = start_delayed_progress(
 					_("Computing commit graph generation numbers"),
 					ctx->commits.nr);
 
@@ -1517,47 +1704,7 @@
 		}
 	}
 
-	for (i = 0; i < ctx->commits.nr; i++) {
-		struct commit *c = ctx->commits.list[i];
-		timestamp_t corrected_commit_date;
-
-		repo_parse_commit(ctx->r, c);
-		corrected_commit_date = commit_graph_data_at(c)->generation;
-
-		display_progress(ctx->progress, i + 1);
-		if (corrected_commit_date != GENERATION_NUMBER_ZERO)
-			continue;
-
-		commit_list_insert(c, &list);
-		while (list) {
-			struct commit *current = list->item;
-			struct commit_list *parent;
-			int all_parents_computed = 1;
-			timestamp_t max_corrected_commit_date = 0;
-
-			for (parent = current->parents; parent; parent = parent->next) {
-				repo_parse_commit(ctx->r, parent->item);
-				corrected_commit_date = commit_graph_data_at(parent->item)->generation;
-
-				if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
-					all_parents_computed = 0;
-					commit_list_insert(parent->item, &list);
-					break;
-				}
-
-				if (corrected_commit_date > max_corrected_commit_date)
-					max_corrected_commit_date = corrected_commit_date;
-			}
-
-			if (all_parents_computed) {
-				pop_commit(&list);
-
-				if (current->date && current->date > max_corrected_commit_date)
-					max_corrected_commit_date = current->date - 1;
-				commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
-			}
-		}
-	}
+	compute_reachable_generation_numbers(&info, 2);
 
 	for (i = 0; i < ctx->commits.nr; i++) {
 		struct commit *c = ctx->commits.list[i];
@@ -1568,6 +1715,35 @@
 	stop_progress(&ctx->progress);
 }
 
+static void set_generation_in_graph_data(struct commit *c, timestamp_t t,
+					 void *data UNUSED)
+{
+	commit_graph_data_at(c)->generation = t;
+}
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+			      struct commit **commits, size_t nr)
+{
+	int generation_version = get_configured_generation_version(r);
+	struct packed_commit_list list = {
+		.list = commits,
+		.alloc = nr,
+		.nr = nr,
+	};
+	struct compute_generation_info info = {
+		.r = r,
+		.commits = &list,
+		.get_generation = get_generation_from_graph_data,
+		.set_generation = set_generation_in_graph_data,
+	};
+
+	compute_reachable_generation_numbers(&info, generation_version);
+}
+
 static void trace2_bloom_filter_write_statistics(struct write_commit_graph_context *ctx)
 {
 	trace2_data_intmax("commit-graph", ctx->r, "filter-computed",
@@ -1882,35 +2058,35 @@
 
 	add_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, GRAPH_FANOUT_SIZE,
 		  write_graph_chunk_fanout);
-	add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, hashsz * ctx->commits.nr,
+	add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, st_mult(hashsz, ctx->commits.nr),
 		  write_graph_chunk_oids);
-	add_chunk(cf, GRAPH_CHUNKID_DATA, (hashsz + 16) * ctx->commits.nr,
+	add_chunk(cf, GRAPH_CHUNKID_DATA, st_mult(hashsz + 16, ctx->commits.nr),
 		  write_graph_chunk_data);
 
 	if (ctx->write_generation_data)
 		add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
-			  sizeof(uint32_t) * ctx->commits.nr,
+			  st_mult(sizeof(uint32_t), ctx->commits.nr),
 			  write_graph_chunk_generation_data);
 	if (ctx->num_generation_data_overflows)
 		add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
-			  sizeof(timestamp_t) * ctx->num_generation_data_overflows,
+			  st_mult(sizeof(timestamp_t), ctx->num_generation_data_overflows),
 			  write_graph_chunk_generation_data_overflow);
 	if (ctx->num_extra_edges)
 		add_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES,
-			  4 * ctx->num_extra_edges,
+			  st_mult(4, ctx->num_extra_edges),
 			  write_graph_chunk_extra_edges);
 	if (ctx->changed_paths) {
 		add_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
-			  sizeof(uint32_t) * ctx->commits.nr,
+			  st_mult(sizeof(uint32_t), ctx->commits.nr),
 			  write_graph_chunk_bloom_indexes);
 		add_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
-			  sizeof(uint32_t) * 3
-				+ ctx->total_bloom_filter_data_size,
+			  st_add(sizeof(uint32_t) * 3,
+				 ctx->total_bloom_filter_data_size),
 			  write_graph_chunk_bloom_data);
 	}
 	if (ctx->num_commit_graphs_after > 1)
 		add_chunk(cf, GRAPH_CHUNKID_BASE,
-			  hashsz * (ctx->num_commit_graphs_after - 1),
+			  st_mult(hashsz, ctx->num_commit_graphs_after - 1),
 			  write_graph_chunk_base);
 
 	hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1928,7 +2104,7 @@
 			    get_num_chunks(cf));
 		ctx->progress = start_delayed_progress(
 			progress_title.buf,
-			get_num_chunks(cf) * ctx->commits.nr);
+			st_mult(get_num_chunks(cf), ctx->commits.nr));
 	}
 
 	write_chunkfile(cf, ctx);
@@ -1985,9 +2161,11 @@
 			free(graph_name);
 		}
 
+		free(ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
 		ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash));
 		final_graph_name = get_split_graph_filename(ctx->odb,
 					ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
+		free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1]);
 		ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;
 
 		result = rename(ctx->graph_name, final_graph_name);
@@ -2034,11 +2212,16 @@
 
 	if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED &&
 	    flags != COMMIT_GRAPH_SPLIT_REPLACE) {
-		while (g && (g->num_commits <= size_mult * num_commits ||
+		while (g && (g->num_commits <= st_mult(size_mult, num_commits) ||
 			    (max_commits && num_commits > max_commits))) {
 			if (g->odb != ctx->odb)
 				break;
 
+			if (unsigned_add_overflows(num_commits, g->num_commits))
+				die(_("cannot merge graphs with %"PRIuMAX", "
+				      "%"PRIuMAX" commits"),
+				    (uintmax_t)num_commits,
+				    (uintmax_t)g->num_commits);
 			num_commits += g->num_commits;
 			g = g->base_graph;
 
@@ -2096,6 +2279,11 @@
 	uint32_t i;
 	uint32_t offset = g->num_commits_in_base;
 
+	if (unsigned_add_overflows(ctx->commits.nr, g->num_commits))
+		die(_("cannot merge graph %s, too many commits: %"PRIuMAX),
+		    oid_to_hex(&g->oid),
+		    (uintmax_t)st_add(ctx->commits.nr, g->num_commits));
+
 	ALLOC_GROW(ctx->commits.list, ctx->commits.nr + g->num_commits, ctx->commits.alloc);
 
 	for (i = 0; i < g->num_commits; i++) {
@@ -2360,13 +2548,13 @@
 			replace = ctx->opts->split_flags & COMMIT_GRAPH_SPLIT_REPLACE;
 	}
 
-	ctx->approx_nr_objects = approximate_object_count();
+	ctx->approx_nr_objects = repo_approximate_object_count(the_repository);
 
 	if (ctx->append && ctx->r->objects->commit_graph) {
 		struct commit_graph *g = ctx->r->objects->commit_graph;
 		for (i = 0; i < g->num_commits; i++) {
 			struct object_id oid;
-			oidread(&oid, g->chunk_oid_lookup + g->hash_len * i);
+			oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
 			oid_array_append(&ctx->oids, &oid);
 		}
 	}
@@ -2426,23 +2614,21 @@
 
 cleanup:
 	free(ctx->graph_name);
+	free(ctx->base_graph_name);
 	free(ctx->commits.list);
 	oid_array_clear(&ctx->oids);
 	clear_topo_level_slab(&topo_levels);
 
-	if (ctx->commit_graph_filenames_after) {
-		for (i = 0; i < ctx->num_commit_graphs_after; i++) {
-			free(ctx->commit_graph_filenames_after[i]);
-			free(ctx->commit_graph_hash_after[i]);
-		}
+	for (i = 0; i < ctx->num_commit_graphs_before; i++)
+		free(ctx->commit_graph_filenames_before[i]);
+	free(ctx->commit_graph_filenames_before);
 
-		for (i = 0; i < ctx->num_commit_graphs_before; i++)
-			free(ctx->commit_graph_filenames_before[i]);
-
-		free(ctx->commit_graph_filenames_after);
-		free(ctx->commit_graph_filenames_before);
-		free(ctx->commit_graph_hash_after);
+	for (i = 0; i < ctx->num_commit_graphs_after; i++) {
+		free(ctx->commit_graph_filenames_after[i]);
+		free(ctx->commit_graph_hash_after[i]);
 	}
+	free(ctx->commit_graph_filenames_after);
+	free(ctx->commit_graph_hash_after);
 
 	free(ctx);
 
@@ -2464,30 +2650,20 @@
 	va_end(ap);
 }
 
-#define GENERATION_ZERO_EXISTS 1
-#define GENERATION_NUMBER_EXISTS 2
-
 static int commit_graph_checksum_valid(struct commit_graph *g)
 {
 	return hashfile_checksum_valid(g->data, g->data_len);
 }
 
-int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
+static int verify_one_commit_graph(struct repository *r,
+				   struct commit_graph *g,
+				   struct progress *progress,
+				   uint64_t *seen)
 {
 	uint32_t i, cur_fanout_pos = 0;
 	struct object_id prev_oid, cur_oid;
-	int generation_zero = 0;
-	struct progress *progress = NULL;
-	int local_error = 0;
-
-	if (!g) {
-		graph_report("no commit-graph file loaded");
-		return 1;
-	}
-
-	verify_commit_graph_error = verify_commit_graph_lite(g);
-	if (verify_commit_graph_error)
-		return verify_commit_graph_error;
+	struct commit *seen_gen_zero = NULL;
+	struct commit *seen_gen_non_zero = NULL;
 
 	if (!commit_graph_checksum_valid(g)) {
 		graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt"));
@@ -2497,7 +2673,7 @@
 	for (i = 0; i < g->num_commits; i++) {
 		struct commit *graph_commit;
 
-		oidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i);
+		oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
 
 		if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
 			graph_report(_("commit-graph has incorrect OID order: %s then %s"),
@@ -2534,22 +2710,18 @@
 	if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH)
 		return verify_commit_graph_error;
 
-	if (flags & COMMIT_GRAPH_WRITE_PROGRESS)
-		progress = start_progress(_("Verifying commits in commit graph"),
-					g->num_commits);
-
 	for (i = 0; i < g->num_commits; i++) {
 		struct commit *graph_commit, *odb_commit;
 		struct commit_list *graph_parents, *odb_parents;
 		timestamp_t max_generation = 0;
 		timestamp_t generation;
 
-		display_progress(progress, i + 1);
-		oidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i);
+		display_progress(progress, ++(*seen));
+		oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i));
 
 		graph_commit = lookup_commit(r, &cur_oid);
 		odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
-		if (parse_commit_internal(odb_commit, 0, 0)) {
+		if (repo_parse_commit_internal(r, odb_commit, 0, 0)) {
 			graph_report(_("failed to parse commit %s from object database for commit-graph"),
 				     oid_to_hex(&cur_oid));
 			continue;
@@ -2581,7 +2753,7 @@
 					     oid_to_hex(&graph_parents->item->object.oid),
 					     oid_to_hex(&odb_parents->item->object.oid));
 
-			generation = commit_graph_generation(graph_parents->item);
+			generation = commit_graph_generation_from_graph(graph_parents->item);
 			if (generation > max_generation)
 				max_generation = generation;
 
@@ -2593,16 +2765,12 @@
 			graph_report(_("commit-graph parent list for commit %s terminates early"),
 				     oid_to_hex(&cur_oid));
 
-		if (!commit_graph_generation(graph_commit)) {
-			if (generation_zero == GENERATION_NUMBER_EXISTS)
-				graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"),
-					     oid_to_hex(&cur_oid));
-			generation_zero = GENERATION_ZERO_EXISTS;
-		} else if (generation_zero == GENERATION_ZERO_EXISTS)
-			graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"),
-				     oid_to_hex(&cur_oid));
+		if (commit_graph_generation_from_graph(graph_commit))
+			seen_gen_non_zero = graph_commit;
+		else
+			seen_gen_zero = graph_commit;
 
-		if (generation_zero == GENERATION_ZERO_EXISTS)
+		if (seen_gen_zero)
 			continue;
 
 		/*
@@ -2627,27 +2795,60 @@
 				     graph_commit->date,
 				     odb_commit->date);
 	}
+
+	if (seen_gen_zero && seen_gen_non_zero)
+		graph_report(_("commit-graph has both zero and non-zero "
+			       "generations (e.g., commits '%s' and '%s')"),
+			     oid_to_hex(&seen_gen_zero->object.oid),
+			     oid_to_hex(&seen_gen_non_zero->object.oid));
+
+	return verify_commit_graph_error;
+}
+
+int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
+{
+	struct progress *progress = NULL;
+	int local_error = 0;
+	uint64_t seen = 0;
+
+	if (!g) {
+		graph_report("no commit-graph file loaded");
+		return 1;
+	}
+
+	if (flags & COMMIT_GRAPH_WRITE_PROGRESS) {
+		uint64_t total = g->num_commits;
+		if (!(flags & COMMIT_GRAPH_VERIFY_SHALLOW))
+			total += g->num_commits_in_base;
+
+		progress = start_progress(_("Verifying commits in commit graph"),
+					  total);
+	}
+
+	for (; g; g = g->base_graph) {
+		local_error |= verify_one_commit_graph(r, g, progress, &seen);
+		if (flags & COMMIT_GRAPH_VERIFY_SHALLOW)
+			break;
+	}
+
 	stop_progress(&progress);
 
-	local_error = verify_commit_graph_error;
-
-	if (!(flags & COMMIT_GRAPH_VERIFY_SHALLOW) && g->base_graph)
-		local_error |= verify_commit_graph(r, g->base_graph, flags);
-
 	return local_error;
 }
 
 void free_commit_graph(struct commit_graph *g)
 {
-	if (!g)
-		return;
-	if (g->data) {
-		munmap((void *)g->data, g->data_len);
-		g->data = NULL;
+	while (g) {
+		struct commit_graph *next = g->base_graph;
+
+		if (g->data)
+			munmap((void *)g->data, g->data_len);
+		free(g->filename);
+		free(g->bloom_filter_settings);
+		free(g);
+
+		g = next;
 	}
-	free(g->filename);
-	free(g->bloom_filter_settings);
-	free(g);
 }
 
 void disable_commit_graph(struct repository *r)
diff --git a/commit-graph.h b/commit-graph.h
index 37faee6..e519cb8 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -1,8 +1,7 @@
 #ifndef COMMIT_GRAPH_H
 #define COMMIT_GRAPH_H
 
-#include "git-compat-util.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "oidset.h"
 
 #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
@@ -10,6 +9,12 @@
 #define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
 
 /*
+ * This environment variable controls whether commits looked up via the
+ * commit graph will be double checked to exist in the object database.
+ */
+#define GIT_COMMIT_GRAPH_PARANOIA "GIT_COMMIT_GRAPH_PARANOIA"
+
+/*
  * This method is only used to enhance coverage of the commit-graph
  * feature in the test suite with the GIT_TEST_COMMIT_GRAPH and
  * GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS environment variables. Do not
@@ -27,6 +32,7 @@
 char *get_commit_graph_filename(struct object_directory *odb);
 char *get_commit_graph_chain_filename(struct object_directory *odb);
 int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
+int open_commit_graph_chain(const char *chain_file, int *fd, struct stat *st);
 
 /*
  * Given a commit struct, try to fill the commit struct info, including:
@@ -94,10 +100,14 @@
 	const unsigned char *chunk_commit_data;
 	const unsigned char *chunk_generation_data;
 	const unsigned char *chunk_generation_data_overflow;
+	size_t chunk_generation_data_overflow_size;
 	const unsigned char *chunk_extra_edges;
+	size_t chunk_extra_edges_size;
 	const unsigned char *chunk_base_graphs;
+	size_t chunk_base_graphs_size;
 	const unsigned char *chunk_bloom_indexes;
 	const unsigned char *chunk_bloom_data;
+	size_t chunk_bloom_data_size;
 
 	struct topo_level_slab *topo_levels;
 	struct bloom_filter_settings *bloom_filter_settings;
@@ -106,6 +116,9 @@
 struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
 						 int fd, struct stat *st,
 						 struct object_directory *odb);
+struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
+						   int fd, struct stat *st,
+						   int *incomplete_chain);
 struct commit_graph *read_commit_graph_one(struct repository *r,
 					   struct object_directory *odb);
 
@@ -190,4 +203,12 @@
  */
 timestamp_t commit_graph_generation(const struct commit *);
 uint32_t commit_graph_position(const struct commit *);
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+			      struct commit **commits, size_t nr);
+
 #endif
diff --git a/commit-reach.c b/commit-reach.c
index 2e33c59..8f9b008 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -1,13 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "commit-graph.h"
 #include "decorate.h"
+#include "hex.h"
 #include "prio-queue.h"
-#include "tree.h"
 #include "ref-filter.h"
 #include "revision.h"
 #include "tag.h"
 #include "commit-reach.h"
+#include "ewah/ewok.h"
 
 /* Remember to update object flag allocation in object.h */
 #define PARENT1		(1u<<16)
@@ -48,13 +49,14 @@
 }
 
 /* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct repository *r,
-						struct commit *one, int n,
-						struct commit **twos,
-						timestamp_t min_generation)
+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,
+				struct commit_list **result)
 {
 	struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
-	struct commit_list *result = NULL;
 	int i;
 	timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
 
@@ -63,8 +65,8 @@
 
 	one->object.flags |= PARENT1;
 	if (!n) {
-		commit_list_append(one, &result);
-		return result;
+		commit_list_append(one, result);
+		return 0;
 	}
 	prio_queue_put(&queue, one);
 
@@ -92,7 +94,7 @@
 		if (flags == (PARENT1 | PARENT2)) {
 			if (!(commit->object.flags & RESULT)) {
 				commit->object.flags |= RESULT;
-				commit_list_insert_by_date(commit, &result);
+				commit_list_insert_by_date(commit, result);
 			}
 			/* Mark parents of a found merge stale */
 			flags |= STALE;
@@ -103,66 +105,97 @@
 			parents = parents->next;
 			if ((p->object.flags & flags) == flags)
 				continue;
-			if (repo_parse_commit(r, p))
-				return NULL;
+			if (repo_parse_commit(r, p)) {
+				clear_prio_queue(&queue);
+				free_commit_list(*result);
+				*result = NULL;
+				/*
+				 * At this stage, we know that the commit is
+				 * missing: `repo_parse_commit()` uses
+				 * `OBJECT_INFO_DIE_IF_CORRUPT` and therefore
+				 * corrupt commits would already have been
+				 * dispatched with a `die()`.
+				 */
+				if (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);
 		}
 	}
 
 	clear_prio_queue(&queue);
-	return result;
+	return 0;
 }
 
-static struct commit_list *merge_bases_many(struct repository *r,
-					    struct commit *one, int n,
-					    struct commit **twos)
+static int merge_bases_many(struct repository *r,
+			    struct commit *one, int n,
+			    struct commit **twos,
+			    struct commit_list **result)
 {
 	struct commit_list *list = NULL;
-	struct commit_list *result = NULL;
 	int i;
 
 	for (i = 0; i < n; i++) {
-		if (one == twos[i])
+		if (one == twos[i]) {
 			/*
 			 * We do not mark this even with RESULT so we do not
 			 * have to clean it up.
 			 */
-			return commit_list_insert(one, &result);
+			*result = commit_list_insert(one, result);
+			return 0;
+		}
 	}
 
+	if (!one)
+		return 0;
 	if (repo_parse_commit(r, one))
-		return NULL;
+		return error(_("could not parse commit %s"),
+			     oid_to_hex(&one->object.oid));
 	for (i = 0; i < n; i++) {
+		if (!twos[i])
+			return 0;
 		if (repo_parse_commit(r, twos[i]))
-			return NULL;
+			return error(_("could not parse commit %s"),
+				     oid_to_hex(&twos[i]->object.oid));
 	}
 
-	list = paint_down_to_common(r, one, n, twos, 0);
+	if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) {
+		free_commit_list(list);
+		return -1;
+	}
 
 	while (list) {
 		struct commit *commit = pop_commit(&list);
 		if (!(commit->object.flags & STALE))
-			commit_list_insert_by_date(commit, &result);
+			commit_list_insert_by_date(commit, result);
 	}
-	return result;
+	return 0;
 }
 
-struct commit_list *get_octopus_merge_bases(struct commit_list *in)
+int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result)
 {
-	struct commit_list *i, *j, *k, *ret = NULL;
+	struct commit_list *i, *j, *k;
 
 	if (!in)
-		return ret;
+		return 0;
 
-	commit_list_insert(in->item, &ret);
+	commit_list_insert(in->item, result);
 
 	for (i = in->next; i; i = i->next) {
 		struct commit_list *new_commits = NULL, *end = NULL;
 
-		for (j = ret; j; j = j->next) {
-			struct commit_list *bases;
-			bases = get_merge_bases(i->item, j->item);
+		for (j = *result; j; j = j->next) {
+			struct commit_list *bases = NULL;
+			if (repo_get_merge_bases(the_repository, i->item,
+						 j->item, &bases) < 0) {
+				free_commit_list(bases);
+				free_commit_list(*result);
+				*result = NULL;
+				return -1;
+			}
 			if (!new_commits)
 				new_commits = bases;
 			else
@@ -170,9 +203,10 @@
 			for (k = bases; k; k = k->next)
 				end = k;
 		}
-		ret = new_commits;
+		free_commit_list(*result);
+		*result = new_commits;
 	}
-	return ret;
+	return 0;
 }
 
 static int remove_redundant_no_gen(struct repository *r,
@@ -190,7 +224,7 @@
 	for (i = 0; i < cnt; i++)
 		repo_parse_commit(r, array[i]);
 	for (i = 0; i < cnt; i++) {
-		struct commit_list *common;
+		struct commit_list *common = NULL;
 		timestamp_t min_generation = commit_graph_generation(array[i]);
 
 		if (redundant[i])
@@ -206,8 +240,16 @@
 			if (curr_generation < min_generation)
 				min_generation = curr_generation;
 		}
-		common = paint_down_to_common(r, array[i], filled,
-					      work, min_generation);
+		if (paint_down_to_common(r, array[i], filled,
+					 work, min_generation, 0, &common)) {
+			clear_commit_marks(array[i], all_flags);
+			clear_commit_marks_many(filled, work, all_flags);
+			free_commit_list(common);
+			free(work);
+			free(redundant);
+			free(filled_index);
+			return -1;
+		}
 		if (array[i]->object.flags & PARENT2)
 			redundant[i] = 1;
 		for (j = 0; j < filled; j++)
@@ -372,69 +414,77 @@
 	return remove_redundant_no_gen(r, array, cnt);
 }
 
-static struct commit_list *get_merge_bases_many_0(struct repository *r,
-						  struct commit *one,
-						  int n,
-						  struct commit **twos,
-						  int cleanup)
+static int get_merge_bases_many_0(struct repository *r,
+				  struct commit *one,
+				  int n,
+				  struct commit **twos,
+				  int cleanup,
+				  struct commit_list **result)
 {
 	struct commit_list *list;
 	struct commit **rslt;
-	struct commit_list *result;
 	int cnt, i;
 
-	result = merge_bases_many(r, one, n, twos);
+	if (merge_bases_many(r, one, n, twos, result) < 0)
+		return -1;
 	for (i = 0; i < n; i++) {
 		if (one == twos[i])
-			return result;
+			return 0;
 	}
-	if (!result || !result->next) {
+	if (!*result || !(*result)->next) {
 		if (cleanup) {
 			clear_commit_marks(one, all_flags);
 			clear_commit_marks_many(n, twos, all_flags);
 		}
-		return result;
+		return 0;
 	}
 
 	/* There are more than one */
-	cnt = commit_list_count(result);
+	cnt = commit_list_count(*result);
 	CALLOC_ARRAY(rslt, cnt);
-	for (list = result, i = 0; list; list = list->next)
+	for (list = *result, i = 0; list; list = list->next)
 		rslt[i++] = list->item;
-	free_commit_list(result);
+	free_commit_list(*result);
+	*result = NULL;
 
 	clear_commit_marks(one, all_flags);
 	clear_commit_marks_many(n, twos, all_flags);
 
 	cnt = remove_redundant(r, rslt, cnt);
-	result = NULL;
+	if (cnt < 0) {
+		free(rslt);
+		return -1;
+	}
 	for (i = 0; i < cnt; i++)
-		commit_list_insert_by_date(rslt[i], &result);
+		commit_list_insert_by_date(rslt[i], result);
 	free(rslt);
-	return result;
+	return 0;
 }
 
-struct commit_list *repo_get_merge_bases_many(struct repository *r,
-					      struct commit *one,
-					      int n,
-					      struct commit **twos)
+int repo_get_merge_bases_many(struct repository *r,
+			      struct commit *one,
+			      int n,
+			      struct commit **twos,
+			      struct commit_list **result)
 {
-	return get_merge_bases_many_0(r, one, n, twos, 1);
+	return get_merge_bases_many_0(r, one, n, twos, 1, result);
 }
 
-struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
-						    struct commit *one,
-						    int n,
-						    struct commit **twos)
+int repo_get_merge_bases_many_dirty(struct repository *r,
+				    struct commit *one,
+				    int n,
+				    struct commit **twos,
+				    struct commit_list **result)
 {
-	return get_merge_bases_many_0(r, one, n, twos, 0);
+	return get_merge_bases_many_0(r, one, n, twos, 0, result);
 }
 
-struct commit_list *repo_get_merge_bases(struct repository *r,
-					 struct commit *one,
-					 struct commit *two)
+int repo_get_merge_bases(struct repository *r,
+			 struct commit *one,
+			 struct commit *two,
+			 struct commit_list **result)
 {
-	return get_merge_bases_many_0(r, one, 1, &two, 1);
+	return get_merge_bases_many_0(r, one, 1, &two, 1, result);
 }
 
 /*
@@ -447,7 +497,7 @@
 	if (!with_commit)
 		return 1;
 
-	if (generation_numbers_enabled(the_repository)) {
+	if (generation_numbers_enabled(r)) {
 		struct commit_list *from_list = NULL;
 		int result;
 		commit_list_insert(commit, &from_list);
@@ -457,11 +507,13 @@
 	} else {
 		while (with_commit) {
 			struct commit *other;
+			int ret;
 
 			other = with_commit->item;
 			with_commit = with_commit->next;
-			if (repo_in_merge_bases_many(r, other, 1, &commit))
-				return 1;
+			ret = repo_in_merge_bases_many(r, other, 1, &commit, 0);
+			if (ret)
+				return ret;
 		}
 		return 0;
 	}
@@ -471,17 +523,18 @@
  * Is "commit" an ancestor of one of the "references"?
  */
 int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
-			     int nr_reference, struct commit **reference)
+			     int nr_reference, struct commit **reference,
+			     int ignore_missing_commits)
 {
-	struct commit_list *bases;
+	struct commit_list *bases = NULL;
 	int ret = 0, i;
 	timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
 
 	if (repo_parse_commit(r, commit))
-		return ret;
+		return ignore_missing_commits ? 0 : -1;
 	for (i = 0; i < nr_reference; i++) {
 		if (repo_parse_commit(r, reference[i]))
-			return ret;
+			return ignore_missing_commits ? 0 : -1;
 
 		generation = commit_graph_generation(reference[i]);
 		if (generation > max_generation)
@@ -492,10 +545,11 @@
 	if (generation > max_generation)
 		return ret;
 
-	bases = paint_down_to_common(r, commit,
-				     nr_reference, reference,
-				     generation);
-	if (commit->object.flags & PARENT2)
+	if (paint_down_to_common(r, commit,
+				 nr_reference, reference,
+				 generation, ignore_missing_commits, &bases))
+		ret = -1;
+	else if (commit->object.flags & PARENT2)
 		ret = 1;
 	clear_commit_marks(commit, all_flags);
 	clear_commit_marks_many(nr_reference, reference, all_flags);
@@ -548,6 +602,10 @@
 		}
 	}
 	num_head = remove_redundant(the_repository, array, num_head);
+	if (num_head < 0) {
+		free(array);
+		return NULL;
+	}
 	for (i = 0; i < num_head; i++)
 		tail = &commit_list_insert(array[i], tail)->next;
 	free(array);
@@ -584,12 +642,14 @@
 		return 0;
 	new_commit = (struct commit *) o;
 
-	if (parse_commit(new_commit) < 0)
+	if (repo_parse_commit(the_repository, new_commit) < 0)
 		return 0;
 
 	commit_list_insert(old_commit, &old_commit_list);
 	ret = repo_is_descendant_of(the_repository,
 				    new_commit, old_commit_list);
+	if (ret < 0)
+		exit(128);
 	free_commit_list(old_commit_list);
 	return ret;
 }
@@ -748,7 +808,7 @@
 		}
 
 		list[nr_commits] = (struct commit *)from_one;
-		if (parse_commit(list[nr_commits]) ||
+		if (repo_parse_commit(the_repository, list[nr_commits]) ||
 		    commit_graph_generation(list[nr_commits]) < min_generation) {
 			result = 0;
 			goto cleanup;
@@ -783,7 +843,7 @@
 				if (!(parent->item->object.flags & assign_flag)) {
 					parent->item->object.flags |= assign_flag;
 
-					if (parse_commit(parent->item) ||
+					if (repo_parse_commit(the_repository, parent->item) ||
 					    parent->item->date < min_commit_date ||
 					    commit_graph_generation(parent->item) < min_generation)
 						continue;
@@ -807,8 +867,12 @@
 	clear_commit_marks_many(nr_commits, list, RESULT | assign_flag);
 	free(list);
 
-	for (i = 0; i < from->nr; i++)
-		from->objects[i].item->flags &= ~assign_flag;
+	for (i = 0; i < from->nr; i++) {
+		struct object *from_one = from->objects[i].item;
+
+		if (from_one)
+			from_one->flags &= ~assign_flag;
+	}
 
 	return result;
 }
@@ -825,7 +889,7 @@
 	while (from_iter) {
 		add_object_array(&from_iter->item->object, NULL, &from_objs);
 
-		if (!parse_commit(from_iter->item)) {
+		if (!repo_parse_commit(the_repository, from_iter->item)) {
 			timestamp_t generation;
 			if (from_iter->item->date < min_commit_date)
 				min_commit_date = from_iter->item->date;
@@ -839,7 +903,7 @@
 	}
 
 	while (to_iter) {
-		if (!parse_commit(to_iter->item)) {
+		if (!repo_parse_commit(the_repository, to_iter->item)) {
 			timestamp_t generation;
 			if (to_iter->item->date < min_commit_date)
 				min_commit_date = to_iter->item->date;
@@ -889,7 +953,7 @@
 		timestamp_t generation;
 		struct commit *c = *item;
 
-		parse_commit(c);
+		repo_parse_commit(the_repository, c);
 		generation = commit_graph_generation(c);
 		if (generation < min_generation)
 			min_generation = generation;
@@ -904,7 +968,7 @@
 		struct commit *c = *item;
 		if (!(c->object.flags & PARENT2)) {
 			c->object.flags |= PARENT2;
-			parse_commit(c);
+			repo_parse_commit(the_repository, c);
 
 			prio_queue_put(&queue, *item);
 		}
@@ -923,7 +987,7 @@
 		for (parents = current->parents; parents; parents = parents->next) {
 			struct commit *p = parents->item;
 
-			parse_commit(p);
+			repo_parse_commit(the_repository, p);
 
 			if (commit_graph_generation(p) < min_generation)
 				continue;
@@ -936,8 +1000,225 @@
 		}
 	}
 
+	clear_prio_queue(&queue);
+
 	clear_commit_marks_many(nr_to, to, PARENT1);
 	clear_commit_marks_many(nr_from, from, PARENT2);
 
 	return found_commits;
 }
+
+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)
+{
+	if (c->object.flags & PARENT2)
+		return;
+	prio_queue_put(queue, c);
+	c->object.flags |= PARENT2;
+}
+
+static struct bitmap *get_bit_array(struct commit *c, int width)
+{
+	struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+	if (!*bitmap)
+		*bitmap = bitmap_word_alloc(width);
+	return *bitmap;
+}
+
+static void free_bit_array(struct commit *c)
+{
+	struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+	if (!*bitmap)
+		return;
+	bitmap_free(*bitmap);
+	*bitmap = NULL;
+}
+
+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 };
+	size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD);
+
+	if (!commits_nr || !counts_nr)
+		return;
+
+	for (size_t i = 0; i < counts_nr; i++) {
+		counts[i].ahead = 0;
+		counts[i].behind = 0;
+	}
+
+	ensure_generations_valid(r, commits, commits_nr);
+
+	init_bit_arrays(&bit_arrays);
+
+	for (size_t i = 0; i < commits_nr; i++) {
+		struct commit *c = commits[i];
+		struct bitmap *bitmap = get_bit_array(c, width);
+
+		bitmap_set(bitmap, i);
+		insert_no_dup(&queue, c);
+	}
+
+	while (queue_has_nonstale(&queue)) {
+		struct commit *c = prio_queue_get(&queue);
+		struct commit_list *p;
+		struct bitmap *bitmap_c = get_bit_array(c, width);
+
+		for (size_t i = 0; i < counts_nr; i++) {
+			int reach_from_tip = !!bitmap_get(bitmap_c, counts[i].tip_index);
+			int reach_from_base = !!bitmap_get(bitmap_c, counts[i].base_index);
+
+			if (reach_from_tip ^ reach_from_base) {
+				if (reach_from_base)
+					counts[i].behind++;
+				else
+					counts[i].ahead++;
+			}
+		}
+
+		for (p = c->parents; p; p = p->next) {
+			struct bitmap *bitmap_p;
+
+			repo_parse_commit(r, p->item);
+
+			bitmap_p = get_bit_array(p->item, width);
+			bitmap_or(bitmap_p, bitmap_c);
+
+			/*
+			 * If this parent is reachable from every starting
+			 * commit, then none of its ancestors can contribute
+			 * to the ahead/behind count. Mark it as STALE, so
+			 * we can stop the walk when every commit in the
+			 * queue is STALE.
+			 */
+			if (bitmap_popcount(bitmap_p) == commits_nr)
+				p->item->object.flags |= STALE;
+
+			insert_no_dup(&queue, p->item);
+		}
+
+		free_bit_array(c);
+	}
+
+	/* STALE is used here, PARENT2 is used by insert_no_dup(). */
+	repo_clear_commit_marks(r, PARENT2 | STALE);
+	clear_bit_arrays(&bit_arrays);
+	clear_prio_queue(&queue);
+}
+
+struct commit_and_index {
+	struct commit *commit;
+	unsigned int index;
+	timestamp_t generation;
+};
+
+static int compare_commit_and_index_by_generation(const void *va, const void *vb)
+{
+	const struct commit_and_index *a = (const struct commit_and_index *)va;
+	const struct commit_and_index *b = (const struct commit_and_index *)vb;
+
+	if (a->generation > b->generation)
+		return 1;
+	if (a->generation < b->generation)
+		return -1;
+	return 0;
+}
+
+void tips_reachable_from_bases(struct repository *r,
+			       struct commit_list *bases,
+			       struct commit **tips, size_t tips_nr,
+			       int mark)
+{
+	struct commit_and_index *commits;
+	size_t min_generation_index = 0;
+	timestamp_t min_generation;
+	struct commit_list *stack = NULL;
+
+	if (!bases || !tips || !tips_nr)
+		return;
+
+	/*
+	 * Do a depth-first search starting at 'bases' to search for the
+	 * tips. Stop at the lowest (un-found) generation number. When
+	 * finding the lowest commit, increase the minimum generation
+	 * number to the next lowest (un-found) generation number.
+	 */
+
+	CALLOC_ARRAY(commits, tips_nr);
+
+	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]);
+	}
+
+	/* Sort with generation number ascending. */
+	QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
+	min_generation = commits[0].generation;
+
+	while (bases) {
+		repo_parse_commit(r, bases->item);
+		commit_list_insert(bases->item, &stack);
+		bases = bases->next;
+	}
+
+	while (stack) {
+		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 (commits[j].commit == c) {
+				tips[commits[j].index]->object.flags |= mark;
+
+				if (j == min_generation_index) {
+					unsigned int k = j + 1;
+					while (k < tips_nr &&
+					       (tips[commits[k].index]->object.flags & mark))
+						k++;
+
+					/* Terminate early if all found. */
+					if (k >= tips_nr)
+						goto done;
+
+					min_generation_index = k;
+					min_generation = commits[k].generation;
+				}
+			}
+		}
+
+		for (p = c->parents; p; p = p->next) {
+			repo_parse_commit(r, p->item);
+
+			/* Have we already explored this parent? */
+			if (p->item->object.flags & SEEN)
+				continue;
+
+			/* Is it below the current minimum generation? */
+			if (commit_graph_generation(p->item) < min_generation)
+				continue;
+
+			/* Ok, we will explore from here on. */
+			p->item->object.flags |= SEEN;
+			explored_all_parents = 0;
+			commit_list_insert(p->item, &stack);
+			break;
+		}
+
+		if (explored_all_parents)
+			pop_commit(&stack);
+	}
+
+done:
+	free(commits);
+	repo_clear_commit_marks(r, SEEN);
+}
diff --git a/commit-reach.h b/commit-reach.h
index 148b56f..bf63cc4 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -9,23 +9,21 @@
 struct object_id;
 struct object_array;
 
-struct commit_list *repo_get_merge_bases(struct repository *r,
-					 struct commit *rev1,
-					 struct commit *rev2);
-struct commit_list *repo_get_merge_bases_many(struct repository *r,
-					      struct commit *one, int n,
-					      struct commit **twos);
+int repo_get_merge_bases(struct repository *r,
+			 struct commit *rev1,
+			 struct commit *rev2,
+			 struct commit_list **result);
+int repo_get_merge_bases_many(struct repository *r,
+			      struct commit *one, int n,
+			      struct commit **twos,
+			      struct commit_list **result);
 /* To be used only when object flags after this call no longer matter */
-struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
-						    struct commit *one, int n,
-						    struct commit **twos);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define get_merge_bases(r1, r2)           repo_get_merge_bases(the_repository, r1, r2)
-#define get_merge_bases_many(one, n, two) repo_get_merge_bases_many(the_repository, one, n, two)
-#define get_merge_bases_many_dirty(one, n, twos) repo_get_merge_bases_many_dirty(the_repository, one, n, twos)
-#endif
+int repo_get_merge_bases_many_dirty(struct repository *r,
+				    struct commit *one, int n,
+				    struct commit **twos,
+				    struct commit_list **result);
 
-struct commit_list *get_octopus_merge_bases(struct commit_list *in);
+int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result);
 
 int repo_is_descendant_of(struct repository *r,
 			  struct commit *commit,
@@ -35,11 +33,8 @@
 			struct commit *reference);
 int repo_in_merge_bases_many(struct repository *r,
 			     struct commit *commit,
-			     int nr_reference, struct commit **reference);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define in_merge_bases(c1, c2) repo_in_merge_bases(the_repository, c1, c2)
-#define in_merge_bases_many(c1, n, cs) repo_in_merge_bases_many(the_repository, c1, n, cs)
-#endif
+			     int nr_reference, struct commit **reference,
+			     int ignore_missing_commits);
 
 /*
  * Takes a list of commits and returns a new list where those
@@ -104,4 +99,44 @@
 					 struct commit **to, int nr_to,
 					 unsigned int reachable_flag);
 
+struct ahead_behind_count {
+	/**
+	 * As input, the *_index members indicate which positions in
+	 * the 'tips' array correspond to the tip and base of this
+	 * comparison.
+	 */
+	size_t tip_index;
+	size_t base_index;
+
+	/**
+	 * These values store the computed counts for each side of the
+	 * symmetric difference:
+	 *
+	 * 'ahead' stores the number of commits reachable from the tip
+	 * and not reachable from the base.
+	 *
+	 * 'behind' stores the number of commits reachable from the base
+	 * and not reachable from the tip.
+	 */
+	unsigned int ahead;
+	unsigned int behind;
+};
+
+/*
+ * Given an array of commits and an array of ahead_behind_count pairs,
+ * compute the ahead/behind counts for each pair.
+ */
+void ahead_behind(struct repository *r,
+		  struct commit **commits, size_t commits_nr,
+		  struct ahead_behind_count *counts, size_t counts_nr);
+
+/*
+ * For all tip commits, add 'mark' to their flags if and only if they
+ * are reachable from one of the commits in 'bases'.
+ */
+void tips_reachable_from_bases(struct repository *r,
+			       struct commit_list *bases,
+			       struct commit **tips, size_t tips_nr,
+			       int mark);
+
 #endif
diff --git a/commit-slab-impl.h b/commit-slab-impl.h
index 557738d..4a414ee 100644
--- a/commit-slab-impl.h
+++ b/commit-slab-impl.h
@@ -1,8 +1,6 @@
 #ifndef COMMIT_SLAB_IMPL_H
 #define COMMIT_SLAB_IMPL_H
 
-#include "git-compat-util.h"
-
 #define implement_static_commit_slab(slabname, elemtype) \
 	implement_commit_slab(slabname, elemtype, MAYBE_UNUSED static)
 
diff --git a/commit.c b/commit.c
index e433c33..1a479a9 100644
--- a/commit.c
+++ b/commit.c
@@ -1,10 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tag.h"
 #include "commit.h"
 #include "commit-graph.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
-#include "object-store.h"
-#include "pkt-line.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
@@ -19,9 +22,12 @@
 #include "advice.h"
 #include "refs.h"
 #include "commit-reach.h"
-#include "run-command.h"
+#include "setup.h"
 #include "shallow.h"
+#include "tree.h"
 #include "hook.h"
+#include "parse.h"
+#include "object-file-convert.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -80,10 +86,10 @@
 	struct object_id oid;
 	struct commit *commit;
 
-	if (get_oid_committish(name, &oid))
+	if (repo_get_oid_committish(the_repository, name, &oid))
 		return NULL;
 	commit = lookup_commit_reference(the_repository, &oid);
-	if (parse_commit(commit))
+	if (repo_parse_commit(the_repository, commit))
 		return NULL;
 	return commit;
 }
@@ -91,6 +97,7 @@
 static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
+	const char *eol;
 
 	if (buf + 6 >= tail)
 		return 0;
@@ -102,16 +109,56 @@
 		return 0;
 	if (memcmp(buf, "committer", 9))
 		return 0;
-	while (buf < tail && *buf++ != '>')
-		/* nada */;
-	if (buf >= tail)
+
+	/*
+	 * Jump to end-of-line so that we can walk backwards to find the
+	 * end-of-email ">". This is more forgiving of malformed cases
+	 * because unexpected characters tend to be in the name and email
+	 * fields.
+	 */
+	eol = memchr(buf, '\n', tail - buf);
+	if (!eol)
 		return 0;
-	dateptr = buf;
-	while (buf < tail && *buf++ != '\n')
-		/* nada */;
-	if (buf >= tail)
+	dateptr = eol;
+	while (dateptr > buf && dateptr[-1] != '>')
+		dateptr--;
+	if (dateptr == buf)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+
+	/*
+	 * Trim leading whitespace, but make sure we have at least one
+	 * non-whitespace character, as parse_timestamp() will otherwise walk
+	 * right past the newline we found in "eol" when skipping whitespace
+	 * itself.
+	 *
+	 * In theory it would be sufficient to allow any character not matched
+	 * by isspace(), but there's a catch: our isspace() does not
+	 * necessarily match the behavior of parse_timestamp(), as the latter
+	 * is implemented by system routines which match more exotic control
+	 * codes, or even locale-dependent sequences.
+	 *
+	 * Since we expect the timestamp to be a number, we can check for that.
+	 * Anything else (e.g., a non-numeric token like "foo") would just
+	 * cause parse_timestamp() to return 0 anyway.
+	 */
+	while (dateptr < eol && isspace(*dateptr))
+		dateptr++;
+	if (!isdigit(*dateptr) && *dateptr != '-')
+		return 0;
+
+	/*
+	 * We know there is at least one digit (or dash), so we'll begin
+	 * parsing there and stop at worst case at eol.
+	 *
+	 * Note that we may feed parse_timestamp() extra characters here if the
+	 * commit is malformed, and it will parse as far as it can. For
+	 * example, "123foo456" would return "123". That might be questionable
+	 * (versus returning "0"), but it would help in a hypothetical case
+	 * like "123456+0100", where the whitespace from the timezone is
+	 * missing. Since such syntactic errors may be baked into history and
+	 * hard to correct now, let's err on trying to make our best guess
+	 * here, rather than insist on perfect syntax.
+	 */
 	return parse_timestamp(dateptr, NULL, 10);
 }
 
@@ -382,7 +429,7 @@
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
 {
-	struct tree *tree = get_commit_tree(commit);
+	struct tree *tree = repo_get_commit_tree(the_repository, commit);
 	return tree ? &tree->object.oid : NULL;
 }
 
@@ -469,7 +516,7 @@
 		 * The clone is shallow if nr_parent < 0, and we must
 		 * not traverse its real parents even when we unhide them.
 		 */
-		if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
+		if (graft && (graft->nr_parent < 0 || !grafts_keep_true_parents))
 			continue;
 		new_parent = lookup_commit(r, &parent);
 		if (!new_parent)
@@ -525,8 +572,21 @@
 		return -1;
 	if (item->object.parsed)
 		return 0;
-	if (use_commit_graph && parse_commit_in_graph(r, item))
+	if (use_commit_graph && parse_commit_in_graph(r, item)) {
+		static int commit_graph_paranoia = -1;
+
+		if (commit_graph_paranoia == -1)
+			commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
+
+		if (commit_graph_paranoia && !has_object(r, &item->object.oid, 0)) {
+			unparse_commit(r, &item->object.oid);
+			return quiet_on_missing ? -1 :
+				error(_("commit %s exists in commit-graph but not in the object database"),
+				      oid_to_hex(&item->object.oid));
+		}
+
 		return 0;
+	}
 
 	if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
 		return quiet_on_missing ? -1 :
@@ -555,7 +615,7 @@
 
 void parse_commit_or_die(struct commit *item)
 {
-	if (parse_commit(item))
+	if (repo_parse_commit(the_repository, item))
 		die("unable to parse commit %s",
 		    item ? oid_to_hex(&item->object.oid) : "(null)");
 }
@@ -688,7 +748,7 @@
 
 	while (parents) {
 		struct commit *commit = parents->item;
-		if (!parse_commit(commit) && !(commit->object.flags & mark)) {
+		if (!repo_parse_commit(the_repository, commit) && !(commit->object.flags & mark)) {
 			commit->object.flags |= mark;
 			commit_list_insert_by_date(commit, list);
 		}
@@ -762,7 +822,8 @@
 void record_author_date(struct author_date_slab *author_date,
 			struct commit *commit)
 {
-	const char *buffer = get_commit_buffer(commit, NULL);
+	const char *buffer = repo_get_commit_buffer(the_repository, commit,
+						    NULL);
 	struct ident_split ident;
 	const char *ident_line;
 	size_t ident_len;
@@ -782,7 +843,7 @@
 	*(author_date_slab_at(author_date, commit)) = date;
 
 fail_exit:
-	unuse_commit_buffer(commit, buffer);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 int compare_commits_by_author_date(const void *a_, const void *b_,
@@ -801,7 +862,8 @@
 	return 0;
 }
 
-int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_,
+					    void *unused UNUSED)
 {
 	const struct commit *a = a_, *b = b_;
 	const timestamp_t generation_a = commit_graph_generation(a),
@@ -821,7 +883,8 @@
 	return 0;
 }
 
-int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_commit_date(const void *a_, const void *b_,
+				   void *unused UNUSED)
 {
 	const struct commit *a = a_, *b = b_;
 	/* newer commits with larger date first */
@@ -963,7 +1026,7 @@
 	commit = lookup_commit(the_repository, oid);
 	if (!commit ||
 	    (commit->object.flags & TMP_MARK) ||
-	    parse_commit(commit))
+	    repo_parse_commit(the_repository, commit))
 		return;
 
 	ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
@@ -990,12 +1053,13 @@
 {
 	struct object_id oid;
 	struct rev_collect revs;
-	struct commit_list *bases;
+	struct commit_list *bases = NULL;
 	int i;
 	struct commit *ret = NULL;
 	char *full_refname;
 
-	switch (dwim_ref(refname, strlen(refname), &oid, &full_refname, 0)) {
+	switch (repo_dwim_ref(the_repository, refname, strlen(refname), &oid,
+			      &full_refname, 0)) {
 	case 0:
 		die("No such ref: '%s'", refname);
 	case 1:
@@ -1014,7 +1078,9 @@
 	for (i = 0; i < revs.nr; i++)
 		revs.commit[i]->object.flags &= ~TMP_MARK;
 
-	bases = get_merge_bases_many(commit, revs.nr, revs.commit);
+	if (repo_get_merge_bases_many(the_repository, commit, revs.nr,
+				      revs.commit, &bases) < 0)
+		exit(128);
 
 	/*
 	 * There should be one and only one merge base, when we found
@@ -1048,12 +1114,11 @@
 	"gpgsig-sha256",
 };
 
-int sign_with_header(struct strbuf *buf, const char *keyid)
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
 {
-	struct strbuf sig = STRBUF_INIT;
 	int inspos, copypos;
 	const char *eoh;
-	const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+	const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
 	int gpg_sig_header_len = strlen(gpg_sig_header);
 
 	/* find the end of the header */
@@ -1063,15 +1128,8 @@
 	else
 		inspos = eoh - buf->buf + 1;
 
-	if (!keyid || !*keyid)
-		keyid = get_signing_key();
-	if (sign_buffer(buf, &sig, keyid)) {
-		strbuf_release(&sig);
-		return -1;
-	}
-
-	for (copypos = 0; sig.buf[copypos]; ) {
-		const char *bol = sig.buf + copypos;
+	for (copypos = 0; sig->buf[copypos]; ) {
+		const char *bol = sig->buf + copypos;
 		const char *eol = strchrnul(bol, '\n');
 		int len = (eol - bol) + !!*eol;
 
@@ -1084,21 +1142,28 @@
 		inspos += len;
 		copypos += len;
 	}
-	strbuf_release(&sig);
 	return 0;
 }
 
-
+static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
+{
+	if (!keyid || !*keyid)
+		keyid = get_signing_key();
+	if (sign_buffer(buf, sig, keyid))
+		return -1;
+	return 0;
+}
 
 int parse_signed_commit(const struct commit *commit,
 			struct strbuf *payload, struct strbuf *signature,
 			const struct git_hash_algo *algop)
 {
 	unsigned long size;
-	const char *buffer = get_commit_buffer(commit, &size);
+	const char *buffer = repo_get_commit_buffer(the_repository, commit,
+						    &size);
 	int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop);
 
-	unuse_commit_buffer(commit, buffer);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 	return ret;
 }
 
@@ -1209,7 +1274,8 @@
 	desc = merge_remote_util(parent);
 	if (!desc || !desc->obj)
 		return;
-	buf = read_object_file(&desc->obj->oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &desc->obj->oid, &type,
+				    &size);
 	if (!buf || type != OBJ_TAG)
 		goto free_return;
 	if (!parse_signature(buf, size, &payload, &signature))
@@ -1271,7 +1337,8 @@
 
 	ret = check_commit_signature(commit, &signature_check);
 
-	find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
+	repo_find_unique_abbrev_r(the_repository, hex, &commit->object.oid,
+				  DEFAULT_ABBREV);
 	switch (signature_check.result) {
 	case 'G':
 		if (ret || (check_trust && signature_check.trust_level < TRUST_MARGINAL))
@@ -1301,6 +1368,39 @@
 	}
 }
 
+static int convert_commit_extra_headers(struct commit_extra_header *orig,
+					struct commit_extra_header **result)
+{
+	const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+	const struct git_hash_algo *algo = the_repository->hash_algo;
+	struct commit_extra_header *extra = NULL, **tail = &extra;
+	struct strbuf out = STRBUF_INIT;
+	while (orig) {
+		struct commit_extra_header *new;
+		CALLOC_ARRAY(new, 1);
+		if (!strcmp(orig->key, "mergetag")) {
+			if (convert_object_file(&out, algo, compat,
+						orig->value, orig->len,
+						OBJ_TAG, 1)) {
+				free(new);
+				free_commit_extra_headers(extra);
+				return -1;
+			}
+			new->key = xstrdup("mergetag");
+			new->value = strbuf_detach(&out, &new->len);
+		} else {
+			new->key = xstrdup(orig->key);
+			new->len = orig->len;
+			new->value = xmemdupz(orig->value, orig->len);
+		}
+		*tail = new;
+		tail = &new->next;
+		orig = orig->next;
+	}
+	*result = extra;
+	return 0;
+}
+
 static void add_extra_header(struct strbuf *buffer,
 			     struct commit_extra_header *extra)
 {
@@ -1316,9 +1416,10 @@
 {
 	struct commit_extra_header *extra = NULL;
 	unsigned long size;
-	const char *buffer = get_commit_buffer(commit, &size);
+	const char *buffer = repo_get_commit_buffer(the_repository, commit,
+						    &size);
 	extra = read_commit_extra_header_lines(buffer, size, exclude);
-	unuse_commit_buffer(commit, buffer);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 	return extra;
 }
 
@@ -1543,6 +1644,49 @@
    "You may want to amend it after fixing the message, or set the config\n"
    "variable i18n.commitEncoding to the encoding your project uses.\n");
 
+static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
+			      const struct object_id *tree,
+			      const struct object_id *parents, size_t parents_len,
+			      const char *author, const char *committer,
+			      struct commit_extra_header *extra)
+{
+	int encoding_is_utf8;
+	size_t i;
+
+	/* Not having i18n.commitencoding is the same as having utf-8 */
+	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+	strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
+	strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
+
+	/*
+	 * NOTE! This ordering means that the same exact tree merged with a
+	 * different order of parents will be a _different_ changeset even
+	 * if everything else stays the same.
+	 */
+	for (i = 0; i < parents_len; i++)
+		strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
+
+	/* Person/date information */
+	if (!author)
+		author = git_author_info(IDENT_STRICT);
+	strbuf_addf(buffer, "author %s\n", author);
+	if (!committer)
+		committer = git_committer_info(IDENT_STRICT);
+	strbuf_addf(buffer, "committer %s\n", committer);
+	if (!encoding_is_utf8)
+		strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
+
+	while (extra) {
+		add_extra_header(buffer, extra);
+		extra = extra->next;
+	}
+	strbuf_addch(buffer, '\n');
+
+	/* And add the comment */
+	strbuf_add(buffer, msg, msg_len);
+}
+
 int commit_tree_extended(const char *msg, size_t msg_len,
 			 const struct object_id *tree,
 			 struct commit_list *parents, struct object_id *ret,
@@ -1550,63 +1694,119 @@
 			 const char *sign_commit,
 			 struct commit_extra_header *extra)
 {
-	int result;
+	struct repository *r = the_repository;
+	int result = 0;
 	int encoding_is_utf8;
-	struct strbuf buffer;
+	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;
+	struct object_id compat_oid_buf;
+	size_t i, nparents;
+
+	/* Not having i18n.commitencoding is the same as having utf-8 */
+	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
 
 	assert_oid_type(tree, OBJ_TREE);
 
 	if (memchr(msg, '\0', msg_len))
 		return error("a NUL byte in commit log message not allowed.");
 
-	/* Not having i18n.commitencoding is the same as having utf-8 */
-	encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
-
-	strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
-	strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
-
-	/*
-	 * NOTE! This ordering means that the same exact tree merged with a
-	 * different order of parents will be a _different_ changeset even
-	 * if everything else stays the same.
-	 */
+	nparents = commit_list_count(parents);
+	CALLOC_ARRAY(parent_buf, nparents);
+	i = 0;
 	while (parents) {
 		struct commit *parent = pop_commit(&parents);
-		strbuf_addf(&buffer, "parent %s\n",
-			    oid_to_hex(&parent->object.oid));
+		oidcpy(&parent_buf[i++], &parent->object.oid);
 	}
 
-	/* Person/date information */
-	if (!author)
-		author = git_author_info(IDENT_STRICT);
-	strbuf_addf(&buffer, "author %s\n", author);
-	if (!committer)
-		committer = git_committer_info(IDENT_STRICT);
-	strbuf_addf(&buffer, "committer %s\n", committer);
-	if (!encoding_is_utf8)
-		strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
-
-	while (extra) {
-		add_extra_header(&buffer, extra);
-		extra = extra->next;
-	}
-	strbuf_addch(&buffer, '\n');
-
-	/* And add the comment */
-	strbuf_add(&buffer, msg, msg_len);
-
-	/* And check the encoding */
-	if (encoding_is_utf8 && !verify_utf8(&buffer))
-		fprintf(stderr, _(commit_utf8_warn));
-
-	if (sign_commit && sign_with_header(&buffer, sign_commit)) {
+	write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
+	if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
 		result = -1;
 		goto out;
 	}
+	if (r->compat_hash_algo) {
+		struct commit_extra_header *compat_extra = NULL;
+		struct object_id mapped_tree;
+		struct object_id *mapped_parents;
 
-	result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
+		CALLOC_ARRAY(mapped_parents, nparents);
+
+		if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
+			result = -1;
+			free(mapped_parents);
+			goto out;
+		}
+		for (i = 0; i < nparents; i++)
+			if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
+				result = -1;
+				free(mapped_parents);
+				goto out;
+			}
+		if (convert_commit_extra_headers(extra, &compat_extra)) {
+			result = -1;
+			free(mapped_parents);
+			goto out;
+		}
+		write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
+				  mapped_parents, nparents, author, committer, compat_extra);
+		free_commit_extra_headers(compat_extra);
+		free(mapped_parents);
+
+		if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
+			result = -1;
+			goto out;
+		}
+	}
+
+	if (sign_commit) {
+		struct sig_pairs {
+			struct strbuf *sig;
+			const struct git_hash_algo *algo;
+		} bufs [2] = {
+			{ &compat_sig, r->compat_hash_algo },
+			{ &sig, r->hash_algo },
+		};
+		int i;
+
+		/*
+		 * We write algorithms in the order they were implemented in
+		 * Git to produce a stable hash when multiple algorithms are
+		 * used.
+		 */
+		if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
+			SWAP(bufs[0], bufs[1]);
+
+		/*
+		 * We traverse each algorithm in order, and apply the signature
+		 * to each buffer.
+		 */
+		for (i = 0; i < ARRAY_SIZE(bufs); i++) {
+			if (!bufs[i].algo)
+				continue;
+			add_header_signature(&buffer, bufs[i].sig, bufs[i].algo);
+			if (r->compat_hash_algo)
+				add_header_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
+		}
+	}
+
+	/* 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);
+		compat_oid = &compat_oid_buf;
+	}
+
+	result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
+					 ret, compat_oid, 0);
 out:
+	free(parent_buf);
 	strbuf_release(&buffer);
+	strbuf_release(&compat_buffer);
+	strbuf_release(&sig);
+	strbuf_release(&compat_sig);
 	return result;
 }
 
@@ -1632,10 +1832,11 @@
 	struct object *obj;
 	struct commit *commit;
 	struct object_id oid;
-	if (get_oid(name, &oid))
+	if (repo_get_oid(the_repository, name, &oid))
 		return NULL;
 	obj = parse_object(the_repository, &oid);
-	commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+	commit = (struct commit *)repo_peel_to_type(the_repository, name, 0,
+						    obj, OBJ_COMMIT);
 	if (commit && !merge_remote_util(commit))
 		set_merge_remote_desc(commit, name, obj);
 	return commit;
@@ -1712,7 +1913,7 @@
  * Returns the number of bytes from the tail to ignore, to be fed as
  * the second parameter to append_signoff().
  */
-size_t ignore_non_trailer(const char *buf, size_t len)
+size_t ignored_log_message_bytes(const char *buf, size_t len)
 {
 	size_t boc = 0;
 	size_t bol = 0;
@@ -1727,7 +1928,8 @@
 		else
 			next_line++;
 
-		if (buf[bol] == comment_line_char || buf[bol] == '\n') {
+		if (starts_with_mem(buf + bol, cutoff - bol, comment_line_str) ||
+		    buf[bol] == '\n') {
 			/* is this the first of the run of comments? */
 			if (!boc)
 				boc = bol;
diff --git a/commit.h b/commit.h
index cc2c5da..62fe0d7 100644
--- a/commit.h
+++ b/commit.h
@@ -2,13 +2,10 @@
 #define COMMIT_H
 
 #include "object.h"
-#include "tree.h"
-#include "strbuf.h"
-#include "decorate.h"
-#include "gpg-interface.h"
-#include "string-list.h"
-#include "pretty.h"
-#include "commit-slab.h"
+
+struct signature_check;
+struct strbuf;
+struct tree;
 
 #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
 #define GENERATION_NUMBER_INFINITY ((1ULL << 63) - 1)
@@ -109,11 +106,6 @@
 	return repo_parse_commit_internal(r, commit, 0, 0);
 }
 
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use)
-#define parse_commit(item) repo_parse_commit(the_repository, item)
-#endif
-
 void parse_commit_or_die(struct commit *item);
 
 struct buffer_slab;
@@ -135,27 +127,21 @@
 /*
  * Get the commit's object contents, either from cache or by reading the object
  * from disk. The resulting memory should not be modified, and must be given
- * to unuse_commit_buffer when the caller is done.
+ * to repo_unuse_commit_buffer when the caller is done.
  */
 const void *repo_get_commit_buffer(struct repository *r,
 				   const struct commit *,
 				   unsigned long *size);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define get_commit_buffer(c, s) repo_get_commit_buffer(the_repository, c, s)
-#endif
 
 /*
  * Tell the commit subsystem that we are done with a particular commit buffer.
  * The commit and buffer should be the input and return value, respectively,
- * from an earlier call to get_commit_buffer.  The buffer may or may not be
+ * from an earlier call to repo_get_commit_buffer.  The buffer may or may not be
  * freed by this call; callers should not access the memory afterwards.
  */
 void repo_unuse_commit_buffer(struct repository *r,
 			      const struct commit *,
 			      const void *buffer);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define unuse_commit_buffer(c, b) repo_unuse_commit_buffer(the_repository, c, b)
-#endif
 
 /*
  * Free any cached object buffer associated with the commit.
@@ -163,7 +149,6 @@
 void free_commit_buffer(struct parsed_object_pool *pool, struct commit *);
 
 struct tree *repo_get_commit_tree(struct repository *, const struct commit *);
-#define get_commit_tree(c) repo_get_commit_tree(the_repository, c)
 struct object_id *get_commit_tree_oid(const struct commit *);
 
 /*
@@ -205,17 +190,10 @@
 
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 
-int has_non_ascii(const char *text);
-const char *logmsg_reencode(const struct commit *commit,
-			    char **commit_encoding,
-			    const char *output_encoding);
 const char *repo_logmsg_reencode(struct repository *r,
 				 const struct commit *commit,
 				 char **commit_encoding,
 				 const char *output_encoding);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define logmsg_reencode(c, enc, out) repo_logmsg_reencode(the_repository, c, enc, out)
-#endif
 
 const char *skip_blank_lines(const char *msg);
 
@@ -316,8 +294,8 @@
 const char *find_commit_header(const char *msg, const char *key,
 			       size_t *out_len);
 
-/* Find the end of the log message, the right place for a new trailer. */
-size_t ignore_non_trailer(const char *buf, size_t len);
+/* Find the number of bytes to ignore from the end of a log message. */
+size_t ignored_log_message_bytes(const char *buf, size_t len);
 
 typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
 				void *cb_data);
@@ -392,5 +370,6 @@
 				  struct strbuf *payload,
 				  struct strbuf *signature,
 				  const struct git_hash_algo *algop);
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo);
 
 #endif /* COMMIT_H */
diff --git a/common-main.c b/common-main.c
index 0a22861..033778b 100644
--- a/common-main.c
+++ b/common-main.c
@@ -1,6 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "attr.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "trace2.h"
 
 /*
  * Many parts of Git have subprograms communicate via pipe, expect the
diff --git a/compat/compiler.h b/compat/compiler.h
index 10dbb65..e9ad9db 100644
--- a/compat/compiler.h
+++ b/compat/compiler.h
@@ -1,7 +1,6 @@
 #ifndef COMPILER_H
 #define COMPILER_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
 
 #ifdef __GLIBC__
diff --git a/compat/disk.h b/compat/disk.h
index 50a32e3..23bc1be 100644
--- a/compat/disk.h
+++ b/compat/disk.h
@@ -1,7 +1,8 @@
 #ifndef COMPAT_DISK_H
 #define COMPAT_DISK_H
 
-#include "git-compat-util.h"
+#include "abspath.h"
+#include "gettext.h"
 
 static int get_disk_info(struct strbuf *out)
 {
diff --git a/compat/fsmonitor/fsm-health-darwin.c b/compat/fsmonitor/fsm-health-darwin.c
index b9f709e..c2afcbe 100644
--- a/compat/fsmonitor/fsm-health-darwin.c
+++ b/compat/fsmonitor/fsm-health-darwin.c
@@ -1,24 +1,24 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsm-health.h"
 #include "fsmonitor--daemon.h"
 
-int fsm_health__ctor(struct fsmonitor_daemon_state *state)
+int fsm_health__ctor(struct fsmonitor_daemon_state *state UNUSED)
 {
 	return 0;
 }
 
-void fsm_health__dtor(struct fsmonitor_daemon_state *state)
+void fsm_health__dtor(struct fsmonitor_daemon_state *state UNUSED)
 {
 	return;
 }
 
-void fsm_health__loop(struct fsmonitor_daemon_state *state)
+void fsm_health__loop(struct fsmonitor_daemon_state *state UNUSED)
 {
 	return;
 }
 
-void fsm_health__stop_async(struct fsmonitor_daemon_state *state)
+void fsm_health__stop_async(struct fsmonitor_daemon_state *state UNUSED)
 {
 }
diff --git a/compat/fsmonitor/fsm-health-win32.c b/compat/fsmonitor/fsm-health-win32.c
index 2ea08c1..2aa8c21 100644
--- a/compat/fsmonitor/fsm-health-win32.c
+++ b/compat/fsmonitor/fsm-health-win32.c
@@ -1,8 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsm-health.h"
 #include "fsmonitor--daemon.h"
+#include "gettext.h"
+#include "simple-ipc.h"
 
 /*
  * Every minute wake up and test our health.
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
index d67b0ee..6f3a954 100644
--- a/compat/fsmonitor/fsm-ipc-darwin.c
+++ b/compat/fsmonitor/fsm-ipc-darwin.c
@@ -1,7 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex.h"
+#include "path.h"
+#include "repository.h"
 #include "strbuf.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-ipc.h"
 #include "fsmonitor-path-utils.h"
 
diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c
index e08c505..41984ea 100644
--- a/compat/fsmonitor/fsm-ipc-win32.c
+++ b/compat/fsmonitor/fsm-ipc-win32.c
@@ -1,9 +1,11 @@
+#include "git-compat-util.h"
 #include "config.h"
 #include "fsmonitor-ipc.h"
+#include "path.h"
 
 const char *fsmonitor_ipc__get_path(struct repository *r) {
 	static char *ret;
 	if (!ret)
-		ret = git_pathdup("fsmonitor--daemon.ipc");
+		ret = repo_git_path(r, "fsmonitor--daemon.ipc");
 	return ret;
 }
diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c
index 97a55a6..2fc6744 100644
--- a/compat/fsmonitor/fsm-listen-darwin.c
+++ b/compat/fsmonitor/fsm-listen-darwin.c
@@ -23,11 +23,15 @@
 #endif
 #endif
 
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.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"
 
 struct fsm_listen_data
 {
@@ -188,12 +192,12 @@
 }
 
 
-static void fsevent_callback(ConstFSEventStreamRef streamRef,
+static void fsevent_callback(ConstFSEventStreamRef streamRef UNUSED,
 			     void *ctx,
 			     size_t num_of_events,
 			     void *event_paths,
 			     const FSEventStreamEventFlags event_flags[],
-			     const FSEventStreamEventId event_ids[])
+			     const FSEventStreamEventId event_ids[] UNUSED)
 {
 	struct fsmonitor_daemon_state *state = ctx;
 	struct fsm_listen_data *data = state->listen_data;
diff --git a/compat/fsmonitor/fsm-listen-win32.c b/compat/fsmonitor/fsm-listen-win32.c
index 03df8d9..5a21dad 100644
--- a/compat/fsmonitor/fsm-listen-win32.c
+++ b/compat/fsmonitor/fsm-listen-win32.c
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "gettext.h"
+#include "simple-ipc.h"
+#include "trace2.h"
 
 /*
  * The documentation of ReadDirectoryChangesW() states that the maximum
@@ -287,8 +290,7 @@
 	SetEvent(state->listen_data->hListener[LISTENER_SHUTDOWN]);
 }
 
-static struct one_watch *create_watch(struct fsmonitor_daemon_state *state,
-				      const char *path)
+static struct one_watch *create_watch(const char *path)
 {
 	struct one_watch *watch = NULL;
 	DWORD desired_access = FILE_LIST_DIRECTORY;
@@ -359,8 +361,7 @@
 	free(watch);
 }
 
-static int start_rdcw_watch(struct fsm_listen_data *data,
-			    struct one_watch *watch)
+static int start_rdcw_watch(struct one_watch *watch)
 {
 	DWORD dwNotifyFilter =
 		FILE_NOTIFY_CHANGE_FILE_NAME |
@@ -733,11 +734,11 @@
 
 	state->listen_error_code = 0;
 
-	if (start_rdcw_watch(data, data->watch_worktree) == -1)
+	if (start_rdcw_watch(data->watch_worktree) == -1)
 		goto force_error_stop;
 
 	if (data->watch_gitdir &&
-	    start_rdcw_watch(data, data->watch_gitdir) == -1)
+	    start_rdcw_watch(data->watch_gitdir) == -1)
 		goto force_error_stop;
 
 	for (;;) {
@@ -753,7 +754,7 @@
 			}
 			if (result == -2) {
 				/* retryable error */
-				if (start_rdcw_watch(data, data->watch_worktree) == -1)
+				if (start_rdcw_watch(data->watch_worktree) == -1)
 					goto force_error_stop;
 				continue;
 			}
@@ -761,7 +762,7 @@
 			/* have data */
 			if (process_worktree_events(state) == LISTENER_SHUTDOWN)
 				goto force_shutdown;
-			if (start_rdcw_watch(data, data->watch_worktree) == -1)
+			if (start_rdcw_watch(data->watch_worktree) == -1)
 				goto force_error_stop;
 			continue;
 		}
@@ -774,7 +775,7 @@
 			}
 			if (result == -2) {
 				/* retryable error */
-				if (start_rdcw_watch(data, data->watch_gitdir) == -1)
+				if (start_rdcw_watch(data->watch_gitdir) == -1)
 					goto force_error_stop;
 				continue;
 			}
@@ -782,7 +783,7 @@
 			/* have data */
 			if (process_gitdir_events(state) == LISTENER_SHUTDOWN)
 				goto force_shutdown;
-			if (start_rdcw_watch(data, data->watch_gitdir) == -1)
+			if (start_rdcw_watch(data->watch_gitdir) == -1)
 				goto force_error_stop;
 			continue;
 		}
@@ -819,16 +820,14 @@
 
 	data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
 
-	data->watch_worktree = create_watch(state,
-					    state->path_worktree_watch.buf);
+	data->watch_worktree = create_watch(state->path_worktree_watch.buf);
 	if (!data->watch_worktree)
 		goto failed;
 
 	check_for_shortnames(data->watch_worktree);
 
 	if (state->nr_paths_watching > 1) {
-		data->watch_gitdir = create_watch(state,
-						  state->path_gitdir_watch.buf);
+		data->watch_gitdir = create_watch(state->path_gitdir_watch.buf);
 		if (!data->watch_gitdir)
 			goto failed;
 	}
diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c
index ce5a8fe..049f97e 100644
--- a/compat/fsmonitor/fsm-path-utils-darwin.c
+++ b/compat/fsmonitor/fsm-path-utils-darwin.c
@@ -1,5 +1,8 @@
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "trace.h"
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
diff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c
index 0d95bbb..f4f9cc1 100644
--- a/compat/fsmonitor/fsm-path-utils-win32.c
+++ b/compat/fsmonitor/fsm-path-utils-win32.c
@@ -1,6 +1,8 @@
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-path-utils.h"
+#include "gettext.h"
+#include "trace.h"
 
 /*
  * Check remote working directory protocol.
@@ -130,7 +132,8 @@
 /*
  * No-op for now.
  */
-int fsmonitor__get_alias(const char *path, struct alias_info *info)
+int fsmonitor__get_alias(const char *path UNUSED,
+			 struct alias_info *info UNUSED)
 {
 	return 0;
 }
@@ -138,8 +141,8 @@
 /*
  * No-op for now.
  */
-char *fsmonitor__resolve_alias(const char *path,
-	const struct alias_info *info)
+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-darwin.c
index 6abbc7a..a382590 100644
--- a/compat/fsmonitor/fsm-settings-darwin.c
+++ b/compat/fsmonitor/fsm-settings-darwin.c
@@ -1,5 +1,6 @@
+#include "git-compat-util.h"
 #include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-ipc.h"
 #include "fsmonitor-settings.h"
 #include "fsmonitor-path-utils.h"
diff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c
index a8af31b..0f2aa32 100644
--- a/compat/fsmonitor/fsm-settings-win32.c
+++ b/compat/fsmonitor/fsm-settings-win32.c
@@ -1,7 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "repository.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 #include "fsmonitor-settings.h"
 #include "fsmonitor-path-utils.h"
 
@@ -25,7 +25,7 @@
 	return FSMONITOR_REASON_OK;
 }
 
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc UNUSED)
 {
 	enum fsmonitor_reason reason;
 
diff --git a/compat/linux/procinfo.c b/compat/linux/procinfo.c
index bc2f938..4bb2d66 100644
--- a/compat/linux/procinfo.c
+++ b/compat/linux/procinfo.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 
 #include "strbuf.h"
 #include "strvec.h"
diff --git a/compat/mingw.c b/compat/mingw.c
index e433740..4876344 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -6,10 +6,16 @@
 #include <wchar.h>
 #include "../strbuf.h"
 #include "../run-command.h"
-#include "../cache.h"
+#include "../abspath.h"
+#include "../alloc.h"
 #include "win32/lazyload.h"
 #include "../config.h"
+#include "../environment.h"
+#include "../trace2.h"
+#include "../symlinks.h"
+#include "../wrapper.h"
 #include "dir.h"
+#include "gettext.h"
 #define SECURITY_WIN32
 #include <sspi.h>
 
@@ -237,7 +243,8 @@
 static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 static char *unset_environment_variables;
 
-int mingw_core_config(const char *var, const char *value, void *cb)
+int mingw_core_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "core.hidedotfiles")) {
 		if (value && !strcasecmp(value, "dotgitonly"))
@@ -248,6 +255,8 @@
 	}
 
 	if (!strcmp(var, "core.unsetenvvars")) {
+		if (!value)
+			return config_error_nonbool(var);
 		free(unset_environment_variables);
 		unset_environment_variables = xstrdup(value);
 		return 0;
@@ -698,13 +707,24 @@
 {
 	ssize_t result = write(fd, buf, len);
 
-	if (result < 0 && errno == EINVAL && buf) {
+	if (result < 0 && (errno == EINVAL || errno == ENOSPC) && buf) {
+		int orig = errno;
+
 		/* check if fd is a pipe */
 		HANDLE h = (HANDLE) _get_osfhandle(fd);
-		if (GetFileType(h) == FILE_TYPE_PIPE)
+		if (GetFileType(h) != FILE_TYPE_PIPE)
+			errno = orig;
+		else if (orig == EINVAL)
 			errno = EPIPE;
-		else
-			errno = EINVAL;
+		else {
+			DWORD buf_size;
+
+			if (!GetNamedPipeInfo(h, NULL, NULL, &buf_size, NULL))
+				buf_size = 4096;
+			if (len > buf_size)
+				return write(fd, buf, buf_size);
+			errno = orig;
+		}
 	}
 
 	return result;
@@ -1340,6 +1360,11 @@
 	return prog;
 }
 
+char *mingw_locate_in_PATH(const char *cmd)
+{
+	return path_lookup(cmd, 0);
+}
+
 static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
 {
 	while (*s && *s != c)
@@ -2670,6 +2695,30 @@
 	return result;
 }
 
+static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
+{
+	SID_NAME_USE pe_use;
+	DWORD len_user = 0, len_domain = 0;
+	BOOL translate_sid_to_user;
+
+	/*
+	 * returns only FALSE, because the string pointers are NULL
+	 */
+	LookupAccountSidA(NULL, sid, NULL, &len_user, NULL, &len_domain,
+			  &pe_use);
+	/*
+	 * Alloc needed space of the strings
+	 */
+	ALLOC_ARRAY((*str), (size_t)len_domain + (size_t)len_user);
+	translate_sid_to_user = LookupAccountSidA(NULL, sid,
+	    (*str) + len_domain, &len_user, *str, &len_domain, &pe_use);
+	if (!translate_sid_to_user)
+		FREE_AND_NULL(*str);
+	else
+		(*str)[len_domain] = '/';
+	return translate_sid_to_user;
+}
+
 static int acls_supported(const char *path)
 {
 	size_t offset = offset_1st_component(path);
@@ -2751,27 +2800,47 @@
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
+			LPSTR str1, str2, str3, str4, to_free1 = NULL,
+			    to_free3 = NULL, to_local_free2 = NULL,
+			    to_local_free4 = NULL;
 
-			if (ConvertSidToStringSidA(sid, &str1))
+			if (user_sid_to_user_name(sid, &str1))
 				to_free1 = str1;
 			else
 				str1 = "(inconvertible)";
-
-			if (!current_user_sid)
-				str2 = "(none)";
-			else if (!IsValidSid(current_user_sid))
-				str2 = "(invalid)";
-			else if (ConvertSidToStringSidA(current_user_sid, &str2))
-				to_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &str2))
+				to_local_free2 = str2;
 			else
 				str2 = "(inconvertible)";
+
+			if (!current_user_sid) {
+				str3 = "(none)";
+				str4 = "(none)";
+			}
+			else if (!IsValidSid(current_user_sid)) {
+				str3 = "(invalid)";
+				str4 = "(invalid)";
+			} else {
+				if (user_sid_to_user_name(current_user_sid,
+							  &str3))
+					to_free3 = str3;
+				else
+					str3 = "(inconvertible)";
+				if (ConvertSidToStringSidA(current_user_sid,
+							   &str4))
+					to_local_free4 = str4;
+				else
+					str4 = "(inconvertible)";
+			}
 			strbuf_addf(report,
 				    "'%s' is owned by:\n"
-				    "\t'%s'\nbut the current user is:\n"
-				    "\t'%s'\n", path, str1, str2);
-			LocalFree(to_free1);
-			LocalFree(to_free2);
+				    "\t%s (%s)\nbut the current user is:\n"
+				    "\t%s (%s)\n",
+				    path, str1, str2, str3, str4);
+			free(to_free1);
+			LocalFree(to_local_free2);
+			free(to_free3);
+			LocalFree(to_local_free4);
 		}
 	}
 
@@ -3089,3 +3158,22 @@
 		  "%u", (v >> 16) & 0x7fff);
 	return 0;
 }
+
+int mingw_have_unix_sockets(void)
+{
+	SC_HANDLE scm, srvc;
+	SERVICE_STATUS_PROCESS status;
+	DWORD bytes;
+	int ret = 0;
+	scm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
+	if (scm) {
+		srvc = OpenServiceA(scm, "afunix", SERVICE_QUERY_STATUS);
+		if (srvc) {
+			if(QueryServiceStatusEx(srvc, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes))
+				ret = status.dwCurrentState == SERVICE_RUNNING;
+			CloseServiceHandle(srvc);
+		}
+		CloseServiceHandle(scm);
+	}
+	return ret;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index 209cf7c..27b6128 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -11,7 +11,9 @@
 #undef _POSIX_THREAD_SAFE_FUNCTIONS
 #endif
 
-int mingw_core_config(const char *var, const char *value, void *cb);
+struct config_context;
+int mingw_core_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb);
 #define platform_core_config mingw_core_config
 
 /*
@@ -175,6 +177,9 @@
 #define kill mingw_kill
 int mingw_kill(pid_t pid, int sig);
 
+#define locate_in_PATH mingw_locate_in_PATH
+char *mingw_locate_in_PATH(const char *cmd);
+
 #ifndef NO_OPENSSL
 #include <openssl/ssl.h>
 static inline int mingw_SSL_set_fd(SSL *ssl, int fd)
@@ -626,3 +631,9 @@
  * Used by Pthread API implementation for Windows
  */
 int err_win_to_posix(DWORD winerr);
+
+#ifndef NO_UNIX_SOCKETS
+int mingw_have_unix_sockets(void);
+#undef have_unix_sockets
+#define have_unix_sockets mingw_have_unix_sockets
+#endif
diff --git a/compat/pread.c b/compat/pread.c
index 978cac4..484e6d4 100644
--- a/compat/pread.c
+++ b/compat/pread.c
@@ -1,4 +1,5 @@
 #include "../git-compat-util.h"
+#include "../wrapper.h"
 
 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset)
 {
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index cce1d57..0bd5c24 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -5,8 +5,12 @@
 
 #define PRECOMPOSE_UNICODE_C
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
+#include "strbuf.h"
 #include "utf8.h"
 #include "precompose_utf8.h"
 
diff --git a/compat/sha1-chunked.c b/compat/sha1-chunked.c
index 6adfcfd..a4a6f93 100644
--- a/compat/sha1-chunked.c
+++ b/compat/sha1-chunked.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash-ll.h"
 
 int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len)
 {
diff --git a/compat/simple-ipc/ipc-shared.c b/compat/simple-ipc/ipc-shared.c
index 1b9d359..cb176d9 100644
--- a/compat/simple-ipc/ipc-shared.c
+++ b/compat/simple-ipc/ipc-shared.c
@@ -1,8 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "simple-ipc.h"
-#include "strbuf.h"
-#include "pkt-line.h"
-#include "thread-utils.h"
 
 #ifndef SUPPORTS_SIMPLE_IPC
 /*
diff --git a/compat/simple-ipc/ipc-unix-socket.c b/compat/simple-ipc/ipc-unix-socket.c
index 28a7928..9b3f2cd 100644
--- a/compat/simple-ipc/ipc-unix-socket.c
+++ b/compat/simple-ipc/ipc-unix-socket.c
@@ -1,8 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "strbuf.h"
-#include "pkt-line.h"
 #include "thread-utils.h"
+#include "trace2.h"
 #include "unix-socket.h"
 #include "unix-stream-server.h"
 
diff --git a/compat/simple-ipc/ipc-win32.c b/compat/simple-ipc/ipc-win32.c
index 20ea7b6..8bfe512 100644
--- a/compat/simple-ipc/ipc-win32.c
+++ b/compat/simple-ipc/ipc-win32.c
@@ -1,8 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "strbuf.h"
 #include "pkt-line.h"
 #include "thread-utils.h"
+#include "trace.h"
+#include "trace2.h"
 #include "accctrl.h"
 #include "aclapi.h"
 
diff --git a/compat/terminal.c b/compat/terminal.c
index ea490a7..0afda73 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -1,5 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "compat/terminal.h"
+#include "gettext.h"
 #include "sigchain.h"
 #include "strbuf.h"
 #include "run-command.h"
@@ -478,10 +479,13 @@
 };
 
 static int sequence_entry_cmp(const void *hashmap_cmp_fn_data UNUSED,
-			      const struct escape_sequence_entry *e1,
-			      const struct escape_sequence_entry *e2,
+			      const struct hashmap_entry *he1,
+			      const struct hashmap_entry *he2,
 			      const void *keydata)
 {
+	const struct escape_sequence_entry
+		*e1 = container_of(he1, const struct escape_sequence_entry, entry),
+		*e2 = container_of(he2, const struct escape_sequence_entry, entry);
 	return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
 }
 
@@ -495,8 +499,7 @@
 		struct strbuf buf = STRBUF_INIT;
 		char *p, *eol;
 
-		hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
-			     NULL, 0);
+		hashmap_init(&sequences, sequence_entry_cmp, NULL, 0);
 
 		strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
 		if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
diff --git a/compat/win32/headless.c b/compat/win32/headless.c
new file mode 100644
index 0000000..8b00dfe
--- /dev/null
+++ b/compat/win32/headless.c
@@ -0,0 +1,115 @@
+/*
+ * headless Git - run Git without opening a console window on Windows
+ */
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#define UNICODE
+#define _UNICODE
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+/*
+ * If `dir` contains the path to a Git exec directory, extend `PATH` to
+ * include the corresponding `bin/` directory (which is where all those
+ * `.dll` files needed by `git.exe` are, on Windows).
+ */
+static int extend_path(wchar_t *dir, size_t dir_len)
+{
+	const wchar_t *suffix = L"\\libexec\\git-core";
+	size_t suffix_len = wcslen(suffix);
+	wchar_t *env;
+	DWORD len;
+
+	if (dir_len < suffix_len)
+		return 0;
+
+	dir_len -= suffix_len;
+	if (memcmp(dir + dir_len, suffix, suffix_len * sizeof(wchar_t)))
+		return 0;
+
+	len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+	if (!len)
+		return 0;
+
+	env = _alloca((dir_len + 5 + len) * sizeof(wchar_t));
+	wcsncpy(env, dir, dir_len);
+	wcscpy(env + dir_len, L"\\bin;");
+	if (!GetEnvironmentVariableW(L"PATH", env + dir_len + 5, len))
+		return 0;
+
+	SetEnvironmentVariableW(L"PATH", env);
+	return 1;
+}
+
+int WINAPI wWinMain(_In_ HINSTANCE instance,
+		    _In_opt_ HINSTANCE previous_instance,
+		    _In_ LPWSTR command_line, _In_ int show)
+{
+	wchar_t git_command_line[32768];
+	size_t size = sizeof(git_command_line) / sizeof(wchar_t);
+	const wchar_t *needs_quotes = L"";
+	int slash = 0, i;
+
+	STARTUPINFO startup_info = {
+		.cb = sizeof(STARTUPINFO),
+		.dwFlags = STARTF_USESHOWWINDOW,
+		.wShowWindow = SW_HIDE,
+	};
+	PROCESS_INFORMATION process_info = { 0 };
+	DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT |
+		CREATE_NEW_CONSOLE | CREATE_NO_WINDOW;
+	DWORD exit_code;
+
+	/* First, determine the full path of argv[0] */
+	for (i = 0; _wpgmptr[i]; i++)
+		if (_wpgmptr[i] == L' ')
+			needs_quotes = L"\"";
+		else if (_wpgmptr[i] == L'\\')
+			slash = i;
+
+	if (slash >= size - 11)
+		return 127; /* Too long path */
+
+	/* If it is in Git's exec path, add the bin/ directory to the PATH */
+	extend_path(_wpgmptr, slash);
+
+	/* Then, add the full path of `git.exe` as argv[0] */
+	i = swprintf_s(git_command_line, size, L"%ls%.*ls\\git.exe%ls",
+		       needs_quotes, slash, _wpgmptr, needs_quotes);
+	if (i < 0)
+		return 127; /* Too long path */
+
+	if (*command_line) {
+		/* Now, append the command-line arguments */
+		i = swprintf_s(git_command_line + i, size - i,
+			       L" %ls", command_line);
+		if (i < 0)
+			return 127;
+	}
+
+	startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+	startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+	startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+	if (!CreateProcess(NULL, /* infer argv[0] from the command line */
+			   git_command_line, /* modified command line */
+			   NULL, /* inherit process handles? */
+			   NULL, /* inherit thread handles? */
+			   FALSE, /* handles inheritable? */
+			   creation_flags,
+			   NULL, /* use this process' environment */
+			   NULL, /* use this process' working directory */
+			   &startup_info, &process_info))
+		return 129; /* could not start */
+	WaitForSingleObject(process_info.hProcess, INFINITE);
+	if (!GetExitCodeProcess(process_info.hProcess, &exit_code))
+		exit_code = 130; /* Could not determine exit code? */
+
+	CloseHandle(process_info.hProcess);
+	CloseHandle(process_info.hThread);
+
+	return (int)exit_code;
+}
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
index a53fd92..3ef0936 100644
--- a/compat/win32/trace2_win32_process_info.c
+++ b/compat/win32/trace2_win32_process_info.c
@@ -1,8 +1,10 @@
-#include "../../cache.h"
+#include "../../git-compat-util.h"
 #include "../../json-writer.h"
+#include "../../repository.h"
+#include "../../trace2.h"
 #include "lazyload.h"
-#include <Psapi.h>
-#include <tlHelp32.h>
+#include <psapi.h>
+#include <tlhelp32.h>
 
 /*
  * An arbitrarily chosen value to limit the size of the ancestor
diff --git a/config.c b/config.c
index 1468565..ae3652b 100644
--- a/config.c
+++ b/config.c
@@ -5,24 +5,39 @@
  * Copyright (C) Johannes Schindelin, 2005
  *
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
 #include "date.h"
 #include "branch.h"
 #include "config.h"
+#include "parse.h"
+#include "convert.h"
 #include "environment.h"
+#include "gettext.h"
+#include "ident.h"
 #include "repository.h"
 #include "lockfile.h"
+#include "mailmap.h"
+#include "attr.h"
 #include "exec-cmd.h"
 #include "strbuf.h"
 #include "quote.h"
 #include "hashmap.h"
 #include "string-list.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
+#include "path.h"
 #include "utf8.h"
-#include "dir.h"
 #include "color.h"
 #include "refs.h"
-#include "worktree.h"
+#include "setup.h"
+#include "strvec.h"
+#include "trace2.h"
+#include "wildmatch.h"
+#include "ws.h"
+#include "write-or-die.h"
 
 struct config_source {
 	struct config_source *prev;
@@ -49,34 +64,7 @@
 	int (*do_ungetc)(int c, struct config_source *conf);
 	long (*do_ftell)(struct config_source *c);
 };
-
-/*
- * These variables record the "current" config source, which
- * can be accessed by parsing callbacks.
- *
- * The "cf" variable will be non-NULL only when we are actually parsing a real
- * config source (file, blob, cmdline, etc).
- *
- * The "current_config_kvi" variable will be non-NULL only when we are feeding
- * cached config from a configset into a callback.
- *
- * They should generally never be non-NULL at the same time. If they are both
- * NULL, then we aren't parsing anything (and depending on the function looking
- * at the variables, it's either a bug for it to be called in the first place,
- * or it's a function which can be reused for non-config purposes, and should
- * fall back to some sane behavior).
- */
-static struct config_source *cf;
-static struct key_value_info *current_config_kvi;
-
-/*
- * Similar to the variables above, this gives access to the "scope" of the
- * current value (repo, global, etc). For cached values, it can be found via
- * the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf" because it transcends a single
- * file (i.e., a file included from .git/config is still in "repo" scope).
- */
-static enum config_scope current_parsing_scope;
+#define CONFIG_SOURCE_INIT { 0 }
 
 static int pack_compression_seen;
 static int zlib_compression_seen;
@@ -107,7 +95,6 @@
 	return ftell(conf->u.file);
 }
 
-
 static int config_buf_fgetc(struct config_source *conf)
 {
 	if (conf->u.buf.pos < conf->u.buf.len)
@@ -139,6 +126,7 @@
 	void *data;
 	const struct config_options *opts;
 	struct git_config_source *config_source;
+	struct repository *repo;
 
 	/*
 	 * All remote URLs discovered when reading all config files.
@@ -147,7 +135,8 @@
 };
 #define CONFIG_INCLUDE_INIT { 0 }
 
-static int git_config_include(const char *var, const char *value, void *data);
+static int git_config_include(const char *var, const char *value,
+			      const struct config_context *ctx, void *data);
 
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] = N_(
@@ -156,7 +145,9 @@
 "from\n"
 "	%s\n"
 "This might be due to circular includes.");
-static int handle_path_include(const char *path, struct config_include_data *inc)
+static int handle_path_include(const struct key_value_info *kvi,
+			       const char *path,
+			       struct config_include_data *inc)
 {
 	int ret = 0;
 	struct strbuf buf = STRBUF_INIT;
@@ -177,14 +168,14 @@
 	if (!is_absolute_path(path)) {
 		char *slash;
 
-		if (!cf || !cf->path) {
+		if (!kvi || !kvi->path) {
 			ret = error(_("relative config includes must come from files"));
 			goto cleanup;
 		}
 
-		slash = find_last_dir_sep(cf->path);
+		slash = find_last_dir_sep(kvi->path);
 		if (slash)
-			strbuf_add(&buf, cf->path, slash - cf->path + 1);
+			strbuf_add(&buf, kvi->path, slash - kvi->path + 1);
 		strbuf_addstr(&buf, path);
 		path = buf.buf;
 	}
@@ -192,10 +183,11 @@
 	if (!access_or_die(path, R_OK, 0)) {
 		if (++inc->depth > MAX_INCLUDE_DEPTH)
 			die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
-			    !cf ? "<unknown>" :
-			    cf->name ? cf->name :
+			    !kvi ? "<unknown>" :
+			    kvi->filename ? kvi->filename :
 			    "the command line");
-		ret = git_config_from_file(git_config_include, path, inc);
+		ret = git_config_from_file_with_options(git_config_include, path, inc,
+							kvi->scope, NULL);
 		inc->depth--;
 	}
 cleanup:
@@ -210,7 +202,8 @@
 		strbuf_addstr(pat, "**");
 }
 
-static int prepare_include_condition_pattern(struct strbuf *pat)
+static int prepare_include_condition_pattern(const struct key_value_info *kvi,
+					     struct strbuf *pat)
 {
 	struct strbuf path = STRBUF_INIT;
 	char *expanded;
@@ -226,11 +219,11 @@
 	if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
 		const char *slash;
 
-		if (!cf || !cf->path)
+		if (!kvi || !kvi->path)
 			return error(_("relative config include "
 				       "conditionals must come from files"));
 
-		strbuf_realpath(&path, cf->path, 1);
+		strbuf_realpath(&path, kvi->path, 1);
 		slash = find_last_dir_sep(path.buf);
 		if (!slash)
 			BUG("how is this possible?");
@@ -245,7 +238,8 @@
 	return prefix;
 }
 
-static int include_by_gitdir(const struct config_options *opts,
+static int include_by_gitdir(const struct key_value_info *kvi,
+			     const struct config_options *opts,
 			     const char *cond, size_t cond_len, int icase)
 {
 	struct strbuf text = STRBUF_INIT;
@@ -261,7 +255,7 @@
 
 	strbuf_realpath(&text, git_dir, 1);
 	strbuf_add(&pattern, cond, cond_len);
-	prefix = prepare_include_condition_pattern(&pattern);
+	prefix = prepare_include_condition_pattern(kvi, &pattern);
 
 again:
 	if (prefix < 0)
@@ -323,7 +317,8 @@
 	return ret;
 }
 
-static int add_remote_url(const char *var, const char *value, void *data)
+static int add_remote_url(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED, void *data)
 {
 	struct string_list *remote_urls = data;
 	const char *remote_name;
@@ -342,27 +337,17 @@
 {
 	struct config_options opts;
 
-	struct config_source *store_cf = cf;
-	struct key_value_info *store_kvi = current_config_kvi;
-	enum config_scope store_scope = current_parsing_scope;
-
 	opts = *inc->opts;
 	opts.unconditional_remote_url = 1;
 
-	cf = NULL;
-	current_config_kvi = NULL;
-	current_parsing_scope = 0;
-
 	inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
 	string_list_init_dup(inc->remote_urls);
-	config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
-
-	cf = store_cf;
-	current_config_kvi = store_kvi;
-	current_parsing_scope = store_scope;
+	config_with_options(add_remote_url, inc->remote_urls,
+			    inc->config_source, inc->repo, &opts);
 }
 
 static int forbid_remote_url(const char *var, const char *value UNUSED,
+			     const struct config_context *ctx UNUSED,
 			     void *data UNUSED)
 {
 	const char *remote_name;
@@ -406,15 +391,16 @@
 					     inc->remote_urls);
 }
 
-static int include_condition_is_true(struct config_include_data *inc,
+static int include_condition_is_true(const struct key_value_info *kvi,
+				     struct config_include_data *inc,
 				     const char *cond, size_t cond_len)
 {
 	const struct config_options *opts = inc->opts;
 
 	if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
-		return include_by_gitdir(opts, cond, cond_len, 0);
+		return include_by_gitdir(kvi, opts, cond, cond_len, 0);
 	else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
-		return include_by_gitdir(opts, cond, cond_len, 1);
+		return include_by_gitdir(kvi, opts, cond, cond_len, 1);
 	else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
 		return include_by_branch(cond, cond_len);
 	else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -425,7 +411,9 @@
 	return 0;
 }
 
-static int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value,
+			      const struct config_context *ctx,
+			      void *data)
 {
 	struct config_include_data *inc = data;
 	const char *cond, *key;
@@ -436,21 +424,21 @@
 	 * Pass along all values, including "include" directives; this makes it
 	 * possible to query information on the includes themselves.
 	 */
-	ret = inc->fn(var, value, inc->data);
+	ret = inc->fn(var, value, ctx, inc->data);
 	if (ret < 0)
 		return ret;
 
 	if (!strcmp(var, "include.path"))
-		ret = handle_path_include(value, inc);
+		ret = handle_path_include(ctx->kvi, value, inc);
 
 	if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
-	    cond && include_condition_is_true(inc, cond, cond_len) &&
+	    cond && include_condition_is_true(ctx->kvi, inc, cond, cond_len) &&
 	    !strcmp(key, "path")) {
 		config_fn_t old_fn = inc->fn;
 
 		if (inc->opts->unconditional_remote_url)
 			inc->fn = forbid_remote_url;
-		ret = handle_path_include(value, inc);
+		ret = handle_path_include(ctx->kvi, value, inc);
 		inc->fn = old_fn;
 	}
 
@@ -608,27 +596,45 @@
 }
 
 static int config_parse_pair(const char *key, const char *value,
-			  config_fn_t fn, void *data)
+			     struct key_value_info *kvi,
+			     config_fn_t fn, void *data)
 {
 	char *canonical_name;
 	int ret;
+	struct config_context ctx = {
+		.kvi = kvi,
+	};
 
 	if (!strlen(key))
 		return error(_("empty config key"));
 	if (git_config_parse_key(key, &canonical_name, NULL))
 		return -1;
 
-	ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+	ret = (fn(canonical_name, value, &ctx, data) < 0) ? -1 : 0;
 	free(canonical_name);
 	return ret;
 }
 
+
+/* for values read from `git_config_from_parameters()` */
+void kvi_from_param(struct key_value_info *out)
+{
+	out->filename = NULL;
+	out->linenr = -1;
+	out->origin_type = CONFIG_ORIGIN_CMDLINE;
+	out->scope = CONFIG_SCOPE_COMMAND;
+	out->path = NULL;
+}
+
 int git_config_parse_parameter(const char *text,
 			       config_fn_t fn, void *data)
 {
 	const char *value;
 	struct strbuf **pair;
 	int ret;
+	struct key_value_info kvi = KVI_INIT;
+
+	kvi_from_param(&kvi);
 
 	pair = strbuf_split_str(text, '=', 2);
 	if (!pair[0])
@@ -647,12 +653,13 @@
 		return error(_("bogus config parameter: %s"), text);
 	}
 
-	ret = config_parse_pair(pair[0]->buf, value, fn, data);
+	ret = config_parse_pair(pair[0]->buf, value, &kvi, fn, data);
 	strbuf_list_free(pair);
 	return ret;
 }
 
-static int parse_config_env_list(char *env, config_fn_t fn, void *data)
+static int parse_config_env_list(char *env, struct key_value_info *kvi,
+				 config_fn_t fn, void *data)
 {
 	char *cur = env;
 	while (cur && *cur) {
@@ -686,7 +693,7 @@
 					     CONFIG_DATA_ENVIRONMENT);
 			}
 
-			if (config_parse_pair(key, value, fn, data) < 0)
+			if (config_parse_pair(key, value, kvi, fn, data) < 0)
 				return -1;
 		}
 		else {
@@ -710,13 +717,9 @@
 	struct strvec to_free = STRVEC_INIT;
 	int ret = 0;
 	char *envw = NULL;
-	struct config_source source;
+	struct key_value_info kvi = KVI_INIT;
 
-	memset(&source, 0, sizeof(source));
-	source.prev = cf;
-	source.origin_type = CONFIG_ORIGIN_CMDLINE;
-	cf = &source;
-
+	kvi_from_param(&kvi);
 	env = getenv(CONFIG_COUNT_ENVIRONMENT);
 	if (env) {
 		unsigned long count;
@@ -752,7 +755,7 @@
 			}
 			strbuf_reset(&envvar);
 
-			if (config_parse_pair(key, value, fn, data) < 0) {
+			if (config_parse_pair(key, value, &kvi, fn, data) < 0) {
 				ret = -1;
 				goto out;
 			}
@@ -763,7 +766,7 @@
 	if (env) {
 		/* sq_dequote will write over it */
 		envw = xstrdup(env);
-		if (parse_config_env_list(envw, fn, data) < 0) {
+		if (parse_config_env_list(envw, &kvi, fn, data) < 0) {
 			ret = -1;
 			goto out;
 		}
@@ -773,25 +776,24 @@
 	strbuf_release(&envvar);
 	strvec_clear(&to_free);
 	free(envw);
-	cf = source.prev;
 	return ret;
 }
 
-static int get_next_char(void)
+static int get_next_char(struct config_source *cs)
 {
-	int c = cf->do_fgetc(cf);
+	int c = cs->do_fgetc(cs);
 
 	if (c == '\r') {
 		/* DOS like systems */
-		c = cf->do_fgetc(cf);
+		c = cs->do_fgetc(cs);
 		if (c != '\n') {
 			if (c != EOF)
-				cf->do_ungetc(c, cf);
+				cs->do_ungetc(c, cs);
 			c = '\r';
 		}
 	}
 
-	if (c != EOF && ++cf->total_len > INT_MAX) {
+	if (c != EOF && ++cs->total_len > INT_MAX) {
 		/*
 		 * This is an absurdly long config file; refuse to parse
 		 * further in order to protect downstream code from integer
@@ -799,39 +801,44 @@
 		 * but we can mark EOF and put trash in the return value,
 		 * which will trigger a parse error.
 		 */
-		cf->eof = 1;
+		cs->eof = 1;
 		return 0;
 	}
 
 	if (c == '\n')
-		cf->linenr++;
+		cs->linenr++;
 	if (c == EOF) {
-		cf->eof = 1;
-		cf->linenr++;
+		cs->eof = 1;
+		cs->linenr++;
 		c = '\n';
 	}
 	return c;
 }
 
-static char *parse_value(void)
+static char *parse_value(struct config_source *cs)
 {
-	int quote = 0, comment = 0, space = 0;
+	int quote = 0, comment = 0;
+	size_t trim_len = 0;
 
-	strbuf_reset(&cf->value);
+	strbuf_reset(&cs->value);
 	for (;;) {
-		int c = get_next_char();
+		int c = get_next_char(cs);
 		if (c == '\n') {
 			if (quote) {
-				cf->linenr--;
+				cs->linenr--;
 				return NULL;
 			}
-			return cf->value.buf;
+			if (trim_len)
+				strbuf_setlen(&cs->value, trim_len);
+			return cs->value.buf;
 		}
 		if (comment)
 			continue;
 		if (isspace(c) && !quote) {
-			if (cf->value.len)
-				space++;
+			if (!trim_len)
+				trim_len = cs->value.len;
+			if (cs->value.len)
+				strbuf_addch(&cs->value, c);
 			continue;
 		}
 		if (!quote) {
@@ -840,10 +847,10 @@
 				continue;
 			}
 		}
-		for (; space; space--)
-			strbuf_addch(&cf->value, ' ');
+		if (trim_len)
+			trim_len = 0;
 		if (c == '\\') {
-			c = get_next_char();
+			c = get_next_char(cs);
 			switch (c) {
 			case '\n':
 				continue;
@@ -863,27 +870,31 @@
 			default:
 				return NULL;
 			}
-			strbuf_addch(&cf->value, c);
+			strbuf_addch(&cs->value, c);
 			continue;
 		}
 		if (c == '"') {
-			quote = 1-quote;
+			quote = 1 - quote;
 			continue;
 		}
-		strbuf_addch(&cf->value, c);
+		strbuf_addch(&cs->value, c);
 	}
 }
 
-static int get_value(config_fn_t fn, void *data, struct strbuf *name)
+static int get_value(struct config_source *cs, struct key_value_info *kvi,
+		     config_fn_t fn, void *data, struct strbuf *name)
 {
 	int c;
 	char *value;
 	int ret;
+	struct config_context ctx = {
+		.kvi = kvi,
+	};
 
 	/* Get the full name */
 	for (;;) {
-		c = get_next_char();
-		if (cf->eof)
+		c = get_next_char(cs);
+		if (cs->eof)
 			break;
 		if (!iskeychar(c))
 			break;
@@ -891,13 +902,13 @@
 	}
 
 	while (c == ' ' || c == '\t')
-		c = get_next_char();
+		c = get_next_char(cs);
 
 	value = NULL;
 	if (c != '\n') {
 		if (c != '=')
 			return -1;
-		value = parse_value();
+		value = parse_value(cs);
 		if (!value)
 			return -1;
 	}
@@ -906,20 +917,22 @@
 	 * the line we just parsed during the call to fn to get
 	 * accurate line number in error messages.
 	 */
-	cf->linenr--;
-	ret = fn(name->buf, value, data);
+	cs->linenr--;
+	kvi->linenr = cs->linenr;
+	ret = fn(name->buf, value, &ctx, data);
 	if (ret >= 0)
-		cf->linenr++;
+		cs->linenr++;
 	return ret;
 }
 
-static int get_extended_base_var(struct strbuf *name, int c)
+static int get_extended_base_var(struct config_source *cs, struct strbuf *name,
+				 int c)
 {
-	cf->subsection_case_sensitive = 0;
+	cs->subsection_case_sensitive = 0;
 	do {
 		if (c == '\n')
 			goto error_incomplete_line;
-		c = get_next_char();
+		c = get_next_char(cs);
 	} while (isspace(c));
 
 	/* We require the format to be '[base "extension"]' */
@@ -928,13 +941,13 @@
 	strbuf_addch(name, '.');
 
 	for (;;) {
-		int c = get_next_char();
+		int c = get_next_char(cs);
 		if (c == '\n')
 			goto error_incomplete_line;
 		if (c == '"')
 			break;
 		if (c == '\\') {
-			c = get_next_char();
+			c = get_next_char(cs);
 			if (c == '\n')
 				goto error_incomplete_line;
 		}
@@ -942,25 +955,25 @@
 	}
 
 	/* Final ']' */
-	if (get_next_char() != ']')
+	if (get_next_char(cs) != ']')
 		return -1;
 	return 0;
 error_incomplete_line:
-	cf->linenr--;
+	cs->linenr--;
 	return -1;
 }
 
-static int get_base_var(struct strbuf *name)
+static int get_base_var(struct config_source *cs, struct strbuf *name)
 {
-	cf->subsection_case_sensitive = 1;
+	cs->subsection_case_sensitive = 1;
 	for (;;) {
-		int c = get_next_char();
-		if (cf->eof)
+		int c = get_next_char(cs);
+		if (cs->eof)
 			return -1;
 		if (c == ']')
 			return 0;
 		if (isspace(c))
-			return get_extended_base_var(name, c);
+			return get_extended_base_var(cs, name, c);
 		if (!iskeychar(c) && c != '.')
 			return -1;
 		strbuf_addch(name, tolower(c));
@@ -973,7 +986,8 @@
 	const struct config_options *opts;
 };
 
-static int do_event(enum config_event_t type, struct parse_event_data *data)
+static int do_event(struct config_source *cs, enum config_event_t type,
+		    struct parse_event_data *data)
 {
 	size_t offset;
 
@@ -984,7 +998,7 @@
 	    data->previous_type == type)
 		return 0;
 
-	offset = cf->do_ftell(cf);
+	offset = cs->do_ftell(cs);
 	/*
 	 * At EOF, the parser always "inserts" an extra '\n', therefore
 	 * the end offset of the event is the current file position, otherwise
@@ -995,7 +1009,7 @@
 
 	if (data->previous_type != CONFIG_EVENT_EOF &&
 	    data->opts->event_fn(data->previous_type, data->previous_offset,
-				 offset, data->opts->event_fn_data) < 0)
+				 offset, cs, data->opts->event_fn_data) < 0)
 		return -1;
 
 	data->previous_type = type;
@@ -1004,12 +1018,24 @@
 	return 0;
 }
 
-static int git_parse_source(config_fn_t fn, void *data,
+static void kvi_from_source(struct config_source *cs,
+			    enum config_scope scope,
+			    struct key_value_info *out)
+{
+	out->filename = strintern(cs->name);
+	out->origin_type = cs->origin_type;
+	out->linenr = cs->linenr;
+	out->scope = scope;
+	out->path = cs->path;
+}
+
+static int git_parse_source(struct config_source *cs, config_fn_t fn,
+			    struct key_value_info *kvi, void *data,
 			    const struct config_options *opts)
 {
 	int comment = 0;
 	size_t baselen = 0;
-	struct strbuf *var = &cf->var;
+	struct strbuf *var = &cs->var;
 	int error_return = 0;
 	char *error_msg = NULL;
 
@@ -1024,7 +1050,7 @@
 	for (;;) {
 		int c;
 
-		c = get_next_char();
+		c = get_next_char(cs);
 		if (bomptr && *bomptr) {
 			/* We are at the file beginning; skip UTF8-encoded BOM
 			 * if present. Sane editors won't put this in on their
@@ -1041,12 +1067,12 @@
 			}
 		}
 		if (c == '\n') {
-			if (cf->eof) {
-				if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
+			if (cs->eof) {
+				if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0)
 					return -1;
 				return 0;
 			}
-			if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+			if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
 				return -1;
 			comment = 0;
 			continue;
@@ -1054,23 +1080,23 @@
 		if (comment)
 			continue;
 		if (isspace(c)) {
-			if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+			if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
 					return -1;
 			continue;
 		}
 		if (c == '#' || c == ';') {
-			if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
+			if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0)
 					return -1;
 			comment = 1;
 			continue;
 		}
 		if (c == '[') {
-			if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
+			if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0)
 					return -1;
 
 			/* Reset prior to determining a new stem */
 			strbuf_reset(var);
-			if (get_base_var(var) < 0 || var->len < 1)
+			if (get_base_var(cs, var) < 0 || var->len < 1)
 				break;
 			strbuf_addch(var, '.');
 			baselen = var->len;
@@ -1079,7 +1105,7 @@
 		if (!isalpha(c))
 			break;
 
-		if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
+		if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0)
 			return -1;
 
 		/*
@@ -1089,42 +1115,42 @@
 		 */
 		strbuf_setlen(var, baselen);
 		strbuf_addch(var, tolower(c));
-		if (get_value(fn, data, var) < 0)
+		if (get_value(cs, kvi, fn, data, var) < 0)
 			break;
 	}
 
-	if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
+	if (do_event(cs, CONFIG_EVENT_ERROR, &event_data) < 0)
 		return -1;
 
-	switch (cf->origin_type) {
+	switch (cs->origin_type) {
 	case CONFIG_ORIGIN_BLOB:
 		error_msg = xstrfmt(_("bad config line %d in blob %s"),
-				      cf->linenr, cf->name);
+				      cs->linenr, cs->name);
 		break;
 	case CONFIG_ORIGIN_FILE:
 		error_msg = xstrfmt(_("bad config line %d in file %s"),
-				      cf->linenr, cf->name);
+				      cs->linenr, cs->name);
 		break;
 	case CONFIG_ORIGIN_STDIN:
 		error_msg = xstrfmt(_("bad config line %d in standard input"),
-				      cf->linenr);
+				      cs->linenr);
 		break;
 	case CONFIG_ORIGIN_SUBMODULE_BLOB:
 		error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
-				       cf->linenr, cf->name);
+				       cs->linenr, cs->name);
 		break;
 	case CONFIG_ORIGIN_CMDLINE:
 		error_msg = xstrfmt(_("bad config line %d in command line %s"),
-				       cf->linenr, cf->name);
+				       cs->linenr, cs->name);
 		break;
 	default:
 		error_msg = xstrfmt(_("bad config line %d in %s"),
-				      cf->linenr, cf->name);
+				      cs->linenr, cs->name);
 	}
 
 	switch (opts && opts->error_action ?
 		opts->error_action :
-		cf->default_error_action) {
+		cs->default_error_action) {
 	case CONFIG_ERROR_DIE:
 		die("%s", error_msg);
 		break;
@@ -1142,213 +1168,81 @@
 	return error_return;
 }
 
-static uintmax_t get_unit_factor(const char *end)
-{
-	if (!*end)
-		return 1;
-	else if (!strcasecmp(end, "k"))
-		return 1024;
-	else if (!strcasecmp(end, "m"))
-		return 1024 * 1024;
-	else if (!strcasecmp(end, "g"))
-		return 1024 * 1024 * 1024;
-	return 0;
-}
-
-static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
-{
-	if (value && *value) {
-		char *end;
-		intmax_t val;
-		intmax_t factor;
-
-		if (max < 0)
-			BUG("max must be a positive integer");
-
-		errno = 0;
-		val = strtoimax(value, &end, 0);
-		if (errno == ERANGE)
-			return 0;
-		if (end == value) {
-			errno = EINVAL;
-			return 0;
-		}
-		factor = get_unit_factor(end);
-		if (!factor) {
-			errno = EINVAL;
-			return 0;
-		}
-		if ((val < 0 && -max / factor > val) ||
-		    (val > 0 && max / factor < val)) {
-			errno = ERANGE;
-			return 0;
-		}
-		val *= factor;
-		*ret = val;
-		return 1;
-	}
-	errno = EINVAL;
-	return 0;
-}
-
-static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
-{
-	if (value && *value) {
-		char *end;
-		uintmax_t val;
-		uintmax_t factor;
-
-		/* negative values would be accepted by strtoumax */
-		if (strchr(value, '-')) {
-			errno = EINVAL;
-			return 0;
-		}
-		errno = 0;
-		val = strtoumax(value, &end, 0);
-		if (errno == ERANGE)
-			return 0;
-		if (end == value) {
-			errno = EINVAL;
-			return 0;
-		}
-		factor = get_unit_factor(end);
-		if (!factor) {
-			errno = EINVAL;
-			return 0;
-		}
-		if (unsigned_mult_overflows(factor, val) ||
-		    factor * val > max) {
-			errno = ERANGE;
-			return 0;
-		}
-		val *= factor;
-		*ret = val;
-		return 1;
-	}
-	errno = EINVAL;
-	return 0;
-}
-
-int git_parse_int(const char *value, int *ret)
-{
-	intmax_t tmp;
-	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
-		return 0;
-	*ret = tmp;
-	return 1;
-}
-
-static int git_parse_int64(const char *value, int64_t *ret)
-{
-	intmax_t tmp;
-	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
-		return 0;
-	*ret = tmp;
-	return 1;
-}
-
-int git_parse_ulong(const char *value, unsigned long *ret)
-{
-	uintmax_t tmp;
-	if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
-		return 0;
-	*ret = tmp;
-	return 1;
-}
-
-int git_parse_ssize_t(const char *value, ssize_t *ret)
-{
-	intmax_t tmp;
-	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
-		return 0;
-	*ret = tmp;
-	return 1;
-}
-
 NORETURN
-static void die_bad_number(const char *name, const char *value)
+static void die_bad_number(const char *name, const char *value,
+			   const struct key_value_info *kvi)
 {
 	const char *error_type = (errno == ERANGE) ?
 		N_("out of range") : N_("invalid unit");
 	const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s");
 
+	if (!kvi)
+		BUG("kvi should not be NULL");
+
 	if (!value)
 		value = "";
 
-	if (!(cf && cf->name))
+	if (!kvi->filename)
 		die(_(bad_numeric), value, name, _(error_type));
 
-	switch (cf->origin_type) {
+	switch (kvi->origin_type) {
 	case CONFIG_ORIGIN_BLOB:
 		die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
-		    value, name, cf->name, _(error_type));
+		    value, name, kvi->filename, _(error_type));
 	case CONFIG_ORIGIN_FILE:
 		die(_("bad numeric config value '%s' for '%s' in file %s: %s"),
-		    value, name, cf->name, _(error_type));
+		    value, name, kvi->filename, _(error_type));
 	case CONFIG_ORIGIN_STDIN:
 		die(_("bad numeric config value '%s' for '%s' in standard input: %s"),
 		    value, name, _(error_type));
 	case CONFIG_ORIGIN_SUBMODULE_BLOB:
 		die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"),
-		    value, name, cf->name, _(error_type));
+		    value, name, kvi->filename, _(error_type));
 	case CONFIG_ORIGIN_CMDLINE:
 		die(_("bad numeric config value '%s' for '%s' in command line %s: %s"),
-		    value, name, cf->name, _(error_type));
+		    value, name, kvi->filename, _(error_type));
 	default:
 		die(_("bad numeric config value '%s' for '%s' in %s: %s"),
-		    value, name, cf->name, _(error_type));
+		    value, name, kvi->filename, _(error_type));
 	}
 }
 
-int git_config_int(const char *name, const char *value)
+int git_config_int(const char *name, const char *value,
+		   const struct key_value_info *kvi)
 {
 	int ret;
 	if (!git_parse_int(value, &ret))
-		die_bad_number(name, value);
+		die_bad_number(name, value, kvi);
 	return ret;
 }
 
-int64_t git_config_int64(const char *name, const char *value)
+int64_t git_config_int64(const char *name, const char *value,
+			 const struct key_value_info *kvi)
 {
 	int64_t ret;
 	if (!git_parse_int64(value, &ret))
-		die_bad_number(name, value);
+		die_bad_number(name, value, kvi);
 	return ret;
 }
 
-unsigned long git_config_ulong(const char *name, const char *value)
+unsigned long git_config_ulong(const char *name, const char *value,
+			       const struct key_value_info *kvi)
 {
 	unsigned long ret;
 	if (!git_parse_ulong(value, &ret))
-		die_bad_number(name, value);
+		die_bad_number(name, value, kvi);
 	return ret;
 }
 
-ssize_t git_config_ssize_t(const char *name, const char *value)
+ssize_t git_config_ssize_t(const char *name, const char *value,
+			   const struct key_value_info *kvi)
 {
 	ssize_t ret;
 	if (!git_parse_ssize_t(value, &ret))
-		die_bad_number(name, value);
+		die_bad_number(name, value, kvi);
 	return ret;
 }
 
-static int git_parse_maybe_bool_text(const char *value)
-{
-	if (!value)
-		return 1;
-	if (!*value)
-		return 0;
-	if (!strcasecmp(value, "true")
-	    || !strcasecmp(value, "yes")
-	    || !strcasecmp(value, "on"))
-		return 1;
-	if (!strcasecmp(value, "false")
-	    || !strcasecmp(value, "no")
-	    || !strcasecmp(value, "off"))
-		return 0;
-	return -1;
-}
-
 static const struct fsync_component_name {
 	const char *name;
 	enum fsync_component component_bits;
@@ -1423,17 +1317,8 @@
 	return (current & ~negative) | positive;
 }
 
-int git_parse_maybe_bool(const char *value)
-{
-	int v = git_parse_maybe_bool_text(value);
-	if (0 <= v)
-		return v;
-	if (git_parse_int(value, &v))
-		return !!v;
-	return -1;
-}
-
-int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+int git_config_bool_or_int(const char *name, const char *value,
+			   const struct key_value_info *kvi, int *is_bool)
 {
 	int v = git_parse_maybe_bool_text(value);
 	if (0 <= v) {
@@ -1441,7 +1326,7 @@
 		return v;
 	}
 	*is_bool = 0;
-	return git_config_int(name, value);
+	return git_config_int(name, value, kvi);
 }
 
 int git_config_bool(const char *name, const char *value)
@@ -1489,7 +1374,8 @@
 	return 0;
 }
 
-static int git_default_core_config(const char *var, const char *value, void *cb)
+static int git_default_core_config(const char *var, const char *value,
+				   const struct config_context *ctx, void *cb)
 {
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
@@ -1501,10 +1387,15 @@
 		return 0;
 	}
 	if (!strcmp(var, "core.checkstat")) {
+		if (!value)
+			return config_error_nonbool(var);
 		if (!strcasecmp(value, "default"))
 			check_stat = 1;
 		else if (!strcasecmp(value, "minimal"))
 			check_stat = 0;
+		else
+			return error(_("invalid value for '%s': '%s'"),
+				     var, value);
 	}
 
 	if (!strcmp(var, "core.quotepath")) {
@@ -1566,7 +1457,7 @@
 		else if (!git_parse_maybe_bool_text(value))
 			default_abbrev = the_hash_algo->hexsz;
 		else {
-			int abbrev = git_config_int(var, value);
+			int abbrev = git_config_int(var, value, ctx->kvi);
 			if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
 				return error(_("abbrev length out of range: %d"), abbrev);
 			default_abbrev = abbrev;
@@ -1578,7 +1469,7 @@
 		return set_disambiguate_hint_config(var, value);
 
 	if (!strcmp(var, "core.loosecompression")) {
-		int level = git_config_int(var, value);
+		int level = git_config_int(var, value, ctx->kvi);
 		if (level == -1)
 			level = Z_DEFAULT_COMPRESSION;
 		else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1589,7 +1480,7 @@
 	}
 
 	if (!strcmp(var, "core.compression")) {
-		int level = git_config_int(var, value);
+		int level = git_config_int(var, value, ctx->kvi);
 		if (level == -1)
 			level = Z_DEFAULT_COMPRESSION;
 		else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1603,7 +1494,7 @@
 
 	if (!strcmp(var, "core.packedgitwindowsize")) {
 		int pgsz_x2 = getpagesize() * 2;
-		packed_git_window_size = git_config_ulong(var, value);
+		packed_git_window_size = git_config_ulong(var, value, ctx->kvi);
 
 		/* This value must be multiple of (pagesize * 2) */
 		packed_git_window_size /= pgsz_x2;
@@ -1614,17 +1505,17 @@
 	}
 
 	if (!strcmp(var, "core.bigfilethreshold")) {
-		big_file_threshold = git_config_ulong(var, value);
+		big_file_threshold = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (!strcmp(var, "core.packedgitlimit")) {
-		packed_git_limit = git_config_ulong(var, value);
+		packed_git_limit = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (!strcmp(var, "core.deltabasecachelimit")) {
-		delta_base_cache_limit = git_config_ulong(var, value);
+		delta_base_cache_limit = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -1661,12 +1552,12 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.checkroundtripencoding")) {
-		check_roundtrip_encoding = xstrdup(value);
-		return 0;
-	}
+	if (!strcmp(var, "core.checkroundtripencoding"))
+		return git_config_string(&check_roundtrip_encoding, var, value);
 
 	if (!strcmp(var, "core.notesref")) {
+		if (!value)
+			return config_error_nonbool(var);
 		notes_ref_name = xstrdup(value);
 		return 0;
 	}
@@ -1674,16 +1565,19 @@
 	if (!strcmp(var, "core.editor"))
 		return git_config_string(&editor_program, var, value);
 
-	if (!strcmp(var, "core.commentchar")) {
+	if (!strcmp(var, "core.commentchar") ||
+	    !strcmp(var, "core.commentstring")) {
 		if (!value)
 			return config_error_nonbool(var);
 		else if (!strcasecmp(value, "auto"))
 			auto_comment_line_char = 1;
-		else if (value[0] && !value[1]) {
-			comment_line_char = value[0];
+		else if (value[0]) {
+			if (strchr(value, '\n'))
+				return error(_("%s cannot contain newline"), var);
+			comment_line_str = xstrdup(value);
 			auto_comment_line_char = 0;
 		} else
-			return error(_("core.commentChar should only be one character"));
+			return error(_("%s must have at least one character"), var);
 		return 0;
 	}
 
@@ -1736,6 +1630,8 @@
 	}
 
 	if (!strcmp(var, "core.createobject")) {
+		if (!value)
+			return config_error_nonbool(var);
 		if (!strcmp(value, "rename"))
 			object_creation_mode = OBJECT_CREATION_USES_RENAMES;
 		else if (!strcmp(value, "link"))
@@ -1770,13 +1666,13 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.usereplacerefs")) {
-		read_replace_refs = git_config_bool(var, value);
+	if (!strcmp(var, "core.maxtreedepth")) {
+		max_allowed_tree_depth = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
 	/* Add other config variables here and to Documentation/config.txt. */
-	return platform_core_config(var, value, cb);
+	return platform_core_config(var, value, ctx, cb);
 }
 
 static int git_default_sparse_config(const char *var, const char *value)
@@ -1878,15 +1774,28 @@
 	return 0;
 }
 
-int git_default_config(const char *var, const char *value, void *cb)
+static int git_default_attr_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "attr.tree"))
+		return git_config_string(&git_attr_tree, var, value);
+
+	/*
+	 * Add other attribute related config variables here and to
+	 * Documentation/config/attr.txt.
+	 */
+	return 0;
+}
+
+int git_default_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (starts_with(var, "core."))
-		return git_default_core_config(var, value, cb);
+		return git_default_core_config(var, value, ctx, cb);
 
 	if (starts_with(var, "user.") ||
 	    starts_with(var, "author.") ||
 	    starts_with(var, "committer."))
-		return git_ident_config(var, value, cb);
+		return git_ident_config(var, value, ctx, cb);
 
 	if (starts_with(var, "i18n."))
 		return git_default_i18n_config(var, value);
@@ -1900,6 +1809,9 @@
 	if (starts_with(var, "mailmap."))
 		return git_default_mailmap_config(var, value);
 
+	if (starts_with(var, "attr."))
+		return git_default_attr_config(var, value);
+
 	if (starts_with(var, "advice.") || starts_with(var, "color.advice"))
 		return git_default_advice_config(var, value);
 
@@ -1909,12 +1821,12 @@
 	}
 
 	if (!strcmp(var, "pack.packsizelimit")) {
-		pack_size_limit_cfg = git_config_ulong(var, value);
+		pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi);
 		return 0;
 	}
 
 	if (!strcmp(var, "pack.compression")) {
-		int level = git_config_int(var, value);
+		int level = git_config_int(var, value, ctx->kvi);
 		if (level == -1)
 			level = Z_DEFAULT_COMPRESSION;
 		else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1936,36 +1848,36 @@
  * fgetc, ungetc, ftell of top need to be initialized before calling
  * this function.
  */
-static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
+static int do_config_from(struct config_source *top, config_fn_t fn,
+			  void *data, enum config_scope scope,
 			  const struct config_options *opts)
 {
+	struct key_value_info kvi = KVI_INIT;
 	int ret;
 
 	/* push config-file parsing state stack */
-	top->prev = cf;
 	top->linenr = 1;
 	top->eof = 0;
 	top->total_len = 0;
 	strbuf_init(&top->value, 1024);
 	strbuf_init(&top->var, 1024);
-	cf = top;
+	kvi_from_source(top, scope, &kvi);
 
-	ret = git_parse_source(fn, data, opts);
+	ret = git_parse_source(top, fn, &kvi, data, opts);
 
-	/* pop config-file parsing state stack */
 	strbuf_release(&top->value);
 	strbuf_release(&top->var);
-	cf = top->prev;
 
 	return ret;
 }
 
 static int do_config_from_file(config_fn_t fn,
-		const enum config_origin_type origin_type,
-		const char *name, const char *path, FILE *f,
-		void *data, const struct config_options *opts)
+			       const enum config_origin_type origin_type,
+			       const char *name, const char *path, FILE *f,
+			       void *data, enum config_scope scope,
+			       const struct config_options *opts)
 {
-	struct config_source top;
+	struct config_source top = CONFIG_SOURCE_INIT;
 	int ret;
 
 	top.u.file = f;
@@ -1978,19 +1890,20 @@
 	top.do_ftell = config_file_ftell;
 
 	flockfile(f);
-	ret = do_config_from(&top, fn, data, opts);
+	ret = do_config_from(&top, fn, data, scope, opts);
 	funlockfile(f);
 	return ret;
 }
 
-static int git_config_from_stdin(config_fn_t fn, void *data)
+static int git_config_from_stdin(config_fn_t fn, void *data,
+				 enum config_scope scope)
 {
 	return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
-				   data, NULL);
+				   data, scope, NULL);
 }
 
 int git_config_from_file_with_options(config_fn_t fn, const char *filename,
-				      void *data,
+				      void *data, enum config_scope scope,
 				      const struct config_options *opts)
 {
 	int ret = -1;
@@ -2001,7 +1914,7 @@
 	f = fopen_or_warn(filename, "r");
 	if (f) {
 		ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
-					  filename, f, data, opts);
+					  filename, f, data, scope, opts);
 		fclose(f);
 	}
 	return ret;
@@ -2009,15 +1922,17 @@
 
 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 {
-	return git_config_from_file_with_options(fn, filename, data, NULL);
+	return git_config_from_file_with_options(fn, filename, data,
+						 CONFIG_SCOPE_UNKNOWN, NULL);
 }
 
 int git_config_from_mem(config_fn_t fn,
 			const enum config_origin_type origin_type,
 			const char *name, const char *buf, size_t len,
-			void *data, const struct config_options *opts)
+			void *data, enum config_scope scope,
+			const struct config_options *opts)
 {
-	struct config_source top;
+	struct config_source top = CONFIG_SOURCE_INIT;
 
 	top.u.buf.buf = buf;
 	top.u.buf.len = len;
@@ -2030,14 +1945,15 @@
 	top.do_ungetc = config_buf_ungetc;
 	top.do_ftell = config_buf_ftell;
 
-	return do_config_from(&top, fn, data, opts);
+	return do_config_from(&top, fn, data, scope, opts);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
 			      const char *name,
 			      struct repository *repo,
 			      const struct object_id *oid,
-			      void *data)
+			      void *data,
+			      enum config_scope scope)
 {
 	enum object_type type;
 	char *buf;
@@ -2053,7 +1969,7 @@
 	}
 
 	ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size,
-				  data, NULL);
+				  data, scope, NULL);
 	free(buf);
 
 	return ret;
@@ -2062,13 +1978,14 @@
 static int git_config_from_blob_ref(config_fn_t fn,
 				    struct repository *repo,
 				    const char *name,
-				    void *data)
+				    void *data,
+				    enum config_scope scope)
 {
 	struct object_id oid;
 
 	if (repo_get_oid(repo, name, &oid) < 0)
 		return error(_("unable to resolve config blob '%s'"), name);
-	return git_config_from_blob_oid(fn, name, repo, &oid, data);
+	return git_config_from_blob_oid(fn, name, repo, &oid, data, scope);
 }
 
 char *git_system_config(void)
@@ -2080,7 +1997,27 @@
 	return system_config;
 }
 
-void git_global_config(char **user_out, char **xdg_out)
+char *git_global_config(void)
+{
+	char *user_config, *xdg_config;
+
+	git_global_config_paths(&user_config, &xdg_config);
+	if (!user_config) {
+		free(xdg_config);
+		return NULL;
+	}
+
+	if (access_or_warn(user_config, R_OK, 0) && xdg_config &&
+	    !access_or_warn(xdg_config, R_OK, 0)) {
+		free(user_config);
+		return xdg_config;
+	} else {
+		free(xdg_config);
+		return user_config;
+	}
+}
+
+void git_global_config_paths(char **user_out, char **xdg_out)
 {
 	char *user_config = xstrdup_or_null(getenv("GIT_CONFIG_GLOBAL"));
 	char *xdg_config = NULL;
@@ -2094,34 +2031,13 @@
 	*xdg_out = xdg_config;
 }
 
-/*
- * Parse environment variable 'k' as a boolean (in various
- * possible spellings); if missing, use the default value 'def'.
- */
-int git_env_bool(const char *k, int def)
-{
-	const char *v = getenv(k);
-	return v ? git_config_bool(k, v) : def;
-}
-
-/*
- * Parse environment variable 'k' as ulong with possibly a unit
- * suffix; if missing, use the default value 'val'.
- */
-unsigned long git_env_ulong(const char *k, unsigned long val)
-{
-	const char *v = getenv(k);
-	if (v && !git_parse_ulong(v, &val))
-		die(_("failed to parse %s"), k);
-	return val;
-}
-
 int git_config_system(void)
 {
 	return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
 static int do_git_config_sequence(const struct config_options *opts,
+				  const struct repository *repo,
 				  config_fn_t fn, void *data)
 {
 	int ret = 0;
@@ -2129,57 +2045,68 @@
 	char *xdg_config = NULL;
 	char *user_config = NULL;
 	char *repo_config;
-	enum config_scope prev_parsing_scope = current_parsing_scope;
+	char *worktree_config;
 
-	if (opts->commondir)
+	/*
+	 * Ensure that either:
+	 * - the git_dir and commondir are both set, or
+	 * - the git_dir and commondir are both NULL
+	 */
+	if (!opts->git_dir != !opts->commondir)
+		BUG("only one of commondir and git_dir is non-NULL");
+
+	if (opts->commondir) {
 		repo_config = mkpathdup("%s/config", opts->commondir);
-	else if (opts->git_dir)
-		BUG("git_dir without commondir");
-	else
+		worktree_config = mkpathdup("%s/config.worktree", opts->git_dir);
+	} else {
 		repo_config = NULL;
+		worktree_config = NULL;
+	}
 
-	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
 	if (git_config_system() && system_config &&
 	    !access_or_die(system_config, R_OK,
 			   opts->system_gently ? ACCESS_EACCES_OK : 0))
-		ret += git_config_from_file(fn, system_config, data);
+		ret += git_config_from_file_with_options(fn, system_config,
+							 data, CONFIG_SCOPE_SYSTEM,
+							 NULL);
 
-	current_parsing_scope = CONFIG_SCOPE_GLOBAL;
-	git_global_config(&user_config, &xdg_config);
+	git_global_config_paths(&user_config, &xdg_config);
 
 	if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
-		ret += git_config_from_file(fn, xdg_config, data);
+		ret += git_config_from_file_with_options(fn, xdg_config, data,
+							 CONFIG_SCOPE_GLOBAL, NULL);
 
 	if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
-		ret += git_config_from_file(fn, user_config, data);
+		ret += git_config_from_file_with_options(fn, user_config, data,
+							 CONFIG_SCOPE_GLOBAL, NULL);
 
-	current_parsing_scope = CONFIG_SCOPE_LOCAL;
 	if (!opts->ignore_repo && repo_config &&
 	    !access_or_die(repo_config, R_OK, 0))
-		ret += git_config_from_file(fn, repo_config, data);
+		ret += git_config_from_file_with_options(fn, repo_config, data,
+							 CONFIG_SCOPE_LOCAL, NULL);
 
-	current_parsing_scope = CONFIG_SCOPE_WORKTREE;
-	if (!opts->ignore_worktree && repository_format_worktree_config) {
-		char *path = git_pathdup("config.worktree");
-		if (!access_or_die(path, R_OK, 0))
-			ret += git_config_from_file(fn, path, data);
-		free(path);
+	if (!opts->ignore_worktree && worktree_config &&
+	    repo && repo->repository_format_worktree_config &&
+	    !access_or_die(worktree_config, R_OK, 0)) {
+			ret += git_config_from_file_with_options(fn, worktree_config, data,
+								 CONFIG_SCOPE_WORKTREE,
+								 NULL);
 	}
 
-	current_parsing_scope = CONFIG_SCOPE_COMMAND;
 	if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
 		die(_("unable to parse command-line config"));
 
-	current_parsing_scope = prev_parsing_scope;
 	free(system_config);
 	free(xdg_config);
 	free(user_config);
 	free(repo_config);
+	free(worktree_config);
 	return ret;
 }
 
 int config_with_options(config_fn_t fn, void *data,
 			struct git_config_source *config_source,
+			struct repository *repo,
 			const struct config_options *opts)
 {
 	struct config_include_data inc = CONFIG_INCLUDE_INIT;
@@ -2189,29 +2116,27 @@
 		inc.fn = fn;
 		inc.data = data;
 		inc.opts = opts;
+		inc.repo = repo;
 		inc.config_source = config_source;
 		fn = git_config_include;
 		data = &inc;
 	}
 
-	if (config_source)
-		current_parsing_scope = config_source->scope;
-
 	/*
 	 * If we have a specific filename, use it. Otherwise, follow the
 	 * regular lookup sequence.
 	 */
 	if (config_source && config_source->use_stdin) {
-		ret = git_config_from_stdin(fn, data);
+		ret = git_config_from_stdin(fn, data, config_source->scope);
 	} else if (config_source && config_source->file) {
-		ret = git_config_from_file(fn, config_source->file, data);
+		ret = git_config_from_file_with_options(fn, config_source->file,
+							data, config_source->scope,
+							NULL);
 	} else if (config_source && config_source->blob) {
-		struct repository *repo = config_source->repo ?
-			config_source->repo : the_repository;
 		ret = git_config_from_blob_ref(fn, repo, config_source->blob,
-						data);
+					       data, config_source->scope);
 	} else {
-		ret = do_git_config_sequence(opts, fn, data);
+		ret = do_git_config_sequence(opts, repo, fn, data);
 	}
 
 	if (inc.remote_urls) {
@@ -2221,26 +2146,24 @@
 	return ret;
 }
 
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_set *set, config_fn_t fn, void *data)
 {
 	int i, value_index;
 	struct string_list *values;
 	struct config_set_element *entry;
-	struct configset_list *list = &cs->list;
+	struct configset_list *list = &set->list;
+	struct config_context ctx = CONFIG_CONTEXT_INIT;
 
 	for (i = 0; i < list->nr; i++) {
 		entry = list->items[i].e;
 		value_index = list->items[i].value_index;
 		values = &entry->value_list;
 
-		current_config_kvi = values->items[value_index].util;
-
-		if (fn(entry->key, values->items[value_index].string, data) < 0)
+		ctx.kvi = values->items[value_index].util;
+		if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
 			git_die_config_linenr(entry->key,
-					      current_config_kvi->filename,
-					      current_config_kvi->linenr);
-
-		current_config_kvi = NULL;
+					      ctx.kvi->filename,
+					      ctx.kvi->linenr);
 	}
 }
 
@@ -2268,7 +2191,7 @@
 		opts.git_dir = gitdir.buf;
 	}
 
-	config_with_options(cb, data, NULL, &opts);
+	config_with_options(cb, data, NULL, NULL, &opts);
 
 	strbuf_release(&commondir);
 	strbuf_release(&gitdir);
@@ -2288,36 +2211,47 @@
 	opts.ignore_cmdline = 1;
 	opts.system_gently = 1;
 
-	config_with_options(cb, data, NULL, &opts);
+	config_with_options(cb, data, NULL, NULL, &opts);
 }
 
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+RESULT_MUST_BE_USED
+static int configset_find_element(struct config_set *set, const char *key,
+				  struct config_set_element **dest)
 {
 	struct config_set_element k;
 	struct config_set_element *found_entry;
 	char *normalized_key;
+	int ret;
+
 	/*
 	 * `key` may come from the user, so normalize it before using it
 	 * for querying entries from the hashmap.
 	 */
-	if (git_config_parse_key(key, &normalized_key, NULL))
-		return NULL;
+	ret = git_config_parse_key(key, &normalized_key, NULL);
+	if (ret)
+		return ret;
 
 	hashmap_entry_init(&k.ent, strhash(normalized_key));
 	k.key = normalized_key;
-	found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
+	found_entry = hashmap_get_entry(&set->config_hash, &k, ent, NULL);
 	free(normalized_key);
-	return found_entry;
+	*dest = found_entry;
+	return 0;
 }
 
-static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+static int configset_add_value(const struct key_value_info *kvi_p,
+			       struct config_set *set, const char *key,
+			       const char *value)
 {
 	struct config_set_element *e;
 	struct string_list_item *si;
 	struct configset_list_item *l_item;
 	struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
+	int ret;
 
-	e = configset_find_element(cs, key);
+	ret = configset_find_element(set, key, &e);
+	if (ret)
+		return ret;
 	/*
 	 * Since the keys are being fed by git_config*() callback mechanism, they
 	 * are already normalized. So simply add them without any further munging.
@@ -2327,28 +2261,16 @@
 		hashmap_entry_init(&e->ent, strhash(key));
 		e->key = xstrdup(key);
 		string_list_init_dup(&e->value_list);
-		hashmap_add(&cs->config_hash, &e->ent);
+		hashmap_add(&set->config_hash, &e->ent);
 	}
 	si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
 
-	ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
-	l_item = &cs->list.items[cs->list.nr++];
+	ALLOC_GROW(set->list.items, set->list.nr + 1, set->list.alloc);
+	l_item = &set->list.items[set->list.nr++];
 	l_item->e = e;
 	l_item->value_index = e->value_list.nr - 1;
 
-	if (!cf)
-		BUG("configset_add_value has no source");
-	if (cf->name) {
-		kv_info->filename = strintern(cf->name);
-		kv_info->linenr = cf->linenr;
-		kv_info->origin_type = cf->origin_type;
-	} else {
-		/* for values read from `git_config_from_parameters()` */
-		kv_info->filename = NULL;
-		kv_info->linenr = -1;
-		kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
-	}
-	kv_info->scope = current_parsing_scope;
+	*kv_info = *kvi_p;
 	si->util = kv_info;
 
 	return 0;
@@ -2367,84 +2289,131 @@
 	return strcmp(e1->key, e2->key);
 }
 
-void git_configset_init(struct config_set *cs)
+void git_configset_init(struct config_set *set)
 {
-	hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
-	cs->hash_initialized = 1;
-	cs->list.nr = 0;
-	cs->list.alloc = 0;
-	cs->list.items = NULL;
+	hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
+	set->hash_initialized = 1;
+	set->list.nr = 0;
+	set->list.alloc = 0;
+	set->list.items = NULL;
 }
 
-void git_configset_clear(struct config_set *cs)
+void git_configset_clear(struct config_set *set)
 {
 	struct config_set_element *entry;
 	struct hashmap_iter iter;
-	if (!cs->hash_initialized)
+	if (!set->hash_initialized)
 		return;
 
-	hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+	hashmap_for_each_entry(&set->config_hash, &iter, entry,
 				ent /* member name */) {
 		free(entry->key);
 		string_list_clear(&entry->value_list, 1);
 	}
-	hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
-	cs->hash_initialized = 0;
-	free(cs->list.items);
-	cs->list.nr = 0;
-	cs->list.alloc = 0;
-	cs->list.items = NULL;
+	hashmap_clear_and_free(&set->config_hash, struct config_set_element, ent);
+	set->hash_initialized = 0;
+	free(set->list.items);
+	set->list.nr = 0;
+	set->list.alloc = 0;
+	set->list.items = NULL;
 }
 
-static int config_set_callback(const char *key, const char *value, void *cb)
+static int config_set_callback(const char *key, const char *value,
+			       const struct config_context *ctx,
+			       void *cb)
 {
-	struct config_set *cs = cb;
-	configset_add_value(cs, key, value);
+	struct config_set *set = cb;
+	configset_add_value(ctx->kvi, set, key, value);
 	return 0;
 }
 
-int git_configset_add_file(struct config_set *cs, const char *filename)
+int git_configset_add_file(struct config_set *set, const char *filename)
 {
-	return git_config_from_file(config_set_callback, filename, cs);
+	return git_config_from_file(config_set_callback, filename, set);
 }
 
-int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+int git_configset_get_value(struct config_set *set, const char *key,
+			    const char **value, struct key_value_info *kvi)
 {
 	const struct string_list *values = NULL;
+	int ret;
+	struct string_list_item item;
 	/*
 	 * Follows "last one wins" semantic, i.e., if there are multiple matches for the
 	 * queried key in the files of the configset, the value returned will be the last
 	 * value in the value list for that key.
 	 */
-	values = git_configset_get_value_multi(cs, key);
+	if ((ret = git_configset_get_value_multi(set, key, &values)))
+		return ret;
 
-	if (!values)
-		return 1;
 	assert(values->nr > 0);
-	*value = values->items[values->nr - 1].string;
+	item = values->items[values->nr - 1];
+	*value = item.string;
+	if (kvi)
+		*kvi = *((struct key_value_info *)item.util);
 	return 0;
 }
 
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+int git_configset_get_value_multi(struct config_set *set, const char *key,
+				  const struct string_list **dest)
 {
-	struct config_set_element *e = configset_find_element(cs, key);
-	return e ? &e->value_list : NULL;
+	struct config_set_element *e;
+	int ret;
+
+	if ((ret = configset_find_element(set, key, &e)))
+		return ret;
+	else if (!e)
+		return 1;
+	*dest = &e->value_list;
+
+	return 0;
 }
 
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+static int check_multi_string(struct string_list_item *item, void *util)
+{
+	return item->string ? 0 : config_error_nonbool(util);
+}
+
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+				   const struct string_list **dest)
+{
+	int ret;
+
+	if ((ret = git_configset_get_value_multi(cs, key, dest)))
+		return ret;
+	if ((ret = for_each_string_list((struct string_list *)*dest,
+					check_multi_string, (void *)key)))
+		return ret;
+
+	return 0;
+}
+
+int git_configset_get(struct config_set *set, const char *key)
+{
+	struct config_set_element *e;
+	int ret;
+
+	if ((ret = configset_find_element(set, key, &e)))
+		return ret;
+	else if (!e)
+		return 1;
+	return 0;
+}
+
+int git_configset_get_string(struct config_set *set, const char *key, char **dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value))
+	if (!git_configset_get_value(set, key, &value, NULL))
 		return git_config_string((const char **)dest, key, value);
 	else
 		return 1;
 }
 
-static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+static int git_configset_get_string_tmp(struct config_set *set, const char *key,
 					const char **dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value)) {
+	if (!git_configset_get_value(set, key, &value, NULL)) {
 		if (!value)
 			return config_error_nonbool(key);
 		*dest = value;
@@ -2454,51 +2423,57 @@
 	}
 }
 
-int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_int(struct config_set *set, const char *key, int *dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value)) {
-		*dest = git_config_int(key, value);
+	struct key_value_info kvi;
+
+	if (!git_configset_get_value(set, key, &value, &kvi)) {
+		*dest = git_config_int(key, value, &kvi);
 		return 0;
 	} else
 		return 1;
 }
 
-int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value)) {
-		*dest = git_config_ulong(key, value);
+	struct key_value_info kvi;
+
+	if (!git_configset_get_value(set, key, &value, &kvi)) {
+		*dest = git_config_ulong(key, value, &kvi);
 		return 0;
 	} else
 		return 1;
 }
 
-int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_bool(struct config_set *set, const char *key, int *dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value)) {
+	if (!git_configset_get_value(set, key, &value, NULL)) {
 		*dest = git_config_bool(key, value);
 		return 0;
 	} else
 		return 1;
 }
 
-int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+int git_configset_get_bool_or_int(struct config_set *set, const char *key,
 				int *is_bool, int *dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value)) {
-		*dest = git_config_bool_or_int(key, value, is_bool);
+	struct key_value_info kvi;
+
+	if (!git_configset_get_value(set, key, &value, &kvi)) {
+		*dest = git_config_bool_or_int(key, value, &kvi, is_bool);
 		return 0;
 	} else
 		return 1;
 }
 
-int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value)) {
+	if (!git_configset_get_value(set, key, &value, NULL)) {
 		*dest = git_parse_maybe_bool(value);
 		if (*dest == -1)
 			return -1;
@@ -2507,10 +2482,10 @@
 		return 1;
 }
 
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
 {
 	const char *value;
-	if (!git_configset_get_value(cs, key, &value))
+	if (!git_configset_get_value(set, key, &value, NULL))
 		return git_config_pathname(dest, key, value);
 	else
 		return 1;
@@ -2531,8 +2506,8 @@
 		git_configset_clear(repo->config);
 
 	git_configset_init(repo->config);
-
-	if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+	if (config_with_options(config_set_callback, repo->config, NULL,
+				repo, &opts) < 0)
 		/*
 		 * config_with_options() normally returns only
 		 * zero, as most errors are fatal, and
@@ -2567,18 +2542,31 @@
 	configset_iter(repo->config, fn, data);
 }
 
+int repo_config_get(struct repository *repo, const char *key)
+{
+	git_config_check_init(repo);
+	return git_configset_get(repo->config, key);
+}
+
 int repo_config_get_value(struct repository *repo,
 			  const char *key, const char **value)
 {
 	git_config_check_init(repo);
-	return git_configset_get_value(repo->config, key, value);
+	return git_configset_get_value(repo->config, key, value, NULL);
 }
 
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
-						      const char *key)
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+				const struct string_list **dest)
 {
 	git_config_check_init(repo);
-	return git_configset_get_value_multi(repo->config, key);
+	return git_configset_get_value_multi(repo->config, key, dest);
+}
+
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+				 const struct string_list **dest)
+{
+	git_config_check_init(repo);
+	return git_configset_get_string_multi(repo->config, key, dest);
 }
 
 int repo_config_get_string(struct repository *repo,
@@ -2658,8 +2646,9 @@
 		.ignore_worktree = 1,
 		.system_gently = 1,
 	};
+
 	git_configset_init(&protected_config);
-	config_with_options(config_set_callback, &protected_config,
+	config_with_options(config_set_callback, &protected_config, NULL,
 			    NULL, &opts);
 }
 
@@ -2681,14 +2670,25 @@
 	repo_config_clear(the_repository);
 }
 
+int git_config_get(const char *key)
+{
+	return repo_config_get(the_repository, key);
+}
+
 int git_config_get_value(const char *key, const char **value)
 {
 	return repo_config_get_value(the_repository, key, value);
 }
 
-const struct string_list *git_config_get_value_multi(const char *key)
+int git_config_get_value_multi(const char *key, const struct string_list **dest)
 {
-	return repo_config_get_value_multi(the_repository, key);
+	return repo_config_get_value_multi(the_repository, key, dest);
+}
+
+int git_config_get_string_multi(const char *key,
+				const struct string_list **dest)
+{
+	return repo_config_get_string_multi(the_repository, key, dest);
 }
 
 int git_config_get_string(const char *key, char **dest)
@@ -2835,7 +2835,8 @@
 		error_fn(err, params);
 		va_end(params);
 	}
-	values = git_config_get_value_multi(key);
+	if (git_config_get_value_multi(key, &values))
+		BUG("for key '%s' we must have a value to report on", key);
 	kv_info = values->items[values->nr - 1].util;
 	git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
 }
@@ -2859,6 +2860,7 @@
 	unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
 	unsigned int key_seen:1, section_seen:1, is_keys_section:1;
 };
+#define CONFIG_STORE_INIT { 0 }
 
 static void config_store_data_clear(struct config_store_data *store)
 {
@@ -2889,8 +2891,8 @@
 		(value && !regexec(store->value_pattern, value, 0, NULL, 0));
 }
 
-static int store_aux_event(enum config_event_t type,
-			   size_t begin, size_t end, void *data)
+static int store_aux_event(enum config_event_t type, size_t begin, size_t end,
+			   struct config_source *cs, void *data)
 {
 	struct config_store_data *store = data;
 
@@ -2902,10 +2904,10 @@
 	if (type == CONFIG_EVENT_SECTION) {
 		int (*cmpfn)(const char *, const char *, size_t);
 
-		if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
-			return error(_("invalid section name '%s'"), cf->var.buf);
+		if (cs->var.len < 2 || cs->var.buf[cs->var.len - 1] != '.')
+			return error(_("invalid section name '%s'"), cs->var.buf);
 
-		if (cf->subsection_case_sensitive)
+		if (cs->subsection_case_sensitive)
 			cmpfn = strncasecmp;
 		else
 			cmpfn = strncmp;
@@ -2913,8 +2915,8 @@
 		/* Is this the section we were looking for? */
 		store->is_keys_section =
 			store->parsed[store->parsed_nr].is_keys_section =
-			cf->var.len - 1 == store->baselen &&
-			!cmpfn(cf->var.buf, store->key, store->baselen);
+			cs->var.len - 1 == store->baselen &&
+			!cmpfn(cs->var.buf, store->key, store->baselen);
 		if (store->is_keys_section) {
 			store->section_seen = 1;
 			ALLOC_GROW(store->seen, store->seen_nr + 1,
@@ -2928,7 +2930,8 @@
 	return 0;
 }
 
-static int store_aux(const char *key, const char *value, void *cb)
+static int store_aux(const char *key, const char *value,
+		     const struct config_context *ctx UNUSED, void *cb)
 {
 	struct config_store_data *store = cb;
 
@@ -3008,6 +3011,7 @@
 }
 
 static ssize_t write_pair(int fd, const char *key, const char *value,
+			  const char *comment,
 			  const struct config_store_data *store)
 {
 	int i;
@@ -3048,7 +3052,11 @@
 			strbuf_addch(&sb, value[i]);
 			break;
 		}
-	strbuf_addf(&sb, "%s\n", quote);
+
+	if (comment)
+		strbuf_addf(&sb, "%s%s\n", quote, comment);
+	else
+		strbuf_addf(&sb, "%s\n", quote);
 
 	ret = write_in_full(fd, sb.buf, sb.len);
 	strbuf_release(&sb);
@@ -3137,9 +3145,9 @@
 }
 
 int git_config_set_in_file_gently(const char *config_filename,
-				  const char *key, const char *value)
+				  const char *key, const char *comment, const char *value)
 {
-	return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
+	return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
 }
 
 void git_config_set_in_file(const char *config_filename,
@@ -3157,10 +3165,10 @@
 				    const char *key, const char *value)
 {
 	/* Only use worktree-specific config if it is already enabled. */
-	if (repository_format_worktree_config) {
+	if (r->repository_format_worktree_config) {
 		char *file = repo_git_path(r, "config.worktree");
 		int ret = git_config_set_multivar_in_file_gently(
-					file, key, value, NULL, 0);
+					file, key, value, NULL, NULL, 0);
 		free(file);
 		return ret;
 	}
@@ -3175,6 +3183,62 @@
 }
 
 /*
+ * The ownership rule is that the caller will own the string
+ * if it receives a piece of memory different from what it passed
+ * as the parameter.
+ */
+const char *git_config_prepare_comment_string(const char *comment)
+{
+	size_t leading_blanks;
+
+	if (!comment)
+		return NULL;
+
+	if (strchr(comment, '\n'))
+		die(_("no multi-line comment allowed: '%s'"), comment);
+
+	/*
+	 * If it begins with one or more leading whitespace characters
+	 * followed by '#", the comment string is used as-is.
+	 *
+	 * If it begins with '#', a SP is inserted between the comment
+	 * and the value the comment is about.
+	 *
+	 * Otherwise, the value is followed by a SP followed by '#'
+	 * followed by SP and then the comment string comes.
+	 */
+
+	leading_blanks = strspn(comment, " \t");
+	if (leading_blanks && comment[leading_blanks] == '#')
+		; /* use it as-is */
+	else if (comment[0] == '#')
+		comment = xstrfmt(" %s", comment);
+	else
+		comment = xstrfmt(" # %s", comment);
+
+	return comment;
+}
+
+static void validate_comment_string(const char *comment)
+{
+	size_t leading_blanks;
+
+	if (!comment)
+		return;
+	/*
+	 * The front-end must have massaged the comment string
+	 * properly before calling us.
+	 */
+	if (strchr(comment, '\n'))
+		BUG("multi-line comments are not permitted: '%s'", comment);
+
+	leading_blanks = strspn(comment, " \t");
+	if (!leading_blanks || comment[leading_blanks] != '#')
+		BUG("comment must begin with one or more SP followed by '#': '%s'",
+		    comment);
+}
+
+/*
  * If value==NULL, unset in (remove from) config,
  * if value_pattern!=NULL, disregard key/value pairs where value does not match.
  * if value_pattern==CONFIG_REGEX_NONE, do not match any existing values
@@ -3202,6 +3266,7 @@
 int git_config_set_multivar_in_file_gently(const char *config_filename,
 					   const char *key, const char *value,
 					   const char *value_pattern,
+					   const char *comment,
 					   unsigned flags)
 {
 	int fd = -1, in_fd = -1;
@@ -3210,9 +3275,9 @@
 	char *filename_buf = NULL;
 	char *contents = NULL;
 	size_t contents_sz;
-	struct config_store_data store;
+	struct config_store_data store = CONFIG_STORE_INIT;
 
-	memset(&store, 0, sizeof(store));
+	validate_comment_string(comment);
 
 	/* parse-key returns negative; flip the sign to feed exit(3) */
 	ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
@@ -3254,7 +3319,7 @@
 		free(store.key);
 		store.key = xstrdup(key);
 		if (write_section(fd, key, &store) < 0 ||
-		    write_pair(fd, key, value, &store) < 0)
+		    write_pair(fd, key, value, comment, &store) < 0)
 			goto write_err_out;
 	} else {
 		struct stat st;
@@ -3302,7 +3367,8 @@
 		 */
 		if (git_config_from_file_with_options(store_aux,
 						      config_filename,
-						      &store, &opts)) {
+						      &store, CONFIG_SCOPE_UNKNOWN,
+						      &opts)) {
 			error(_("invalid config file %s"), config_filename);
 			ret = CONFIG_INVALID_FILE;
 			goto out_free;
@@ -3407,7 +3473,7 @@
 				if (write_section(fd, key, &store) < 0)
 					goto write_err_out;
 			}
-			if (write_pair(fd, key, value, &store) < 0)
+			if (write_pair(fd, key, value, comment, &store) < 0)
 				goto write_err_out;
 		}
 
@@ -3445,7 +3511,6 @@
 write_err_out:
 	ret = write_error(get_lock_file_path(&lock));
 	goto out_free;
-
 }
 
 void git_config_set_multivar_in_file(const char *config_filename,
@@ -3453,7 +3518,7 @@
 				     const char *value_pattern, unsigned flags)
 {
 	if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
-						    value_pattern, flags))
+						    value_pattern, NULL, flags))
 		return;
 	if (value)
 		die(_("could not set '%s' to '%s'"), key, value);
@@ -3476,7 +3541,7 @@
 	int res = git_config_set_multivar_in_file_gently(file,
 							 key, value,
 							 value_pattern,
-							 flags);
+							 NULL, flags);
 	free(file);
 	return res;
 }
@@ -3663,6 +3728,7 @@
 						output[0] = '\t';
 					}
 				} else {
+					strbuf_release(&copystr);
 					copystr = store_create_section(new_name, &store);
 				}
 			}
@@ -3709,6 +3775,7 @@
 	free(filename_buf);
 	config_store_data_clear(&store);
 	strbuf_release(&buf);
+	strbuf_release(&copystr);
 	return ret;
 }
 
@@ -3782,16 +3849,8 @@
 	return 0;
 }
 
-const char *current_config_origin_type(void)
+const char *config_origin_type_name(enum config_origin_type type)
 {
-	int type;
-	if (current_config_kvi)
-		type = current_config_kvi->origin_type;
-	else if(cf)
-		type = cf->origin_type;
-	else
-		BUG("current_config_origin_type called outside config callback");
-
 	switch (type) {
 	case CONFIG_ORIGIN_BLOB:
 		return "blob";
@@ -3828,34 +3887,6 @@
 	}
 }
 
-const char *current_config_name(void)
-{
-	const char *name;
-	if (current_config_kvi)
-		name = current_config_kvi->filename;
-	else if (cf)
-		name = cf->name;
-	else
-		BUG("current_config_name called outside config callback");
-	return name ? name : "";
-}
-
-enum config_scope current_config_scope(void)
-{
-	if (current_config_kvi)
-		return current_config_kvi->scope;
-	else
-		return current_parsing_scope;
-}
-
-int current_config_line(void)
-{
-	if (current_config_kvi)
-		return current_config_kvi->linenr;
-	else
-		return cf->linenr;
-}
-
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
 	int i;
diff --git a/config.h b/config.h
index 7606246..f4966e3 100644
--- a/config.h
+++ b/config.h
@@ -3,7 +3,8 @@
 
 #include "hashmap.h"
 #include "string-list.h"
-
+#include "repository.h"
+#include "parse.h"
 
 /**
  * The config API gives callers a way to access Git configuration files
@@ -49,13 +50,12 @@
 struct git_config_source {
 	unsigned int use_stdin:1;
 	const char *file;
-	/* The repository if blob is not NULL; leave blank for the_repository */
-	struct repository *repo;
 	const char *blob;
 	enum config_scope scope;
 };
 
 enum config_origin_type {
+	CONFIG_ORIGIN_UNKNOWN = 0,
 	CONFIG_ORIGIN_BLOB,
 	CONFIG_ORIGIN_FILE,
 	CONFIG_ORIGIN_STDIN,
@@ -72,6 +72,7 @@
 	CONFIG_EVENT_ERROR
 };
 
+struct config_source;
 /*
  * The parser event function (if not NULL) is called with the event type and
  * the begin/end offsets of the parsed elements.
@@ -81,6 +82,7 @@
  */
 typedef int (*config_parser_event_fn_t)(enum config_event_t type,
 					size_t begin_offset, size_t end_offset,
+					struct config_source *cs,
 					void *event_fn_data);
 
 struct config_options {
@@ -100,6 +102,10 @@
 
 	const char *commondir;
 	const char *git_dir;
+	/*
+	 * event_fn and event_fn_data are for internal use only. Handles events
+	 * emitted by the config parser.
+	 */
 	config_parser_event_fn_t event_fn;
 	void *event_fn_data;
 	enum config_error_action {
@@ -110,8 +116,31 @@
 	} error_action;
 };
 
+/* Config source metadata for a given config key-value pair */
+struct key_value_info {
+	const char *filename;
+	int linenr;
+	enum config_origin_type origin_type;
+	enum config_scope scope;
+	const char *path;
+};
+#define KVI_INIT { \
+	.filename = NULL, \
+	.linenr = -1, \
+	.origin_type = CONFIG_ORIGIN_UNKNOWN, \
+	.scope = CONFIG_SCOPE_UNKNOWN, \
+	.path = NULL, \
+}
+
+/* Captures additional information that a config callback can use. */
+struct config_context {
+	/* Config source metadata for key and value. */
+	const struct key_value_info *kvi;
+};
+#define CONFIG_CONTEXT_INIT { 0 }
+
 /**
- * A config callback function takes three parameters:
+ * A config callback function takes four parameters:
  *
  * - the name of the parsed variable. This is in canonical "flat" form: the
  *   section, subsection, and variable segments will be separated by dots,
@@ -122,15 +151,22 @@
  *   value specified, the value will be NULL (typically this means it
  *   should be interpreted as boolean true).
  *
+ * - the 'config context', that is, additional information about the config
+ *   iteration operation provided by the config machinery. For example, this
+ *   includes information about the config source being parsed (e.g. the
+ *   filename).
+ *
  * - a void pointer passed in by the caller of the config API; this can
  *   contain callback-specific data
  *
  * A config callback should return 0 for success, or -1 if the variable
  * could not be parsed properly.
  */
-typedef int (*config_fn_t)(const char *, const char *, void *);
+typedef int (*config_fn_t)(const char *, const char *,
+			   const struct config_context *, void *);
 
-int git_default_config(const char *, const char *, void *);
+int git_default_config(const char *, const char *,
+		       const struct config_context *, void *);
 
 /**
  * Read a specific file in git-config format.
@@ -141,16 +177,18 @@
 int git_config_from_file(config_fn_t fn, const char *, void *);
 
 int git_config_from_file_with_options(config_fn_t fn, const char *,
-				      void *,
+				      void *, enum config_scope,
 				      const struct config_options *);
 int git_config_from_mem(config_fn_t fn,
 			const enum config_origin_type,
 			const char *name,
 			const char *buf, size_t len,
-			void *data, const struct config_options *opts);
+			void *data, enum config_scope scope,
+			const struct config_options *opts);
 int git_config_from_blob_oid(config_fn_t fn, const char *name,
 			     struct repository *repo,
-			     const struct object_id *oid, void *data);
+			     const struct object_id *oid, void *data,
+			     enum config_scope scope);
 void git_config_push_parameter(const char *text);
 void git_config_push_env(const char *spec);
 int git_config_from_parameters(config_fn_t fn, void *data);
@@ -195,6 +233,7 @@
  */
 int config_with_options(config_fn_t fn, void *,
 			struct git_config_source *config_source,
+			struct repository *repo,
 			const struct config_options *opts);
 
 /**
@@ -204,36 +243,30 @@
  * The following helper functions aid in parsing string values
  */
 
-int git_parse_ssize_t(const char *, ssize_t *);
-int git_parse_ulong(const char *, unsigned long *);
-int git_parse_int(const char *value, int *ret);
-
-/**
- * Same as `git_config_bool`, except that it returns -1 on error rather
- * than dying.
- */
-int git_parse_maybe_bool(const char *);
-
 /**
  * Parse the string to an integer, including unit factors. Dies on error;
  * otherwise, returns the parsed result.
  */
-int git_config_int(const char *, const char *);
+int git_config_int(const char *, const char *, const struct key_value_info *);
 
-int64_t git_config_int64(const char *, const char *);
+int64_t git_config_int64(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 *);
+unsigned long git_config_ulong(const char *, const char *,
+			       const struct key_value_info *);
 
-ssize_t git_config_ssize_t(const char *, const char *);
+ssize_t git_config_ssize_t(const char *, const char *,
+			   const struct key_value_info *);
 
 /**
  * Same as `git_config_bool`, except that integers are returned as-is, and
  * an `is_bool` flag is unset.
  */
-int git_config_bool_or_int(const char *, const char *, int *);
+int git_config_bool_or_int(const char *, const char *,
+			   const struct key_value_info *, int *);
 
 /**
  * Parse a string into a boolean value, respecting keywords like "true" and
@@ -257,7 +290,7 @@
 
 int git_config_expiry_date(timestamp_t *, const char *, const char *);
 int git_config_color(char *, const char *, const char *);
-int git_config_set_in_file_gently(const char *, const char *, const char *);
+int git_config_set_in_file_gently(const char *, const char *, const char *, const char *);
 
 /**
  * write config values to a specific config file, takes a key/value pair as
@@ -303,7 +336,9 @@
 int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
 void git_config_set_multivar(const char *, const char *, const char *, unsigned);
 int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
-int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);
+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
+
+const char *git_config_prepare_comment_string(const char *);
 
 /**
  * takes four parameters:
@@ -342,8 +377,6 @@
 int git_config_rename_section_in_file(const char *, const char *, const char *);
 int git_config_copy_section(const char *, const char *);
 int git_config_copy_section_in_file(const char *, const char *, const char *);
-int git_env_bool(const char *, int);
-unsigned long git_env_ulong(const char *, unsigned long);
 int git_config_system(void);
 int config_error_nonbool(const char *);
 #if defined(__GNUC__)
@@ -351,14 +384,13 @@
 #endif
 
 char *git_system_config(void);
-void git_global_config(char **user, char **xdg);
+char *git_global_config(void);
+void git_global_config_paths(char **user, char **xdg);
 
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
-enum config_scope current_config_scope(void);
-const char *current_config_origin_type(void);
-const char *current_config_name(void);
-int current_config_line(void);
+const char *config_origin_type_name(enum config_origin_type type);
+void kvi_from_param(struct key_value_info *out);
 
 /*
  * Match and parse a config key of the form:
@@ -450,10 +482,31 @@
 /**
  * Finds and returns the value list, sorted in order of increasing priority
  * for the configuration variable `key` and config set `cs`. When the
- * configuration variable `key` is not found, returns NULL. The caller
- * should not free or modify the returned pointer, as it is owned by the cache.
+ * configuration variable `key` is not found, returns 1 without touching
+ * `value`.
+ *
+ * The key will be parsed for validity with git_config_parse_key(), on
+ * error a negative value will be returned.
+ *
+ * The caller should not free or modify the returned pointer, as it is
+ * owned by the cache.
  */
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+RESULT_MUST_BE_USED
+int git_configset_get_value_multi(struct config_set *cs, const char *key,
+				  const struct string_list **dest);
+
+/**
+ * A validation wrapper for git_configset_get_value_multi() which does
+ * for it what git_configset_get_string() does for
+ * git_configset_get_value().
+ *
+ * The configuration syntax allows for "[section] key", which will
+ * give us a NULL entry in the "struct string_list", as opposed to
+ * "[section] key =" which is the empty string. Most users of the API
+ * are not prepared to handle NULL in a "struct string_list".
+ */
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+				   const struct string_list **dest);
 
 /**
  * Clears `config_set` structure, removes all saved variable-value pairs.
@@ -465,6 +518,13 @@
  * value in the 'dest' pointer.
  */
 
+/**
+ * git_configset_get() returns negative values on error, see
+ * repo_config_get() below.
+ */
+RESULT_MUST_BE_USED
+int git_configset_get(struct config_set *cs, const char *key);
+
 /*
  * Finds the highest-priority value for the configuration variable `key`
  * and config set `cs`, stores the pointer to it in `value` and returns 0.
@@ -472,7 +532,8 @@
  * touching `value`. The caller should not free or modify `value`, as it
  * is owned by the cache.
  */
-int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_value(struct config_set *cs, const char *key,
+			    const char **dest, struct key_value_info *kvi);
 
 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);
@@ -485,10 +546,22 @@
 /* Functions for reading a repository's config */
 struct repository;
 void repo_config(struct repository *repo, config_fn_t fn, void *data);
+
+/**
+ * Run only the discover part of the repo_config_get_*() functions
+ * below, in addition to 1 if not found, returns negative values on
+ * error (e.g. if the key itself is invalid).
+ */
+RESULT_MUST_BE_USED
+int repo_config_get(struct repository *repo, const char *key);
 int repo_config_get_value(struct repository *repo,
 			  const char *key, const char **value);
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
-						      const char *key);
+RESULT_MUST_BE_USED
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+				const struct string_list **dest);
+RESULT_MUST_BE_USED
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+				 const struct string_list **dest);
 int repo_config_get_string(struct repository *repo,
 			   const char *key, char **dest);
 int repo_config_get_string_tmp(struct repository *repo,
@@ -521,8 +594,15 @@
  * manner, the config API provides two functions `git_config_get_value`
  * and `git_config_get_value_multi`. They both read values from an internal
  * cache generated previously from reading the config files.
+ *
+ * For those git_config_get*() functions that aren't documented,
+ * consult the corresponding repo_config_get*() function's
+ * documentation.
  */
 
+RESULT_MUST_BE_USED
+int git_config_get(const char *key);
+
 /**
  * Finds the highest-priority value for the configuration variable `key`,
  * stores the pointer to it in `value` and returns 0. When the
@@ -535,10 +615,17 @@
 /**
  * Finds and returns the value list, sorted in order of increasing priority
  * for the configuration variable `key`. When the configuration variable
- * `key` is not found, returns NULL. The caller should not free or modify
- * the returned pointer, as it is owned by the cache.
+ * `key` is not found, returns 1 without touching `value`.
+ *
+ * The caller should not free or modify the returned pointer, as it is
+ * owned by the cache.
  */
-const struct string_list *git_config_get_value_multi(const char *key);
+RESULT_MUST_BE_USED
+int git_config_get_value_multi(const char *key,
+			       const struct string_list **dest);
+RESULT_MUST_BE_USED
+int git_config_get_string_multi(const char *key,
+				const struct string_list **dest);
 
 /**
  * Resets and invalidates the config cache.
@@ -612,13 +699,6 @@
 /* parse either "this many days" integer, or "5.days.ago" approxidate */
 int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
 
-struct key_value_info {
-	const char *filename;
-	int linenr;
-	enum config_origin_type origin_type;
-	enum config_scope scope;
-};
-
 /**
  * First prints the error message specified by the caller in `err` and then
  * dies printing the line number and the file name of the highest priority
diff --git a/config.mak.uname b/config.mak.uname
index 64c44db..fcf3e2d 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -158,6 +158,19 @@
 		ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
 			MSGFMT = /usr/local/opt/gettext/bin/msgfmt
 		endif
+	# On newer ARM-based machines the default installation path has changed to
+	# /opt/homebrew. Include it in our search paths so that the user does not
+	# have to configure this manually.
+	#
+	# Note that we do not employ the same workaround as above where we manually
+	# add gettext. The issue was fixed more than three years ago by now, and at
+	# that point there haven't been any ARM-based Macs yet.
+	else ifeq ($(shell test -d /opt/homebrew/ && echo y),y)
+		BASIC_CFLAGS += -I/opt/homebrew/include
+		BASIC_LDFLAGS += -L/opt/homebrew/lib
+		ifeq ($(shell test -x /opt/homebrew/bin/msgfmt && echo y),y)
+			MSGFMT = /opt/homebrew/bin/msgfmt
+		endif
 	endif
 
 	# The builtin FSMonitor on MacOS builds upon Simple-IPC.  Both require
@@ -434,7 +447,6 @@
 	NO_POLL = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
 	NO_IPV6 = YesPlease
-	NO_UNIX_SOCKETS = YesPlease
 	NO_SETENV = YesPlease
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
@@ -526,6 +538,8 @@
 endif
 	X = .exe
 
+	EXTRA_PROGRAMS += headless-git$X
+
 compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS
 endif
 ifeq ($(uname_S),Interix)
@@ -623,6 +637,18 @@
 	SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
 	SHELL_PATH = /usr/coreutils/bin/bash
 endif
+ifeq ($(uname_S),OS/390)
+	NO_SYS_POLL_H = YesPlease
+	NO_STRCASESTR = YesPlease
+	NO_REGEX = YesPlease
+	NO_MMAP = YesPlease
+	NO_NSEC = YesPlease
+	NO_STRLCPY = YesPlease
+	NO_MEMMEM = YesPlease
+	NO_GECOS_IN_PWENT = YesPlease
+	HAVE_STRINGS_H = YesPlease
+	NEEDS_MODE_TRANSLATION = YesPlease
+endif
 ifeq ($(uname_S),MINGW)
 	ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
 		$(error "Building with MSys is no longer supported")
@@ -634,7 +660,6 @@
 	NO_LIBGEN_H = YesPlease
 	NO_POLL = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
-	NO_UNIX_SOCKETS = YesPlease
 	NO_SETENV = YesPlease
 	NO_STRCASESTR = YesPlease
 	NO_STRLCPY = YesPlease
@@ -705,6 +730,7 @@
 	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
 		-fstack-protector-strong
 	EXTLIBS += -lntdll
+	EXTRA_PROGRAMS += headless-git$X
 	INSTALL = /bin/install
 	INTERNAL_QSORT = YesPlease
 	HAVE_LIBCHARSET_H = YesPlease
diff --git a/configure.ac b/configure.ac
index 38ff866..d1a96da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -94,7 +94,7 @@
 [AC_ARG_WITH([$1],
  [AS_HELP_STRING([--with-$1=VALUE], $3)],
  if test -n "$withval"; then
-  if test "$withval" = "yes" -o "$withval" = "no"; then
+  if test "$withval" = "yes" || test "$withval" = "no"; then
     AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
 		     [a value for $1 ($2).  Maybe you do...?])
   fi
@@ -546,6 +546,8 @@
 # git-http-push are not built, and you cannot use http:// and https://
 # transports.
 
+if test -z "$NO_CURL"; then
+
 GIT_STASH_FLAGS($CURLDIR)
 
 AC_CHECK_LIB([curl], [curl_global_init],
@@ -554,6 +556,8 @@
 
 GIT_UNSTASH_FLAGS($CURLDIR)
 
+fi
+
 GIT_CONF_SUBST([NO_CURL])
 
 if test -z "$NO_CURL"; then
@@ -581,6 +585,8 @@
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
 
+if test -z "$NO_EXPAT"; then
+
 GIT_STASH_FLAGS($EXPATDIR)
 
 AC_CHECK_LIB([expat], [XML_ParserCreate],
@@ -589,6 +595,8 @@
 
 GIT_UNSTASH_FLAGS($EXPATDIR)
 
+fi
+
 GIT_CONF_SUBST([NO_EXPAT])
 
 #
@@ -636,7 +644,6 @@
 GIT_UNSTASH_FLAGS($ICONVDIR)
 
 GIT_CONF_SUBST([NEEDS_LIBICONV])
-GIT_CONF_SUBST([NO_ICONV])
 
 if test -n "$NO_ICONV"; then
     NEEDS_LIBICONV=
@@ -644,6 +651,8 @@
 
 fi
 
+GIT_CONF_SUBST([NO_ICONV])
+
 #
 # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib.
 
diff --git a/connect.c b/connect.c
index 63e5964..0d77737 100644
--- a/connect.c
+++ b/connect.c
@@ -1,6 +1,8 @@
 #include "git-compat-util.h"
-#include "cache.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "pkt-line.h"
 #include "quote.h"
 #include "refs.h"
@@ -10,7 +12,9 @@
 #include "url.h"
 #include "string-list.h"
 #include "oid-array.h"
+#include "path.h"
 #include "transport.h"
+#include "trace2.h"
 #include "strbuf.h"
 #include "version.h"
 #include "protocol.h"
@@ -19,7 +23,7 @@
 
 static char *server_capabilities_v1;
 static struct strvec server_capabilities_v2 = STRVEC_INIT;
-static const char *next_server_feature_value(const char *feature, int *len, int *offset);
+static const char *next_server_feature_value(const char *feature, size_t *len, size_t *offset);
 
 static int check_ref(const char *name, unsigned int flags)
 {
@@ -30,7 +34,8 @@
 		return 0;
 
 	/* REF_NORMAL means that we don't want the magic fake tag refs */
-	if ((flags & REF_NORMAL) && check_refname_format(name, 0))
+	if ((flags & REF_NORMAL) && check_refname_format(name,
+							 REFNAME_ALLOW_ONELEVEL))
 		return 0;
 
 	/* REF_HEADS means that we want regular branch heads */
@@ -201,10 +206,10 @@
 static void annotate_refs_with_symref_info(struct ref *ref)
 {
 	struct string_list symref = STRING_LIST_INIT_DUP;
-	int offset = 0;
+	size_t offset = 0;
 
 	while (1) {
-		int len;
+		size_t len;
 		const char *val;
 
 		val = next_server_feature_value("symref", &len, &offset);
@@ -227,7 +232,7 @@
 static void process_capabilities(struct packet_reader *reader, int *linelen)
 {
 	const char *feat_val;
-	int feat_len;
+	size_t feat_len;
 	const char *line = reader->line;
 	int nul_location = strlen(line);
 	if (nul_location == *linelen)
@@ -259,7 +264,8 @@
 		return 0;
 	name++;
 
-	return oideq(null_oid(), &oid) && !strcmp(name, "capabilities^{}");
+	return oideq(reader->hash_algo->null_oid, &oid) &&
+		!strcmp(name, "capabilities^{}");
 }
 
 static void check_no_capabilities(const char *line, int len)
@@ -591,9 +597,10 @@
 	return list;
 }
 
-const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset)
+const char *parse_feature_value(const char *feature_list, const char *feature, size_t *lenp, size_t *offset)
 {
-	int len;
+	const char *orig_start = feature_list;
+	size_t len;
 
 	if (!feature_list)
 		return NULL;
@@ -612,19 +619,19 @@
 				if (lenp)
 					*lenp = 0;
 				if (offset)
-					*offset = found + len - feature_list;
+					*offset = found + len - orig_start;
 				return value;
 			}
 			/* feature with a value (e.g., "agent=git/1.2.3") */
 			else if (*value == '=') {
-				int end;
+				size_t end;
 
 				value++;
 				end = strcspn(value, " \t\n");
 				if (lenp)
 					*lenp = end;
 				if (offset)
-					*offset = value + end - feature_list;
+					*offset = value + end - orig_start;
 				return value;
 			}
 			/*
@@ -639,8 +646,8 @@
 
 int server_supports_hash(const char *desired, int *feature_supported)
 {
-	int offset = 0;
-	int len;
+	size_t offset = 0;
+	size_t len;
 	const char *hash;
 
 	hash = next_server_feature_value("object-format", &len, &offset);
@@ -664,12 +671,12 @@
 	return !!parse_feature_value(feature_list, feature, NULL, NULL);
 }
 
-static const char *next_server_feature_value(const char *feature, int *len, int *offset)
+static const char *next_server_feature_value(const char *feature, size_t *len, size_t *offset)
 {
 	return parse_feature_value(server_capabilities_v1, feature, len, offset);
 }
 
-const char *server_feature_value(const char *feature, int *len)
+const char *server_feature_value(const char *feature, size_t *len)
 {
 	return parse_feature_value(server_capabilities_v1, feature, len, NULL);
 }
@@ -958,7 +965,7 @@
 static char *git_proxy_command;
 
 static int git_proxy_command_options(const char *var, const char *value,
-		void *cb)
+		const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "core.gitproxy")) {
 		const char *for_pos;
@@ -1004,7 +1011,7 @@
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static int git_use_proxy(const char *host)
@@ -1406,6 +1413,7 @@
  * the connection failed).
  */
 struct child_process *git_connect(int fd[2], const char *url,
+				  const char *name,
 				  const char *prog, int flags)
 {
 	char *hostandport, *path;
@@ -1415,10 +1423,11 @@
 
 	/*
 	 * NEEDSWORK: If we are trying to use protocol v2 and we are planning
-	 * to perform a push, then fallback to v0 since the client doesn't know
-	 * how to push yet using v2.
+	 * to perform any operation that doesn't involve upload-pack (i.e., a
+	 * 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-receive-pack", prog))
+	if (version == protocol_v2 && strcmp("git-upload-pack", name))
 		version = protocol_v0;
 
 	/* Without this we cannot rely on waitpid() to tell
diff --git a/connect.h b/connect.h
index b26f7de..1645126 100644
--- a/connect.h
+++ b/connect.h
@@ -7,19 +7,19 @@
 #define CONNECT_DIAG_URL      (1u << 1)
 #define CONNECT_IPV4          (1u << 2)
 #define CONNECT_IPV6          (1u << 3)
-struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
+struct child_process *git_connect(int fd[2], const char *url, const char *name, const char *prog, int flags);
 int finish_connect(struct child_process *conn);
 int git_connection_is_socket(struct child_process *conn);
 int server_supports(const char *feature);
 int parse_feature_request(const char *features, const char *feature);
-const char *server_feature_value(const char *feature, int *len_ret);
+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);
 
 int server_supports_hash(const char *desired, int *feature_supported);
-const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset);
+const char *parse_feature_value(const char *feature_list, const char *feature, size_t *lenp, size_t *offset);
 int server_supports_v2(const char *c);
 void ensure_server_supports_v2(const char *c);
 int server_feature_v2(const char *c, const char **v);
diff --git a/connected.c b/connected.c
index b90fd61..8f89376 100644
--- a/connected.c
+++ b/connected.c
@@ -1,5 +1,7 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "connected.h"
@@ -54,7 +56,7 @@
 		strbuf_release(&idx_file);
 	}
 
-	if (has_promisor_remote()) {
+	if (repo_has_promisor_remote(the_repository)) {
 		/*
 		 * For partial clones, we don't want to have to do a regular
 		 * connectivity check because we have to enumerate and exclude
@@ -97,7 +99,7 @@
 	strvec_push(&rev_list.args,"rev-list");
 	strvec_push(&rev_list.args, "--objects");
 	strvec_push(&rev_list.args, "--stdin");
-	if (has_promisor_remote())
+	if (repo_has_promisor_remote(the_repository))
 		strvec_push(&rev_list.args, "--exclude-promisor-objects");
 	if (!opt->is_deepening_fetch) {
 		strvec_push(&rev_list.args, "--not");
diff --git a/contrib/README b/contrib/README
index 05f291c..21d3d0e 100644
--- a/contrib/README
+++ b/contrib/README
@@ -23,7 +23,7 @@
 lesser degree various foreign SCM interfaces, so you know the
 drill.
 
-I expect that things that start their life in the contrib/ area
+I expect things that start their life in the contrib/ area
 to graduate out of contrib/ once they mature, either by becoming
 projects on their own, or moving to the toplevel directory.  On
 the other hand, I expect I'll be proposing removal of disused
@@ -31,7 +31,7 @@
 
 If you have new things to add to this area, please first propose
 it on the git mailing list, and after a list discussion proves
-there are some general interests (it does not have to be a
+there is general interest (it does not have to be a
 list-wide consensus for a tool targeted to a relatively narrow
 audience -- for example I do not work with projects whose
 upstream is svn, so I have no use for git-svn myself, but it is
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 2f6e019..804629c 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -227,7 +227,7 @@
 add_compile_definitions(SHA256_BLK INTERNAL_QSORT RUNTIME_PREFIX)
 add_compile_definitions(NO_OPENSSL SHA1_DC SHA1DC_NO_STANDARD_INCLUDES
 			SHA1DC_INIT_SAFE_HASH_DEFAULT=0
-			SHA1DC_CUSTOM_INCLUDE_SHA1_C="cache.h"
+			SHA1DC_CUSTOM_INCLUDE_SHA1_C="git-compat-util.h"
 			SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="git-compat-util.h" )
 list(APPEND compat_SOURCES sha1dc_git.c sha1dc/sha1.c sha1dc/ubc_check.c block-sha1/sha1.c sha256/block/sha256.c compat/qsort_s.c)
 
@@ -738,6 +738,15 @@
 	else()
 		message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}")
 	endif()
+
+	add_executable(headless-git ${CMAKE_SOURCE_DIR}/compat/win32/headless.c)
+	if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+		target_link_options(headless-git PUBLIC -municode -Wl,-subsystem,windows)
+	elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+		target_link_options(headless-git PUBLIC /NOLOGO /ENTRY:wWinMainCRTStartup /SUBSYSTEM:WINDOWS)
+	else()
+		message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}")
+	endif()
 elseif(UNIX)
 	target_link_libraries(common-main pthread rt)
 endif()
@@ -965,6 +974,35 @@
 parse_makefile_for_sources(test-reftable_SOURCES "REFTABLE_TEST_OBJS")
 list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 
+#unit-tests
+add_library(unit-test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
+
+parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "")
+foreach(unit_test ${unit_test_PROGRAMS})
+	add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c")
+	target_link_libraries("${unit_test}" unit-test-lib common-main)
+	set_target_properties("${unit_test}"
+		PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+	if(MSVC)
+		set_target_properties("${unit_test}"
+			PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+		set_target_properties("${unit_test}"
+			PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+	endif()
+	list(APPEND PROGRAMS_BUILT "${unit_test}")
+
+	# t-basic intentionally fails tests, to validate the unit-test infrastructure.
+	# Therefore, it should only be run as part of t0080, which verifies that it
+	# fails only in the expected ways.
+	#
+	# All other unit tests should be run.
+	if(NOT ${unit_test} STREQUAL "t-basic")
+		add_test(NAME "t.unit-tests.${unit_test}"
+			COMMAND "./${unit_test}"
+			WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t/unit-tests/bin)
+	endif()
+endforeach()
+
 #test-tool
 parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
 
@@ -1084,17 +1122,18 @@
 	file(COPY ${CMAKE_SOURCE_DIR}/contrib/completion/git-completion.bash DESTINATION ${CMAKE_BINARY_DIR}/contrib/completion/)
 endif()
 
-file(GLOB test_scipts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
+file(GLOB test_scripts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
 
 #test
-foreach(tsh ${test_scipts})
-	add_test(NAME ${tsh}
+foreach(tsh ${test_scripts})
+	string(REGEX REPLACE ".*/(.*)\\.sh" "\\1" test_name ${tsh})
+	add_test(NAME "t.suite.${test_name}"
 		COMMAND ${SH_EXE} ${tsh} --no-bin-wrappers --no-chain-lint -vx
 		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t)
 endforeach()
 
 # This test script takes an extremely long time and is known to time out even
 # on fast machines because it requires in excess of one hour to run
-set_tests_properties("${CMAKE_SOURCE_DIR}/t/t7112-reset-submodule.sh" PROPERTIES TIMEOUT 4000)
+set_tests_properties("t.suite.t7112-reset-submodule" PROPERTIES TIMEOUT 4000)
 
 endif()#BUILD_TESTING
diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm
index 1a25789..b2e68a1 100644
--- a/contrib/buildsystems/Generators/Vcxproj.pm
+++ b/contrib/buildsystems/Generators/Vcxproj.pm
@@ -76,7 +76,7 @@
 
     my $libs_release = "\n    ";
     my $libs_debug = "\n    ";
-    if (!$static_library) {
+    if (!$static_library && $name ne 'headless-git') {
       $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
       $libs_debug = $libs_release;
       $libs_debug =~ s/zlib\.lib/zlibd\.lib/g;
@@ -230,7 +230,7 @@
     print F << "EOM";
   </ItemGroup>
 EOM
-    if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') {
+    if ((!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') && !($name =~ /headless-git/)) {
       my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"};
       my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"};
       my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"};
diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl
index ed6c459..069be7e 100755
--- a/contrib/buildsystems/engine.pl
+++ b/contrib/buildsystems/engine.pl
@@ -371,6 +371,7 @@
 #    exit(1);
     foreach (@objfiles) {
         my $sourcefile = $_;
+        $sourcefile =~ s/^headless-git\.o$/compat\/win32\/headless.c/;
         $sourcefile =~ s/\.o$/.c/;
         push(@sources, $sourcefile);
         push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}});
diff --git a/contrib/coccinelle/README b/contrib/coccinelle/README
index d1daa1f..055ad0e 100644
--- a/contrib/coccinelle/README
+++ b/contrib/coccinelle/README
@@ -1,7 +1,9 @@
-This directory provides examples of Coccinelle (http://coccinelle.lip6.fr/)
-semantic patches that might be useful to developers.
+= coccinelle
 
-There are two types of semantic patches:
+This directory provides Coccinelle (http://coccinelle.lip6.fr/) semantic patches
+that might be useful to developers.
+
+==  Types of semantic patches
 
  * Using the semantic transformation to check for bad patterns in the code;
    The target 'make coccicheck' is designed to check for these patterns and
@@ -42,7 +44,7 @@
    This allows to expose plans of pending large scale refactorings without
    impacting the bad pattern checks.
 
-Git-specific tips & things to know about how we run "spatch":
+== Git-specific tips & things to know about how we run "spatch":
 
  * The "make coccicheck" will piggy-back on
    "COMPUTE_HEADER_DEPENDENCIES". If you've built a given object file
@@ -90,3 +92,33 @@
 
    The absolute times will differ for you, but the relative speedup
    from caching should be on that order.
+
+== Authoring and reviewing coccinelle changes
+
+* When a .cocci is made, both the Git changes and .cocci file should be
+  reviewed. When reviewing such a change, do your best to understand the .cocci
+  changes (e.g. by asking the author to explain the change) and be explicit
+  about your understanding of the changes. This helps us decide whether input
+  from coccinelle experts is needed or not. If you aren't sure of the cocci
+  changes, indicate what changes you actively endorse and leave an Acked-by
+  (instead of Reviewed-by).
+
+* Authors should consider that reviewers may not be coccinelle experts, thus the
+  the .cocci changes may not be self-evident. A plain text description of the
+  changes is strongly encouraged, especially when using more esoteric features
+  of the language.
+
+* .cocci rules should target only the problem it is trying to solve; "collateral
+  damage" is not allowed. Reviewers should look out and flag overly-broad rules.
+
+* Consider the cost-benefit ratio of .cocci changes. In particular, consider the
+  effect on the runtime of "make coccicheck", and how often your .cocci check
+  will catch something valuable. As a rule of thumb, rules that can bail early
+  if a file doesn't have a particular token will have a small impact on runtime,
+  and vice-versa.
+
+* .cocci files used for refactoring should be temporarily kept in-tree to aid
+  the refactoring of out-of-tree code (e.g. in-flight topics). Periodically
+  evaluate the cost-benefit ratio to determine when the file should be removed.
+  For example, consider how many out-of-tree users are left and how much this
+  slows down "make coccicheck".
diff --git a/contrib/coccinelle/config_fn_ctx.pending.cocci b/contrib/coccinelle/config_fn_ctx.pending.cocci
new file mode 100644
index 0000000..6d3d100
--- /dev/null
+++ b/contrib/coccinelle/config_fn_ctx.pending.cocci
@@ -0,0 +1,144 @@
+@ get_fn @
+identifier fn, R;
+@@
+(
+(
+git_config_from_file
+|
+git_config_from_file_with_options
+|
+git_config_from_mem
+|
+git_config_from_blob_oid
+|
+read_early_config
+|
+read_very_early_config
+|
+config_with_options
+|
+git_config
+|
+git_protected_config
+|
+config_from_gitmodules
+)
+  (fn, ...)
+|
+repo_config(R, fn, ...)
+)
+
+@ extends get_fn @
+identifier C1, C2, D;
+@@
+int fn(const char *C1, const char *C2,
++ const struct config_context *ctx,
+  void *D);
+
+@ extends get_fn @
+@@
+int fn(const char *, const char *,
++ const struct config_context *,
+  void *);
+
+@ extends get_fn @
+// Don't change fns that look like callback fns but aren't
+identifier fn2 != tar_filter_config && != git_diff_heuristic_config &&
+  != git_default_submodule_config && != git_color_config &&
+  != bundle_list_update && != parse_object_filter_config;
+identifier C1, C2, D1, D2, S;
+attribute name UNUSED;
+@@
+int fn(const char *C1, const char *C2,
++ const struct config_context *ctx,
+  void *D1) {
+<+...
+(
+fn2(C1, C2
++ , ctx
+, D2);
+|
+if(fn2(C1, C2
++ , ctx
+, D2) < 0) { ... }
+|
+return fn2(C1, C2
++ , ctx
+, D2);
+|
+S = fn2(C1, C2
++ , ctx
+, D2);
+)
+...+>
+  }
+
+@ extends get_fn@
+identifier C1, C2, D;
+attribute name UNUSED;
+@@
+int fn(const char *C1, const char *C2,
++ const struct config_context *ctx UNUSED,
+  void *D) {...}
+
+
+// The previous rules don't catch all callbacks, especially if they're defined
+// in a separate file from the git_config() call. Fix these manually.
+@@
+identifier C1, C2, D;
+attribute name UNUSED;
+@@
+int
+(
+git_ident_config
+|
+urlmatch_collect_fn
+|
+write_one_config
+|
+forbid_remote_url
+|
+credential_config_callback
+)
+  (const char *C1, const char *C2,
++ const struct config_context *ctx UNUSED,
+  void *D) {...}
+
+@@
+identifier C1, C2, D, D2, S, fn2;
+@@
+int
+(
+http_options
+|
+git_status_config
+|
+git_commit_config
+|
+git_default_core_config
+|
+grep_config
+)
+  (const char *C1, const char *C2,
++ const struct config_context *ctx,
+  void *D) {
+<+...
+(
+fn2(C1, C2
++ , ctx
+, D2);
+|
+if(fn2(C1, C2
++ , ctx
+, D2) < 0) { ... }
+|
+return fn2(C1, C2
++ , ctx
+, D2);
+|
+S = fn2(C1, C2
++ , ctx
+, D2);
+)
+...+>
+  }
diff --git a/contrib/coccinelle/git_config_number.cocci b/contrib/coccinelle/git_config_number.cocci
new file mode 100644
index 0000000..7b57dce
--- /dev/null
+++ b/contrib/coccinelle/git_config_number.cocci
@@ -0,0 +1,27 @@
+@@
+identifier C1, C2, C3;
+@@
+(
+(
+git_config_int
+|
+git_config_int64
+|
+git_config_ulong
+|
+git_config_ssize_t
+)
+  (C1, C2
++ , ctx->kvi
+  )
+|
+(
+git_configset_get_value
+|
+git_config_bool_or_int
+)
+  (C1, C2
++ , ctx->kvi
+ , C3
+  )
+)
diff --git a/contrib/coccinelle/tests/unused.c b/contrib/coccinelle/tests/unused.c
deleted file mode 100644
index 8294d73..0000000
--- a/contrib/coccinelle/tests/unused.c
+++ /dev/null
@@ -1,82 +0,0 @@
-void test_strbuf(void)
-{
-	struct strbuf sb1 = STRBUF_INIT;
-	struct strbuf sb2 = STRBUF_INIT;
-	struct strbuf sb3 = STRBUF_INIT;
-	struct strbuf sb4 = STRBUF_INIT;
-	struct strbuf sb5;
-	struct strbuf sb6 = { 0 };
-	struct strbuf sb7 = STRBUF_INIT;
-	struct strbuf sb8 = STRBUF_INIT;
-	struct strbuf *sp1;
-	struct strbuf *sp2;
-	struct strbuf *sp3;
-	struct strbuf *sp4 = xmalloc(sizeof(struct strbuf));
-	struct strbuf *sp5 = xmalloc(sizeof(struct strbuf));
-	struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
-	struct strbuf *sp7;
-
-	strbuf_init(&sb5, 0);
-	strbuf_init(sp1, 0);
-	strbuf_init(sp2, 0);
-	strbuf_init(sp3, 0);
-	strbuf_init(sp4, 0);
-	strbuf_init(sp5, 0);
-	strbuf_init(sp6, 0);
-	strbuf_init(sp7, 0);
-	sp7 = xmalloc(sizeof(struct strbuf));
-
-	use_before(&sb3);
-	use_as_str("%s", sb7.buf);
-	use_as_str("%s", sp1->buf);
-	use_as_str("%s", sp6->buf);
-	pass_pp(&sp3);
-
-	strbuf_release(&sb1);
-	strbuf_reset(&sb2);
-	strbuf_release(&sb3);
-	strbuf_release(&sb4);
-	strbuf_release(&sb5);
-	strbuf_release(&sb6);
-	strbuf_release(&sb7);
-	strbuf_release(sp1);
-	strbuf_release(sp2);
-	strbuf_release(sp3);
-	strbuf_release(sp4);
-	strbuf_release(sp5);
-	strbuf_release(sp6);
-	strbuf_release(sp7);
-
-	use_after(&sb4);
-
-	if (when_strict())
-		return;
-	strbuf_release(&sb8);
-}
-
-void test_other(void)
-{
-	struct string_list l = STRING_LIST_INIT_DUP;
-	struct strbuf sb = STRBUF_INIT;
-
-	string_list_clear(&l, 0);
-	string_list_clear(&sb, 0);
-}
-
-void test_worktrees(void)
-{
-	struct worktree **w1 = get_worktrees();
-	struct worktree **w2 = get_worktrees();
-	struct worktree **w3;
-	struct worktree **w4;
-
-	w3 = get_worktrees();
-	w4 = get_worktrees();
-
-	use_it(w4);
-
-	free_worktrees(w1);
-	free_worktrees(w2);
-	free_worktrees(w3);
-	free_worktrees(w4);
-}
diff --git a/contrib/coccinelle/tests/unused.res b/contrib/coccinelle/tests/unused.res
deleted file mode 100644
index 6d3e745..0000000
--- a/contrib/coccinelle/tests/unused.res
+++ /dev/null
@@ -1,45 +0,0 @@
-void test_strbuf(void)
-{
-	struct strbuf sb3 = STRBUF_INIT;
-	struct strbuf sb4 = STRBUF_INIT;
-	struct strbuf sb7 = STRBUF_INIT;
-	struct strbuf *sp1;
-	struct strbuf *sp3;
-	struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
-	strbuf_init(sp1, 0);
-	strbuf_init(sp3, 0);
-	strbuf_init(sp6, 0);
-
-	use_before(&sb3);
-	use_as_str("%s", sb7.buf);
-	use_as_str("%s", sp1->buf);
-	use_as_str("%s", sp6->buf);
-	pass_pp(&sp3);
-
-	strbuf_release(&sb3);
-	strbuf_release(&sb4);
-	strbuf_release(&sb7);
-	strbuf_release(sp1);
-	strbuf_release(sp3);
-	strbuf_release(sp6);
-
-	use_after(&sb4);
-
-	if (when_strict())
-		return;
-}
-
-void test_other(void)
-{
-}
-
-void test_worktrees(void)
-{
-	struct worktree **w4;
-
-	w4 = get_worktrees();
-
-	use_it(w4);
-
-	free_worktrees(w4);
-}
diff --git a/contrib/coccinelle/the_repository.cocci b/contrib/coccinelle/the_repository.cocci
new file mode 100644
index 0000000..765ad68
--- /dev/null
+++ b/contrib/coccinelle/the_repository.cocci
@@ -0,0 +1,123 @@
+// Fully migrated "the_repository" additions
+@@
+@@
+(
+// cache.h
+- get_oid
++ repo_get_oid
+|
+- get_oid_commit
++ repo_get_oid_commit
+|
+- get_oid_committish
++ repo_get_oid_committish
+|
+- get_oid_tree
++ repo_get_oid_tree
+|
+- get_oid_treeish
++ repo_get_oid_treeish
+|
+- get_oid_blob
++ repo_get_oid_blob
+|
+- get_oid_mb
++ repo_get_oid_mb
+|
+- find_unique_abbrev
++ repo_find_unique_abbrev
+|
+- find_unique_abbrev_r
++ repo_find_unique_abbrev_r
+|
+- for_each_abbrev
++ repo_for_each_abbrev
+|
+- interpret_branch_name
++ repo_interpret_branch_name
+|
+- peel_to_type
++ repo_peel_to_type
+// commit-reach.h
+|
+- get_merge_bases
++ repo_get_merge_bases
+|
+- get_merge_bases_many
++ repo_get_merge_bases_many
+|
+- get_merge_bases_many_dirty
++ repo_get_merge_bases_many_dirty
+|
+- in_merge_bases
++ repo_in_merge_bases
+|
+- in_merge_bases_many
++ repo_in_merge_bases_many
+// commit.h
+|
+- parse_commit_internal
++ repo_parse_commit_internal
+|
+- parse_commit
++ repo_parse_commit
+|
+- get_commit_buffer
++ repo_get_commit_buffer
+|
+- unuse_commit_buffer
++ repo_unuse_commit_buffer
+|
+- logmsg_reencode
++ repo_logmsg_reencode
+|
+- get_commit_tree
++ repo_get_commit_tree
+// diff.h
+|
+- diff_setup
++ repo_diff_setup
+// object-store.h
+|
+- read_object_file
++ repo_read_object_file
+|
+- has_object_file
++ repo_has_object_file
+|
+- has_object_file_with_flags
++ repo_has_object_file_with_flags
+// pretty.h
+|
+- format_commit_message
++ repo_format_commit_message
+// packfile.h
+|
+- approximate_object_count
++ repo_approximate_object_count
+// promisor-remote.h
+|
+- promisor_remote_reinit
++ repo_promisor_remote_reinit
+|
+- promisor_remote_find
++ repo_promisor_remote_find
+|
+- has_promisor_remote
++ repo_has_promisor_remote
+// refs.h
+|
+- dwim_ref
++ repo_dwim_ref
+// rerere.h
+|
+- rerere
++ repo_rerere
+// revision.h
+|
+- init_revisions
++ repo_init_revisions
+)
+  (
++ the_repository,
+  ...)
diff --git a/contrib/coccinelle/the_repository.pending.cocci b/contrib/coccinelle/the_repository.pending.cocci
deleted file mode 100644
index 747d382..0000000
--- a/contrib/coccinelle/the_repository.pending.cocci
+++ /dev/null
@@ -1,128 +0,0 @@
-// This file is used for the ongoing refactoring of
-// bringing the index or repository struct in all of
-// our code base.
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- read_object_file(
-+ repo_read_object_file(the_repository,
-  E, F, G)
-
-@@
-expression E;
-@@
-- has_object_file(
-+ repo_has_object_file(the_repository,
-  E)
-
-@@
-expression E;
-@@
-- has_object_file_with_flags(
-+ repo_has_object_file_with_flags(the_repository,
-  E)
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- parse_commit_internal(
-+ repo_parse_commit_internal(the_repository,
-  E, F, G)
-
-@@
-expression E;
-expression F;
-@@
-- parse_commit_gently(
-+ repo_parse_commit_gently(the_repository,
-  E, F)
-
-@@
-expression E;
-@@
-- parse_commit(
-+ repo_parse_commit(the_repository,
-  E)
-
-@@
-expression E;
-expression F;
-@@
-- get_merge_bases(
-+ repo_get_merge_bases(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- get_merge_bases_many(
-+ repo_get_merge_bases_many(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- get_merge_bases_many_dirty(
-+ repo_get_merge_bases_many_dirty(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-@@
-- in_merge_bases(
-+ repo_in_merge_bases(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- in_merge_bases_many(
-+ repo_in_merge_bases_many(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-@@
-- get_commit_buffer(
-+ repo_get_commit_buffer(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-@@
-- unuse_commit_buffer(
-+ repo_unuse_commit_buffer(the_repository,
-  E, F);
-
-@@
-expression E;
-expression F;
-expression G;
-@@
-- logmsg_reencode(
-+ repo_logmsg_reencode(the_repository,
-  E, F, G);
-
-@@
-expression E;
-expression F;
-expression G;
-expression H;
-@@
-- format_commit_message(
-+ repo_format_commit_message(the_repository,
-  E, F, G, H);
diff --git a/contrib/coccinelle/unused.cocci b/contrib/coccinelle/unused.cocci
deleted file mode 100644
index d84046f..0000000
--- a/contrib/coccinelle/unused.cocci
+++ /dev/null
@@ -1,43 +0,0 @@
-// This rule finds sequences of "unused" declerations and uses of a
-// variable, where "unused" is defined to include only calling the
-// equivalent of alloc, init & free functions on the variable.
-@@
-type T;
-identifier I;
-// STRBUF_INIT, but also e.g. STRING_LIST_INIT_DUP (so no anchoring)
-constant INIT_MACRO =~ "_INIT";
-identifier MALLOC1 =~ "^x?[mc]alloc$";
-identifier INIT_ASSIGN1 =~ "^get_worktrees$";
-identifier INIT_CALL1 =~ "^[a-z_]*_init$";
-identifier REL1 =~ "^[a-z_]*_(release|reset|clear|free)$";
-identifier REL2 =~ "^(release|clear|free)_[a-z_]*$";
-@@
-
-(
-- T I;
-|
-- T I = { 0 };
-|
-- T I = INIT_MACRO;
-|
-- T I = MALLOC1(...);
-|
-- T I = INIT_ASSIGN1(...);
-)
-
-<... when != \( I \| &I \)
-(
-- \( INIT_CALL1 \)( \( I \| &I \), ...);
-|
-- I = \( INIT_ASSIGN1 \)(...);
-|
-- I = MALLOC1(...);
-)
-...>
-
-(
-- \( REL1 \| REL2 \)( \( I \| &I \), ...);
-|
-- \( REL1 \| REL2 \)( \( &I \| I \) );
-)
-  ... when != \( I \| &I \)
diff --git a/contrib/coccinelle/xstrncmpz.cocci b/contrib/coccinelle/xstrncmpz.cocci
new file mode 100644
index 0000000..ccb39e2
--- /dev/null
+++ b/contrib/coccinelle/xstrncmpz.cocci
@@ -0,0 +1,28 @@
+@@
+expression S, T, L;
+@@
+(
+- strncmp(S, T, L) || S[L]
++ !!xstrncmpz(S, T, L)
+|
+- strncmp(S, T, L) || S[L] != '\0'
++ !!xstrncmpz(S, T, L)
+|
+- strncmp(S, T, L) || T[L]
++ !!xstrncmpz(T, S, L)
+|
+- strncmp(S, T, L) || T[L] != '\0'
++ !!xstrncmpz(T, S, L)
+|
+- !strncmp(S, T, L) && !S[L]
++ !xstrncmpz(S, T, L)
+|
+- !strncmp(S, T, L) && S[L] == '\0'
++ !xstrncmpz(S, T, L)
+|
+- !strncmp(S, T, L) && !T[L]
++ !xstrncmpz(T, S, L)
+|
+- !strncmp(S, T, L) && T[L] == '\0'
++ !xstrncmpz(T, S, L)
+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index dc95c34..75193de 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -28,6 +28,8 @@
 # completion style.  For example '!f() { : git commit ; ... }; f' will
 # tell the completion to use commit completion.  This also works with aliases
 # of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
+# Note that "git" is optional --- '!f() { : commit; ...}; f' would complete
+# just like the 'git commit' command.
 #
 # If you have a command that is not part of git, but you would still
 # like completion, you can use __git_complete:
@@ -120,6 +122,40 @@
 		${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null
 }
 
+# Helper function to read the first line of a file into a variable.
+# __git_eread requires 2 arguments, the file path and the name of the
+# variable, in that order.
+#
+# This is taken from git-prompt.sh.
+__git_eread ()
+{
+	test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
+}
+
+# Runs git in $__git_repo_path to determine whether a pseudoref exists.
+# 1: The pseudo-ref to search
+__git_pseudoref_exists ()
+{
+	local ref=$1
+	local head
+
+	__git_find_repo_path
+
+	# If the reftable is in use, we have to shell out to 'git rev-parse'
+	# to determine whether the ref exists instead of looking directly in
+	# the filesystem to determine whether the ref exists. Otherwise, use
+	# Bash builtins since executing Git commands are expensive on some
+	# platforms.
+	if __git_eread "$__git_repo_path/HEAD" head; then
+		if [ "$head" == "ref: refs/heads/.invalid" ]; then
+			__git show-ref --exists "$ref"
+			return $?
+		fi
+	fi
+
+	[ -f "$__git_repo_path/$ref" ]
+}
+
 # Removes backslash escaping, single quotes and double quotes from a word,
 # stores the result in the variable $dequoted_word.
 # 1: The word to dequote.
@@ -418,16 +454,18 @@
 
 # This function is equivalent to
 #
-#    __gitcomp "$(git xxx --git-completion-helper) ..."
+#    ___git_resolved_builtins=$(git xxx --git-completion-helper)
 #
-# except that the output is cached. Accept 1-3 arguments:
+# except that the result of the execution is cached.
+#
+# Accept 1-3 arguments:
 # 1: the git command to execute, this is also the cache key
+#    (use "_" when the command contains spaces, e.g. "remote add"
+#    becomes "remote_add")
 # 2: extra options to be added on top (e.g. negative forms)
 # 3: options to be excluded
-__gitcomp_builtin ()
+__git_resolve_builtins ()
 {
-	# spaces must be replaced with underscore for multi-word
-	# commands, e.g. "git remote add" becomes remote_add.
 	local cmd="$1"
 	local incl="${2-}"
 	local excl="${3-}"
@@ -453,7 +491,24 @@
 		eval "$var=\"$options\""
 	fi
 
-	__gitcomp "$options"
+	___git_resolved_builtins="$options"
+}
+
+# This function is equivalent to
+#
+#    __gitcomp "$(git xxx --git-completion-helper) ..."
+#
+# except that the output is cached. Accept 1-3 arguments:
+# 1: the git command to execute, this is also the cache key
+#    (use "_" when the command contains spaces, e.g. "remote add"
+#    becomes "remote_add")
+# 2: extra options to be added on top (e.g. negative forms)
+# 3: options to be excluded
+__gitcomp_builtin ()
+{
+	__git_resolve_builtins "$1" "$2" "$3"
+
+	__gitcomp "$___git_resolved_builtins"
 }
 
 # Variation of __gitcomp_nl () that appends to the existing list of
@@ -520,6 +575,26 @@
 	true
 }
 
+# Find the current subcommand for commands that follow the syntax:
+#
+#    git <command> <subcommand>
+#
+# 1: List of possible subcommands.
+# 2: Optional subcommand to return when none is found.
+__git_find_subcommand ()
+{
+	local subcommand subcommands="$1" default_subcommand="$2"
+
+	for subcommand in $subcommands; do
+		if [ "$subcommand" = "${words[__git_cmd_idx+1]}" ]; then
+			echo $subcommand
+			return
+		fi
+	done
+
+	echo $default_subcommand
+}
+
 # Execute 'git ls-files', unless the --committable option is specified, in
 # which case it runs 'git diff-index' to find out the files that can be
 # committed.  It return paths relative to the directory specified in the first
@@ -767,7 +842,7 @@
 			track=""
 			;;
 		*)
-			for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD; do
+			for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD REVERT_HEAD BISECT_HEAD AUTO_MERGE; do
 				case "$i" in
 				$match*|$umatch*)
 					if [ -e "$dir/$i" ]; then
@@ -1182,7 +1257,7 @@
 			:)	: skip null command ;;
 			\'*)	: skip opening quote after sh -c ;;
 			*)
-				cur="$word"
+				cur="${word%;}"
 				break
 			esac
 		done
@@ -1447,12 +1522,32 @@
 {
 	__git_has_doubledash && return
 
-	local subcommands="start bad good skip reset visualize replay log run"
-	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	__git_find_repo_path
+
+	# If a bisection is in progress get the terms being used.
+	local term_bad term_good
+	if [ -f "$__git_repo_path"/BISECT_TERMS ]; then
+		term_bad=$(__git bisect terms --term-bad)
+		term_good=$(__git bisect terms --term-good)
+	fi
+
+	# We will complete any custom terms, but still always complete the
+	# more usual bad/new/good/old because git bisect gives a good error
+	# message if these are given when not in use, and that's better than
+	# silent refusal to complete if the user is confused.
+	#
+	# We want to recognize 'view' but not complete it, because it overlaps
+	# with 'visualize' too much and is just an alias for it.
+	#
+	local completable_subcommands="start bad new $term_bad good old $term_good terms skip reset visualize replay log run help"
+	local all_subcommands="$completable_subcommands view"
+
+	local subcommand="$(__git_find_on_cmdline "$all_subcommands")"
+
 	if [ -z "$subcommand" ]; then
 		__git_find_repo_path
 		if [ -f "$__git_repo_path"/BISECT_START ]; then
-			__gitcomp "$subcommands"
+			__gitcomp "$completable_subcommands"
 		else
 			__gitcomp "replay start"
 		fi
@@ -1460,7 +1555,26 @@
 	fi
 
 	case "$subcommand" in
-	bad|good|reset|skip|start)
+	start)
+		case "$cur" in
+		--*)
+			__gitcomp "--first-parent --no-checkout --term-new --term-bad --term-old --term-good"
+			return
+			;;
+		*)
+			__git_complete_refs
+			;;
+		esac
+		;;
+	terms)
+		__gitcomp "--term-good --term-old --term-bad --term-new"
+		return
+		;;
+	visualize|view)
+		__git_complete_log_opts
+		return
+		;;
+	bad|new|"$term_bad"|good|old|"$term_good"|reset|skip)
 		__git_complete_refs
 		;;
 	*)
@@ -1607,7 +1721,7 @@
 
 		if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
 			__git_complete_refs --mode="refs"
-		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+		elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then
 			__git_complete_refs --mode="remote-heads"
 		else
 			__git_complete_refs $dwim_opt --mode="refs"
@@ -1622,8 +1736,7 @@
 
 _git_cherry_pick ()
 {
-	__git_find_repo_path
-	if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
+	if __git_pseudoref_exists CHERRY_PICK_HEAD; then
 		__gitcomp "$__git_cherry_pick_inprogress_options"
 		return
 	fi
@@ -1677,6 +1790,11 @@
 
 __git_untracked_file_modes="all no normal"
 
+__git_trailer_tokens ()
+{
+	__git config --name-only --get-regexp '^trailer\..*\.key$' | cut -d. -f 2- | rev | cut -d. -f2- | rev
+}
+
 _git_commit ()
 {
 	case "$prev" in
@@ -1701,6 +1819,10 @@
 		__gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
 		return
 		;;
+	--trailer=*)
+		__gitcomp_nl "$(__git_trailer_tokens)" "" "${cur##--trailer=}" ":"
+		return
+		;;
 	--*)
 		__gitcomp_builtin commit
 		return
@@ -1733,32 +1855,44 @@
 __git_color_moved_ws_opts="no ignore-space-at-eol ignore-space-change
 			ignore-all-space allow-indentation-change"
 
+__git_ws_error_highlight_opts="context old new all default"
+
+# Options for the diff machinery (diff, log, show, stash, range-diff, ...)
 __git_diff_common_options="--stat --numstat --shortstat --summary
 			--patch-with-stat --name-only --name-status --color
 			--no-color --color-words --no-renames --check
 			--color-moved --color-moved= --no-color-moved
 			--color-moved-ws= --no-color-moved-ws
 			--full-index --binary --abbrev --diff-filter=
+			--find-copies --find-object --find-renames
+			--no-relative --relative
 			--find-copies-harder --ignore-cr-at-eol
 			--text --ignore-space-at-eol --ignore-space-change
 			--ignore-all-space --ignore-blank-lines --exit-code
-			--quiet --ext-diff --no-ext-diff
+			--quiet --ext-diff --no-ext-diff --unified=
 			--no-prefix --src-prefix= --dst-prefix=
-			--inter-hunk-context=
+			--inter-hunk-context= --function-context
 			--patience --histogram --minimal
 			--raw --word-diff --word-diff-regex=
 			--dirstat --dirstat= --dirstat-by-file
 			--dirstat-by-file= --cumulative
-			--diff-algorithm=
+			--diff-algorithm= --default-prefix
 			--submodule --submodule= --ignore-submodules
 			--indent-heuristic --no-indent-heuristic
-			--textconv --no-textconv
-			--patch --no-patch
-			--anchored=
+			--textconv --no-textconv --break-rewrites
+			--patch --no-patch --cc --combined-all-paths
+			--anchored= --compact-summary --ignore-matching-lines=
+			--irreversible-delete --line-prefix --no-stat
+			--output= --output-indicator-context=
+			--output-indicator-new= --output-indicator-old=
+			--ws-error-highlight=
+			--pickaxe-all --pickaxe-regex --patch-with-raw
 "
 
-__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex
-			--base --ours --theirs --no-index --relative --merge-base
+# Options for diff/difftool
+__git_diff_difftool_options="--cached --staged
+			--base --ours --theirs --no-index --merge-base
+			--ita-invisible-in-index --ita-visible-in-index
 			$__git_diff_common_options"
 
 _git_diff ()
@@ -1782,6 +1916,10 @@
 		__gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
 		return
 		;;
+	--ws-error-highlight=*)
+		__gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
+		return
+		;;
 	--*)
 		__gitcomp "$__git_diff_difftool_options"
 		return
@@ -2012,6 +2150,16 @@
 	--min-age= --until= --before=
 	--min-parents= --max-parents=
 	--no-min-parents --no-max-parents
+	--alternate-refs --ancestry-path
+	--author-date-order --basic-regexp
+	--bisect --boundary --exclude-first-parent-only
+	--exclude-hidden --extended-regexp
+	--fixed-strings --grep-reflog
+	--ignore-missing --left-only --perl-regexp
+	--reflog --regexp-ignore-case --remove-empty
+	--right-only --show-linear-break
+	--show-notes-by-default --show-pulls
+	--since-as-filter --single-worktree
 "
 # Options that go well for log and gitk (not shortlog)
 __git_log_gitk_options="
@@ -2024,17 +2172,26 @@
 	--author= --committer= --grep=
 	--all-match --invert-grep
 "
+# Options accepted by log and show
+__git_log_show_options="
+	--diff-merges --diff-merges= --no-diff-merges --dd --remerge-diff
+	--encoding=
+"
+
+__git_diff_merges_opts="off none on first-parent 1 separate m combined c dense-combined cc remerge r"
 
 __git_log_pretty_formats="oneline short medium full fuller reference email raw format: tformat: mboxrd"
 __git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default human raw unix auto: format:"
 
-_git_log ()
+# Complete porcelain (i.e. not git-rev-list) options and at least some
+# option arguments accepted by git-log.  Note that this same set of options
+# are also accepted by some other git commands besides git-log.
+__git_complete_log_opts ()
 {
-	__git_has_doubledash && return
-	__git_find_repo_path
+	COMPREPLY=()
 
 	local merge=""
-	if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+	if __git_pseudoref_exists MERGE_HEAD; then
 		merge="--merge"
 	fi
 	case "$prev,$cur" in
@@ -2072,15 +2229,24 @@
 		__gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
 		return
 		;;
+	--ws-error-highlight=*)
+		__gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
+		return
+		;;
 	--no-walk=*)
 		__gitcomp "sorted unsorted" "" "${cur##--no-walk=}"
 		return
 		;;
+	--diff-merges=*)
+                __gitcomp "$__git_diff_merges_opts" "" "${cur##--diff-merges=}"
+                return
+                ;;
 	--*)
 		__gitcomp "
 			$__git_log_common_options
 			$__git_log_shortlog_options
 			$__git_log_gitk_options
+			$__git_log_show_options
 			--root --topo-order --date-order --reverse
 			--follow --full-diff
 			--abbrev-commit --no-abbrev-commit --abbrev=
@@ -2095,9 +2261,10 @@
 			--no-walk --no-walk= --do-walk
 			--parents --children
 			--expand-tabs --expand-tabs= --no-expand-tabs
+			--clear-decorations --decorate-refs=
+			--decorate-refs-exclude=
 			$merge
 			$__git_diff_common_options
-			--pickaxe-all --pickaxe-regex
 			"
 		return
 		;;
@@ -2117,6 +2284,16 @@
 		return
 		;;
 	esac
+}
+
+_git_log ()
+{
+	__git_has_doubledash && return
+	__git_find_repo_path
+
+	__git_complete_log_opts
+        [ ${#COMPREPLY[@]} -eq 0 ] || return
+
 	__git_complete_revlist
 }
 
@@ -2333,13 +2510,30 @@
 
 _git_reflog ()
 {
-	local subcommands="show delete expire"
-	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	local subcommands subcommand
 
-	if [ -z "$subcommand" ]; then
-		__gitcomp "$subcommands"
-	else
-		__git_complete_refs
+	__git_resolve_builtins "reflog"
+
+	subcommands="$___git_resolved_builtins"
+	subcommand="$(__git_find_subcommand "$subcommands" "show")"
+
+	case "$subcommand,$cur" in
+	show,--*)
+		__gitcomp "
+			$__git_log_common_options
+			"
+		return
+		;;
+	$subcommand,--*)
+		__gitcomp_builtin "reflog_$subcommand"
+		return
+		;;
+	esac
+
+	__git_complete_refs
+
+	if [ $((cword - __git_cmd_idx)) -eq 1 ]; then
+		__gitcompappend "$subcommands" "" "$cur" " "
 	fi
 }
 
@@ -2484,7 +2678,7 @@
 
 		if [ -n "$(__git_find_on_cmdline "-c -C -d --detach")" ]; then
 			__git_complete_refs --mode="refs"
-		elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+		elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then
 			__git_complete_refs --mode="remote-heads"
 		else
 			__git_complete_refs $dwim_opt --mode="heads"
@@ -2522,6 +2716,33 @@
 	__git_config_vars="$(git help --config-for-completion)"
 }
 
+__git_config_vars_all=
+__git_compute_config_vars_all ()
+{
+	test -n "$__git_config_vars_all" ||
+	__git_config_vars_all="$(git --no-pager help --config)"
+}
+
+__git_compute_first_level_config_vars_for_section ()
+{
+	local section="$1"
+	__git_compute_config_vars
+	local this_section="__git_first_level_config_vars_for_section_${section}"
+	test -n "${!this_section}" ||
+	printf -v "__git_first_level_config_vars_for_section_${section}" %s \
+		"$(echo "$__git_config_vars" | awk -F. "/^${section}\.[a-z]/ { print \$2 }")"
+}
+
+__git_compute_second_level_config_vars_for_section ()
+{
+	local section="$1"
+	__git_compute_config_vars_all
+	local this_section="__git_second_level_config_vars_for_section_${section}"
+	test -n "${!this_section}" ||
+	printf -v "__git_second_level_config_vars_for_section_${section}" %s \
+		"$(echo "$__git_config_vars_all" | awk -F. "/^${section}\.</ { print \$3 }")"
+}
+
 __git_config_sections=
 __git_compute_config_sections ()
 {
@@ -2666,73 +2887,50 @@
 	done
 
 	case "$cur_" in
-	branch.*.*)
+	branch.*.*|guitool.*.*|difftool.*.*|man.*.*|mergetool.*.*|remote.*.*|submodule.*.*|url.*.*)
 		local pfx="${cur_%.*}."
 		cur_="${cur_##*.}"
-		__gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" "$sfx"
+		local section="${pfx%.*.}"
+		__git_compute_second_level_config_vars_for_section "${section}"
+		local this_section="__git_second_level_config_vars_for_section_${section}"
+		__gitcomp "${!this_section}" "$pfx" "$cur_" "$sfx"
 		return
 		;;
 	branch.*)
 		local pfx="${cur_%.*}."
 		cur_="${cur_#*.}"
+		local section="${pfx%.}"
 		__gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
-		__gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "${sfx- }"
-		return
-		;;
-	guitool.*.*)
-		local pfx="${cur_%.*}."
-		cur_="${cur_##*.}"
-		__gitcomp "
-			argPrompt cmd confirm needsFile noConsole noRescan
-			prompt revPrompt revUnmerged title
-			" "$pfx" "$cur_" "$sfx"
-		return
-		;;
-	difftool.*.*)
-		local pfx="${cur_%.*}."
-		cur_="${cur_##*.}"
-		__gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
-		return
-		;;
-	man.*.*)
-		local pfx="${cur_%.*}."
-		cur_="${cur_##*.}"
-		__gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
-		return
-		;;
-	mergetool.*.*)
-		local pfx="${cur_%.*}."
-		cur_="${cur_##*.}"
-		__gitcomp "cmd path trustExitCode" "$pfx" "$cur_" "$sfx"
+		__git_compute_first_level_config_vars_for_section "${section}"
+		local this_section="__git_first_level_config_vars_for_section_${section}"
+		__gitcomp_nl_append "${!this_section}" "$pfx" "$cur_" "${sfx:- }"
 		return
 		;;
 	pager.*)
 		local pfx="${cur_%.*}."
 		cur_="${cur_#*.}"
 		__git_compute_all_commands
-		__gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "${sfx- }"
-		return
-		;;
-	remote.*.*)
-		local pfx="${cur_%.*}."
-		cur_="${cur_##*.}"
-		__gitcomp "
-			url proxy fetch push mirror skipDefaultUpdate
-			receivepack uploadpack tagOpt pushurl
-			" "$pfx" "$cur_" "$sfx"
+		__gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "${sfx:- }"
 		return
 		;;
 	remote.*)
 		local pfx="${cur_%.*}."
 		cur_="${cur_#*.}"
+		local section="${pfx%.}"
 		__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
-		__gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "${sfx- }"
+		__git_compute_first_level_config_vars_for_section "${section}"
+		local this_section="__git_first_level_config_vars_for_section_${section}"
+		__gitcomp_nl_append "${!this_section}" "$pfx" "$cur_" "${sfx:- }"
 		return
 		;;
-	url.*.*)
+	submodule.*)
 		local pfx="${cur_%.*}."
-		cur_="${cur_##*.}"
-		__gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx"
+		cur_="${cur_#*.}"
+		local section="${pfx%.}"
+		__gitcomp_nl "$(__git config -f "$(__git rev-parse --show-toplevel)/.gitmodules" --get-regexp 'submodule.*.path' | awk -F. '{print $2}')" "$pfx" "$cur_" "."
+		__git_compute_first_level_config_vars_for_section "${section}"
+		local this_section="__git_first_level_config_vars_for_section_${section}"
+		__gitcomp_nl_append "${!this_section}" "$pfx" "$cur_" "${sfx:- }"
 		return
 		;;
 	*.*)
@@ -2911,7 +3109,7 @@
 		__gitcomp_builtin restore
 		;;
 	*)
-		if __git rev-parse --verify --quiet HEAD >/dev/null; then
+		if __git_pseudoref_exists HEAD; then
 			__git_complete_index_file "--modified"
 		fi
 	esac
@@ -2921,8 +3119,7 @@
 
 _git_revert ()
 {
-	__git_find_repo_path
-	if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
+	if __git_pseudoref_exists REVERT_HEAD; then
 		__gitcomp "$__git_revert_inprogress_options"
 		return
 	fi
@@ -2992,10 +3189,19 @@
 		__gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
 		return
 		;;
+	--ws-error-highlight=*)
+		__gitcomp "$__git_ws_error_highlight_opts" "" "${cur##--ws-error-highlight=}"
+		return
+		;;
+	--diff-merges=*)
+                __gitcomp "$__git_diff_merges_opts" "" "${cur##--diff-merges=}"
+                return
+                ;;
 	--*)
 		__gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit
 			--oneline --show-signature
 			--expand-tabs --expand-tabs= --no-expand-tabs
+			$__git_log_show_options
 			$__git_diff_common_options
 			"
 		return
@@ -3034,12 +3240,119 @@
 			COMPREPLY+=("$c/")
 			_found=1
 		fi
-	done < <(git ls-tree -z -d --name-only HEAD $_tmp_dir)
+	done < <(__git ls-tree -z -d --name-only HEAD $_tmp_dir)
 
 	if [[ $_found == 0 ]] && [[ "$cur" =~ /$ ]]; then
 		# No possible further completions any deeper, so assume we're at
 		# a leaf directory and just consider it complete
 		__gitcomp_direct_append "$cur "
+	elif [[ $_found == 0 ]]; then
+		# No possible completions found.  Avoid falling back to
+		# bash's default file and directory completion, because all
+		# valid completions have already been searched and the
+		# fallbacks can do nothing but mislead.  In fact, they can
+		# mislead in three different ways:
+		#    1) Fallback file completion makes no sense when asking
+		#       for directory completions, as this function does.
+		#    2) Fallback directory completion is bad because
+		#       e.g. "/pro" is invalid and should NOT complete to
+		#       "/proc".
+		#    3) Fallback file/directory completion only completes
+		#       on paths that exist in the current working tree,
+		#       i.e. which are *already* part of their
+		#       sparse-checkout.  Thus, normal file and directory
+		#       completion is always useless for "git
+		#       sparse-checkout add" and is also probelmatic for
+		#       "git sparse-checkout set" unless using it to
+		#       strictly narrow the checkout.
+		COMPREPLY=( "" )
+	fi
+}
+
+# In non-cone mode, the arguments to {set,add} are supposed to be
+# patterns, relative to the toplevel directory.  These can be any kind
+# of general pattern, like 'subdir/*.c' and we can't complete on all
+# of those.  However, if the user presses Tab to get tab completion, we
+# presume that they are trying to provide a pattern that names a specific
+# path.
+__gitcomp_slash_leading_paths ()
+{
+	local dequoted_word pfx="" cur_ toplevel
+
+	# Since we are dealing with a sparse-checkout, subdirectories may not
+	# exist in the local working copy.  Therefore, we want to run all
+	# ls-files commands relative to the repository toplevel.
+	toplevel="$(git rev-parse --show-toplevel)/"
+
+	__git_dequote "$cur"
+
+	# If the paths provided by the user already start with '/', then
+	# they are considered relative to the toplevel of the repository
+	# already.  If they do not start with /, then we need to adjust
+	# them to start with the appropriate prefix.
+	case "$cur" in
+	/*)
+		cur="${cur:1}"
+		;;
+	*)
+		pfx="$(__git rev-parse --show-prefix)"
+	esac
+
+	# Since sparse-index is limited to cone-mode, in non-cone-mode the
+	# list of valid paths is precisely the cached files in the index.
+	#
+	# NEEDSWORK:
+	#   1) We probably need to take care of cases where ls-files
+	#      responds with special quoting.
+	#   2) We probably need to take care of cases where ${cur} has
+	#      some kind of special quoting.
+	#   3) On top of any quoting from 1 & 2, we have to provide an extra
+	#      level of quoting for any paths that contain a '*', '?', '\',
+	#      '[', ']', or leading '#' or '!' since those will be
+	#      interpreted by sparse-checkout as something other than a
+	#      literal path character.
+	# Since there are two types of quoting here, this might get really
+	# complex.  For now, just punt on all of this...
+	completions="$(__git -C "${toplevel}" -c core.quotePath=false \
+			 ls-files --cached -- "${pfx}${cur}*" \
+			 | sed -e s%^%/% -e 's%$% %')"
+	# Note, above, though that we needed all of the completions to be
+	# prefixed with a '/', and we want to add a space so that bash
+	# completion will actually complete an entry and let us move on to
+	# the next one.
+
+	# Return what we've found.
+	if test -n "$completions"; then
+		# We found some completions; return them
+		local IFS=$'\n'
+		COMPREPLY=($completions)
+	else
+		# Do NOT fall back to bash-style all-local-files-and-dirs
+		# when we find no match.  Such options are worse than
+		# useless:
+		#     1. "git sparse-checkout add" needs paths that are NOT
+		#        currently in the working copy.  "git
+		#        sparse-checkout set" does as well, except in the
+		#        special cases when users are only trying to narrow
+		#        their sparse checkout to a subset of what they
+		#        already have.
+		#
+		#     2. A path like '.config' is ambiguous as to whether
+		#        the user wants all '.config' files throughout the
+		#        tree, or just the one under the current directory.
+		#        It would result in a warning from the
+		#        sparse-checkout command due to this.  As such, all
+		#        completions of paths should be prefixed with a
+		#        '/'.
+		#
+		#     3. We don't want paths prefixed with a '/' to
+		#        complete files in the system root directory, we
+		#        want it to complete on files relative to the
+		#        repository root.
+		#
+		# As such, make sure that NO completions are offered rather
+		# than falling back to bash's default completions.
+		COMPREPLY=( "" )
 	fi
 }
 
@@ -3047,6 +3360,7 @@
 {
 	local subcommands="list init set disable add reapply"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	local using_cone=true
 	if [ -z "$subcommand" ]; then
 		__gitcomp "$subcommands"
 		return
@@ -3057,9 +3371,18 @@
 		__gitcomp_builtin sparse-checkout_$subcommand "" "--"
 		;;
 	set,*|add,*)
-		if [ "$(__git config core.sparseCheckoutCone)" == "true" ] ||
-		[ -n "$(__git_find_on_cmdline --cone)" ]; then
+		if [[ "$(__git config core.sparseCheckout)" == "true" &&
+		      "$(__git config core.sparseCheckoutCone)" == "false" &&
+		      -z "$(__git_find_on_cmdline --cone)" ]]; then
+			using_cone=false
+		fi
+		if [[ -n "$(__git_find_on_cmdline --no-cone)" ]]; then
+			using_cone=false
+		fi
+		if [[ "$using_cone" == "true" ]]; then
 			__gitcomp_directories
+		else
+			 __gitcomp_slash_leading_paths
 		fi
 	esac
 }
@@ -3306,7 +3629,7 @@
 	# Generate completion reply from worktree list skipping the first
 	# entry: it's the path of the main worktree, which can't be moved,
 	# removed, locked, etc.
-	__gitcomp_nl "$(git worktree list --porcelain |
+	__gitcomp_nl "$(__git worktree list --porcelain |
 		sed -n -e '2,$ s/^worktree //p')"
 }
 
@@ -3542,7 +3865,7 @@
 	__git_find_repo_path
 
 	local merge=""
-	if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+	if __git_pseudoref_exists MERGE_HEAD; then
 		merge="--merge"
 	fi
 	case "$cur" in
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 57972c2..5330e76 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -100,9 +100,7 @@
 #
 # If you would like a colored hint about the current dirty state, set
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb" and are available only when
-# using __git_ps1 for PROMPT_COMMAND or precmd in Bash,
-# but always available in Zsh.
+# the colored output of "git status -sb".
 #
 # If you would like __git_ps1 to do nothing in the case when the current
 # directory is set up to be ignored by git, then set
@@ -143,7 +141,7 @@
 
 	# parse configuration values
 	local option
-	for option in ${GIT_PS1_SHOWUPSTREAM}; do
+	for option in ${GIT_PS1_SHOWUPSTREAM-}; do
 		case "$option" in
 		git|svn) upstream_type="$option" ;;
 		verbose) verbose=1 ;;
@@ -259,12 +257,12 @@
 		local c_lblue='%F{blue}'
 		local c_clear='%f'
 	else
-		# Using \[ and \] around colors is necessary to prevent
+		# Using \001 and \002 around colors is necessary to prevent
 		# issues with command line editing/browsing/completion!
-		local c_red='\[\e[31m\]'
-		local c_green='\[\e[32m\]'
-		local c_lblue='\[\e[1;34m\]'
-		local c_clear='\[\e[0m\]'
+		local c_red=$'\001\e[31m\002'
+		local c_green=$'\001\e[32m\002'
+		local c_lblue=$'\001\e[1;34m\002'
+		local c_clear=$'\001\e[0m\002'
 	fi
 	local bad_color=$c_red
 	local ok_color=$c_green
@@ -300,7 +298,7 @@
 # variable, in that order.
 __git_eread ()
 {
-	test -r "$1" && IFS=$'\r\n' read "$2" <"$1"
+	test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
 }
 
 # see if a cherry-pick or revert is in progress, if the user has committed a
@@ -410,7 +408,7 @@
 
 	local repo_info rev_parse_exit_code
 	repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
-		--is-bare-repository --is-inside-work-tree \
+		--is-bare-repository --is-inside-work-tree --show-ref-format \
 		--short HEAD 2>/dev/null)"
 	rev_parse_exit_code="$?"
 
@@ -423,6 +421,8 @@
 		short_sha="${repo_info##*$'\n'}"
 		repo_info="${repo_info%$'\n'*}"
 	fi
+	local ref_format="${repo_info##*$'\n'}"
+	repo_info="${repo_info%$'\n'*}"
 	local inside_worktree="${repo_info##*$'\n'}"
 	repo_info="${repo_info%$'\n'*}"
 	local bare_repo="${repo_info##*$'\n'}"
@@ -481,12 +481,25 @@
 			b="$(git symbolic-ref HEAD 2>/dev/null)"
 		else
 			local head=""
-			if ! __git_eread "$g/HEAD" head; then
-				return $exit
-			fi
-			# is it a symbolic ref?
-			b="${head#ref: }"
-			if [ "$head" = "$b" ]; then
+
+			case "$ref_format" in
+			files)
+				if ! __git_eread "$g/HEAD" head; then
+					return $exit
+				fi
+
+				if [[ $head == "ref: "* ]]; then
+					head="${head#ref: }"
+				else
+					head=""
+				fi
+				;;
+			*)
+				head="$(git symbolic-ref HEAD 2>/dev/null)"
+				;;
+			esac
+
+			if test -z "$head"; then
 				detached=yes
 				b="$(
 				case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -504,6 +517,8 @@
 
 				b="$short_sha..."
 				b="($b)"
+			else
+				b="$head"
 			fi
 		fi
 	fi
@@ -513,7 +528,7 @@
 	fi
 
 	local conflict="" # state indicator for unresolved conflicts
-	if [[ "${GIT_PS1_SHOWCONFLICTSTATE}" == "yes" ]] &&
+	if [[ "${GIT_PS1_SHOWCONFLICTSTATE-}" == "yes" ]] &&
 	   [[ $(git ls-files --unmerged 2>/dev/null) ]]; then
 		conflict="|CONFLICT"
 	fi
@@ -574,11 +589,8 @@
 		b="\${__git_ps1_branch_name}"
 	fi
 
-	# NO color option unless in PROMPT_COMMAND mode or it's Zsh
 	if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-		if [ $pcmode = yes ] || [ -n "${ZSH_VERSION-}" ]; then
-			__git_ps1_colorize_gitstring
-		fi
+		__git_ps1_colorize_gitstring
 	fi
 
 	local f="$h$w$i$s$u$p"
diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
index 4ec419f..6ce9603 100755
--- a/contrib/coverage-diff.sh
+++ b/contrib/coverage-diff.sh
@@ -74,8 +74,7 @@
 	sort >uncovered_lines.txt
 
 	comm -12 uncovered_lines.txt new_lines.txt |
-	sed -e 's/$/\)/' |
-	sed -e 's/^/ /' >uncovered_new_lines.txt
+	sed -e 's/$/\)/' -e 's/^/ /' >uncovered_new_lines.txt
 
 	grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
 	echo $file >>coverage-data.txt &&
@@ -91,11 +90,7 @@
 
 echo "Commits introducing uncovered code:"
 
-commit_list=$(cat coverage-data.txt |
-	grep -E '^[0-9a-f]{7,} ' |
-	awk '{print $1;}' |
-	sort |
-	uniq)
+commit_list=$(awk '/^[0-9a-f]{7,}/ { print $1 }' coverage-data.txt | sort -u)
 
 (
 	for commit in $commit_list
diff --git a/contrib/credential/gnome-keyring/.gitignore b/contrib/credential/gnome-keyring/.gitignore
deleted file mode 100644
index 88d8fcd..0000000
--- a/contrib/credential/gnome-keyring/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-git-credential-gnome-keyring
diff --git a/contrib/credential/gnome-keyring/Makefile b/contrib/credential/gnome-keyring/Makefile
deleted file mode 100644
index 22c19df..0000000
--- a/contrib/credential/gnome-keyring/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-MAIN:=git-credential-gnome-keyring
-all:: $(MAIN)
-
-CC = gcc
-RM = rm -f
-CFLAGS = -g -O2 -Wall
-PKG_CONFIG = pkg-config
-
--include ../../../config.mak.autogen
--include ../../../config.mak
-
-INCS:=$(shell $(PKG_CONFIG) --cflags gnome-keyring-1 glib-2.0)
-LIBS:=$(shell $(PKG_CONFIG) --libs gnome-keyring-1 glib-2.0)
-
-SRCS:=$(MAIN).c
-OBJS:=$(SRCS:.c=.o)
-
-%.o: %.c
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
-
-$(MAIN): $(OBJS)
-	$(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
-
-clean:
-	@$(RM) $(MAIN) $(OBJS)
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
deleted file mode 100644
index 5927e27..0000000
--- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
- *               2012 Philipp A. Hartmann <pah@qo.cx>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Credits:
- * - GNOME Keyring API handling originally written by John Szakmeister
- * - ported to credential helper API by Philipp A. Hartmann
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <gnome-keyring.h>
-
-#ifdef GNOME_KEYRING_DEFAULT
-
-   /* Modern gnome-keyring */
-
-#include <gnome-keyring-memory.h>
-
-#else
-
-   /*
-    * Support ancient gnome-keyring, circ. RHEL 5.X.
-    * GNOME_KEYRING_DEFAULT seems to have been introduced with Gnome 2.22,
-    * and the other features roughly around Gnome 2.20, 6 months before.
-    * Ubuntu 8.04 used Gnome 2.22 (I think).  Not sure any distro used 2.20.
-    * So the existence/non-existence of GNOME_KEYRING_DEFAULT seems like
-    * a decent thing to use as an indicator.
-    */
-
-#define GNOME_KEYRING_DEFAULT NULL
-
-/*
- * ancient gnome-keyring returns DENIED when an entry is not found.
- * Setting NO_MATCH to DENIED will prevent us from reporting DENIED
- * errors during get and erase operations, but we will still report
- * DENIED errors during a store.
- */
-#define GNOME_KEYRING_RESULT_NO_MATCH GNOME_KEYRING_RESULT_DENIED
-
-#define gnome_keyring_memory_alloc g_malloc
-#define gnome_keyring_memory_free gnome_keyring_free_password
-#define gnome_keyring_memory_strdup g_strdup
-
-static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
-{
-	switch (result) {
-	case GNOME_KEYRING_RESULT_OK:
-		return "OK";
-	case GNOME_KEYRING_RESULT_DENIED:
-		return "Denied";
-	case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
-		return "No Keyring Daemon";
-	case GNOME_KEYRING_RESULT_ALREADY_UNLOCKED:
-		return "Already UnLocked";
-	case GNOME_KEYRING_RESULT_NO_SUCH_KEYRING:
-		return "No Such Keyring";
-	case GNOME_KEYRING_RESULT_BAD_ARGUMENTS:
-		return "Bad Arguments";
-	case GNOME_KEYRING_RESULT_IO_ERROR:
-		return "IO Error";
-	case GNOME_KEYRING_RESULT_CANCELLED:
-		return "Cancelled";
-	case GNOME_KEYRING_RESULT_ALREADY_EXISTS:
-		return "Already Exists";
-	default:
-		return "Unknown Error";
-	}
-}
-
-/*
- * Support really ancient gnome-keyring, circ. RHEL 4.X.
- * Just a guess for the Glib version.  Glib 2.8 was roughly Gnome 2.12 ?
- * Which was released with gnome-keyring 0.4.3 ??
- */
-#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 8
-
-static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
-{
-	gpointer *data = (gpointer *)user_data;
-	int *done = (int *)data[0];
-	GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
-
-	*r = result;
-	*done = 1;
-}
-
-static void wait_for_request_completion(int *done)
-{
-	GMainContext *mc = g_main_context_default();
-	while (!*done)
-		g_main_context_iteration(mc, TRUE);
-}
-
-static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, guint32 id)
-{
-	int done = 0;
-	GnomeKeyringResult result;
-	gpointer data[] = { &done, &result };
-
-	gnome_keyring_item_delete(keyring, id, gnome_keyring_done_cb, data,
-		NULL);
-
-	wait_for_request_completion(&done);
-
-	return result;
-}
-
-#endif
-#endif
-
-/*
- * This credential struct and API is simplified from git's credential.{h,c}
- */
-struct credential {
-	char *protocol;
-	char *host;
-	unsigned short port;
-	char *path;
-	char *username;
-	char *password;
-};
-
-#define CREDENTIAL_INIT { 0 }
-
-typedef int (*credential_op_cb)(struct credential *);
-
-struct credential_operation {
-	char *name;
-	credential_op_cb op;
-};
-
-#define CREDENTIAL_OP_END { NULL, NULL }
-
-/* ----------------- GNOME Keyring functions ----------------- */
-
-/* create a special keyring option string, if path is given */
-static char *keyring_object(struct credential *c)
-{
-	if (!c->path)
-		return NULL;
-
-	if (c->port)
-		return g_strdup_printf("%s:%hd/%s", c->host, c->port, c->path);
-
-	return g_strdup_printf("%s/%s", c->host, c->path);
-}
-
-static int keyring_get(struct credential *c)
-{
-	char *object = NULL;
-	GList *entries;
-	GnomeKeyringNetworkPasswordData *password_data;
-	GnomeKeyringResult result;
-
-	if (!c->protocol || !(c->host || c->path))
-		return EXIT_FAILURE;
-
-	object = keyring_object(c);
-
-	result = gnome_keyring_find_network_password_sync(
-				c->username,
-				NULL /* domain */,
-				c->host,
-				object,
-				c->protocol,
-				NULL /* authtype */,
-				c->port,
-				&entries);
-
-	g_free(object);
-
-	if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-		return EXIT_SUCCESS;
-
-	if (result == GNOME_KEYRING_RESULT_CANCELLED)
-		return EXIT_SUCCESS;
-
-	if (result != GNOME_KEYRING_RESULT_OK) {
-		g_critical("%s", gnome_keyring_result_to_message(result));
-		return EXIT_FAILURE;
-	}
-
-	/* pick the first one from the list */
-	password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
-
-	gnome_keyring_memory_free(c->password);
-	c->password = gnome_keyring_memory_strdup(password_data->password);
-
-	if (!c->username)
-		c->username = g_strdup(password_data->user);
-
-	gnome_keyring_network_password_list_free(entries);
-
-	return EXIT_SUCCESS;
-}
-
-
-static int keyring_store(struct credential *c)
-{
-	guint32 item_id;
-	char *object = NULL;
-	GnomeKeyringResult result;
-
-	/*
-	 * Sanity check that what we are storing is actually sensible.
-	 * In particular, we can't make a URL without a protocol field.
-	 * Without either a host or pathname (depending on the scheme),
-	 * we have no primary key. And without a username and password,
-	 * we are not actually storing a credential.
-	 */
-	if (!c->protocol || !(c->host || c->path) ||
-	    !c->username || !c->password)
-		return EXIT_FAILURE;
-
-	object = keyring_object(c);
-
-	result = gnome_keyring_set_network_password_sync(
-				GNOME_KEYRING_DEFAULT,
-				c->username,
-				NULL /* domain */,
-				c->host,
-				object,
-				c->protocol,
-				NULL /* authtype */,
-				c->port,
-				c->password,
-				&item_id);
-
-	g_free(object);
-
-	if (result != GNOME_KEYRING_RESULT_OK &&
-	    result != GNOME_KEYRING_RESULT_CANCELLED) {
-		g_critical("%s", gnome_keyring_result_to_message(result));
-		return EXIT_FAILURE;
-	}
-
-	return EXIT_SUCCESS;
-}
-
-static int keyring_erase(struct credential *c)
-{
-	char *object = NULL;
-	GList *entries;
-	GnomeKeyringNetworkPasswordData *password_data;
-	GnomeKeyringResult result;
-
-	/*
-	 * Sanity check that we actually have something to match
-	 * against. The input we get is a restrictive pattern,
-	 * so technically a blank credential means "erase everything".
-	 * But it is too easy to accidentally send this, since it is equivalent
-	 * to empty input. So explicitly disallow it, and require that the
-	 * pattern have some actual content to match.
-	 */
-	if (!c->protocol && !c->host && !c->path && !c->username)
-		return EXIT_FAILURE;
-
-	object = keyring_object(c);
-
-	result = gnome_keyring_find_network_password_sync(
-				c->username,
-				NULL /* domain */,
-				c->host,
-				object,
-				c->protocol,
-				NULL /* authtype */,
-				c->port,
-				&entries);
-
-	g_free(object);
-
-	if (result == GNOME_KEYRING_RESULT_NO_MATCH)
-		return EXIT_SUCCESS;
-
-	if (result == GNOME_KEYRING_RESULT_CANCELLED)
-		return EXIT_SUCCESS;
-
-	if (result != GNOME_KEYRING_RESULT_OK) {
-		g_critical("%s", gnome_keyring_result_to_message(result));
-		return EXIT_FAILURE;
-	}
-
-	/* pick the first one from the list (delete all matches?) */
-	password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
-
-	result = gnome_keyring_item_delete_sync(
-		password_data->keyring, password_data->item_id);
-
-	gnome_keyring_network_password_list_free(entries);
-
-	if (result != GNOME_KEYRING_RESULT_OK) {
-		g_critical("%s", gnome_keyring_result_to_message(result));
-		return EXIT_FAILURE;
-	}
-
-	return EXIT_SUCCESS;
-}
-
-/*
- * Table with helper operation callbacks, used by generic
- * credential helper main function.
- */
-static struct credential_operation const credential_helper_ops[] = {
-	{ "get",   keyring_get },
-	{ "store", keyring_store },
-	{ "erase", keyring_erase },
-	CREDENTIAL_OP_END
-};
-
-/* ------------------ credential functions ------------------ */
-
-static void credential_init(struct credential *c)
-{
-	memset(c, 0, sizeof(*c));
-}
-
-static void credential_clear(struct credential *c)
-{
-	g_free(c->protocol);
-	g_free(c->host);
-	g_free(c->path);
-	g_free(c->username);
-	gnome_keyring_memory_free(c->password);
-
-	credential_init(c);
-}
-
-static int credential_read(struct credential *c)
-{
-	char *buf;
-	size_t line_len;
-	char *key;
-	char *value;
-
-	key = buf = gnome_keyring_memory_alloc(1024);
-
-	while (fgets(buf, 1024, stdin)) {
-		line_len = strlen(buf);
-
-		if (line_len && buf[line_len-1] == '\n')
-			buf[--line_len] = '\0';
-
-		if (!line_len)
-			break;
-
-		value = strchr(buf, '=');
-		if (!value) {
-			g_warning("invalid credential line: %s", key);
-			gnome_keyring_memory_free(buf);
-			return -1;
-		}
-		*value++ = '\0';
-
-		if (!strcmp(key, "protocol")) {
-			g_free(c->protocol);
-			c->protocol = g_strdup(value);
-		} else if (!strcmp(key, "host")) {
-			g_free(c->host);
-			c->host = g_strdup(value);
-			value = strrchr(c->host, ':');
-			if (value) {
-				*value++ = '\0';
-				c->port = atoi(value);
-			}
-		} else if (!strcmp(key, "path")) {
-			g_free(c->path);
-			c->path = g_strdup(value);
-		} else if (!strcmp(key, "username")) {
-			g_free(c->username);
-			c->username = g_strdup(value);
-		} else if (!strcmp(key, "password")) {
-			gnome_keyring_memory_free(c->password);
-			c->password = gnome_keyring_memory_strdup(value);
-			while (*value)
-				*value++ = '\0';
-		}
-		/*
-		 * Ignore other lines; we don't know what they mean, but
-		 * this future-proofs us when later versions of git do
-		 * learn new lines, and the helpers are updated to match.
-		 */
-	}
-
-	gnome_keyring_memory_free(buf);
-
-	return 0;
-}
-
-static void credential_write_item(FILE *fp, const char *key, const char *value)
-{
-	if (!value)
-		return;
-	fprintf(fp, "%s=%s\n", key, value);
-}
-
-static void credential_write(const struct credential *c)
-{
-	/* only write username/password, if set */
-	credential_write_item(stdout, "username", c->username);
-	credential_write_item(stdout, "password", c->password);
-}
-
-static void usage(const char *name)
-{
-	struct credential_operation const *try_op = credential_helper_ops;
-	const char *basename = strrchr(name, '/');
-
-	basename = (basename) ? basename + 1 : name;
-	fprintf(stderr, "usage: %s <", basename);
-	while (try_op->name) {
-		fprintf(stderr, "%s", (try_op++)->name);
-		if (try_op->name)
-			fprintf(stderr, "%s", "|");
-	}
-	fprintf(stderr, "%s", ">\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int ret = EXIT_SUCCESS;
-
-	struct credential_operation const *try_op = credential_helper_ops;
-	struct credential cred = CREDENTIAL_INIT;
-
-	if (!argv[1]) {
-		usage(argv[0]);
-		exit(EXIT_FAILURE);
-	}
-
-	g_set_application_name("Git Credential Helper");
-
-	/* lookup operation callback */
-	while (try_op->name && strcmp(argv[1], try_op->name))
-		try_op++;
-
-	/* unsupported operation given -- ignore silently */
-	if (!try_op->name || !try_op->op)
-		goto out;
-
-	ret = credential_read(&cred);
-	if (ret)
-		goto out;
-
-	/* perform credential operation */
-	ret = (*try_op->op)(&cred);
-
-	credential_write(&cred);
-
-out:
-	credential_clear(&cred);
-	return ret;
-}
diff --git a/contrib/credential/libsecret/.gitignore b/contrib/credential/libsecret/.gitignore
new file mode 100644
index 0000000..4fa2235
--- /dev/null
+++ b/contrib/credential/libsecret/.gitignore
@@ -0,0 +1 @@
+git-credential-libsecret
diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c
index 2c5d76d..90034d0 100644
--- a/contrib/credential/libsecret/git-credential-libsecret.c
+++ b/contrib/credential/libsecret/git-credential-libsecret.c
@@ -39,6 +39,8 @@
 	char *path;
 	char *username;
 	char *password;
+	char *password_expiry_utc;
+	char *oauth_refresh_token;
 };
 
 #define CREDENTIAL_INIT { 0 }
@@ -52,8 +54,29 @@
 
 #define CREDENTIAL_OP_END { NULL, NULL }
 
+static void credential_clear(struct credential *c);
+
 /* ----------------- Secret Service functions ----------------- */
 
+static const SecretSchema schema = {
+	"org.git.Password",
+	/* Ignore schema name during search for backwards compatibility */
+	SECRET_SCHEMA_DONT_MATCH_NAME,
+	{
+		/*
+		 * libsecret assumes attribute values are non-confidential and
+		 * unchanging, so we can't include oauth_refresh_token or
+		 * password_expiry_utc.
+		 */
+		{  "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
+		{  "object", SECRET_SCHEMA_ATTRIBUTE_STRING },
+		{  "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING },
+		{  "port", SECRET_SCHEMA_ATTRIBUTE_INTEGER },
+		{  "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
+		{  NULL, 0 },
+	}
+};
+
 static char *make_label(struct credential *c)
 {
 	if (c->port)
@@ -101,7 +124,7 @@
 
 	attributes = make_attr_list(c);
 	items = secret_service_search_sync(service,
-					   SECRET_SCHEMA_COMPAT_NETWORK,
+					   &schema,
 					   attributes,
 					   SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK,
 					   NULL,
@@ -117,6 +140,7 @@
 		SecretItem *item;
 		SecretValue *secret;
 		const char *s;
+		gchar **parts;
 
 		item = items->data;
 		secret = secret_item_get_secret(item);
@@ -130,8 +154,30 @@
 
 		s = secret_value_get_text(secret);
 		if (s) {
-			g_free(c->password);
-			c->password = g_strdup(s);
+			/*
+			 * Passwords and other attributes encoded in following format:
+			 *   hunter2
+			 *   password_expiry_utc=1684189401
+			 *   oauth_refresh_token=xyzzy
+			 */
+			parts = g_strsplit(s, "\n", 0);
+			if (g_strv_length(parts) >= 1) {
+				g_free(c->password);
+				c->password = g_strdup(parts[0]);
+			} else {
+				g_free(c->password);
+				c->password = g_strdup("");
+			}
+			for (int i = 1; i < g_strv_length(parts); i++) {
+				if (g_str_has_prefix(parts[i], "password_expiry_utc=")) {
+					g_free(c->password_expiry_utc);
+					c->password_expiry_utc = g_strdup(&parts[i][20]);
+				} else if (g_str_has_prefix(parts[i], "oauth_refresh_token=")) {
+					g_free(c->oauth_refresh_token);
+					c->oauth_refresh_token = g_strdup(&parts[i][20]);
+				}
+			}
+			g_strfreev(parts);
 		}
 
 		g_hash_table_unref(attributes);
@@ -148,6 +194,7 @@
 	char *label = NULL;
 	GHashTable *attributes = NULL;
 	GError *error = NULL;
+	GString *secret = NULL;
 
 	/*
 	 * Sanity check that what we are storing is actually sensible.
@@ -162,13 +209,23 @@
 
 	label = make_label(c);
 	attributes = make_attr_list(c);
-	secret_password_storev_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+	secret = g_string_new(c->password);
+	if (c->password_expiry_utc) {
+		g_string_append_printf(secret, "\npassword_expiry_utc=%s",
+			c->password_expiry_utc);
+	}
+	if (c->oauth_refresh_token) {
+		g_string_append_printf(secret, "\noauth_refresh_token=%s",
+			c->oauth_refresh_token);
+	}
+	secret_password_storev_sync(&schema,
 				    attributes,
 				    NULL,
 				    label,
-				    c->password,
+				    secret->str,
 				    NULL,
 				    &error);
+	g_string_free(secret, TRUE);
 	g_free(label);
 	g_hash_table_unref(attributes);
 
@@ -185,6 +242,7 @@
 {
 	GHashTable *attributes = NULL;
 	GError *error = NULL;
+	struct credential existing = CREDENTIAL_INIT;
 
 	/*
 	 * Sanity check that we actually have something to match
@@ -197,8 +255,22 @@
 	if (!c->protocol && !c->host && !c->path && !c->username)
 		return EXIT_FAILURE;
 
+	if (c->password) {
+		existing.host = g_strdup(c->host);
+		existing.path = g_strdup(c->path);
+		existing.port = c->port;
+		existing.protocol = g_strdup(c->protocol);
+		existing.username = g_strdup(c->username);
+		keyring_get(&existing);
+		if (existing.password && strcmp(c->password, existing.password)) {
+			credential_clear(&existing);
+			return EXIT_SUCCESS;
+		}
+		credential_clear(&existing);
+	}
+
 	attributes = make_attr_list(c);
-	secret_password_clearv_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+	secret_password_clearv_sync(&schema,
 				    attributes,
 				    NULL,
 				    &error);
@@ -238,23 +310,24 @@
 	g_free(c->path);
 	g_free(c->username);
 	g_free(c->password);
+	g_free(c->password_expiry_utc);
+	g_free(c->oauth_refresh_token);
 
 	credential_init(c);
 }
 
 static int credential_read(struct credential *c)
 {
-	char *buf;
-	size_t line_len;
+	char *buf = NULL;
+	size_t alloc;
+	ssize_t line_len;
 	char *key;
 	char *value;
 
-	key = buf = g_malloc(1024);
+	while ((line_len = getline(&buf, &alloc, stdin)) > 0) {
+		key = buf;
 
-	while (fgets(buf, 1024, stdin)) {
-		line_len = strlen(buf);
-
-		if (line_len && buf[line_len-1] == '\n')
+		if (buf[line_len-1] == '\n')
 			buf[--line_len] = '\0';
 
 		if (!line_len)
@@ -285,11 +358,19 @@
 		} else if (!strcmp(key, "username")) {
 			g_free(c->username);
 			c->username = g_strdup(value);
+		} else if (!strcmp(key, "password_expiry_utc")) {
+			g_free(c->password_expiry_utc);
+			c->password_expiry_utc = g_strdup(value);
 		} else if (!strcmp(key, "password")) {
 			g_free(c->password);
 			c->password = g_strdup(value);
 			while (*value)
 				*value++ = '\0';
+		} else if (!strcmp(key, "oauth_refresh_token")) {
+			g_free(c->oauth_refresh_token);
+			c->oauth_refresh_token = g_strdup(value);
+			while (*value)
+				*value++ = '\0';
 		}
 		/*
 		 * Ignore other lines; we don't know what they mean, but
@@ -298,7 +379,7 @@
 		 */
 	}
 
-	g_free(buf);
+	free(buf);
 
 	return 0;
 }
@@ -315,6 +396,10 @@
 	/* only write username/password, if set */
 	credential_write_item(stdout, "username", c->username);
 	credential_write_item(stdout, "password", c->password);
+	credential_write_item(stdout, "password_expiry_utc",
+		c->password_expiry_utc);
+	credential_write_item(stdout, "oauth_refresh_token",
+		c->oauth_refresh_token);
 }
 
 static void usage(const char *name)
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index e29cc28..5f2e5f1 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -113,14 +113,16 @@
 
 static void read_credential(void)
 {
-	char buf[1024];
+	char *buf = NULL;
+	size_t alloc;
+	ssize_t line_len;
 
-	while (fgets(buf, sizeof(buf), stdin)) {
+	while ((line_len = getline(&buf, &alloc, stdin)) > 0) {
 		char *v;
 
 		if (!strcmp(buf, "\n"))
 			break;
-		buf[strlen(buf)-1] = '\0';
+		buf[line_len-1] = '\0';
 
 		v = strchr(buf, '=');
 		if (!v)
@@ -165,6 +167,8 @@
 		 * learn new lines, and the helpers are updated to match.
 		 */
 	}
+
+	free(buf);
 }
 
 int main(int argc, const char **argv)
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c
index ead6e26..4be0d58 100644
--- a/contrib/credential/wincred/git-credential-wincred.c
+++ b/contrib/credential/wincred/git-credential-wincred.c
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <io.h>
 #include <fcntl.h>
+#include <wincred.h>
 
 /* common helpers */
 
@@ -33,65 +34,8 @@
 	return ret;
 }
 
-/* MinGW doesn't have wincred.h, so we need to define stuff */
-
-typedef struct _CREDENTIAL_ATTRIBUTEW {
-	LPWSTR Keyword;
-	DWORD  Flags;
-	DWORD  ValueSize;
-	LPBYTE Value;
-} CREDENTIAL_ATTRIBUTEW, *PCREDENTIAL_ATTRIBUTEW;
-
-typedef struct _CREDENTIALW {
-	DWORD                  Flags;
-	DWORD                  Type;
-	LPWSTR                 TargetName;
-	LPWSTR                 Comment;
-	FILETIME               LastWritten;
-	DWORD                  CredentialBlobSize;
-	LPBYTE                 CredentialBlob;
-	DWORD                  Persist;
-	DWORD                  AttributeCount;
-	PCREDENTIAL_ATTRIBUTEW Attributes;
-	LPWSTR                 TargetAlias;
-	LPWSTR                 UserName;
-} CREDENTIALW, *PCREDENTIALW;
-
-#define CRED_TYPE_GENERIC 1
-#define CRED_PERSIST_LOCAL_MACHINE 2
-#define CRED_MAX_ATTRIBUTES 64
-
-typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
-typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
-    PCREDENTIALW **);
-typedef VOID (WINAPI *CredFreeT)(PVOID);
-typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
-
-static HMODULE advapi;
-static CredWriteWT CredWriteW;
-static CredEnumerateWT CredEnumerateW;
-static CredFreeT CredFree;
-static CredDeleteWT CredDeleteW;
-
-static void load_cred_funcs(void)
-{
-	/* load DLLs */
-	advapi = LoadLibraryExA("advapi32.dll", NULL,
-				LOAD_LIBRARY_SEARCH_SYSTEM32);
-	if (!advapi)
-		die("failed to load advapi32.dll");
-
-	/* get function pointers */
-	CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
-	CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
-	    "CredEnumerateW");
-	CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
-	CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
-	if (!CredWriteW || !CredEnumerateW || !CredFree || !CredDeleteW)
-		die("failed to load functions");
-}
-
-static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
+static WCHAR *wusername, *password, *protocol, *host, *path, target[1024],
+	*password_expiry_utc, *oauth_refresh_token;
 
 static void write_item(const char *what, LPCWSTR wbuf, int wlen)
 {
@@ -165,7 +109,18 @@
 	return match_part_with_last(ptarget, want, delim, 1);
 }
 
-static int match_cred(const CREDENTIALW *cred)
+static int match_cred_password(const CREDENTIALW *cred) {
+	int ret;
+	WCHAR *cred_password = xmalloc(cred->CredentialBlobSize);
+	wcsncpy_s(cred_password, cred->CredentialBlobSize,
+		(LPCWSTR)cred->CredentialBlob,
+		cred->CredentialBlobSize / sizeof(WCHAR));
+	ret = !wcscmp(cred_password, password);
+	free(cred_password);
+	return ret;
+}
+
+static int match_cred(const CREDENTIALW *cred, int match_password)
 {
 	LPCWSTR target = cred->TargetName;
 	if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L""))
@@ -175,7 +130,8 @@
 		match_part(&target, protocol, L"://") &&
 		match_part_last(&target, wusername, L"@") &&
 		match_part(&target, host, L"/") &&
-		match_part(&target, path, L"");
+		match_part(&target, path, L"") &&
+		(!match_password || match_cred_password(cred));
 }
 
 static void get_credential(void)
@@ -183,18 +139,47 @@
 	CREDENTIALW **creds;
 	DWORD num_creds;
 	int i;
+	CREDENTIAL_ATTRIBUTEW *attr;
+	WCHAR *secret;
+	WCHAR *line;
+	WCHAR *remaining_lines;
+	WCHAR *part;
+	WCHAR *remaining_parts;
 
 	if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
 		return;
 
 	/* search for the first credential that matches username */
 	for (i = 0; i < num_creds; ++i)
-		if (match_cred(creds[i])) {
+		if (match_cred(creds[i], 0)) {
 			write_item("username", creds[i]->UserName,
 				creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
-			write_item("password",
-				(LPCWSTR)creds[i]->CredentialBlob,
-				creds[i]->CredentialBlobSize / sizeof(WCHAR));
+			if (creds[i]->CredentialBlobSize > 0) {
+				secret = xmalloc(creds[i]->CredentialBlobSize);
+				wcsncpy_s(secret, creds[i]->CredentialBlobSize, (LPCWSTR)creds[i]->CredentialBlob, creds[i]->CredentialBlobSize / sizeof(WCHAR));
+				line = wcstok_s(secret, L"\r\n", &remaining_lines);
+				write_item("password", line, line ? wcslen(line) : 0);
+				while(line != NULL) {
+					part = wcstok_s(line, L"=", &remaining_parts);
+					if (!wcscmp(part, L"oauth_refresh_token")) {
+						write_item("oauth_refresh_token", remaining_parts, remaining_parts ? wcslen(remaining_parts) : 0);
+					}
+					line = wcstok_s(NULL, L"\r\n", &remaining_lines);
+				}
+				free(secret);
+			} else {
+				write_item("password",
+						(LPCWSTR)creds[i]->CredentialBlob,
+						creds[i]->CredentialBlobSize / sizeof(WCHAR));
+			}
+			for (int j = 0; j < creds[i]->AttributeCount; j++) {
+				attr = creds[i]->Attributes + j;
+				if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
+					write_item("password_expiry_utc", (LPCWSTR)attr->Value,
+					attr->ValueSize / sizeof(WCHAR));
+					break;
+				}
+			}
 			break;
 		}
 
@@ -204,22 +189,43 @@
 static void store_credential(void)
 {
 	CREDENTIALW cred;
+	CREDENTIAL_ATTRIBUTEW expiry_attr;
+	WCHAR *secret;
+	int wlen;
 
 	if (!wusername || !password)
 		return;
 
+	if (oauth_refresh_token) {
+		wlen = _scwprintf(L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token);
+		secret = xmalloc(sizeof(WCHAR) * wlen);
+		_snwprintf_s(secret, sizeof(WCHAR) * wlen, wlen, L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token);
+	} else {
+		secret = _wcsdup(password);
+	}
+
 	cred.Flags = 0;
 	cred.Type = CRED_TYPE_GENERIC;
 	cred.TargetName = target;
 	cred.Comment = L"saved by git-credential-wincred";
-	cred.CredentialBlobSize = (wcslen(password)) * sizeof(WCHAR);
-	cred.CredentialBlob = (LPVOID)password;
+	cred.CredentialBlobSize = wcslen(secret) * sizeof(WCHAR);
+	cred.CredentialBlob = (LPVOID)_wcsdup(secret);
 	cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
 	cred.AttributeCount = 0;
 	cred.Attributes = NULL;
+	if (password_expiry_utc != NULL) {
+		expiry_attr.Keyword = L"git_password_expiry_utc";
+		expiry_attr.Value = (LPVOID)password_expiry_utc;
+		expiry_attr.ValueSize = (wcslen(password_expiry_utc)) * sizeof(WCHAR);
+		expiry_attr.Flags = 0;
+		cred.Attributes = &expiry_attr;
+		cred.AttributeCount = 1;
+	}
 	cred.TargetAlias = NULL;
 	cred.UserName = wusername;
 
+	free(secret);
+
 	if (!CredWriteW(&cred, 0))
 		die("CredWrite failed");
 }
@@ -234,7 +240,7 @@
 		return;
 
 	for (i = 0; i < num_creds; ++i) {
-		if (match_cred(creds[i]))
+		if (match_cred(creds[i], password != NULL))
 			CredDeleteW(creds[i]->TargetName, creds[i]->Type, 0);
 	}
 
@@ -249,16 +255,27 @@
 	return wstr;
 }
 
+#define KB (1024)
+
 static void read_credential(void)
 {
-	char buf[1024];
+	size_t alloc = 100 * KB;
+	char *buf = calloc(alloc, sizeof(*buf));
 
-	while (fgets(buf, sizeof(buf), stdin)) {
+	while (fgets(buf, alloc, stdin)) {
 		char *v;
-		int len = strlen(buf);
+		size_t len = strlen(buf);
+		int ends_in_newline = 0;
 		/* strip trailing CR / LF */
-		while (len && strchr("\r\n", buf[len - 1]))
+		if (len && buf[len - 1] == '\n') {
 			buf[--len] = 0;
+			ends_in_newline = 1;
+		}
+		if (len && buf[len - 1] == '\r')
+			buf[--len] = 0;
+
+		if (!ends_in_newline)
+			die("bad input: %s", buf);
 
 		if (!*buf)
 			break;
@@ -278,12 +295,18 @@
 			wusername = utf8_to_utf16_dup(v);
 		} else if (!strcmp(buf, "password"))
 			password = utf8_to_utf16_dup(v);
+		else if (!strcmp(buf, "password_expiry_utc"))
+			password_expiry_utc = utf8_to_utf16_dup(v);
+		else if (!strcmp(buf, "oauth_refresh_token"))
+			oauth_refresh_token = utf8_to_utf16_dup(v);
 		/*
 		 * Ignore other lines; we don't know what they mean, but
 		 * this future-proofs us when later versions of git do
 		 * learn new lines, and the helpers are updated to match.
 		 */
 	}
+
+	free(buf);
 }
 
 int main(int argc, char *argv[])
@@ -292,7 +315,7 @@
 	    "usage: git credential-wincred <get|store|erase>\n";
 
 	if (!argv[1])
-		die(usage);
+		die("%s", usage);
 
 	/* git use binary pipes to avoid CRLF-issues */
 	_setmode(_fileno(stdin), _O_BINARY);
@@ -300,8 +323,6 @@
 
 	read_credential();
 
-	load_cred_funcs();
-
 	if (!protocol || !(host || path))
 		return 0;
 
diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm
index 376f577..636add6 100644
--- a/contrib/diff-highlight/DiffHighlight.pm
+++ b/contrib/diff-highlight/DiffHighlight.pm
@@ -1,6 +1,6 @@
 package DiffHighlight;
 
-use 5.008;
+use 5.008001;
 use warnings FATAL => 'all';
 use strict;
 
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
index 40c4b0d..47e0c55 100755
--- a/contrib/git-jump/git-jump
+++ b/contrib/git-jump/git-jump
@@ -9,7 +9,7 @@
 
 diff: elements are diff hunks. Arguments are given to diff.
 
-merge: elements are merge conflicts. Arguments are ignored.
+merge: elements are merge conflicts. Arguments are given to ls-files -u.
 
 grep: elements are grep hits. Arguments are given to git grep or, if
       configured, to the command in `jump.grepCmd`.
diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
deleted file mode 100755
index 7eb1b24..0000000
--- a/contrib/hg-to-git/hg-to-git.py
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/env python
-
-""" hg-to-git.py - A Mercurial to GIT converter
-
-    Copyright (C)2007 Stelian Pop <stelian@popies.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2, or (at your option)
-    any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, see <http://www.gnu.org/licenses/>.
-"""
-
-import os, os.path, sys
-import tempfile, pickle, getopt
-import re
-
-if sys.hexversion < 0x02030000:
-   # The behavior of the pickle module changed significantly in 2.3
-   sys.stderr.write("hg-to-git.py: requires Python 2.3 or later.\n")
-   sys.exit(1)
-
-# Maps hg version -> git version
-hgvers = {}
-# List of children for each hg revision
-hgchildren = {}
-# List of parents for each hg revision
-hgparents = {}
-# Current branch for each hg revision
-hgbranch = {}
-# Number of new changesets converted from hg
-hgnewcsets = 0
-
-#------------------------------------------------------------------------------
-
-def usage():
-
-        print("""\
-%s: [OPTIONS] <hgprj>
-
-options:
-    -s, --gitstate=FILE: name of the state to be saved/read
-                         for incrementals
-    -n, --nrepack=INT:   number of changesets that will trigger
-                         a repack (default=0, -1 to deactivate)
-    -v, --verbose:       be verbose
-
-required:
-    hgprj:  name of the HG project to import (directory)
-""" % sys.argv[0])
-
-#------------------------------------------------------------------------------
-
-def getgitenv(user, date):
-    env = ''
-    elems = re.compile('(.*?)\s+<(.*)>').match(user)
-    if elems:
-        env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
-        env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
-        env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
-        env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
-    else:
-        env += 'export GIT_AUTHOR_NAME="%s" ;' % user
-        env += 'export GIT_COMMITTER_NAME="%s" ;' % user
-        env += 'export GIT_AUTHOR_EMAIL= ;'
-        env += 'export GIT_COMMITTER_EMAIL= ;'
-
-    env += 'export GIT_AUTHOR_DATE="%s" ;' % date
-    env += 'export GIT_COMMITTER_DATE="%s" ;' % date
-    return env
-
-#------------------------------------------------------------------------------
-
-state = ''
-opt_nrepack = 0
-verbose = False
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose'])
-    for o, a in opts:
-        if o in ('-s', '--gitstate'):
-            state = a
-            state = os.path.abspath(state)
-        if o in ('-n', '--nrepack'):
-            opt_nrepack = int(a)
-        if o in ('-v', '--verbose'):
-            verbose = True
-    if len(args) != 1:
-        raise Exception('params')
-except:
-    usage()
-    sys.exit(1)
-
-hgprj = args[0]
-os.chdir(hgprj)
-
-if state:
-    if os.path.exists(state):
-        if verbose:
-            print('State does exist, reading')
-        f = open(state, 'r')
-        hgvers = pickle.load(f)
-    else:
-        print('State does not exist, first run')
-
-sock = os.popen('hg tip --template "{rev}"')
-tip = sock.read()
-if sock.close():
-    sys.exit(1)
-if verbose:
-    print('tip is', tip)
-
-# Calculate the branches
-if verbose:
-    print('analysing the branches...')
-hgchildren["0"] = ()
-hgparents["0"] = (None, None)
-hgbranch["0"] = "master"
-for cset in range(1, int(tip) + 1):
-    hgchildren[str(cset)] = ()
-    prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ')
-    prnts = map(lambda x: x[:x.find(':')], prnts)
-    if prnts[0] != '':
-        parent = prnts[0].strip()
-    else:
-        parent = str(cset - 1)
-    hgchildren[parent] += ( str(cset), )
-    if len(prnts) > 1:
-        mparent = prnts[1].strip()
-        hgchildren[mparent] += ( str(cset), )
-    else:
-        mparent = None
-
-    hgparents[str(cset)] = (parent, mparent)
-
-    if mparent:
-        # For merge changesets, take either one, preferably the 'master' branch
-        if hgbranch[mparent] == 'master':
-            hgbranch[str(cset)] = 'master'
-        else:
-            hgbranch[str(cset)] = hgbranch[parent]
-    else:
-        # Normal changesets
-        # For first children, take the parent branch, for the others create a new branch
-        if hgchildren[parent][0] == str(cset):
-            hgbranch[str(cset)] = hgbranch[parent]
-        else:
-            hgbranch[str(cset)] = "branch-" + str(cset)
-
-if "0" not in hgvers:
-    print('creating repository')
-    os.system('git init')
-
-# loop through every hg changeset
-for cset in range(int(tip) + 1):
-
-    # incremental, already seen
-    if str(cset) in hgvers:
-        continue
-    hgnewcsets += 1
-
-    # get info
-    log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines()
-    tag = log_data[0].strip()
-    date = log_data[1].strip()
-    user = log_data[2].strip()
-    parent = hgparents[str(cset)][0]
-    mparent = hgparents[str(cset)][1]
-
-    #get comment
-    (fdcomment, filecomment) = tempfile.mkstemp()
-    csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip()
-    os.write(fdcomment, csetcomment)
-    os.close(fdcomment)
-
-    print('-----------------------------------------')
-    print('cset:', cset)
-    print('branch:', hgbranch[str(cset)])
-    print('user:', user)
-    print('date:', date)
-    print('comment:', csetcomment)
-    if parent:
-        print('parent:', parent)
-    if mparent:
-        print('mparent:', mparent)
-    if tag:
-        print('tag:', tag)
-    print('-----------------------------------------')
-
-    # checkout the parent if necessary
-    if cset != 0:
-        if hgbranch[str(cset)] == "branch-" + str(cset):
-            print('creating new branch', hgbranch[str(cset)])
-            os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
-        else:
-            print('checking out branch', hgbranch[str(cset)])
-            os.system('git checkout %s' % hgbranch[str(cset)])
-
-    # merge
-    if mparent:
-        if hgbranch[parent] == hgbranch[str(cset)]:
-            otherbranch = hgbranch[mparent]
-        else:
-            otherbranch = hgbranch[parent]
-        print('merging', otherbranch, 'into', hgbranch[str(cset)])
-        os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
-
-    # remove everything except .git and .hg directories
-    os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
-
-    # repopulate with checkouted files
-    os.system('hg update -C %d' % cset)
-
-    # add new files
-    os.system('git ls-files -x .hg --others | git update-index --add --stdin')
-    # delete removed files
-    os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
-
-    # commit
-    os.system(getgitenv(user, date) + 'git commit --allow-empty --allow-empty-message -a -F %s' % filecomment)
-    os.unlink(filecomment)
-
-    # tag
-    if tag and tag != 'tip':
-        os.system(getgitenv(user, date) + 'git tag %s' % tag)
-
-    # delete branch if not used anymore...
-    if mparent and len(hgchildren[str(cset)]):
-        print("Deleting unused branch:", otherbranch)
-        os.system('git branch -d %s' % otherbranch)
-
-    # retrieve and record the version
-    vvv = os.popen('git show --quiet --pretty=format:%H').read()
-    print('record', cset, '->', vvv)
-    hgvers[str(cset)] = vvv
-
-if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
-    os.system('git repack -a -d')
-
-# write the state for incrementals
-if state:
-    if verbose:
-        print('Writing state')
-    f = open(state, 'w')
-    pickle.dump(hgvers, f)
-
-# vim: et ts=8 sw=4 sts=4
diff --git a/contrib/hg-to-git/hg-to-git.txt b/contrib/hg-to-git/hg-to-git.txt
deleted file mode 100644
index 91f8fe6..0000000
--- a/contrib/hg-to-git/hg-to-git.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-hg-to-git.py is able to convert a Mercurial repository into a git one,
-and preserves the branches in the process (unlike tailor)
-
-hg-to-git.py can probably be greatly improved (it's a rather crude
-combination of shell and python) but it does already work quite well for
-me. Features:
-	- supports incremental conversion
-	  (for keeping a git repo in sync with a hg one)
-        - supports hg branches
-        - converts hg tags
-
-Note that the git repository will be created 'in place' (at the same
-location as the source hg repo). You will have to manually remove the
-'.hg' directory after the conversion.
-
-Also note that the incremental conversion uses 'simple' hg changesets
-identifiers (ordinals, as opposed to SHA-1 ids), and since these ids
-are not stable across different repositories the hg-to-git.py state file
-is forever tied to one hg repository.
-
-Stelian Pop <stelian@popies.net>
diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm
index 917d9e2..ff78112 100644
--- a/contrib/mw-to-git/Git/Mediawiki.pm
+++ b/contrib/mw-to-git/Git/Mediawiki.pm
@@ -1,6 +1,6 @@
 package Git::Mediawiki;
 
-use 5.008;
+use 5.008001;
 use strict;
 use POSIX;
 use Git;
diff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
index 6187ec6..7139995 100755
--- a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
+++ b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
@@ -161,7 +161,7 @@
 		git add foo.forbidden &&
 		git commit -m "add a file" &&
 		git push 2>actual &&
-		test_i18ngrep "foo.forbidden is not a permitted file" actual
+		test_grep "foo.forbidden is not a permitted file" actual
 	)
 '
 
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 10c9c87..5dab3f5 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -33,19 +33,19 @@
 git subtree pull  --prefix=<prefix> <repository> <ref>
 git subtree push  --prefix=<prefix> <repository> <refspec>
 --
-h,help        show the help
-q             quiet
-d             show debug messages
+h,help!       show the help
+q,quiet!      quiet
+d,debug!      show debug messages
 P,prefix=     the name of the subdir to split out
  options for 'split' (also: 'push')
 annotate=     add a prefix to commit message of new commits
-b,branch=     create a new branch from the split subtree
+b,branch!=    create a new branch from the split subtree
 ignore-joins  ignore prior --rejoin commits
 onto=         try connecting new tree to an existing one
 rejoin        merge the new branch back into HEAD
  options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin')
 squash        merge subtree changes as a single commit
-m,message=    use the given message as the commit message for the merge commit
+m,message!=   use the given message as the commit message for the merge commit
 "
 
 indent=0
@@ -373,7 +373,8 @@
 
 # Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH [REPOSITORY]
 process_subtree_split_trailer () {
-	assert test $# = 2 -o $# = 3
+	assert test $# -ge 2
+	assert test $# -le 3
 	b="$1"
 	sq="$2"
 	repository=""
@@ -402,7 +403,8 @@
 
 # Usage: find_latest_squash DIR [REPOSITORY]
 find_latest_squash () {
-	assert test $# = 1 -o $# = 2
+	assert test $# -ge 1
+	assert test $# -le 2
 	dir="$1"
 	repository=""
 	if test "$#" = 2
@@ -455,7 +457,8 @@
 
 # Usage: find_existing_splits DIR REV [REPOSITORY]
 find_existing_splits () {
-	assert test $# = 2 -o $# = 3
+	assert test $# -ge 2
+	assert test $# -le 3
 	debug "Looking for prior splits..."
 	local indent=$(($indent + 1))
 
@@ -489,13 +492,13 @@
 			;;
 		END)
 			debug "Main is: '$main'"
-			if test -z "$main" -a -n "$sub"
+			if test -z "$main" && test -n "$sub"
 			then
 				# squash commits refer to a subtree
 				debug "  Squash: $sq from $sub"
 				cache_set "$sq" "$sub"
 			fi
-			if test -n "$main" -a -n "$sub"
+			if test -n "$main" && test -n "$sub"
 			then
 				debug "  Prior: $main -> $sub"
 				cache_set $main $sub
@@ -638,10 +641,16 @@
 	while read mode type tree name
 	do
 		assert test "$name" = "$dir"
-		assert test "$type" = "tree" -o "$type" = "commit"
-		test "$type" = "commit" && continue  # ignore submodules
-		echo $tree
-		break
+
+		case "$type" in
+		commit)
+			continue;; # ignore submodules
+		tree)
+			echo $tree
+			break;;
+		*)
+			die "fatal: tree entry is of type ${type}, expected tree or commit";;
+		esac
 	done || exit $?
 }
 
@@ -778,6 +787,22 @@
 		die "fatal: '$1' does not look like a ref"
 }
 
+# Usage: check if a commit from another subtree should be
+# ignored from processing for splits
+should_ignore_subtree_split_commit () {
+	assert test $# = 1
+	local rev="$1"
+	if test -n "$(git log -1 --grep="git-subtree-dir:" $rev)"
+	then
+		if test -z "$(git log -1 --grep="git-subtree-mainline:" $rev)" &&
+			test -z "$(git log -1 --grep="git-subtree-dir: $arg_prefix$" $rev)"
+		then
+			return 0
+		fi
+	fi
+	return 1
+}
+
 # Usage: process_split_commit REV PARENTS
 process_split_commit () {
 	assert test $# = 2
@@ -916,7 +941,7 @@
 	if test $# -eq 0
 	then
 		rev=$(git rev-parse HEAD)
-	elif test $# -eq 1 -o $# -eq 2
+	elif test $# -eq 1 || test $# -eq 2
 	then
 		rev=$(git rev-parse -q --verify "$1^{commit}") ||
 			die "fatal: '$1' does not refer to a commit"
@@ -963,7 +988,19 @@
 	eval "$grl" |
 	while read rev parents
 	do
-		process_split_commit "$rev" "$parents"
+		if should_ignore_subtree_split_commit "$rev"
+		then
+			continue
+		fi
+		parsedparents=''
+		for parent in $parents
+		do
+			if ! should_ignore_subtree_split_commit "$parent"
+			then
+				parsedparents="$parsedparents$parent "
+			fi
+		done
+		process_split_commit "$rev" "$parsedparents"
 	done || exit $?
 
 	latest_new=$(cache_get latest_new) || exit $?
@@ -1006,8 +1043,11 @@
 
 # Usage: cmd_merge REV [REPOSITORY]
 cmd_merge () {
-	test $# -eq 1 -o $# -eq 2 ||
+	if test $# -lt 1 || test $# -gt 2
+	then
 		die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'"
+	fi
+
 	rev=$(git rev-parse -q --verify "$1^{commit}") ||
 		die "fatal: '$1' does not refer to a commit"
 	repository=""
diff --git a/contrib/subtree/t/Makefile b/contrib/subtree/t/Makefile
index 4655e09..093399c 100644
--- a/contrib/subtree/t/Makefile
+++ b/contrib/subtree/t/Makefile
@@ -74,9 +74,7 @@
 	$(MAKE) clean
 
 aggregate-results:
-	for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
-		echo "$$f"; \
-	done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+	@'$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
 
 valgrind:
 	$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 341c169..c3bd2a5 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -63,7 +63,7 @@
 	git -C "$1" log -1 --format=%B HEAD^2 >msg &&
 	test_commit -C "$1-sub" --annotate sub2 &&
 	git clone --no-local "$1" "$1-clone" &&
-	new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+	new_commit=$(sed -e "s/$commit/$tag/" msg | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
 	git -C "$1-clone" replace HEAD^2 $new_commit
 }
 
@@ -71,7 +71,7 @@
 	test_expect_code 129 git subtree -h >out 2>err &&
 	test_must_be_empty err &&
 	grep -e "^ *or: git subtree pull" out &&
-	grep -e --annotate out
+	grep -F -e "--[no-]annotate" out
 '
 
 #
@@ -385,6 +385,46 @@
 	)
 '
 
+# Tests that commits from other subtrees are not processed as
+# part of a split.
+#
+# This test performs the following:
+# - Creates Repo with subtrees 'subA' and 'subB'
+# - Creates commits in the repo including changes to subtrees
+# - Runs the following 'split' and commit' commands in order:
+# 	- Perform 'split' on subtree A
+# 	- Perform 'split' on subtree B
+# 	- Create new commits with changes to subtree A and B
+# 	- Perform split on subtree A
+# 	- Check that the commits in subtree B are not processed
+#			as part of the subtree A split
+test_expect_success 'split with multiple subtrees' '
+	subtree_test_create_repo "$test_count" &&
+	subtree_test_create_repo "$test_count/subA" &&
+	subtree_test_create_repo "$test_count/subB" &&
+	test_create_commit "$test_count" main1 &&
+	test_create_commit "$test_count/subA" subA1 &&
+	test_create_commit "$test_count/subA" subA2 &&
+	test_create_commit "$test_count/subA" subA3 &&
+	test_create_commit "$test_count/subB" subB1 &&
+	git -C "$test_count" fetch ./subA HEAD &&
+	git -C "$test_count" subtree add --prefix=subADir FETCH_HEAD &&
+	git -C "$test_count" fetch ./subB HEAD &&
+	git -C "$test_count" subtree add --prefix=subBDir FETCH_HEAD &&
+	test_create_commit "$test_count" subADir/main-subA1 &&
+	test_create_commit "$test_count" subBDir/main-subB1 &&
+	git -C "$test_count" subtree split --prefix=subADir \
+		--squash --rejoin -m "Sub A Split 1" &&
+	git -C "$test_count" subtree split --prefix=subBDir \
+		--squash --rejoin -m "Sub B Split 1" &&
+	test_create_commit "$test_count" subADir/main-subA2 &&
+	test_create_commit "$test_count" subBDir/main-subB2 &&
+	git -C "$test_count" subtree split --prefix=subADir \
+		--squash --rejoin -m "Sub A Split 2" &&
+	test "$(git -C "$test_count" subtree split --prefix=subBDir \
+		--squash --rejoin -d -m "Sub B Split 1" 2>&1 | grep -w "\[1\]")" = ""
+'
+
 test_expect_success 'split sub dir/ with --rejoin from scratch' '
 	subtree_test_create_repo "$test_count" &&
 	test_create_commit "$test_count" main1 &&
diff --git a/contrib/vscode/init.sh b/contrib/vscode/init.sh
index 521d303..f2d61bb 100755
--- a/contrib/vscode/init.sh
+++ b/contrib/vscode/init.sh
@@ -92,7 +92,6 @@
         "isexe",
         "iskeychar",
         "kompare",
-        "mksnpath",
         "mktag",
         "mktree",
         "mmblob",
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 888c34a..989197a 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -79,7 +79,7 @@
 # create the links to the original repo.  explicitly exclude index, HEAD and
 # logs/HEAD from the list since they are purely related to the current working
 # directory, and should not be shared.
-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
+for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable
 do
 	# create a containing directory if needed
 	case $x in
diff --git a/convert.c b/convert.c
index a54d169..35b25eb 100644
--- a/convert.c
+++ b/convert.c
@@ -1,14 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
-#include "object-store.h"
+#include "convert.h"
+#include "copy.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "attr.h"
 #include "run-command.h"
 #include "quote.h"
+#include "read-cache-ll.h"
 #include "sigchain.h"
 #include "pkt-line.h"
 #include "sub-process.h"
+#include "trace.h"
 #include "utf8.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -626,23 +633,21 @@
 	 */
 	struct child_process child_process = CHILD_PROCESS_INIT;
 	struct filter_params *params = (struct filter_params *)data;
+	const char *format = params->cmd;
 	int write_err, status;
 
 	/* apply % substitution to cmd */
 	struct strbuf cmd = STRBUF_INIT;
-	struct strbuf path = STRBUF_INIT;
-	struct strbuf_expand_dict_entry dict[] = {
-		{ "f", NULL, },
-		{ NULL, NULL, },
-	};
 
-	/* quote the path to preserve spaces, etc. */
-	sq_quote_buf(&path, params->path);
-	dict[0].value = path.buf;
-
-	/* expand all %f with the quoted path */
-	strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict);
-	strbuf_release(&path);
+	/* expand all %f with the quoted path; quote to preserve space, etc. */
+	while (strbuf_expand_step(&cmd, &format)) {
+		if (skip_prefix(format, "%", &format))
+			strbuf_addch(&cmd, '%');
+		else if (skip_prefix(format, "f", &format))
+			sq_quote_buf(&cmd, params->path);
+		else
+			strbuf_addch(&cmd, '%');
+	}
 
 	strvec_push(&child_process.args, cmd.buf);
 	child_process.use_shell = 1;
@@ -1008,7 +1013,9 @@
 	return 0;
 }
 
-static int read_convert_config(const char *var, const char *value, void *cb UNUSED)
+static int read_convert_config(const char *var, const char *value,
+			       const struct config_context *ctx UNUSED,
+			       void *cb UNUSED)
 {
 	const char *key, *name;
 	size_t namelen;
@@ -1021,7 +1028,7 @@
 	if (parse_config_key(var, "filter", &name, &namelen, &key) < 0 || !name)
 		return 0;
 	for (drv = user_convert; drv; drv = drv->next)
-		if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
+		if (!xstrncmpz(drv->name, name, namelen))
 			break;
 	if (!drv) {
 		CALLOC_ARRAY(drv, 1);
@@ -1308,7 +1315,7 @@
 		git_config(read_convert_config, NULL);
 	}
 
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	ccheck = check->items;
 	ca->crlf_action = git_path_check_crlf(ccheck + 4);
 	if (ca->crlf_action == CRLF_UNDEFINED)
diff --git a/convert.h b/convert.h
index 0a6e408..ab8b4fa 100644
--- a/convert.h
+++ b/convert.h
@@ -4,7 +4,7 @@
 #ifndef CONVERT_H
 #define CONVERT_H
 
-#include "hash.h"
+#include "hash-ll.h"
 #include "string-list.h"
 
 struct index_state;
@@ -92,7 +92,7 @@
 		   struct conv_attrs *ca, const char *path);
 
 extern enum eol core_eol;
-extern char *check_roundtrip_encoding;
+extern const char *check_roundtrip_encoding;
 const char *get_cached_convert_stats_ascii(struct index_state *istate,
 					   const char *path);
 const char *get_wt_convert_stats_ascii(const char *path);
diff --git a/copy.c b/copy.c
index 4de6a11..23d84c6 100644
--- a/copy.c
+++ b/copy.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "copy.h"
+#include "path.h"
 
 int copy_fd(int ifd, int ofd)
 {
diff --git a/copy.h b/copy.h
new file mode 100644
index 0000000..2af77cb
--- /dev/null
+++ b/copy.h
@@ -0,0 +1,10 @@
+#ifndef COPY_H
+#define COPY_H
+
+#define COPY_READ_ERROR (-2)
+#define COPY_WRITE_ERROR (-3)
+int copy_fd(int ifd, int ofd);
+int copy_file(const char *dst, const char *src, int mode);
+int copy_file_with_time(const char *dst, const char *src, int mode);
+
+#endif /* COPY_H */
diff --git a/credential.c b/credential.c
index f320113..18098bd 100644
--- a/credential.c
+++ b/credential.c
@@ -1,11 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
 #include "credential.h"
+#include "gettext.h"
 #include "string-list.h"
 #include "run-command.h"
 #include "url.h"
 #include "prompt.h"
 #include "sigchain.h"
+#include "strbuf.h"
 #include "urlmatch.h"
 #include "git-compat-util.h"
 
@@ -22,19 +25,22 @@
 	free(c->path);
 	free(c->username);
 	free(c->password);
+	free(c->oauth_refresh_token);
 	string_list_clear(&c->helpers, 0);
+	strvec_clear(&c->wwwauth_headers);
 
 	credential_init(c);
 }
 
 int credential_match(const struct credential *want,
-		     const struct credential *have)
+		     const struct credential *have, int match_password)
 {
 #define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
 	return CHECK(protocol) &&
 	       CHECK(host) &&
 	       CHECK(path) &&
-	       CHECK(username);
+	       CHECK(username) &&
+	       (!match_password || CHECK(password));
 #undef CHECK
 }
 
@@ -43,6 +49,7 @@
 						   const char *url);
 
 static int credential_config_callback(const char *var, const char *value,
+				      const struct config_context *ctx UNUSED,
 				      void *data)
 {
 	struct credential *c = data;
@@ -81,8 +88,8 @@
 static void credential_describe(struct credential *c, struct strbuf *out);
 static void credential_format(struct credential *c, struct strbuf *out);
 
-static int select_all(const struct urlmatch_item *a,
-		      const struct urlmatch_item *b)
+static int select_all(const struct urlmatch_item *a UNUSED,
+		      const struct urlmatch_item *b UNUSED)
 {
 	return 0;
 }
@@ -97,7 +104,7 @@
 		warning(_("skipping credential lookup for key: credential.%s"),
 			url);
 	else
-		matches = credential_match(&want, c);
+		matches = credential_match(&want, c, 0);
 	credential_clear(&want);
 
 	return matches;
@@ -235,11 +242,16 @@
 		} else if (!strcmp(key, "path")) {
 			free(c->path);
 			c->path = xstrdup(value);
+		} else if (!strcmp(key, "wwwauth[]")) {
+			strvec_push(&c->wwwauth_headers, value);
 		} else if (!strcmp(key, "password_expiry_utc")) {
 			errno = 0;
 			c->password_expiry_utc = parse_timestamp(value, NULL, 10);
 			if (c->password_expiry_utc == 0 || errno == ERANGE)
 				c->password_expiry_utc = TIME_MAX;
+		} else if (!strcmp(key, "oauth_refresh_token")) {
+			free(c->oauth_refresh_token);
+			c->oauth_refresh_token = xstrdup(value);
 		} else if (!strcmp(key, "url")) {
 			credential_from_url(c, value);
 		} else if (!strcmp(key, "quit")) {
@@ -275,11 +287,14 @@
 	credential_write_item(fp, "path", c->path, 0);
 	credential_write_item(fp, "username", c->username, 0);
 	credential_write_item(fp, "password", c->password, 0);
+	credential_write_item(fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
 	if (c->password_expiry_utc != TIME_MAX) {
 		char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
 		credential_write_item(fp, "password_expiry_utc", s, 0);
 		free(s);
 	}
+	for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
+		credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
 }
 
 static int run_credential_helper(struct credential *c,
@@ -398,6 +413,7 @@
 
 	FREE_AND_NULL(c->username);
 	FREE_AND_NULL(c->password);
+	FREE_AND_NULL(c->oauth_refresh_token);
 	c->password_expiry_utc = TIME_MAX;
 	c->approved = 0;
 }
diff --git a/credential.h b/credential.h
index 935b28a..acc41ad 100644
--- a/credential.h
+++ b/credential.h
@@ -2,6 +2,7 @@
 #define CREDENTIAL_H
 
 #include "string-list.h"
+#include "strvec.h"
 
 /**
  * The credentials API provides an abstracted way of gathering username and
@@ -115,6 +116,20 @@
 	 */
 	struct string_list helpers;
 
+	/**
+	 * A `strvec` of WWW-Authenticate header values. Each string
+	 * is the value of a WWW-Authenticate header in an HTTP response,
+	 * in the order they were received in the response.
+	 */
+	struct strvec wwwauth_headers;
+
+	/**
+	 * Internal use only. Keeps track of if we previously matched against a
+	 * WWW-Authenticate header line in order to re-fold future continuation
+	 * lines into one value.
+	 */
+	unsigned header_is_last_match:1;
+
 	unsigned approved:1,
 		 configured:1,
 		 quit:1,
@@ -126,12 +141,14 @@
 	char *protocol;
 	char *host;
 	char *path;
+	char *oauth_refresh_token;
 	timestamp_t password_expiry_utc;
 };
 
 #define CREDENTIAL_INIT { \
 	.helpers = STRING_LIST_INIT_DUP, \
 	.password_expiry_utc = TIME_MAX, \
+	.wwwauth_headers = STRVEC_INIT, \
 }
 
 /* Initialize a credential structure, setting all fields to empty. */
@@ -194,6 +211,6 @@
 int credential_from_url_gently(struct credential *, const char *url, int quiet);
 
 int credential_match(const struct credential *want,
-		     const struct credential *have);
+		     const struct credential *have, int match_password);
 
 #endif /* CREDENTIAL_H */
diff --git a/csum-file.c b/csum-file.c
index cce13c0..870748e 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -7,9 +7,10 @@
  * files. Useful when you write a file that you want to be
  * able to verify hasn't been messed with afterwards.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "progress.h"
 #include "csum-file.h"
+#include "hash.h"
 
 static void verify_buffer_or_die(struct hashfile *f,
 				 const void *buf,
@@ -206,7 +207,7 @@
 	    lseek(f->fd, offset, SEEK_SET) != offset)
 		return -1;
 	f->total = offset;
-	f->ctx = checkpoint->ctx;
+	the_hash_algo->clone_fn(&f->ctx, &checkpoint->ctx);
 	f->offset = 0; /* hashflush() was called in checkpoint */
 	return 0;
 }
diff --git a/csum-file.h b/csum-file.h
index 793a59d..bc5bec2 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -1,8 +1,8 @@
 #ifndef CSUM_FILE_H
 #define CSUM_FILE_H
 
-#include "cache.h"
-#include "hash.h"
+#include "hash-ll.h"
+#include "write-or-die.h"
 
 struct progress;
 
diff --git a/ctype.c b/ctype.c
index fc0225c..3451745 100644
--- a/ctype.c
+++ b/ctype.c
@@ -28,39 +28,3 @@
 	A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X,		/* 112..127 */
 	/* Nothing in the 128.. range */
 };
-
-/* For case-insensitive kwset */
-const unsigned char tolower_trans_tbl[256] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-	 ' ',  '!',  '"',  '#',  '$',  '%',  '&', 0x27,
-	 '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
-	 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
-	 '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
-	 '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
-	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
-	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
-	 'x',  'y',  'z',  '[', 0x5c,  ']',  '^',  '_',
-	 '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
-	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
-	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
-	 'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f,
-	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
-	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
-	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
-	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
-	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
-	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
-	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
-	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
-	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
-	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
-	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
-	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
-	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
-	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
-	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
-	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
-};
diff --git a/daemon.c b/daemon.c
index 0ae7d12..17d331b 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,7 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "path.h"
 #include "pkt-line.h"
+#include "protocol.h"
 #include "run-command.h"
+#include "setup.h"
 #include "strbuf.h"
 #include "string-list.h"
 
@@ -137,42 +142,6 @@
 	exit(1);
 }
 
-struct expand_path_context {
-	const char *directory;
-	struct hostinfo *hostinfo;
-};
-
-static size_t expand_path(struct strbuf *sb, const char *placeholder, void *ctx)
-{
-	struct expand_path_context *context = ctx;
-	struct hostinfo *hi = context->hostinfo;
-
-	switch (placeholder[0]) {
-	case 'H':
-		strbuf_addbuf(sb, &hi->hostname);
-		return 1;
-	case 'C':
-		if (placeholder[1] == 'H') {
-			strbuf_addstr(sb, get_canon_hostname(hi));
-			return 2;
-		}
-		break;
-	case 'I':
-		if (placeholder[1] == 'P') {
-			strbuf_addstr(sb, get_ip_address(hi));
-			return 2;
-		}
-		break;
-	case 'P':
-		strbuf_addbuf(sb, &hi->tcp_port);
-		return 1;
-	case 'D':
-		strbuf_addstr(sb, context->directory);
-		return 1;
-	}
-	return 0;
-}
-
 static const char *path_ok(const char *directory, struct hostinfo *hi)
 {
 	static char rpath[PATH_MAX];
@@ -216,10 +185,7 @@
 	}
 	else if (interpolated_path && hi->saw_extended_args) {
 		struct strbuf expanded_path = STRBUF_INIT;
-		struct expand_path_context context;
-
-		context.directory = directory;
-		context.hostinfo = hi;
+		const char *format = interpolated_path;
 
 		if (*dir != '/') {
 			/* Allow only absolute */
@@ -227,8 +193,24 @@
 			return NULL;
 		}
 
-		strbuf_expand(&expanded_path, interpolated_path,
-			      expand_path, &context);
+		while (strbuf_expand_step(&expanded_path, &format)) {
+			if (skip_prefix(format, "%", &format))
+				strbuf_addch(&expanded_path, '%');
+			else if (skip_prefix(format, "H", &format))
+				strbuf_addbuf(&expanded_path, &hi->hostname);
+			else if (skip_prefix(format, "CH", &format))
+				strbuf_addstr(&expanded_path,
+					      get_canon_hostname(hi));
+			else if (skip_prefix(format, "IP", &format))
+				strbuf_addstr(&expanded_path,
+					      get_ip_address(hi));
+			else if (skip_prefix(format, "P", &format))
+				strbuf_addbuf(&expanded_path, &hi->tcp_port);
+			else if (skip_prefix(format, "D", &format))
+				strbuf_addstr(&expanded_path, directory);
+			else
+				strbuf_addch(&expanded_path, '%');
+		}
 
 		rlen = strlcpy(interp_path, expanded_path.buf,
 			       sizeof(interp_path));
@@ -928,7 +910,7 @@
 		add_child(&cld, addr, addrlen);
 }
 
-static void child_handler(int signo)
+static void child_handler(int signo UNUSED)
 {
 	/*
 	 * Otherwise empty handler because systemcalls will get interrupted
@@ -1261,19 +1243,20 @@
 int cmd_main(int argc, const char **argv)
 {
 	int listen_port = 0;
-	struct string_list listen_addr = STRING_LIST_INIT_NODUP;
+	struct string_list listen_addr = STRING_LIST_INIT_DUP;
 	int serve_mode = 0, inetd_mode = 0;
 	const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
 	int detach = 0;
 	struct credentials *cred = NULL;
 	int i;
+	int ret;
 
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		const char *v;
 
 		if (skip_prefix(arg, "--listen=", &v)) {
-			string_list_append(&listen_addr, xstrdup_tolower(v));
+			string_list_append_nodup(&listen_addr, xstrdup_tolower(v));
 			continue;
 		}
 		if (skip_prefix(arg, "--port=", &v)) {
@@ -1455,22 +1438,26 @@
 			die_errno("failed to redirect stderr to /dev/null");
 	}
 
-	if (inetd_mode || serve_mode)
-		return execute();
+	if (inetd_mode || serve_mode) {
+		ret = execute();
+	} else {
+		if (detach) {
+			if (daemonize())
+				die("--detach not supported on this platform");
+		}
 
-	if (detach) {
-		if (daemonize())
-			die("--detach not supported on this platform");
+		if (pid_file)
+			write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
+
+		/* prepare argv for serving-processes */
+		strvec_push(&cld_argv, argv[0]); /* git-daemon */
+		strvec_push(&cld_argv, "--serve");
+		for (i = 1; i < argc; ++i)
+			strvec_push(&cld_argv, argv[i]);
+
+		ret = serve(&listen_addr, listen_port, cred);
 	}
 
-	if (pid_file)
-		write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
-
-	/* prepare argv for serving-processes */
-	strvec_push(&cld_argv, argv[0]); /* git-daemon */
-	strvec_push(&cld_argv, "--serve");
-	for (i = 1; i < argc; ++i)
-		strvec_push(&cld_argv, argv[i]);
-
-	return serve(&listen_addr, listen_port, cred);
+	string_list_clear(&listen_addr, 0);
+	return ret;
 }
diff --git a/date.c b/date.c
index 6f45eeb..44cf222 100644
--- a/date.c
+++ b/date.c
@@ -4,8 +4,11 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "date.h"
+#include "gettext.h"
+#include "pager.h"
+#include "strbuf.h"
 
 /*
  * This is like mktime, but without normalization of tm_wday and tm_yday.
@@ -339,14 +342,18 @@
 				tm->tm_hour, tm->tm_min, tm->tm_sec,
 				tz);
 	else if (mode->type == DATE_ISO8601_STRICT) {
-		char sign = (tz >= 0) ? '+' : '-';
-		tz = abs(tz);
-		strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
+		strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d",
 				tm->tm_year + 1900,
 				tm->tm_mon + 1,
 				tm->tm_mday,
-				tm->tm_hour, tm->tm_min, tm->tm_sec,
-				sign, tz / 100, tz % 100);
+				tm->tm_hour, tm->tm_min, tm->tm_sec);
+		if (tz == 0) {
+			strbuf_addch(&timebuf, 'Z');
+		} else {
+			strbuf_addch(&timebuf, tz >= 0 ? '+' : '-');
+			tz = abs(tz);
+			strbuf_addf(&timebuf, "%02d:%02d", tz / 100, tz % 100);
+		}
 	} else if (mode->type == DATE_RFC2822)
 		strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
 			weekday_names[tm->tm_wday], tm->tm_mday,
@@ -1365,20 +1372,6 @@
 	return (timestamp_t)update_tm(&tm, &now, 0);
 }
 
-timestamp_t approxidate_relative(const char *date)
-{
-	struct timeval tv;
-	timestamp_t timestamp;
-	int offset;
-	int errors = 0;
-
-	if (!parse_date_basic(date, &timestamp, &offset))
-		return timestamp;
-
-	get_time(&tv);
-	return approxidate_str(date, (const struct timeval *) &tv, &errors);
-}
-
 timestamp_t approxidate_careful(const char *date, int *error_ret)
 {
 	struct timeval tv;
diff --git a/date.h b/date.h
index 5d4eaba..6136212 100644
--- a/date.h
+++ b/date.h
@@ -68,7 +68,6 @@
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
 timestamp_t approxidate_careful(const char *, int *);
-timestamp_t approxidate_relative(const char *date);
 int date_overflows(timestamp_t date);
 time_t tm_to_time_t(const struct tm *tm);
 #endif
diff --git a/decorate.c b/decorate.c
index 2036d15..69aeb14 100644
--- a/decorate.c
+++ b/decorate.c
@@ -2,7 +2,7 @@
  * decorate.c - decorate a git object with some arbitrary
  * data.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "decorate.h"
 
@@ -81,3 +81,18 @@
 			j = 0;
 	}
 }
+
+void clear_decoration(struct decoration *n, void (*free_cb)(void *))
+{
+	if (free_cb) {
+		unsigned int i;
+		for (i = 0; i < n->size; i++) {
+			void *d = n->entries[i].decoration;
+			if (d)
+				free_cb(d);
+		}
+	}
+
+	FREE_AND_NULL(n->entries);
+	n->size = n->nr = 0;
+}
diff --git a/decorate.h b/decorate.h
index ee43dee..cdeb17c 100644
--- a/decorate.h
+++ b/decorate.h
@@ -58,4 +58,14 @@
  */
 void *lookup_decoration(struct decoration *n, const struct object *obj);
 
+/*
+ * Clear all decoration entries, releasing any memory used by the structure.
+ * If free_cb is not NULL, it is called for every decoration value currently
+ * stored.
+ *
+ * After clearing, the decoration struct can be used again. The "name" field is
+ * retained.
+ */
+void clear_decoration(struct decoration *n, void (*free_cb)(void *));
+
 #endif
diff --git a/delta-islands.c b/delta-islands.c
index afdec0a..f7e0794 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -1,16 +1,13 @@
-#include "cache.h"
-#include "attr.h"
+#include "git-compat-util.h"
 #include "object.h"
-#include "blob.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tag.h"
 #include "tree.h"
-#include "delta.h"
 #include "pack.h"
 #include "tree-walk.h"
 #include "diff.h"
-#include "revision.h"
-#include "list-objects.h"
 #include "progress.h"
 #include "refs.h"
 #include "khash.h"
@@ -287,7 +284,7 @@
 		if (!tree || parse_tree(tree) < 0)
 			die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
 
-		init_tree_desc(&desc, tree->buffer, tree->size);
+		init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 		while (tree_entry(&desc, &entry)) {
 			struct object *obj;
 
@@ -338,7 +335,9 @@
 	kh_destroy_str(remote_islands);
 }
 
-static int island_config_callback(const char *k, const char *v, void *cb)
+static int island_config_callback(const char *k, const char *v,
+				  const struct config_context *ctx UNUSED,
+				  void *cb)
 {
 	struct island_load_data *ild = cb;
 
@@ -506,8 +505,9 @@
 		struct commit_list *p;
 		struct island_bitmap *root_marks = kh_value(island_marks, pos);
 
-		parse_commit(commit);
-		set_island_marks(&get_commit_tree(commit)->object, root_marks);
+		repo_parse_commit(the_repository, commit);
+		set_island_marks(&repo_get_commit_tree(the_repository, commit)->object,
+				 root_marks);
 		for (p = commit->parents; p; p = p->next)
 			set_island_marks(&p->item->object, root_marks);
 	}
diff --git a/detect-compiler b/detect-compiler
index 50087f5..a87650b 100755
--- a/detect-compiler
+++ b/detect-compiler
@@ -17,7 +17,15 @@
 }
 
 get_version() {
-	get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/'
+	# A string that begins with a digit up to the next SP
+	ver=$(get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/')
+
+	# There are known -variant suffixes that do not affect the
+	# meaning of the main version number.  Strip them.
+	ver=${ver%-win32}
+	ver=${ver%-posix}
+
+	echo "$ver"
 }
 
 print_flags() {
diff --git a/diagnose.c b/diagnose.c
index 8f26569..4d096c8 100644
--- a/diagnose.c
+++ b/diagnose.c
@@ -1,12 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diagnose.h"
 #include "compat/disk.h"
 #include "archive.h"
 #include "dir.h"
 #include "help.h"
+#include "gettext.h"
+#include "hex.h"
 #include "strvec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "packfile.h"
+#include "parse-options.h"
+#include "write-or-die.h"
 
 struct archive_dir {
 	const char *path;
@@ -43,7 +47,8 @@
 	return error(_("invalid --%s value '%s'"), opt->long_name, arg);
 }
 
-static void dir_file_stats_objects(const char *full_path, size_t full_path_len,
+static void dir_file_stats_objects(const char *full_path,
+				   size_t full_path_len UNUSED,
 				   const char *file_name, void *data)
 {
 	struct strbuf *buf = data;
@@ -66,42 +71,6 @@
 	return 0;
 }
 
-/*
- * Get the d_type of a dirent. If the d_type is unknown, derive it from
- * stat.st_mode.
- *
- * Note that 'path' is assumed to have a trailing slash. It is also modified
- * in-place during the execution of the function, but is then reverted to its
- * original value before returning.
- */
-static unsigned char get_dtype(struct dirent *e, struct strbuf *path)
-{
-	struct stat st;
-	unsigned char dtype = DTYPE(e);
-	size_t base_path_len;
-
-	if (dtype != DT_UNKNOWN)
-		return dtype;
-
-	/* d_type unknown in dirent, try to fall back on lstat results */
-	base_path_len = path->len;
-	strbuf_addstr(path, e->d_name);
-	if (lstat(path->buf, &st))
-		goto cleanup;
-
-	/* determine d_type from st_mode */
-	if (S_ISREG(st.st_mode))
-		dtype = DT_REG;
-	else if (S_ISDIR(st.st_mode))
-		dtype = DT_DIR;
-	else if (S_ISLNK(st.st_mode))
-		dtype = DT_LNK;
-
-cleanup:
-	strbuf_setlen(path, base_path_len);
-	return dtype;
-}
-
 static int count_files(struct strbuf *path)
 {
 	DIR *dir = opendir(path->buf);
@@ -112,7 +81,7 @@
 		return 0;
 
 	while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
-		if (get_dtype(e, path) == DT_REG)
+		if (get_dtype(e, path, 0) == DT_REG)
 			count++;
 
 	closedir(dir);
@@ -141,7 +110,7 @@
 	base_path_len = count_path.len;
 
 	while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
-		if (get_dtype(e, &count_path) == DT_DIR &&
+		if (get_dtype(e, &count_path, 0) == DT_DIR &&
 		    strlen(e->d_name) == 2 &&
 		    !hex_to_bytes(&c, e->d_name, 1)) {
 			strbuf_setlen(&count_path, base_path_len);
@@ -186,7 +155,7 @@
 
 		strbuf_add_absolute_path(&abspath, at_root ? "." : path);
 		strbuf_addch(&abspath, '/');
-		dtype = get_dtype(e, &abspath);
+		dtype = get_dtype(e, &abspath, 0);
 
 		strbuf_setlen(&buf, len);
 		strbuf_addstr(&buf, e->d_name);
diff --git a/diagnose.h b/diagnose.h
index 7a4951a..f525219 100644
--- a/diagnose.h
+++ b/diagnose.h
@@ -2,7 +2,8 @@
 #define DIAGNOSE_H
 
 #include "strbuf.h"
-#include "parse-options.h"
+
+struct option;
 
 enum diagnose_mode {
 	DIAGNOSE_NONE,
diff --git a/diff-lib.c b/diff-lib.c
index dec040c..683f11e 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -1,16 +1,23 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
-#include "quote.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
+#include "read-cache.h"
 #include "revision.h"
 #include "cache-tree.h"
 #include "unpack-trees.h"
 #include "refs.h"
+#include "repository.h"
 #include "submodule.h"
+#include "symlinks.h"
+#include "trace.h"
 #include "dir.h"
 #include "fsmonitor.h"
 #include "commit-reach.h"
@@ -28,14 +35,20 @@
  * exists for ce that is a submodule -- it is a submodule that is not
  * checked out).  Return negative for an error.
  */
-static int check_removed(const struct index_state *istate, const struct cache_entry *ce, struct stat *st)
+static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
-	assert(is_fsmonitor_refreshed(istate));
-	if (!(ce->ce_flags & CE_FSMONITOR_VALID) && lstat(ce->name, st) < 0) {
+	int stat_err;
+
+	if (!(ce->ce_flags & CE_FSMONITOR_VALID))
+		stat_err = lstat(ce->name, st);
+	else
+		stat_err = fake_lstat(ce, st);
+	if (stat_err < 0) {
 		if (!is_missing_file_error(errno))
 			return -1;
 		return 1;
 	}
+
 	if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
 		return 1;
 	if (S_ISDIR(st->st_mode)) {
@@ -88,7 +101,7 @@
 	return changed;
 }
 
-int run_diff_files(struct rev_info *revs, unsigned int option)
+void run_diff_files(struct rev_info *revs, unsigned int option)
 {
 	int entries, i;
 	int diff_unmerged_stage = revs->max_count;
@@ -114,7 +127,16 @@
 		if (diff_can_quit_early(&revs->diffopt))
 			break;
 
-		if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
+		/*
+		 * NEEDSWORK:
+		 * Here we filter with pathspec but the result is further
+		 * filtered out when --relative is in effect.  To end-users,
+		 * a pathspec element that matched only to paths outside the
+		 * current directory is like not matching anything at all;
+		 * the handling of ps_matched[] here may become problematic
+		 * if/when we add the "--error-unmatch" option to "git diff".
+		 */
+		if (!ce_path_match(istate, ce, &revs->prune_data, revs->ps_matched))
 			continue;
 
 		if (revs->diffopt.prefix &&
@@ -141,7 +163,7 @@
 			memset(&(dpath->parent[0]), 0,
 			       sizeof(struct combine_diff_parent)*5);
 
-			changed = check_removed(istate, ce, &st);
+			changed = check_removed(ce, &st);
 			if (!changed)
 				wt_mode = ce_mode_from_stat(ce, st.st_mode);
 			else {
@@ -221,7 +243,7 @@
 		} else {
 			struct stat st;
 
-			changed = check_removed(istate, ce, &st);
+			changed = check_removed(ce, &st);
 			if (changed) {
 				if (changed < 0) {
 					perror(ce->name);
@@ -264,7 +286,6 @@
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
 	trace_performance_since(start, "diff-files");
-	return 0;
 }
 
 /*
@@ -296,7 +317,7 @@
 	if (!cached && !ce_uptodate(ce)) {
 		int changed;
 		struct stat st;
-		changed = check_removed(istate, ce, &st);
+		changed = check_removed(ce, &st);
 		if (changed < 0)
 			return -1;
 		else if (changed) {
@@ -550,7 +571,7 @@
 	opts.pathspec = &revs->diffopt.pathspec;
 	opts.pathspec->recursive = 1;
 
-	init_tree_desc(&t, tree->buffer, tree->size);
+	init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
 	return unpack_trees(1, &t, &opts);
 }
 
@@ -558,14 +579,12 @@
 {
 	int i;
 	struct commit *mb_child[2] = {0};
-	struct commit_list *merge_bases;
+	struct commit_list *merge_bases = NULL;
 
 	for (i = 0; i < revs->pending.nr; i++) {
 		struct object *obj = revs->pending.objects[i].item;
 		if (obj->flags)
 			die(_("--merge-base does not work with ranges"));
-		if (obj->type != OBJ_COMMIT)
-			die(_("--merge-base only works with commits"));
 	}
 
 	/*
@@ -581,13 +600,14 @@
 	if (revs->pending.nr == 1) {
 		struct object_id oid;
 
-		if (get_oid("HEAD", &oid))
+		if (repo_get_oid(the_repository, "HEAD", &oid))
 			die(_("unable to get HEAD"));
 
 		mb_child[1] = lookup_commit_reference(the_repository, &oid);
 	}
 
-	merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]);
+	if (repo_get_merge_bases(the_repository, mb_child[0], mb_child[1], &merge_bases) < 0)
+		exit(128);
 	if (!merge_bases)
 		die(_("no merge base found"));
 	if (merge_bases->next)
@@ -598,7 +618,7 @@
 	free_commit_list(merge_bases);
 }
 
-int run_diff_index(struct rev_info *revs, unsigned int option)
+void run_diff_index(struct rev_info *revs, unsigned int option)
 {
 	struct object_array_entry *ent;
 	int cached = !!(option & DIFF_INDEX_CACHED);
@@ -632,7 +652,6 @@
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
 	trace_performance_leave("diff-index");
-	return 0;
 }
 
 int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
@@ -664,10 +683,17 @@
 	setup_revisions(0, NULL, &rev, &opt);
 	rev.diffopt.flags.quick = 1;
 	rev.diffopt.flags.exit_with_status = 1;
-	if (flags)
+	if (flags) {
 		diff_flags_or(&rev.diffopt.flags, flags);
+		/*
+		 * Now that flags are merged, honor override_submodule_config
+		 * and ignore_submodules from passed flags.
+		 */
+		if (flags->override_submodule_config)
+			rev.diffopt.flags.ignore_submodules = flags->ignore_submodules;
+	}
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
-	run_diff_index(&rev, 1);
+	run_diff_index(&rev, DIFF_INDEX_CACHED);
 	has_changes = rev.diffopt.flags.has_changes;
 	release_revisions(&rev);
 	return (has_changes != 0);
diff --git a/diff-merges.c b/diff-merges.c
index 85cbefa..4550758 100644
--- a/diff-merges.c
+++ b/diff-merges.c
@@ -1,5 +1,7 @@
+#include "git-compat-util.h"
 #include "diff-merges.h"
 
+#include "gettext.h"
 #include "revision.h"
 
 typedef void (*diff_merges_setup_func_t)(struct rev_info *);
@@ -129,6 +131,9 @@
 	} else if (!strcmp(arg, "--cc")) {
 		set_dense_combined(revs);
 		revs->merges_imply_patch = 1;
+	} else if (!strcmp(arg, "--dd")) {
+		set_first_parent(revs);
+		revs->merges_imply_patch = 1;
 	} else if (!strcmp(arg, "--remerge-diff")) {
 		set_remerge_diff(revs);
 		revs->merges_imply_patch = 1;
diff --git a/diff-no-index.c b/diff-no-index.c
index 05fafd0..3a89656 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -4,16 +4,14 @@
  * Copyright (c) 2008 by Junio C Hamano
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "color.h"
 #include "commit.h"
-#include "blob.h"
-#include "tag.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "gettext.h"
 #include "revision.h"
-#include "log-tree.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "dir.h"
@@ -40,42 +38,81 @@
  */
 static const char file_from_standard_input[] = "-";
 
-static int get_mode(const char *path, int *mode)
+/*
+ * For paths given on the command-line we treat "-" as stdin and named
+ * pipes and symbolic links to named pipes specially.
+ */
+enum special {
+	SPECIAL_NONE,
+	SPECIAL_STDIN,
+	SPECIAL_PIPE,
+};
+
+static int get_mode(const char *path, int *mode, enum special *special)
 {
 	struct stat st;
 
-	if (!path || !strcmp(path, "/dev/null"))
+	if (!path || !strcmp(path, "/dev/null")) {
 		*mode = 0;
 #ifdef GIT_WINDOWS_NATIVE
-	else if (!strcasecmp(path, "nul"))
+	} else if (!strcasecmp(path, "nul")) {
 		*mode = 0;
 #endif
-	else if (path == file_from_standard_input)
+	} else if (path == file_from_standard_input) {
 		*mode = create_ce_mode(0666);
-	else if (lstat(path, &st))
+		*special = SPECIAL_STDIN;
+	} else if (lstat(path, &st)) {
 		return error("Could not access '%s'", path);
-	else
+	} else {
 		*mode = st.st_mode;
+	}
+	/*
+	 * For paths on the command-line treat named pipes and symbolic
+	 * links that resolve to a named pipe specially.
+	 */
+	if (special &&
+	    (S_ISFIFO(*mode) ||
+	     (S_ISLNK(*mode) && !stat(path, &st) && S_ISFIFO(st.st_mode)))) {
+		*mode = create_ce_mode(0666);
+		*special = SPECIAL_PIPE;
+	}
+
 	return 0;
 }
 
-static int populate_from_stdin(struct diff_filespec *s)
+static void populate_common(struct diff_filespec *s, struct strbuf *buf)
 {
-	struct strbuf buf = STRBUF_INIT;
 	size_t size = 0;
 
-	if (strbuf_read(&buf, 0, 0) < 0)
-		return error_errno("error while reading from stdin");
-
 	s->should_munmap = 0;
-	s->data = strbuf_detach(&buf, &size);
+	s->data = strbuf_detach(buf, &size);
 	s->size = size;
 	s->should_free = 1;
 	s->is_stdin = 1;
-	return 0;
 }
 
-static struct diff_filespec *noindex_filespec(const char *name, int mode)
+static void populate_from_pipe(struct diff_filespec *s)
+{
+	struct strbuf buf = STRBUF_INIT;
+	int fd = xopen(s->path, O_RDONLY);
+
+	if (strbuf_read(&buf, fd, 0) < 0)
+		die_errno("error while reading from '%s'", s->path);
+	close(fd);
+	populate_common(s, &buf);
+}
+
+static void populate_from_stdin(struct diff_filespec *s)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	if (strbuf_read(&buf, 0, 0) < 0)
+		die_errno("error while reading from stdin");
+	populate_common(s, &buf);
+}
+
+static struct diff_filespec *noindex_filespec(const char *name, int mode,
+					      enum special special)
 {
 	struct diff_filespec *s;
 
@@ -83,17 +120,22 @@
 		name = "/dev/null";
 	s = alloc_filespec(name);
 	fill_filespec(s, null_oid(), 0, mode);
-	if (name == file_from_standard_input)
+	if (special == SPECIAL_STDIN)
 		populate_from_stdin(s);
+	else if (special == SPECIAL_PIPE)
+		populate_from_pipe(s);
 	return s;
 }
 
 static int queue_diff(struct diff_options *o,
-		      const char *name1, const char *name2)
+		      const char *name1, const char *name2, int recursing)
 {
 	int mode1 = 0, mode2 = 0;
+	enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE;
 
-	if (get_mode(name1, &mode1) || get_mode(name2, &mode2))
+	/* Paths can only be special if we're not recursing. */
+	if (get_mode(name1, &mode1, recursing ? NULL : &special1) ||
+	    get_mode(name2, &mode2, recursing ? NULL : &special2))
 		return -1;
 
 	if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) {
@@ -101,14 +143,14 @@
 
 		if (S_ISDIR(mode1)) {
 			/* 2 is file that is created */
-			d1 = noindex_filespec(NULL, 0);
-			d2 = noindex_filespec(name2, mode2);
+			d1 = noindex_filespec(NULL, 0, SPECIAL_NONE);
+			d2 = noindex_filespec(name2, mode2, special2);
 			name2 = NULL;
 			mode2 = 0;
 		} else {
 			/* 1 is file that is deleted */
-			d1 = noindex_filespec(name1, mode1);
-			d2 = noindex_filespec(NULL, 0);
+			d1 = noindex_filespec(name1, mode1, special1);
+			d2 = noindex_filespec(NULL, 0, SPECIAL_NONE);
 			name1 = NULL;
 			mode1 = 0;
 		}
@@ -173,7 +215,7 @@
 				n2 = buffer2.buf;
 			}
 
-			ret = queue_diff(o, n1, n2);
+			ret = queue_diff(o, n1, n2, 1);
 		}
 		string_list_clear(&p1, 0);
 		string_list_clear(&p2, 0);
@@ -187,10 +229,11 @@
 		if (o->flags.reverse_diff) {
 			SWAP(mode1, mode2);
 			SWAP(name1, name2);
+			SWAP(special1, special2);
 		}
 
-		d1 = noindex_filespec(name1, mode1);
-		d2 = noindex_filespec(name2, mode2);
+		d1 = noindex_filespec(name1, mode1, special1);
+		d2 = noindex_filespec(name2, mode2, special2);
 		diff_queue(&diff_queued_diff, d1, d2);
 		return 0;
 	}
@@ -215,13 +258,27 @@
  */
 static void fixup_paths(const char **path, struct strbuf *replacement)
 {
-	unsigned int isdir0, isdir1;
+	struct stat st;
+	unsigned int isdir0 = 0, isdir1 = 0;
+	unsigned int ispipe0 = 0, ispipe1 = 0;
 
-	if (path[0] == file_from_standard_input ||
-	    path[1] == file_from_standard_input)
-		return;
-	isdir0 = is_directory(path[0]);
-	isdir1 = is_directory(path[1]);
+	if (path[0] != file_from_standard_input && !stat(path[0], &st)) {
+		isdir0 = S_ISDIR(st.st_mode);
+		ispipe0 = S_ISFIFO(st.st_mode);
+	}
+
+	if (path[1] != file_from_standard_input && !stat(path[1], &st)) {
+		isdir1 = S_ISDIR(st.st_mode);
+		ispipe1 = S_ISFIFO(st.st_mode);
+	}
+
+	if ((path[0] == file_from_standard_input && isdir1) ||
+	    (isdir0 && path[1] == file_from_standard_input))
+		die(_("cannot compare stdin to a directory"));
+
+	if ((isdir0 && ispipe1) || (ispipe0 && isdir1))
+		die(_("cannot compare a named pipe to a directory"));
+
 	if (isdir0 == isdir1)
 		return;
 	if (isdir0) {
@@ -295,7 +352,7 @@
 	setup_diff_pager(&revs->diffopt);
 	revs->diffopt.flags.exit_with_status = 1;
 
-	if (queue_diff(&revs->diffopt, paths[0], paths[1]))
+	if (queue_diff(&revs->diffopt, paths[0], paths[1], 0))
 		goto out;
 	diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
 	diffcore_std(&revs->diffopt);
@@ -305,7 +362,7 @@
 	 * The return code for --no-index imitates diff(1):
 	 * 0 = no changes, 1 = changes, else error
 	 */
-	ret = diff_result_code(&revs->diffopt, 0);
+	ret = diff_result_code(&revs->diffopt);
 
 out:
 	for (i = 0; i < ARRAY_SIZE(to_free); i++)
diff --git a/diff.c b/diff.c
index 469e18a..108c187 100644
--- a/diff.c
+++ b/diff.c
@@ -1,34 +1,45 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "base85.h"
 #include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
 #include "tempfile.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "delta.h"
+#include "hex.h"
 #include "xdiff-interface.h"
 #include "color.h"
-#include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "userdiff.h"
-#include "submodule-config.h"
 #include "submodule.h"
 #include "hashmap.h"
 #include "mem-pool.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "graph.h"
+#include "oid-array.h"
 #include "packfile.h"
+#include "pager.h"
 #include "parse-options.h"
 #include "help.h"
 #include "promisor-remote.h"
 #include "dir.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "read-cache-ll.h"
+#include "setup.h"
 #include "strmap.h"
+#include "ws.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -51,7 +62,10 @@
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
+static const char *diff_src_prefix = "a/";
+static const char *diff_dst_prefix = "b/";
 static int diff_relative;
+static int diff_stat_name_width;
 static int diff_stat_graph_width;
 static int diff_dirstat_permille_default = 30;
 static struct diff_options default_diff_options;
@@ -127,7 +141,7 @@
 	int i;
 
 	if (*params_copy)
-		string_list_split_in_place(&params, params_copy, ',', -1);
+		string_list_split_in_place(&params, params_copy, ",", -1);
 	for (i = 0; i < params.nr; i++) {
 		const char *p = params.items[i].string;
 		if (!strcmp(p, "changes")) {
@@ -343,7 +357,8 @@
 	return ret;
 }
 
-int git_diff_ui_config(const char *var, const char *value, void *cb)
+int git_diff_ui_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 		diff_use_color_default = git_config_colorbool(var, value);
@@ -357,20 +372,24 @@
 		return 0;
 	}
 	if (!strcmp(var, "diff.colormovedws")) {
-		unsigned cm = parse_color_moved_ws(value);
+		unsigned cm;
+		if (!value)
+			return config_error_nonbool(var);
+		cm = parse_color_moved_ws(value);
 		if (cm & COLOR_MOVED_WS_ERROR)
 			return -1;
 		diff_color_moved_ws_default = cm;
 		return 0;
 	}
 	if (!strcmp(var, "diff.context")) {
-		diff_context_default = git_config_int(var, value);
+		diff_context_default = git_config_int(var, value, ctx->kvi);
 		if (diff_context_default < 0)
 			return -1;
 		return 0;
 	}
 	if (!strcmp(var, "diff.interhunkcontext")) {
-		diff_interhunk_context_default = git_config_int(var, value);
+		diff_interhunk_context_default = git_config_int(var, value,
+								ctx->kvi);
 		if (diff_interhunk_context_default < 0)
 			return -1;
 		return 0;
@@ -391,12 +410,22 @@
 		diff_no_prefix = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "diff.srcprefix")) {
+		return git_config_string(&diff_src_prefix, var, value);
+	}
+	if (!strcmp(var, "diff.dstprefix")) {
+		return git_config_string(&diff_dst_prefix, var, value);
+	}
 	if (!strcmp(var, "diff.relative")) {
 		diff_relative = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "diff.statnamewidth")) {
+		diff_stat_name_width = git_config_int(var, value, ctx->kvi);
+		return 0;
+	}
 	if (!strcmp(var, "diff.statgraphwidth")) {
-		diff_stat_graph_width = git_config_int(var, value);
+		diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "diff.external"))
@@ -406,10 +435,15 @@
 	if (!strcmp(var, "diff.orderfile"))
 		return git_config_pathname(&diff_order_file_cfg, var, value);
 
-	if (!strcmp(var, "diff.ignoresubmodules"))
+	if (!strcmp(var, "diff.ignoresubmodules")) {
+		if (!value)
+			return config_error_nonbool(var);
 		handle_ignore_submodules_arg(&default_diff_options, value);
+	}
 
 	if (!strcmp(var, "diff.submodule")) {
+		if (!value)
+			return config_error_nonbool(var);
 		if (parse_submodule_params(&default_diff_options, value))
 			warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
 				value);
@@ -417,24 +451,28 @@
 	}
 
 	if (!strcmp(var, "diff.algorithm")) {
+		if (!value)
+			return config_error_nonbool(var);
 		diff_algorithm = parse_algorithm_value(value);
 		if (diff_algorithm < 0)
-			return -1;
+			return error(_("unknown value for config '%s': %s"),
+				     var, value);
 		return 0;
 	}
 
 	if (git_color_config(var, value, cb) < 0)
 		return -1;
 
-	return git_diff_basic_config(var, value, cb);
+	return git_diff_basic_config(var, value, ctx, cb);
 }
 
-int git_diff_basic_config(const char *var, const char *value, void *cb)
+int git_diff_basic_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	const char *name;
 
 	if (!strcmp(var, "diff.renamelimit")) {
-		diff_rename_limit_default = git_config_int(var, value);
+		diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -452,9 +490,13 @@
 	}
 
 	if (!strcmp(var, "diff.wserrorhighlight")) {
-		int val = parse_ws_error_highlight(value);
+		int val;
+		if (!value)
+			return config_error_nonbool(var);
+		val = parse_ws_error_highlight(value);
 		if (val < 0)
-			return -1;
+			return error(_("unknown value for config '%s': %s"),
+				     var, value);
 		ws_error_highlight_default = val;
 		return 0;
 	}
@@ -469,6 +511,8 @@
 
 	if (!strcmp(var, "diff.dirstat")) {
 		struct strbuf errmsg = STRBUF_INIT;
+		if (!value)
+			return config_error_nonbool(var);
 		default_diff_options.dirstat_permille = diff_dirstat_permille_default;
 		if (parse_dirstat_params(&default_diff_options, value, &errmsg))
 			warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
@@ -481,7 +525,7 @@
 	if (git_diff_heuristic_config(var, value, cb) < 0)
 		return -1;
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static char *quote_two(const char *one, const char *two)
@@ -2688,12 +2732,14 @@
 	number_width = decimal_width(max_change) > number_width ?
 		decimal_width(max_change) : number_width;
 
+	if (options->stat_name_width == -1)
+		options->stat_name_width = diff_stat_name_width;
 	if (options->stat_graph_width == -1)
 		options->stat_graph_width = diff_stat_graph_width;
 
 	/*
-	 * Guarantee 3/8*16==6 for the graph part
-	 * and 5/8*16==10 for the filename part
+	 * Guarantee 3/8*16 == 6 for the graph part
+	 * and 5/8*16 == 10 for the filename part
 	 */
 	if (width < 16 + 6 + number_width)
 		width = 16 + 6 + number_width;
@@ -2993,6 +3039,24 @@
 	return strcmp(a->name, b->name);
 }
 
+static void conclude_dirstat(struct diff_options *options,
+			     struct dirstat_dir *dir,
+			     unsigned long changed)
+{
+	struct dirstat_file *to_free = dir->files;
+
+	if (!changed) {
+		/* This can happen even with many files, if everything was renames */
+		;
+	} else {
+		/* Show all directories with more than x% of the changes */
+		QSORT(dir->files, dir->nr, dirstat_compare);
+		gather_dirstat(options, dir, changed, "", 0);
+	}
+
+	free(to_free);
+}
+
 static void show_dirstat(struct diff_options *options)
 {
 	int i;
@@ -3082,13 +3146,7 @@
 		dir.nr++;
 	}
 
-	/* This can happen even with many files, if everything was renames */
-	if (!changed)
-		return;
-
-	/* Show all directories with more than x% of the changes */
-	QSORT(dir.files, dir.nr, dirstat_compare);
-	gather_dirstat(options, &dir, changed, "", 0);
+	conclude_dirstat(options, &dir, changed);
 }
 
 static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
@@ -3126,13 +3184,7 @@
 		dir.nr++;
 	}
 
-	/* This can happen even with many files, if everything was renames */
-	if (!changed)
-		return;
-
-	/* Show all directories with more than x% of the changes */
-	QSORT(dir.files, dir.nr, dirstat_compare);
-	gather_dirstat(options, &dir, changed, "", 0);
+	conclude_dirstat(options, &dir, changed);
 }
 
 static void free_diffstat_file(struct diffstat_file *f)
@@ -3374,6 +3426,17 @@
 		options->b_prefix = b;
 }
 
+void diff_set_noprefix(struct diff_options *options)
+{
+	options->a_prefix = options->b_prefix = "";
+}
+
+void diff_set_default_prefix(struct diff_options *options)
+{
+	options->a_prefix = diff_src_prefix;
+	options->b_prefix = diff_dst_prefix;
+}
+
 struct userdiff_driver *get_textconv(struct repository *r,
 				     struct diff_filespec *one)
 {
@@ -3530,18 +3593,21 @@
 		strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset);
 		if (xfrm_msg)
 			strbuf_addstr(&header, xfrm_msg);
+		o->found_changes = 1;
 		must_show_header = 1;
 	}
 	else if (lbl[1][0] == '/') {
 		strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset);
 		if (xfrm_msg)
 			strbuf_addstr(&header, xfrm_msg);
+		o->found_changes = 1;
 		must_show_header = 1;
 	}
 	else {
 		if (one->mode != two->mode) {
 			strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset);
 			strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset);
+			o->found_changes = 1;
 			must_show_header = 1;
 		}
 		if (xfrm_msg)
@@ -4326,7 +4392,8 @@
 		add_external_diff_name(o->repo, &cmd.args, two);
 		if (other) {
 			strvec_push(&cmd.args, other);
-			strvec_push(&cmd.args, xfrm_msg);
+			if (xfrm_msg)
+				strvec_push(&cmd.args, xfrm_msg);
 		}
 	}
 
@@ -4351,7 +4418,7 @@
 static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
 {
 	if (startup_info->have_repository)
-		return find_unique_abbrev(oid, abbrev);
+		return repo_find_unique_abbrev(the_repository, oid, abbrev);
 	else {
 		char *hex = oid_to_hex(oid);
 		if (abbrev < 0)
@@ -4674,10 +4741,9 @@
 		options->flags.ignore_untracked_in_submodules = 1;
 
 	if (diff_no_prefix) {
-		options->a_prefix = options->b_prefix = "";
+		diff_set_noprefix(options);
 	} else if (!diff_mnemonic_prefix) {
-		options->a_prefix = "a/";
-		options->b_prefix = "b/";
+		diff_set_default_prefix(options);
 	}
 
 	options->color_moved = diff_color_moved_default;
@@ -4721,6 +4787,31 @@
 	return filter_bit[(int) status];
 }
 
+int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error)
+{
+	unsigned forbidden_magic;
+
+	if (ps->nr != 1) {
+		if (die_on_error)
+			die(_("--follow requires exactly one pathspec"));
+		return 0;
+	}
+
+	forbidden_magic = ps->items[0].magic & ~(PATHSPEC_FROMTOP |
+						 PATHSPEC_LITERAL);
+	if (forbidden_magic) {
+		if (die_on_error) {
+			struct strbuf sb = STRBUF_INIT;
+			pathspec_magic_names(forbidden_magic, &sb);
+			die(_("pathspec magic not supported by --follow: %s"),
+			    sb.buf);
+		}
+		return 0;
+	}
+
+	return 1;
+}
+
 void diff_setup_done(struct diff_options *options)
 {
 	unsigned check_mask = DIFF_FORMAT_NAME |
@@ -4775,6 +4866,10 @@
 	else
 		options->prefix_length = 0;
 
+	/*
+	 * --name-only, --name-status, --checkdiff, and -s
+	 * turn other output format off.
+	 */
 	if (options->output_format & (DIFF_FORMAT_NAME |
 				      DIFF_FORMAT_NAME_STATUS |
 				      DIFF_FORMAT_CHECKDIFF |
@@ -4828,8 +4923,8 @@
 
 	options->diff_path_counter = 0;
 
-	if (options->flags.follow_renames && options->pathspec.nr != 1)
-		die(_("--follow requires exactly one pathspec"));
+	if (options->flags.follow_renames)
+		diff_check_follow_pathspec(&options->pathspec, 1);
 
 	if (!options->use_color || external_diff())
 		options->color_moved = 0;
@@ -4906,6 +5001,7 @@
 	} else
 		BUG("%s should not get here", opt->long_name);
 
+	options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
 	options->output_format |= DIFF_FORMAT_DIFFSTAT;
 	options->stat_name_width = name_width;
 	options->stat_graph_width = graph_width;
@@ -4925,6 +5021,7 @@
 	 * The caller knows a dirstat-related option is given from the command
 	 * line; allow it to say "return this_function();"
 	 */
+	options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
 	options->output_format |= DIFF_FORMAT_DIRSTAT;
 	return 1;
 }
@@ -4988,7 +5085,7 @@
 	struct object_id oid;
 
 	BUG_ON_OPT_NEG(unset);
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		return error(_("unable to resolve '%s'"), arg);
 
 	if (!opt->objfind)
@@ -5124,6 +5221,7 @@
 		options->flags.stat_with_summary = 0;
 	} else {
 		options->flags.stat_with_summary = 1;
+		options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
 		options->output_format |= DIFF_FORMAT_DIFFSTAT;
 	}
 	return 0;
@@ -5261,8 +5359,20 @@
 
 	BUG_ON_OPT_NEG(unset);
 	BUG_ON_OPT_ARG(optarg);
-	options->a_prefix = "";
-	options->b_prefix = "";
+	diff_set_noprefix(options);
+	return 0;
+}
+
+static int diff_opt_default_prefix(const struct option *opt,
+				   const char *optarg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(optarg);
+	diff_src_prefix = "a/";
+	diff_dst_prefix = "b/";
+	diff_set_default_prefix(options);
 	return 0;
 }
 
@@ -5451,6 +5561,10 @@
 	return 0;
 }
 
+/*
+ * Consider adding new flags to __git_diff_common_options
+ * in contrib/completion/git-completion.bash
+ */
 struct option *add_diff_options(const struct option *opts,
 				struct diff_options *options)
 {
@@ -5459,9 +5573,8 @@
 		OPT_BITOP('p', "patch", &options->output_format,
 			  N_("generate patch"),
 			  DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
-		OPT_BIT_F('s', "no-patch", &options->output_format,
-			  N_("suppress diff output"),
-			  DIFF_FORMAT_NO_OUTPUT, PARSE_OPT_NONEG),
+		OPT_SET_INT('s', "no-patch", &options->output_format,
+			    N_("suppress diff output"), DIFF_FORMAT_NO_OUTPUT),
 		OPT_BITOP('u', NULL, &options->output_format,
 			  N_("generate patch"),
 			  DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
@@ -5470,9 +5583,9 @@
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_unified),
 		OPT_BOOL('W', "function-context", &options->flags.funccontext,
 			 N_("generate diffs with <n> lines context")),
-		OPT_BIT_F(0, "raw", &options->output_format,
+		OPT_BITOP(0, "raw", &options->output_format,
 			  N_("generate the diff in raw format"),
-			  DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
+			  DIFF_FORMAT_RAW, DIFF_FORMAT_NO_OUTPUT),
 		OPT_BITOP(0, "patch-with-raw", &options->output_format,
 			  N_("synonym for '-p --raw'"),
 			  DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW,
@@ -5481,13 +5594,13 @@
 			  N_("synonym for '-p --stat'"),
 			  DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT,
 			  DIFF_FORMAT_NO_OUTPUT),
-		OPT_BIT_F(0, "numstat", &options->output_format,
+		OPT_BITOP(0, "numstat", &options->output_format,
 			  N_("machine friendly --stat"),
-			  DIFF_FORMAT_NUMSTAT, PARSE_OPT_NONEG),
-		OPT_BIT_F(0, "shortstat", &options->output_format,
+			  DIFF_FORMAT_NUMSTAT, DIFF_FORMAT_NO_OUTPUT),
+		OPT_BITOP(0, "shortstat", &options->output_format,
 			  N_("output only the last line of --stat"),
-			  DIFF_FORMAT_SHORTSTAT, PARSE_OPT_NONEG),
-		OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
+			  DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
+		OPT_CALLBACK_F('X', "dirstat", options, N_("<param1>,<param2>..."),
 			       N_("output the distribution of relative amount of changes for each sub-directory"),
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
 			       diff_opt_dirstat),
@@ -5495,16 +5608,16 @@
 			       N_("synonym for --dirstat=cumulative"),
 			       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
 			       diff_opt_dirstat),
-		OPT_CALLBACK_F(0, "dirstat-by-file", options, N_("<param1,param2>..."),
-			       N_("synonym for --dirstat=files,param1,param2..."),
+		OPT_CALLBACK_F(0, "dirstat-by-file", options, N_("<param1>,<param2>..."),
+			       N_("synonym for --dirstat=files,<param1>,<param2>..."),
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
 			       diff_opt_dirstat),
 		OPT_BIT_F(0, "check", &options->output_format,
 			  N_("warn if changes introduce conflict markers or whitespace errors"),
 			  DIFF_FORMAT_CHECKDIFF, PARSE_OPT_NONEG),
-		OPT_BIT_F(0, "summary", &options->output_format,
+		OPT_BITOP(0, "summary", &options->output_format,
 			  N_("condensed summary such as creations, renames and mode changes"),
-			  DIFF_FORMAT_SUMMARY, PARSE_OPT_NONEG),
+			  DIFF_FORMAT_SUMMARY, DIFF_FORMAT_NO_OUTPUT),
 		OPT_BIT_F(0, "name-only", &options->output_format,
 			  N_("show only names of changed files"),
 			  DIFF_FORMAT_NAME, PARSE_OPT_NONEG),
@@ -5555,6 +5668,9 @@
 		OPT_CALLBACK_F(0, "no-prefix", options, NULL,
 			       N_("do not show any source or destination prefix"),
 			       PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix),
+		OPT_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),
@@ -6130,6 +6246,8 @@
 		fprintf(opt->file, "%s", diff_line_prefix(opt));
 		write_name_quoted(name_a, opt->file, opt->line_termination);
 	}
+
+	opt->found_changes = 1;
 }
 
 static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs)
@@ -6608,6 +6726,21 @@
 		separator++;
 	}
 
+	if (output_format & DIFF_FORMAT_PATCH) {
+		if (separator) {
+			emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
+			if (options->stat_sep)
+				/* attach patch instead of inline */
+				emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
+						 NULL, 0, 0);
+		}
+
+		diff_flush_patch_all_file_pairs(options);
+	}
+
+	if (output_format & DIFF_FORMAT_CALLBACK)
+		options->format_callback(q, options, options->format_callback_data);
+
 	if (output_format & DIFF_FORMAT_NO_OUTPUT &&
 	    options->flags.exit_with_status &&
 	    options->flags.diff_from_contents) {
@@ -6629,21 +6762,6 @@
 		}
 	}
 
-	if (output_format & DIFF_FORMAT_PATCH) {
-		if (separator) {
-			emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0);
-			if (options->stat_sep)
-				/* attach patch instead of inline */
-				emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP,
-						 NULL, 0, 0);
-		}
-
-		diff_flush_patch_all_file_pairs(options);
-	}
-
-	if (output_format & DIFF_FORMAT_CALLBACK)
-		options->format_callback(q, options, options->format_callback_data);
-
 free_queue:
 	diff_free_queue(q);
 	DIFF_QUEUE_CLEAR(q);
@@ -6844,6 +6962,13 @@
 	oid_array_clear(&to_fetch);
 }
 
+void init_diffstat_widths(struct diff_options *options)
+{
+	options->stat_width = -1;        /* use full terminal width */
+	options->stat_name_width = -1;   /* respect diff.statNameWidth config */
+	options->stat_graph_width = -1;  /* respect diff.statGraphWidth config */
+}
+
 void diffcore_std(struct diff_options *options)
 {
 	int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT |
@@ -6860,7 +6985,7 @@
 	 * If no prefetching occurs, diffcore_rename() will prefetch if it
 	 * decides that it needs inexact rename detection.
 	 */
-	if (options->repo == the_repository && has_promisor_remote() &&
+	if (options->repo == the_repository && repo_has_promisor_remote(the_repository) &&
 	    (options->output_format & output_formats_to_prefetch ||
 	     options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
 		diff_queued_diff_prefetch(options->repo);
@@ -6897,16 +7022,14 @@
 	options->found_follow = 0;
 }
 
-int diff_result_code(struct diff_options *opt, int status)
+int diff_result_code(struct diff_options *opt)
 {
 	int result = 0;
 
 	diff_warn_rename_limit("diff.renameLimit",
 			       opt->needed_rename_limit,
 			       opt->degraded_cc_to_c);
-	if (!opt->flags.exit_with_status &&
-	    !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
-		return status;
+
 	if (opt->flags.exit_with_status &&
 	    opt->flags.has_changes)
 		result |= 01;
@@ -6953,6 +7076,7 @@
 		if (check_pair_status(p))
 			diff_flush_stat(p, options, diffstat);
 	}
+	options->found_changes = !!diffstat->nr;
 }
 
 void diff_addremove(struct diff_options *options,
diff --git a/diff.h b/diff.h
index 8d770b1..66bd8ae 100644
--- a/diff.h
+++ b/diff.h
@@ -4,10 +4,11 @@
 #ifndef DIFF_H
 #define DIFF_H
 
-#include "tree-walk.h"
+#include "hash-ll.h"
 #include "pathspec.h"
-#include "object.h"
-#include "oidset.h"
+#include "strbuf.h"
+
+struct oidset;
 
 /**
  * The diff API is for programs that compare two sets of files (e.g. two trees,
@@ -71,7 +72,6 @@
 struct option;
 struct repository;
 struct rev_info;
-struct strbuf;
 struct userdiff_driver;
 
 typedef int (*pathchange_fn_t)(struct diff_options *options,
@@ -497,6 +497,8 @@
 void diff_tree_combined_merge(const struct commit *commit, struct rev_info *rev);
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
+void diff_set_noprefix(struct diff_options *options);
+void diff_set_default_prefix(struct diff_options *options);
 
 int diff_can_quit_early(struct diff_options *);
 
@@ -531,17 +533,24 @@
 int parse_long_opt(const char *opt, const char **argv,
 		   const char **optarg);
 
-int git_diff_basic_config(const char *var, const char *value, void *cb);
+struct config_context;
+int git_diff_basic_config(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb);
 int git_diff_heuristic_config(const char *var, const char *value, void *cb);
 void init_diff_ui_defaults(void);
-int git_diff_ui_config(const char *var, const char *value, void *cb);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define diff_setup(diffopts) repo_diff_setup(the_repository, diffopts)
-#endif
+int git_diff_ui_config(const char *var, const char *value,
+		       const struct config_context *ctx, void *cb);
 void repo_diff_setup(struct repository *, struct diff_options *);
 struct option *add_diff_options(const struct option *, struct diff_options *);
 int diff_opt_parse(struct diff_options *, const char **, int, const char *);
 void diff_setup_done(struct diff_options *);
+
+/*
+ * Returns true if the pathspec can work with --follow mode. If die_on_error is
+ * set, die() with a specific error message rather than returning false.
+ */
+int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error);
+
 int git_config_rename(const char *var, const char *value);
 
 #define DIFF_DETECT_RENAME	1
@@ -564,6 +573,7 @@
 
 #define DIFF_PICKAXE_IGNORE_CASE	32
 
+void init_diffstat_widths(struct diff_options *);
 void diffcore_std(struct diff_options *);
 void diffcore_fix_diff_index(void);
 
@@ -617,7 +627,7 @@
 #define DIFF_STATUS_FILTER_BROKEN	'B'
 
 /*
- * This is different from find_unique_abbrev() in that
+ * This is different from repo_find_unique_abbrev() in that
  * it stuffs the result with dots for alignment.
  */
 const char *diff_aligned_abbrev(const struct object_id *sha1, int);
@@ -628,17 +638,17 @@
 #define DIFF_SILENT_ON_REMOVED 01
 /* report racily-clean paths as modified */
 #define DIFF_RACY_IS_MODIFIED 02
-int run_diff_files(struct rev_info *revs, unsigned int option);
+void run_diff_files(struct rev_info *revs, unsigned int option);
 
 #define DIFF_INDEX_CACHED 01
 #define DIFF_INDEX_MERGE_BASE 02
-int run_diff_index(struct rev_info *revs, unsigned int option);
+void run_diff_index(struct rev_info *revs, unsigned int option);
 
 int do_diff_cache(const struct object_id *, struct diff_options *);
 int diff_flush_patch_id(struct diff_options *, struct object_id *, int);
 void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx);
 
-int diff_result_code(struct diff_options *, int);
+int diff_result_code(struct diff_options *);
 
 int diff_no_index(struct rev_info *,
 		  int implicit_no_index, int, const char **);
@@ -697,4 +707,6 @@
 			int insertions, int deletions);
 void setup_diff_pager(struct diff_options *);
 
+extern int diff_auto_refresh_index;
+
 #endif /* DIFF_H */
diff --git a/diffcore-break.c b/diffcore-break.c
index 0d4a149..49ba38a 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -1,9 +1,10 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
-#include "diff.h"
+#include "git-compat-util.h"
 #include "diffcore.h"
+#include "hash.h"
+#include "object.h"
 #include "promisor-remote.h"
 
 static int should_break(struct repository *r,
@@ -65,7 +66,7 @@
 	    oideq(&src->oid, &dst->oid))
 		return 0; /* they are the same */
 
-	if (r == the_repository && has_promisor_remote()) {
+	if (r == the_repository && repo_has_promisor_remote(the_repository)) {
 		options.missing_object_cb = diff_queued_diff_prefetch;
 		options.missing_object_data = r;
 	}
diff --git a/diffcore-delta.c b/diffcore-delta.c
index 18d8f76..ba6cbee 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -1,5 +1,4 @@
-#include "cache.h"
-#include "diff.h"
+#include "git-compat-util.h"
 #include "diffcore.h"
 
 /*
@@ -159,6 +158,10 @@
 		n = 0;
 		accum1 = accum2 = 0;
 	}
+	if (n > 0) {
+		hashval = (accum1 + accum2 * 0x61) % HASHBASE;
+		hash = add_spanhash(hash, hashval, n);
+	}
 	QSORT(hash->data, (size_t)1ul << hash->alloc_log2, spanhash_cmp);
 	return hash;
 }
diff --git a/diffcore-order.c b/diffcore-order.c
index 19e7331..e7d20eb 100644
--- a/diffcore-order.c
+++ b/diffcore-order.c
@@ -1,9 +1,11 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "wildmatch.h"
 
 static char **order;
 static int order_cnt;
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 03fcbcb..b195fa4 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -2,12 +2,13 @@
  * Copyright (C) 2005 Junio C Hamano
  * Copyright (C) 2010 Google Inc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "xdiff-interface.h"
 #include "kwset.h"
-#include "commit.h"
+#include "oidset.h"
+#include "pretty.h"
 #include "quote.h"
 
 typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two,
diff --git a/diffcore-rename.c b/diffcore-rename.c
index c0422d9..5a6e2bc 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -2,14 +2,18 @@
  *
  * Copyright (C) 2005 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "hashmap.h"
+#include "mem-pool.h"
+#include "oid-array.h"
 #include "progress.h"
 #include "promisor-remote.h"
+#include "string-list.h"
 #include "strmap.h"
+#include "trace2.h"
 
 /* Table of rename/copy destinations */
 
@@ -981,7 +985,7 @@
 			strintmap_set(&dests, base, i);
 	}
 
-	if (options->repo == the_repository && has_promisor_remote()) {
+	if (options->repo == the_repository && repo_has_promisor_remote(the_repository)) {
 		dpf_options.missing_object_cb = basename_prefetch;
 		dpf_options.missing_object_data = &prefetch_options;
 	}
@@ -1567,7 +1571,7 @@
 
 	/* Finish setting up dpf_options */
 	prefetch_options.skip_unmodified = skip_unmodified;
-	if (options->repo == the_repository && has_promisor_remote()) {
+	if (options->repo == the_repository && repo_has_promisor_remote(the_repository)) {
 		dpf_options.missing_object_cb = inexact_prefetch;
 		dpf_options.missing_object_data = &prefetch_options;
 	}
diff --git a/diffcore-rotate.c b/diffcore-rotate.c
index 445f060..533986c 100644
--- a/diffcore-rotate.c
+++ b/diffcore-rotate.c
@@ -2,7 +2,8 @@
  * Copyright (C) 2021, Google LLC.
  * Based on diffcore-order.c, which is Copyright (C) 2005, Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "diff.h"
 #include "diffcore.h"
 
diff --git a/diffcore.h b/diffcore.h
index 9b588a1..5ffe4ec 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -4,9 +4,11 @@
 #ifndef DIFFCORE_H
 #define DIFFCORE_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct diff_options;
+struct mem_pool;
+struct oid_array;
 struct repository;
 struct strintmap;
 struct strmap;
diff --git a/dir-iterator.c b/dir-iterator.c
index cedd304..de61984 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -1,12 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
 #include "iterator.h"
 #include "dir-iterator.h"
+#include "string-list.h"
 
 struct dir_iterator_level {
 	DIR *dir;
 
 	/*
+	 * The directory entries of the current level. This list will only be
+	 * populated when the iterator is ordered. In that case, `dir` will be
+	 * set to `NULL`.
+	 */
+	struct string_list entries;
+	size_t entries_idx;
+
+	/*
 	 * The length of the directory part of path at this level
 	 * (including a trailing '/'):
 	 */
@@ -43,6 +52,31 @@
 	unsigned int flags;
 };
 
+static int next_directory_entry(DIR *dir, const char *path,
+				struct dirent **out)
+{
+	struct dirent *de;
+
+repeat:
+	errno = 0;
+	de = readdir(dir);
+	if (!de) {
+		if (errno) {
+			warning_errno("error reading directory '%s'",
+				      path);
+			return -1;
+		}
+
+		return 1;
+	}
+
+	if (is_dot_or_dotdot(de->d_name))
+		goto repeat;
+
+	*out = de;
+	return 0;
+}
+
 /*
  * Push a level in the iter stack and initialize it with information from
  * the directory pointed by iter->base->path. It is assumed that this
@@ -72,6 +106,35 @@
 		return -1;
 	}
 
+	string_list_init_dup(&level->entries);
+	level->entries_idx = 0;
+
+	/*
+	 * When the iterator is sorted we read and sort all directory entries
+	 * directly.
+	 */
+	if (iter->flags & DIR_ITERATOR_SORTED) {
+		struct dirent *de;
+
+		while (1) {
+			int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
+			if (ret < 0) {
+				if (errno != ENOENT &&
+				    iter->flags & DIR_ITERATOR_PEDANTIC)
+					return -1;
+				continue;
+			} else if (ret > 0) {
+				break;
+			}
+
+			string_list_append(&level->entries, de->d_name);
+		}
+		string_list_sort(&level->entries);
+
+		closedir(level->dir);
+		level->dir = NULL;
+	}
+
 	return 0;
 }
 
@@ -88,21 +151,22 @@
 		warning_errno("error closing directory '%s'",
 			      iter->base.path.buf);
 	level->dir = NULL;
+	string_list_clear(&level->entries, 0);
 
 	return --iter->levels_nr;
 }
 
 /*
  * Populate iter->base with the necessary information on the next iteration
- * entry, represented by the given dirent de. Return 0 on success and -1
+ * entry, represented by the given name. Return 0 on success and -1
  * otherwise, setting errno accordingly.
  */
 static int prepare_next_entry_data(struct dir_iterator_int *iter,
-				   struct dirent *de)
+				   const char *name)
 {
 	int err, saved_errno;
 
-	strbuf_addstr(&iter->base.path, de->d_name);
+	strbuf_addstr(&iter->base.path, name);
 	/*
 	 * We have to reset these because the path strbuf might have
 	 * been realloc()ed at the previous strbuf_addstr().
@@ -139,27 +203,34 @@
 		struct dirent *de;
 		struct dir_iterator_level *level =
 			&iter->levels[iter->levels_nr - 1];
+		const char *name;
 
 		strbuf_setlen(&iter->base.path, level->prefix_len);
-		errno = 0;
-		de = readdir(level->dir);
 
-		if (!de) {
-			if (errno) {
-				warning_errno("error reading directory '%s'",
-					      iter->base.path.buf);
+		if (level->dir) {
+			int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
+			if (ret < 0) {
 				if (iter->flags & DIR_ITERATOR_PEDANTIC)
 					goto error_out;
-			} else if (pop_level(iter) == 0) {
-				return dir_iterator_abort(dir_iterator);
+				continue;
+			} else if (ret > 0) {
+				if (pop_level(iter) == 0)
+					return dir_iterator_abort(dir_iterator);
+				continue;
 			}
-			continue;
+
+			name = de->d_name;
+		} else {
+			if (level->entries_idx >= level->entries.nr) {
+				if (pop_level(iter) == 0)
+					return dir_iterator_abort(dir_iterator);
+				continue;
+			}
+
+			name = level->entries.items[level->entries_idx++].string;
 		}
 
-		if (is_dot_or_dotdot(de->d_name))
-			continue;
-
-		if (prepare_next_entry_data(iter, de)) {
+		if (prepare_next_entry_data(iter, name)) {
 			if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
 				goto error_out;
 			continue;
@@ -188,6 +259,8 @@
 			warning_errno("error closing directory '%s'",
 				      iter->base.path.buf);
 		}
+
+		string_list_clear(&level->entries, 0);
 	}
 
 	free(iter->levels);
diff --git a/dir-iterator.h b/dir-iterator.h
index 479e1ec..6d43880 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -54,8 +54,11 @@
  *   and ITER_ERROR is returned immediately. In both cases, a meaningful
  *   warning is emitted. Note: ENOENT errors are always ignored so that
  *   the API users may remove files during iteration.
+ *
+ * - DIR_ITERATOR_SORTED: sort directory entries alphabetically.
  */
 #define DIR_ITERATOR_PEDANTIC (1 << 0)
+#define DIR_ITERATOR_SORTED   (1 << 1)
 
 struct dir_iterator {
 	/* The current path: */
diff --git a/dir.c b/dir.c
index 4e99f0c..20ebe4c 100644
--- a/dir.c
+++ b/dir.c
@@ -5,19 +5,31 @@
  * Copyright (C) Linus Torvalds, 2005-2006
  *		 Junio Hamano, 2005-2006
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "convert.h"
 #include "dir.h"
-#include "object-store.h"
-#include "attr.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
 #include "utf8.h"
 #include "varint.h"
 #include "ewah/ewok.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
+#include "tree.h"
 
 /*
  * Tells read_directory_recursive how a file or directory should be treated.
@@ -267,7 +279,7 @@
 	*size_out = 0;
 	*data_out = NULL;
 
-	data = read_object_file(oid, &type, &sz);
+	data = repo_read_object_file(the_repository, oid, &type, &sz);
 	if (!data || type != OBJ_BLOB) {
 		free(data);
 		return -1;
@@ -363,7 +375,7 @@
 		return 0;
 
 	if (item->attr_match_nr &&
-	    !match_pathspec_attrs(istate, name, namelen, item))
+	    !match_pathspec_attrs(istate, name - prefix, namelen + prefix, item))
 		return 0;
 
 	/* If the match was just the prefix, we matched */
@@ -1190,7 +1202,7 @@
 	struct pattern_list *pl;
 	struct exclude_list_group *group;
 
-	group = &dir->exclude_list_group[group_type];
+	group = &dir->internal.exclude_list_group[group_type];
 	ALLOC_GROW(group->pl, group->nr + 1, group->alloc);
 	pl = &group->pl[group->nr++];
 	memset(pl, 0, sizeof(*pl));
@@ -1211,7 +1223,7 @@
 	 * differently when dir->untracked is non-NULL.
 	 */
 	if (!dir->untracked)
-		dir->unmanaged_exclude_files++;
+		dir->internal.unmanaged_exclude_files++;
 	pl = add_pattern_list(dir, EXC_FILE, fname);
 	if (add_patterns(fname, "", 0, pl, NULL, 0, oid_stat) < 0)
 		die(_("cannot use %s as an exclude file"), fname);
@@ -1219,7 +1231,7 @@
 
 void add_patterns_from_file(struct dir_struct *dir, const char *fname)
 {
-	dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
+	dir->internal.unmanaged_exclude_files++; /* see validate_untracked_cache() */
 	add_patterns_from_file_1(dir, fname, NULL);
 }
 
@@ -1519,7 +1531,7 @@
 	struct exclude_list_group *group;
 	struct path_pattern *pattern;
 	for (i = EXC_CMDL; i <= EXC_FILE; i++) {
-		group = &dir->exclude_list_group[i];
+		group = &dir->internal.exclude_list_group[i];
 		for (j = group->nr - 1; j >= 0; j--) {
 			pattern = last_matching_pattern_from_list(
 				pathname, pathlen, basename, dtype_p,
@@ -1545,20 +1557,20 @@
 	struct untracked_cache_dir *untracked;
 	int current;
 
-	group = &dir->exclude_list_group[EXC_DIRS];
+	group = &dir->internal.exclude_list_group[EXC_DIRS];
 
 	/*
 	 * Pop the exclude lists from the EXCL_DIRS exclude_list_group
 	 * which originate from directories not in the prefix of the
 	 * path being checked.
 	 */
-	while ((stk = dir->exclude_stack) != NULL) {
+	while ((stk = dir->internal.exclude_stack) != NULL) {
 		if (stk->baselen <= baselen &&
-		    !strncmp(dir->basebuf.buf, base, stk->baselen))
+		    !strncmp(dir->internal.basebuf.buf, base, stk->baselen))
 			break;
-		pl = &group->pl[dir->exclude_stack->exclude_ix];
-		dir->exclude_stack = stk->prev;
-		dir->pattern = NULL;
+		pl = &group->pl[dir->internal.exclude_stack->exclude_ix];
+		dir->internal.exclude_stack = stk->prev;
+		dir->internal.pattern = NULL;
 		free((char *)pl->src); /* see strbuf_detach() below */
 		clear_pattern_list(pl);
 		free(stk);
@@ -1566,7 +1578,7 @@
 	}
 
 	/* Skip traversing into sub directories if the parent is excluded */
-	if (dir->pattern)
+	if (dir->internal.pattern)
 		return;
 
 	/*
@@ -1574,12 +1586,12 @@
 	 * memset(dir, 0, sizeof(*dir)) before use. Changing all of
 	 * them seems lots of work for little benefit.
 	 */
-	if (!dir->basebuf.buf)
-		strbuf_init(&dir->basebuf, PATH_MAX);
+	if (!dir->internal.basebuf.buf)
+		strbuf_init(&dir->internal.basebuf, PATH_MAX);
 
 	/* Read from the parent directories and push them down. */
 	current = stk ? stk->baselen : -1;
-	strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
+	strbuf_setlen(&dir->internal.basebuf, current < 0 ? 0 : current);
 	if (dir->untracked)
 		untracked = stk ? stk->ucd : dir->untracked->root;
 	else
@@ -1599,32 +1611,33 @@
 				die("oops in prep_exclude");
 			cp++;
 			untracked =
-				lookup_untracked(dir->untracked, untracked,
+				lookup_untracked(dir->untracked,
+						 untracked,
 						 base + current,
 						 cp - base - current);
 		}
-		stk->prev = dir->exclude_stack;
+		stk->prev = dir->internal.exclude_stack;
 		stk->baselen = cp - base;
 		stk->exclude_ix = group->nr;
 		stk->ucd = untracked;
 		pl = add_pattern_list(dir, EXC_DIRS, NULL);
-		strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
-		assert(stk->baselen == dir->basebuf.len);
+		strbuf_add(&dir->internal.basebuf, base + current, stk->baselen - current);
+		assert(stk->baselen == dir->internal.basebuf.len);
 
 		/* Abort if the directory is excluded */
 		if (stk->baselen) {
 			int dt = DT_DIR;
-			dir->basebuf.buf[stk->baselen - 1] = 0;
-			dir->pattern = last_matching_pattern_from_lists(dir,
+			dir->internal.basebuf.buf[stk->baselen - 1] = 0;
+			dir->internal.pattern = last_matching_pattern_from_lists(dir,
 									istate,
-				dir->basebuf.buf, stk->baselen - 1,
-				dir->basebuf.buf + current, &dt);
-			dir->basebuf.buf[stk->baselen - 1] = '/';
-			if (dir->pattern &&
-			    dir->pattern->flags & PATTERN_FLAG_NEGATIVE)
-				dir->pattern = NULL;
-			if (dir->pattern) {
-				dir->exclude_stack = stk;
+				dir->internal.basebuf.buf, stk->baselen - 1,
+				dir->internal.basebuf.buf + current, &dt);
+			dir->internal.basebuf.buf[stk->baselen - 1] = '/';
+			if (dir->internal.pattern &&
+			    dir->internal.pattern->flags & PATTERN_FLAG_NEGATIVE)
+				dir->internal.pattern = NULL;
+			if (dir->internal.pattern) {
+				dir->internal.exclude_stack = stk;
 				return;
 			}
 		}
@@ -1647,15 +1660,15 @@
 		      */
 		     !is_null_oid(&untracked->exclude_oid))) {
 			/*
-			 * dir->basebuf gets reused by the traversal, but we
-			 * need fname to remain unchanged to ensure the src
-			 * member of each struct path_pattern correctly
+			 * dir->internal.basebuf gets reused by the traversal,
+			 * but we need fname to remain unchanged to ensure the
+			 * src member of each struct path_pattern correctly
 			 * back-references its source file.  Other invocations
 			 * of add_pattern_list provide stable strings, so we
 			 * strbuf_detach() and free() here in the caller.
 			 */
 			struct strbuf sb = STRBUF_INIT;
-			strbuf_addbuf(&sb, &dir->basebuf);
+			strbuf_addbuf(&sb, &dir->internal.basebuf);
 			strbuf_addstr(&sb, dir->exclude_per_dir);
 			pl->src = strbuf_detach(&sb, NULL);
 			add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
@@ -1681,10 +1694,10 @@
 			invalidate_gitignore(dir->untracked, untracked);
 			oidcpy(&untracked->exclude_oid, &oid_stat.oid);
 		}
-		dir->exclude_stack = stk;
+		dir->internal.exclude_stack = stk;
 		current = stk->baselen;
 	}
-	strbuf_setlen(&dir->basebuf, baselen);
+	strbuf_setlen(&dir->internal.basebuf, baselen);
 }
 
 /*
@@ -1704,8 +1717,8 @@
 
 	prep_exclude(dir, istate, pathname, basename-pathname);
 
-	if (dir->pattern)
-		return dir->pattern;
+	if (dir->internal.pattern)
+		return dir->internal.pattern;
 
 	return last_matching_pattern_from_lists(dir, istate, pathname, pathlen,
 			basename, dtype_p);
@@ -1742,7 +1755,7 @@
 	if (index_file_exists(istate, pathname, len, ignore_case))
 		return NULL;
 
-	ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
+	ALLOC_GROW(dir->entries, dir->nr+1, dir->internal.alloc);
 	return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
 }
 
@@ -1753,7 +1766,7 @@
 	if (!index_name_is_other(istate, pathname, len))
 		return NULL;
 
-	ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
+	ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->internal.ignored_alloc);
 	return dir->ignored[dir->ignored_nr++] = dir_entry_new(pathname, len);
 }
 
@@ -2165,7 +2178,8 @@
 		       PATHSPEC_LITERAL |
 		       PATHSPEC_GLOB |
 		       PATHSPEC_ICASE |
-		       PATHSPEC_EXCLUDE);
+		       PATHSPEC_EXCLUDE |
+		       PATHSPEC_ATTR);
 
 	for (i = 0; i < pathspec->nr; i++) {
 		const struct pathspec_item *item = &pathspec->items[i];
@@ -2221,6 +2235,39 @@
 	return DT_UNKNOWN;
 }
 
+unsigned char get_dtype(struct dirent *e, struct strbuf *path,
+			int follow_symlink)
+{
+	struct stat st;
+	unsigned char dtype = DTYPE(e);
+	size_t base_path_len;
+
+	if (dtype != DT_UNKNOWN && !(follow_symlink && dtype == DT_LNK))
+		return dtype;
+
+	/*
+	 * d_type unknown or unfollowed symlink, try to fall back on [l]stat
+	 * results. If [l]stat fails, explicitly set DT_UNKNOWN.
+	 */
+	base_path_len = path->len;
+	strbuf_addstr(path, e->d_name);
+	if ((follow_symlink && stat(path->buf, &st)) ||
+	    (!follow_symlink && lstat(path->buf, &st)))
+		goto cleanup;
+
+	/* determine d_type from st_mode */
+	if (S_ISREG(st.st_mode))
+		dtype = DT_REG;
+	else if (S_ISDIR(st.st_mode))
+		dtype = DT_DIR;
+	else if (S_ISLNK(st.st_mode))
+		dtype = DT_LNK;
+
+cleanup:
+	strbuf_setlen(path, base_path_len);
+	return dtype;
+}
+
 static int resolve_dtype(int dtype, struct index_state *istate,
 			 const char *path, int len)
 {
@@ -2569,7 +2616,7 @@
 
 	if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only))
 		goto out;
-	dir->visited_directories++;
+	dir->internal.visited_directories++;
 
 	if (untracked)
 		untracked->check_only = !!check_only;
@@ -2578,7 +2625,7 @@
 		/* check how the file or directory should be treated */
 		state = treat_path(dir, untracked, &cdir, istate, &path,
 				   baselen, pathspec);
-		dir->visited_paths++;
+		dir->internal.visited_paths++;
 
 		if (state > dir_state)
 			dir_state = state;
@@ -2586,7 +2633,8 @@
 		/* recurse into subdir if instructed by treat_path */
 		if (state == path_recurse) {
 			struct untracked_cache_dir *ud;
-			ud = lookup_untracked(dir->untracked, untracked,
+			ud = lookup_untracked(dir->untracked,
+					      untracked,
 					      path.buf + baselen,
 					      path.len - baselen);
 			subdir_state =
@@ -2846,7 +2894,7 @@
 	 * condition also catches running setup_standard_excludes()
 	 * before setting dir->untracked!
 	 */
-	if (dir->unmanaged_exclude_files)
+	if (dir->internal.unmanaged_exclude_files)
 		return NULL;
 
 	/*
@@ -2875,7 +2923,7 @@
 	 * EXC_CMDL is not considered in the cache. If people set it,
 	 * skip the cache.
 	 */
-	if (dir->exclude_list_group[EXC_CMDL].nr)
+	if (dir->internal.exclude_list_group[EXC_CMDL].nr)
 		return NULL;
 
 	if (!ident_in_untracked(dir->untracked)) {
@@ -2935,15 +2983,15 @@
 
 	/* Validate $GIT_DIR/info/exclude and core.excludesfile */
 	root = dir->untracked->root;
-	if (!oideq(&dir->ss_info_exclude.oid,
+	if (!oideq(&dir->internal.ss_info_exclude.oid,
 		   &dir->untracked->ss_info_exclude.oid)) {
 		invalidate_gitignore(dir->untracked, root);
-		dir->untracked->ss_info_exclude = dir->ss_info_exclude;
+		dir->untracked->ss_info_exclude = dir->internal.ss_info_exclude;
 	}
-	if (!oideq(&dir->ss_excludes_file.oid,
+	if (!oideq(&dir->internal.ss_excludes_file.oid,
 		   &dir->untracked->ss_excludes_file.oid)) {
 		invalidate_gitignore(dir->untracked, root);
-		dir->untracked->ss_excludes_file = dir->ss_excludes_file;
+		dir->untracked->ss_excludes_file = dir->internal.ss_excludes_file;
 	}
 
 	/* Make sure this directory is not dropped out at saving phase */
@@ -2969,9 +3017,9 @@
 	}
 
 	trace2_data_intmax("read_directory", repo,
-			   "directories-visited", dir->visited_directories);
+			   "directories-visited", dir->internal.visited_directories);
 	trace2_data_intmax("read_directory", repo,
-			   "paths-visited", dir->visited_paths);
+			   "paths-visited", dir->internal.visited_paths);
 
 	if (!dir->untracked)
 		return;
@@ -2993,8 +3041,8 @@
 	struct untracked_cache_dir *untracked;
 
 	trace2_region_enter("dir", "read_directory", istate->repo);
-	dir->visited_paths = 0;
-	dir->visited_directories = 0;
+	dir->internal.visited_paths = 0;
+	dir->internal.visited_directories = 0;
 
 	if (has_symlink_leading_path(path, len)) {
 		trace2_region_leave("dir", "read_directory", istate->repo);
@@ -3342,14 +3390,14 @@
 		excludes_file = xdg_config_home("ignore");
 	if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
 		add_patterns_from_file_1(dir, excludes_file,
-					 dir->untracked ? &dir->ss_excludes_file : NULL);
+					 dir->untracked ? &dir->internal.ss_excludes_file : NULL);
 
 	/* per repository user preference */
 	if (startup_info->have_repository) {
 		const char *path = git_path_info_exclude();
 		if (!access_or_warn(path, R_OK, 0))
 			add_patterns_from_file_1(dir, path,
-						 dir->untracked ? &dir->ss_info_exclude : NULL);
+						 dir->untracked ? &dir->internal.ss_info_exclude : NULL);
 	}
 }
 
@@ -3405,7 +3453,7 @@
 	struct dir_struct new = DIR_INIT;
 
 	for (i = EXC_CMDL; i <= EXC_FILE; i++) {
-		group = &dir->exclude_list_group[i];
+		group = &dir->internal.exclude_list_group[i];
 		for (j = 0; j < group->nr; j++) {
 			pl = &group->pl[j];
 			if (i == EXC_DIRS)
@@ -3422,13 +3470,13 @@
 	free(dir->ignored);
 	free(dir->entries);
 
-	stk = dir->exclude_stack;
+	stk = dir->internal.exclude_stack;
 	while (stk) {
 		struct exclude_stack *prev = stk->prev;
 		free(stk);
 		stk = prev;
 	}
-	strbuf_release(&dir->basebuf);
+	strbuf_release(&dir->internal.basebuf);
 
 	memcpy(dir, &new, sizeof(*dir));
 }
@@ -3870,6 +3918,26 @@
 				 path, strlen(path));
 }
 
+void untracked_cache_invalidate_trimmed_path(struct index_state *istate,
+					     const char *path,
+					     int safe_path)
+{
+	size_t len = strlen(path);
+
+	if (!len)
+		BUG("untracked_cache_invalidate_trimmed_path given zero length path");
+
+	if (path[len - 1] != '/') {
+		untracked_cache_invalidate_path(istate, path, safe_path);
+	} else {
+		struct strbuf tmp = STRBUF_INIT;
+
+		strbuf_add(&tmp, path, len - 1);
+		untracked_cache_invalidate_path(istate, tmp.buf, safe_path);
+		strbuf_release(&tmp);
+	}
+}
+
 void untracked_cache_remove_from_index(struct index_state *istate,
 				       const char *path)
 {
diff --git a/dir.h b/dir.h
index 8acfc04..45a7b9e 100644
--- a/dir.h
+++ b/dir.h
@@ -1,10 +1,14 @@
 #ifndef DIR_H
 #define DIR_H
 
-#include "cache.h"
+#include "hash-ll.h"
 #include "hashmap.h"
+#include "pathspec.h"
+#include "statinfo.h"
 #include "strbuf.h"
 
+struct repository;
+
 /**
  * The directory listing API is used to enumerate paths in the work tree,
  * optionally taking `.git/info/exclude` and `.gitignore` files per directory
@@ -39,6 +43,8 @@
  *
  */
 
+struct repository;
+
 struct dir_entry {
 	unsigned int len;
 	char name[FLEX_ARRAY]; /* more */
@@ -212,17 +218,6 @@
  */
 struct dir_struct {
 
-	/* The number of members in `entries[]` array. */
-	int nr;
-
-	/* Internal use; keeps track of allocation of `entries[]` array.*/
-	int alloc;
-
-	/* The number of members in `ignored[]` array. */
-	int ignored_nr;
-
-	int ignored_alloc;
-
 	/* bit-field of options */
 	enum {
 
@@ -287,66 +282,103 @@
 		DIR_SKIP_NESTED_GIT = 1<<9
 	} flags;
 
+	/* The number of members in `entries[]` array. */
+	int nr; /* output only */
+
+	/* The number of members in `ignored[]` array. */
+	int ignored_nr; /* output only */
+
 	/* An array of `struct dir_entry`, each element of which describes a path. */
-	struct dir_entry **entries;
+	struct dir_entry **entries; /* output only */
 
 	/**
 	 * used for ignored paths with the `DIR_SHOW_IGNORED_TOO` and
 	 * `DIR_COLLECT_IGNORED` flags.
 	 */
-	struct dir_entry **ignored;
+	struct dir_entry **ignored; /* output only */
+
+	/* Enable/update untracked file cache if set */
+	struct untracked_cache *untracked;
 
 	/**
-	 * The name of the file to be read in each directory for excluded files
-	 * (typically `.gitignore`).
+	 * Deprecated: ls-files is the only allowed caller; all other callers
+	 * should leave this as NULL; it pre-dated the
+	 * setup_standard_excludes() mechanism that replaces this.
+	 *
+	 * This field tracks the name of the file to be read in each directory
+	 * for excluded files (typically `.gitignore`).
 	 */
 	const char *exclude_per_dir;
 
-	/*
-	 * We maintain three groups of exclude pattern lists:
-	 *
-	 * EXC_CMDL lists patterns explicitly given on the command line.
-	 * EXC_DIRS lists patterns obtained from per-directory ignore files.
-	 * EXC_FILE lists patterns from fallback ignore files, e.g.
-	 *   - .git/info/exclude
-	 *   - core.excludesfile
-	 *
-	 * Each group contains multiple exclude lists, a single list
-	 * per source.
-	 */
+	struct dir_struct_internal {
+		/* Keeps track of allocation of `entries[]` array.*/
+		int alloc;
+
+		/* Keeps track of allocation of `ignored[]` array. */
+		int ignored_alloc;
+
+		/*
+		 * We maintain three groups of exclude pattern lists:
+		 *
+		 * EXC_CMDL lists patterns explicitly given on the command line.
+		 * EXC_DIRS lists patterns obtained from per-directory ignore
+		 *          files.
+		 * EXC_FILE lists patterns from fallback ignore files, e.g.
+		 *   - .git/info/exclude
+		 *   - core.excludesfile
+		 *
+		 * Each group contains multiple exclude lists, a single list
+		 * per source.
+		 */
 #define EXC_CMDL 0
 #define EXC_DIRS 1
 #define EXC_FILE 2
-	struct exclude_list_group exclude_list_group[3];
+		struct exclude_list_group exclude_list_group[3];
 
-	/*
-	 * Temporary variables which are used during loading of the
-	 * per-directory exclude lists.
-	 *
-	 * exclude_stack points to the top of the exclude_stack, and
-	 * basebuf contains the full path to the current
-	 * (sub)directory in the traversal. Exclude points to the
-	 * matching exclude struct if the directory is excluded.
-	 */
-	struct exclude_stack *exclude_stack;
-	struct path_pattern *pattern;
-	struct strbuf basebuf;
+		/*
+		 * Temporary variables which are used during loading of the
+		 * per-directory exclude lists.
+		 *
+		 * exclude_stack points to the top of the exclude_stack, and
+		 * basebuf contains the full path to the current
+		 * (sub)directory in the traversal. Exclude points to the
+		 * matching exclude struct if the directory is excluded.
+		 */
+		struct exclude_stack *exclude_stack;
+		struct path_pattern *pattern;
+		struct strbuf basebuf;
 
-	/* Enable untracked file cache if set */
-	struct untracked_cache *untracked;
-	struct oid_stat ss_info_exclude;
-	struct oid_stat ss_excludes_file;
-	unsigned unmanaged_exclude_files;
+		/* Additional metadata related to 'untracked' */
+		struct oid_stat ss_info_exclude;
+		struct oid_stat ss_excludes_file;
+		unsigned unmanaged_exclude_files;
 
-	/* Stats about the traversal */
-	unsigned visited_paths;
-	unsigned visited_directories;
+		/* Stats about the traversal */
+		unsigned visited_paths;
+		unsigned visited_directories;
+	} internal;
 };
 
 #define DIR_INIT { 0 }
 
 struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp);
 
+/*
+ * Get the d_type of a dirent. If the d_type is unknown, derive it from
+ * stat.st_mode using the path to the dirent's containing directory (path) and
+ * the name of the dirent itself.
+ *
+ * If 'follow_symlink' is 1, this function will attempt to follow DT_LNK types
+ * using 'stat'. Links are *not* followed recursively, so a symlink pointing
+ * to another symlink will still resolve to 'DT_LNK'.
+ *
+ * Note that 'path' is assumed to have a trailing slash. It is also modified
+ * in-place during the execution of the function, but is then reverted to its
+ * original value before returning.
+ */
+unsigned char get_dtype(struct dirent *e, struct strbuf *path,
+			int follow_symlink);
+
 /*Count the number of slashes for string s*/
 int count_slashes(const char *s);
 
@@ -363,10 +395,6 @@
 int simple_length(const char *match);
 int no_wildcard(const char *string);
 char *common_prefix(const struct pathspec *pathspec);
-int match_pathspec(struct index_state *istate,
-		   const struct pathspec *pathspec,
-		   const char *name, int namelen,
-		   int prefix, char *seen, int is_dir);
 int report_path_error(const char *ps_matched, const struct pathspec *pathspec);
 int within_depth(const char *name, int namelen, int depth, int max_depth);
 
@@ -533,15 +561,6 @@
 			 const char *submodule_name,
 			 char *seen);
 
-static inline int ce_path_match(struct index_state *istate,
-				const struct cache_entry *ce,
-				const struct pathspec *pathspec,
-				char *seen)
-{
-	return match_pathspec(istate, pathspec, ce->name, ce_namelen(ce), 0, seen,
-			      S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
-}
-
 static inline int dir_path_match(struct index_state *istate,
 				 const struct dir_entry *ent,
 				 const struct pathspec *pathspec,
@@ -557,6 +576,13 @@
 int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
 
 void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
+/*
+ * Invalidate the untracked-cache for this path, but first strip
+ * off a trailing slash, if present.
+ */
+void untracked_cache_invalidate_trimmed_path(struct index_state *,
+					     const char *path,
+					     int safe_path);
 void untracked_cache_remove_from_index(struct index_state *, const char *);
 void untracked_cache_add_to_index(struct index_state *, const char *);
 
@@ -642,4 +668,5 @@
 
 	return path_match_flags(path, what | PATH_MATCH_NATIVE);
 }
+
 #endif
diff --git a/editor.c b/editor.c
index 008c04f..b67b802 100644
--- a/editor.c
+++ b/editor.c
@@ -1,5 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "pager.h"
+#include "path.h"
 #include "strbuf.h"
 #include "strvec.h"
 #include "run-command.h"
@@ -126,3 +133,31 @@
 {
 	return launch_specified_editor(git_sequence_editor(), path, buffer, env);
 }
+
+int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
+			      const char *const *env)
+{
+	char *path2 = NULL;
+	int fd, res = 0;
+
+	if (!is_absolute_path(path))
+		path = path2 = xstrdup(git_path("%s", path));
+
+	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+	if (fd < 0)
+		res = error_errno(_("could not open '%s' for writing"), path);
+	else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
+		res = error_errno(_("could not write to '%s'"), path);
+		close(fd);
+	} else if (close(fd) < 0)
+		res = error_errno(_("could not close '%s'"), path);
+	else {
+		strbuf_reset(buffer);
+		if (launch_editor(path, buffer, env) < 0)
+			res = error_errno(_("could not edit '%s'"), path);
+		unlink(path);
+	}
+
+	free(path2);
+	return res;
+}
diff --git a/editor.h b/editor.h
new file mode 100644
index 0000000..8016bb5
--- /dev/null
+++ b/editor.h
@@ -0,0 +1,34 @@
+#ifndef EDITOR_H
+#define EDITOR_H
+
+struct strbuf;
+
+const char *git_editor(void);
+const char *git_sequence_editor(void);
+int is_terminal_dumb(void);
+
+/**
+ * Launch the user preferred editor to edit a file and fill the buffer
+ * with the file's contents upon the user completing their editing. The
+ * third argument can be used to set the environment which the editor is
+ * run in. If the buffer is NULL the editor is launched as usual but the
+ * file's contents are not read into the buffer upon completion.
+ */
+int launch_editor(const char *path, struct strbuf *buffer,
+		  const char *const *env);
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer,
+			   const char *const *env);
+
+/*
+ * In contrast to `launch_editor()`, this function writes out the contents
+ * of the specified file first, then clears the `buffer`, then launches
+ * the editor and reads back in the file contents into the `buffer`.
+ * Finally, it deletes the temporary file.
+ *
+ * If `path` is relative, it refers to a file in the `.git` directory.
+ */
+int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
+			      const char *const *env);
+
+#endif
diff --git a/entry.c b/entry.c
index 971ab26..f918a3a 100644
--- a/entry.c
+++ b/entry.c
@@ -1,9 +1,14 @@
-#include "cache.h"
-#include "blob.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "name-hash.h"
+#include "sparse-index.h"
 #include "streaming.h"
 #include "submodule.h"
+#include "symlinks.h"
 #include "progress.h"
 #include "fsmonitor.h"
 #include "entry.h"
@@ -86,7 +91,8 @@
 {
 	enum object_type type;
 	unsigned long ul;
-	void *blob_data = read_object_file(&ce->oid, &type, &ul);
+	void *blob_data = repo_read_object_file(the_repository, &ce->oid,
+						&type, &ul);
 
 	*size = ul;
 	if (blob_data) {
@@ -574,3 +580,8 @@
 		return;
 	schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+	return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}
diff --git a/entry.h b/entry.h
index 2d4fbb8..ca3ed35 100644
--- a/entry.h
+++ b/entry.h
@@ -1,9 +1,11 @@
 #ifndef ENTRY_H
 #define ENTRY_H
 
-#include "cache.h"
 #include "convert.h"
 
+struct cache_entry;
+struct index_state;
+
 struct checkout {
 	struct index_state *istate;
 	const char *base_dir;
@@ -60,4 +62,10 @@
 void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
 			   struct stat *st);
 
+/*
+ * Calls the correct function out of {unlink,rmdir}_or_warn based on
+ * the supplied file mode.
+ */
+int remove_or_warn(unsigned int mode, const char *path);
+
 #endif /* ENTRY_H */
diff --git a/environment.c b/environment.c
index 1ee3686..a73ba9c 100644
--- a/environment.c
+++ b/environment.c
@@ -7,19 +7,28 @@
  * even if you might want to know where the git directory etc
  * are.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "branch.h"
+#include "convert.h"
 #include "environment.h"
+#include "gettext.h"
 #include "repository.h"
 #include "config.h"
 #include "refs.h"
 #include "fmt-merge-msg.h"
 #include "commit.h"
 #include "strvec.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "path.h"
+#include "replace-object.h"
 #include "tmp-objdir.h"
 #include "chdir-notify.h"
+#include "setup.h"
 #include "shallow.h"
+#include "trace.h"
+#include "write-or-die.h"
 
 int trust_executable_bit = 1;
 int trust_ctime = 1;
@@ -33,7 +42,6 @@
 int warn_ambiguous_refs = 1;
 int warn_on_object_refname_ambiguity = 1;
 int repository_format_precious_objects;
-int repository_format_worktree_config;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
 char *apply_default_whitespace;
@@ -50,16 +58,13 @@
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 96 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
-int pager_use_color = 1;
 const char *editor_program;
 const char *askpass_program;
 const char *excludes_file;
 enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
-int read_replace_refs = 1;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
-char *check_roundtrip_encoding = "SHIFT-JIS";
-unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
+const char *check_roundtrip_encoding = "SHIFT-JIS";
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
@@ -68,7 +73,7 @@
 #endif
 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
 char *notes_ref_name;
-int grafts_replace_parents = 1;
+int grafts_keep_true_parents;
 int core_apply_sparse_checkout;
 int core_sparse_checkout_cone;
 int sparse_expect_files_outside_of_patterns;
@@ -76,6 +81,20 @@
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
 unsigned long pack_size_limit_cfg;
 enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
+int max_allowed_tree_depth =
+#ifdef _MSC_VER
+	/*
+	 * When traversing into too-deep trees, Visual C-compiled Git seems to
+	 * run into some internal stack overflow detection in the
+	 * `RtlpAllocateHeap()` function that is called from within
+	 * `git_inflate_init()`'s call tree. The following value seems to be
+	 * low enough to avoid that by letting Git exit with an error before
+	 * the stack overflow can occur.
+	 */
+	512;
+#else
+	2048;
+#endif
 
 #ifndef PROTECT_HFS_DEFAULT
 #define PROTECT_HFS_DEFAULT 0
@@ -91,7 +110,7 @@
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
  */
-char comment_line_char = '#';
+const char *comment_line_str = "#";
 int auto_comment_line_char;
 
 /* Parallel index stat data preload? */
@@ -103,7 +122,7 @@
 static char *git_namespace;
 
 /*
- * Repository-local GIT_* environment variables; see cache.h for details.
+ * Repository-local GIT_* environment variables; see environment.h for details.
  */
 const char * const local_repo_env[] = {
 	ALTERNATE_DB_ENVIRONMENT,
@@ -177,7 +196,7 @@
 	strvec_clear(&to_free);
 
 	if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
-		read_replace_refs = 0;
+		disable_replace_refs();
 	replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
 	git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
 							  : "refs/replace/");
@@ -188,6 +207,9 @@
 	shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
 	if (shallow_file)
 		set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+	if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+		fetch_if_missing = 0;
 }
 
 int is_bare_repository(void)
diff --git a/environment.h b/environment.h
index d438b5c..05fd94d 100644
--- a/environment.h
+++ b/environment.h
@@ -1,7 +1,15 @@
 #ifndef ENVIRONMENT_H
 #define ENVIRONMENT_H
 
-#include "strvec.h"
+struct repository;
+struct strvec;
+
+/*
+ * The character that begins a commented line in user-editable file
+ * that is subject to stripspace.
+ */
+extern const char *comment_line_str;
+extern int auto_comment_line_char;
 
 /*
  * Wrapper of getenv() that returns a strdup value. This value is kept
@@ -9,4 +17,217 @@
  */
 const char *getenv_safe(struct strvec *argv, const char *name);
 
+/* Double-check local_repo_env below if you add to this list. */
+#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
+#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
+#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
+#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
+#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
+#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
+#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
+#define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
+#define GIT_SHALLOW_FILE_ENVIRONMENT "GIT_SHALLOW_FILE"
+#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
+#define CONFIG_ENVIRONMENT "GIT_CONFIG"
+#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
+#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
+#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
+#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
+#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
+#define GITATTRIBUTES_FILE ".gitattributes"
+#define INFOATTRIBUTES_FILE "info/attributes"
+#define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define GITMODULES_FILE ".gitmodules"
+#define GITMODULES_INDEX ":.gitmodules"
+#define GITMODULES_HEAD "HEAD:.gitmodules"
+#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
+#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
+#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
+#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
+#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
+#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
+#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
+#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
+#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
+#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
+
+/*
+ * Environment variable used in handshaking the wire protocol.
+ * Contains a colon ':' separated list of keys with optional values
+ * 'key[=value]'.  Presence of unknown keys and values must be
+ * ignored.
+ */
+#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
+/* HTTP header used to handshake the wire protocol */
+#define GIT_PROTOCOL_HEADER "Git-Protocol"
+
+/*
+ * This environment variable is expected to contain a boolean indicating
+ * whether we should or should not treat:
+ *
+ *   GIT_DIR=foo.git git ...
+ *
+ * as if GIT_WORK_TREE=. was given. It's not expected that users will make use
+ * of this, but we use it internally to communicate to sub-processes that we
+ * are in a bare repo. If not set, defaults to true.
+ */
+#define GIT_IMPLICIT_WORK_TREE_ENVIRONMENT "GIT_IMPLICIT_WORK_TREE"
+
+/*
+ * Repository-local GIT_* environment variables; these will be cleared
+ * when git spawns a sub-process that runs inside another repository.
+ * The array is NULL-terminated, which makes it easy to pass in the "env"
+ * parameter of a run-command invocation, or to do a simple walk.
+ */
+extern const char * const local_repo_env[];
+
+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).
+ */
+int have_git_dir(void);
+
+extern int is_bare_repository_cfg;
+int is_bare_repository(void);
+extern char *git_work_tree_cfg;
+const char *get_git_dir(void);
+const char *get_git_common_dir(void);
+const char *get_object_directory(void);
+char *get_index_file(void);
+char *get_graft_file(struct repository *r);
+void set_git_dir(const char *path, int make_realpath);
+const char *get_git_namespace(void);
+const char *strip_namespace(const char *namespaced_ref);
+const char *get_git_work_tree(void);
+void set_git_work_tree(const char *tree);
+
+#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
+
+/* Environment bits from configuration mechanism */
+extern int trust_executable_bit;
+extern int trust_ctime;
+extern int check_stat;
+extern int has_symlinks;
+extern int minimum_abbrev, default_abbrev;
+extern int ignore_case;
+extern int assume_unchanged;
+extern int prefer_symlink_refs;
+extern int warn_ambiguous_refs;
+extern int warn_on_object_refname_ambiguity;
+extern char *apply_default_whitespace;
+extern char *apply_default_ignorewhitespace;
+extern const char *git_attributes_file;
+extern const char *git_hooks_path;
+extern int zlib_compression_level;
+extern int pack_compression_level;
+extern size_t packed_git_window_size;
+extern size_t packed_git_limit;
+extern size_t delta_base_cache_limit;
+extern unsigned long big_file_threshold;
+extern unsigned long pack_size_limit_cfg;
+extern int max_allowed_tree_depth;
+
+/*
+ * Accessors for the core.sharedrepository config which lazy-load the value
+ * from the config (if not already set). The "reset" function can be
+ * used to unset "set" or cached value, meaning that the value will be loaded
+ * fresh from the config file on the next call to get_shared_repository().
+ */
+void set_shared_repository(int value);
+int get_shared_repository(void);
+void reset_shared_repository(void);
+
+extern int core_preload_index;
+extern int precomposed_unicode;
+extern int protect_hfs;
+extern int protect_ntfs;
+
+extern int core_apply_sparse_checkout;
+extern int core_sparse_checkout_cone;
+extern int sparse_expect_files_outside_of_patterns;
+
+/*
+ * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
+ */
+int use_optional_locks(void);
+
+enum log_refs_config {
+	LOG_REFS_UNSET = -1,
+	LOG_REFS_NONE = 0,
+	LOG_REFS_NORMAL,
+	LOG_REFS_ALWAYS
+};
+extern enum log_refs_config log_all_ref_updates;
+
+enum rebase_setup_type {
+	AUTOREBASE_NEVER = 0,
+	AUTOREBASE_LOCAL,
+	AUTOREBASE_REMOTE,
+	AUTOREBASE_ALWAYS
+};
+
+enum push_default_type {
+	PUSH_DEFAULT_NOTHING = 0,
+	PUSH_DEFAULT_MATCHING,
+	PUSH_DEFAULT_SIMPLE,
+	PUSH_DEFAULT_UPSTREAM,
+	PUSH_DEFAULT_CURRENT,
+	PUSH_DEFAULT_UNSPECIFIED
+};
+
+extern enum rebase_setup_type autorebase;
+extern enum push_default_type push_default;
+
+enum object_creation_mode {
+	OBJECT_CREATION_USES_HARDLINKS = 0,
+	OBJECT_CREATION_USES_RENAMES = 1
+};
+
+extern enum object_creation_mode object_creation_mode;
+
+extern char *notes_ref_name;
+
+extern int grafts_keep_true_parents;
+
+extern int repository_format_precious_objects;
+
+/*
+ * Create a temporary file rooted in the object database directory, or
+ * die on failure. The filename is taken from "pattern", which should have the
+ * usual "XXXXXX" trailer, and the resulting filename is written into the
+ * "template" buffer. Returns the open descriptor.
+ */
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+int odb_pack_keep(const char *name);
+
+const char *get_log_output_encoding(void);
+const char *get_commit_output_encoding(void);
+
+extern const char *git_commit_encoding;
+extern const char *git_log_output_encoding;
+
+extern const char *editor_program;
+extern const char *askpass_program;
+extern const char *excludes_file;
+
+/*
+ * Should we print an ellipsis after an abbreviated SHA-1 value
+ * when doing diff-raw output or indicating a detached HEAD?
+ */
+int print_sha1_ellipsis(void);
+
 #endif
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
index ac61864..ac7e0af 100644
--- a/ewah/bitmap.c
+++ b/ewah/bitmap.c
@@ -16,7 +16,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "ewok.h"
 
 #define EWAH_MASK(x) ((eword_t)1 << (x % BITS_IN_EWORD))
@@ -169,6 +169,15 @@
 	return count;
 }
 
+int bitmap_is_empty(struct bitmap *self)
+{
+	size_t i;
+	for (i = 0; i < self->word_alloc; i++)
+		if (self->words[i])
+			return 0;
+	return 1;
+}
+
 int bitmap_equals(struct bitmap *self, struct bitmap *other)
 {
 	struct bitmap *big, *small;
diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c
index 6fe48d3..8785cbc 100644
--- a/ewah/ewah_bitmap.c
+++ b/ewah/ewah_bitmap.c
@@ -19,7 +19,6 @@
 #include "git-compat-util.h"
 #include "ewok.h"
 #include "ewok_rlw.h"
-#include "cache.h"
 
 static inline size_t min_size(size_t a, size_t b)
 {
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 7eb8b9b..c11d76c 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -189,5 +189,6 @@
 void bitmap_or(struct bitmap *self, const struct bitmap *other);
 
 size_t bitmap_popcount(struct bitmap *self);
+int bitmap_is_empty(struct bitmap *self);
 
 #endif
diff --git a/exec-cmd.c b/exec-cmd.c
index 0232bbc..909777f 100644
--- a/exec-cmd.c
+++ b/exec-cmd.c
@@ -1,7 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
 #include "exec-cmd.h"
-#include "quote.h"
+#include "gettext.h"
+#include "path.h"
+#include "run-command.h"
 #include "strvec.h"
+#include "trace.h"
+#include "trace2.h"
 
 #if defined(RUNTIME_PREFIX)
 
diff --git a/fetch-pack.c b/fetch-pack.c
index 04016d1..091f9a8 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1,24 +1,28 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "repository.h"
 #include "config.h"
+#include "date.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "commit.h"
 #include "tag.h"
-#include "exec-cmd.h"
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
 #include "remote.h"
 #include "run-command.h"
 #include "connect.h"
-#include "transport.h"
+#include "trace2.h"
 #include "version.h"
 #include "oid-array.h"
 #include "oidset.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "connected.h"
 #include "fetch-negotiator.h"
 #include "fsck.h"
@@ -722,7 +726,7 @@
 	*refs = newlist;
 }
 
-static void mark_alternate_complete(struct fetch_negotiator *unused,
+static void mark_alternate_complete(struct fetch_negotiator *negotiator UNUSED,
 				    struct object *obj)
 {
 	mark_complete(&obj->oid);
@@ -762,9 +766,9 @@
 		if (!commit) {
 			struct object *o;
 
-			if (!has_object_file_with_flags(&ref->old_oid,
-						OBJECT_INFO_QUICK |
-						OBJECT_INFO_SKIP_FETCH_OBJECT))
+			if (!repo_has_object_file_with_flags(the_repository, &ref->old_oid,
+							     OBJECT_INFO_QUICK |
+							     OBJECT_INFO_SKIP_FETCH_OBJECT))
 				continue;
 			o = parse_object(the_repository, &ref->old_oid);
 			if (!o || o->type != OBJ_COMMIT)
@@ -1094,7 +1098,7 @@
 	struct ref *ref = copy_ref_list(orig_ref);
 	struct object_id oid;
 	const char *agent_feature;
-	int agent_len;
+	size_t agent_len;
 	struct fetch_negotiator negotiator_alloc;
 	struct fetch_negotiator *negotiator;
 
@@ -1112,7 +1116,7 @@
 		agent_supported = 1;
 		if (agent_len)
 			print_verbose(args, _("Server version is %.*s"),
-				      agent_len, agent_feature);
+				      (int)agent_len, agent_feature);
 	}
 
 	if (!server_supports("session-id"))
@@ -1853,8 +1857,11 @@
 	return ref;
 }
 
-static int fetch_pack_config_cb(const char *var, const char *value, void *cb)
+static int fetch_pack_config_cb(const char *var, const char *value,
+				const struct config_context *ctx, void *cb)
 {
+	const char *msg_id;
+
 	if (strcmp(var, "fetch.fsck.skiplist") == 0) {
 		const char *path;
 
@@ -1866,16 +1873,18 @@
 		return 0;
 	}
 
-	if (skip_prefix(var, "fetch.fsck.", &var)) {
-		if (is_valid_msg_type(var, value))
+	if (skip_prefix(var, "fetch.fsck.", &msg_id)) {
+		if (!value)
+			return config_error_nonbool(var);
+		if (is_valid_msg_type(msg_id, value))
 			strbuf_addf(&fsck_msg_types, "%c%s=%s",
-				fsck_msg_types.len ? ',' : '=', var, value);
+				fsck_msg_types.len ? ',' : '=', msg_id, value);
 		else
-			warning("Skipping unknown msg id '%s'", var);
+			warning("Skipping unknown msg id '%s'", msg_id);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 static void fetch_pack_config(void)
@@ -1904,10 +1913,10 @@
 	if (did_setup)
 		return;
 	fetch_pack_config();
-	if (0 <= transfer_unpack_limit)
-		unpack_limit = transfer_unpack_limit;
-	else if (0 <= fetch_unpack_limit)
+	if (0 <= fetch_unpack_limit)
 		unpack_limit = fetch_unpack_limit;
+	else if (0 <= transfer_unpack_limit)
+		unpack_limit = transfer_unpack_limit;
 	did_setup = 1;
 }
 
@@ -1963,7 +1972,7 @@
 		struct oid_array extra = OID_ARRAY_INIT;
 		struct object_id *oid = si->shallow->oid;
 		for (i = 0; i < si->shallow->nr; i++)
-			if (has_object_file(&oid[i]))
+			if (repo_has_object_file(the_repository, &oid[i]))
 				oid_array_append(&extra, &oid[i]);
 		if (extra.nr) {
 			setup_alternate_shallow(&shallow_lock,
@@ -2207,7 +2216,7 @@
 					   the_repository, "%d",
 					   negotiation_round);
 	}
-	trace2_region_enter("fetch-pack", "negotiate_using_fetch", the_repository);
+	trace2_region_leave("fetch-pack", "negotiate_using_fetch", the_repository);
 	trace2_data_intmax("negotiate_using_fetch", the_repository,
 			   "total_rounds", negotiation_round);
 	clear_common_flag(acked_commits);
diff --git a/fetch-pack.h b/fetch-pack.h
index 8c7752f..6775d26 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -2,7 +2,6 @@
 #define FETCH_PACK_H
 
 #include "string-list.h"
-#include "run-command.h"
 #include "protocol.h"
 #include "list-objects-filter-options.h"
 #include "oidset.h"
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index f48f44f..ae201e2 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -1,8 +1,12 @@
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "diff.h"
 #include "diff-merges.h"
+#include "hex.h"
 #include "revision.h"
 #include "tag.h"
 #include "string-list.h"
@@ -10,18 +14,18 @@
 #include "fmt-merge-msg.h"
 #include "commit-reach.h"
 #include "gpg-interface.h"
+#include "wildmatch.h"
 
 static int use_branch_desc;
 static int suppress_dest_pattern_seen;
 static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
 
-int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+int fmt_merge_msg_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
-	int status = 0;
-
 	if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
 		int is_bool;
-		merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+		merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool);
 		if (!is_bool && merge_log_config < 0)
 			return error("%s: negative length %s", key, value);
 		if (is_bool && merge_log_config)
@@ -37,10 +41,7 @@
 			string_list_append(&suppress_dest_patterns, value);
 		suppress_dest_pattern_seen = 1;
 	} else {
-		status = git_gpg_config(key, value, NULL);
-		if (status)
-			return status;
-		return git_default_config(key, value, cb);
+		return git_default_config(key, value, ctx, cb);
 	}
 	return 0;
 }
@@ -271,9 +272,10 @@
 static void record_person(int which, struct string_list *people,
 			  struct commit *commit)
 {
-	const char *buffer = get_commit_buffer(commit, NULL);
+	const char *buffer = repo_get_commit_buffer(the_repository, commit,
+						    NULL);
 	record_person_from_buf(which, people, buffer);
-	unuse_commit_buffer(commit, buffer);
+	repo_unuse_commit_buffer(the_repository, commit, buffer);
 }
 
 static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
@@ -319,7 +321,7 @@
 	     skip_prefix(me, them->items->string, &me) &&
 	     starts_with(me, " <")))
 		return;
-	strbuf_addf(out, "\n%c %s ", comment_line_char, label);
+	strbuf_addf(out, "\n%s %s ", comment_line_str, label);
 	add_people_count(out, them);
 }
 
@@ -384,7 +386,8 @@
 		if (subjects.nr > limit)
 			continue;
 
-		format_commit_message(commit, "%s", &sb, &ctx);
+		repo_format_commit_message(the_repository, commit, "%s", &sb,
+					   &ctx);
 		strbuf_ltrim(&sb);
 
 		if (!sb.len)
@@ -506,7 +509,8 @@
 	strbuf_complete_line(tagbuf);
 	if (sig->len) {
 		strbuf_addch(tagbuf, '\n');
-		strbuf_add_commented_lines(tagbuf, sig->buf, sig->len);
+		strbuf_add_commented_lines(tagbuf, sig->buf, sig->len,
+					   comment_line_str);
 	}
 }
 
@@ -519,7 +523,8 @@
 		struct object_id *oid = origins.items[i].util;
 		enum object_type type;
 		unsigned long size;
-		char *buf = read_object_file(oid, &type, &size);
+		char *buf = repo_read_object_file(the_repository, oid, &type,
+						  &size);
 		char *origbuf = buf;
 		unsigned long len = size;
 		struct signature_check sigc = { NULL };
@@ -551,7 +556,8 @@
 				strbuf_addch(&tagline, '\n');
 				strbuf_add_commented_lines(&tagline,
 						origins.items[first_tag].string,
-						strlen(origins.items[first_tag].string));
+						strlen(origins.items[first_tag].string),
+						comment_line_str);
 				strbuf_insert(&tagbuf, 0, tagline.buf,
 					      tagline.len);
 				strbuf_release(&tagline);
@@ -559,7 +565,8 @@
 			strbuf_addch(&tagbuf, '\n');
 			strbuf_add_commented_lines(&tagbuf,
 					origins.items[i].string,
-					strlen(origins.items[i].string));
+					strlen(origins.items[i].string),
+					comment_line_str);
 			fmt_tag_signature(&tagbuf, &sig, buf, len);
 		}
 		strbuf_release(&payload);
@@ -605,7 +612,9 @@
 		 * util field yet.
 		 */
 		obj = parse_object(the_repository, &oid);
-		parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
+		parent = (struct commit *)repo_peel_to_type(the_repository,
+							    NULL, 0, obj,
+							    OBJ_COMMIT);
 		if (!parent)
 			continue;
 		commit_list_insert(parent, &parents);
diff --git a/fmt-merge-msg.h b/fmt-merge-msg.h
index 9905404..73ca3e4 100644
--- a/fmt-merge-msg.h
+++ b/fmt-merge-msg.h
@@ -13,7 +13,8 @@
 };
 
 extern int merge_log_config;
-int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+int fmt_merge_msg_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb);
 int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 		  struct fmt_merge_msg_opts *);
 
diff --git a/fsck.c b/fsck.c
index 2b18717..78af29d 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,5 +1,9 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "date.h"
+#include "dir.h"
+#include "hex.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
 #include "object.h"
 #include "attr.h"
@@ -12,14 +16,14 @@
 #include "refs.h"
 #include "url.h"
 #include "utf8.h"
-#include "decorate.h"
 #include "oidset.h"
 #include "packfile.h"
 #include "submodule-config.h"
 #include "config.h"
-#include "credential.h"
 #include "help.h"
 
+static ssize_t max_tree_entry_len = 4096;
+
 #define STR(x) #x
 #define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
 static struct {
@@ -150,15 +154,29 @@
 		       const char *msg_id_str, const char *msg_type_str)
 {
 	int msg_id = parse_msg_id(msg_id_str);
-	enum fsck_msg_type msg_type = parse_msg_type(msg_type_str);
+	char *to_free = NULL;
+	enum fsck_msg_type msg_type;
 
 	if (msg_id < 0)
 		die("Unhandled message id: %s", msg_id_str);
 
+	if (msg_id == FSCK_MSG_LARGE_PATHNAME) {
+		const char *colon = strchr(msg_type_str, ':');
+		if (colon) {
+			msg_type_str = to_free =
+				xmemdupz(msg_type_str, colon - msg_type_str);
+			colon++;
+			if (!git_parse_ssize_t(colon, &max_tree_entry_len))
+				die("unable to parse max tree entry len: %s", colon);
+		}
+	}
+	msg_type = parse_msg_type(msg_type_str);
+
 	if (msg_type != FSCK_ERROR && msg_id_info[msg_id].msg_type == FSCK_FATAL)
 		die("Cannot demote %s to %s", msg_id_str, msg_type_str);
 
 	fsck_set_msg_type_from_ids(options, msg_id, msg_type);
+	free(to_free);
 }
 
 void fsck_set_msg_types(struct fsck_options *options, const char *values)
@@ -309,7 +327,8 @@
 		return -1;
 
 	name = fsck_get_object_name(options, &tree->object.oid);
-	if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+	if (init_tree_desc_gently(&desc, &tree->object.oid,
+				  tree->buffer, tree->size, 0))
 		return -1;
 	while (tree_entry_gently(&desc, &entry)) {
 		struct object *obj;
@@ -353,7 +372,7 @@
 	int result;
 	const char *name;
 
-	if (parse_commit(commit))
+	if (repo_parse_commit(the_repository, commit))
 		return -1;
 
 	name = fsck_get_object_name(options, &commit->object.oid);
@@ -361,7 +380,7 @@
 		fsck_put_object_name(options, get_commit_tree_oid(commit),
 				     "%s:", name);
 
-	result = options->walk((struct object *)get_commit_tree(commit),
+	result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),
 			       OBJ_TREE, data, options);
 	if (result < 0)
 		return result;
@@ -574,12 +593,14 @@
 	int has_bad_modes = 0;
 	int has_dup_entries = 0;
 	int not_properly_sorted = 0;
+	int has_large_name = 0;
 	struct tree_desc desc;
 	unsigned o_mode;
 	const char *o_name;
 	struct name_stack df_dup_candidates = { NULL };
 
-	if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
+	if (init_tree_desc_gently(&desc, tree_oid, buffer, size,
+				  TREE_DESC_RAW_MODES)) {
 		retval += report(options, tree_oid, OBJ_TREE,
 				 FSCK_MSG_BAD_TREE,
 				 "cannot be parsed as a tree");
@@ -603,6 +624,7 @@
 		has_dotdot |= !strcmp(name, "..");
 		has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name);
 		has_zero_pad |= *(char *)desc.buffer == '0';
+		has_large_name |= tree_entry_len(&desc.entry) > max_tree_entry_len;
 
 		if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
 			if (!S_ISLNK(mode))
@@ -745,6 +767,10 @@
 		retval += report(options, tree_oid, OBJ_TREE,
 				 FSCK_MSG_TREE_NOT_SORTED,
 				 "not properly sorted");
+	if (has_large_name)
+		retval += report(options, tree_oid, OBJ_TREE,
+				 FSCK_MSG_LARGE_PATHNAME,
+				 "contains excessively large pathname");
 	return retval;
 }
 
@@ -1022,145 +1048,15 @@
 	return ret;
 }
 
-static int starts_with_dot_slash(const char *const path)
-{
-	return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
-				PATH_MATCH_XPLATFORM);
-}
-
-static int starts_with_dot_dot_slash(const char *const path)
-{
-	return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
-				PATH_MATCH_XPLATFORM);
-}
-
-static int submodule_url_is_relative(const char *url)
-{
-	return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
-}
-
-/*
- * Count directory components that a relative submodule URL should chop
- * from the remote_url it is to be resolved against.
- *
- * In other words, this counts "../" components at the start of a
- * submodule URL.
- *
- * Returns the number of directory components to chop and writes a
- * pointer to the next character of url after all leading "./" and
- * "../" components to out.
- */
-static int count_leading_dotdots(const char *url, const char **out)
-{
-	int result = 0;
-	while (1) {
-		if (starts_with_dot_dot_slash(url)) {
-			result++;
-			url += strlen("../");
-			continue;
-		}
-		if (starts_with_dot_slash(url)) {
-			url += strlen("./");
-			continue;
-		}
-		*out = url;
-		return result;
-	}
-}
-/*
- * Check whether a transport is implemented by git-remote-curl.
- *
- * If it is, returns 1 and writes the URL that would be passed to
- * git-remote-curl to the "out" parameter.
- *
- * Otherwise, returns 0 and leaves "out" untouched.
- *
- * Examples:
- *   http::https://example.com/repo.git -> 1, https://example.com/repo.git
- *   https://example.com/repo.git -> 1, https://example.com/repo.git
- *   git://example.com/repo.git -> 0
- *
- * This is for use in checking for previously exploitable bugs that
- * required a submodule URL to be passed to git-remote-curl.
- */
-static int url_to_curl_url(const char *url, const char **out)
-{
-	/*
-	 * We don't need to check for case-aliases, "http.exe", and so
-	 * on because in the default configuration, is_transport_allowed
-	 * prevents URLs with those schemes from being cloned
-	 * automatically.
-	 */
-	if (skip_prefix(url, "http::", out) ||
-	    skip_prefix(url, "https::", out) ||
-	    skip_prefix(url, "ftp::", out) ||
-	    skip_prefix(url, "ftps::", out))
-		return 1;
-	if (starts_with(url, "http://") ||
-	    starts_with(url, "https://") ||
-	    starts_with(url, "ftp://") ||
-	    starts_with(url, "ftps://")) {
-		*out = url;
-		return 1;
-	}
-	return 0;
-}
-
-static int check_submodule_url(const char *url)
-{
-	const char *curl_url;
-
-	if (looks_like_command_line_option(url))
-		return -1;
-
-	if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
-		char *decoded;
-		const char *next;
-		int has_nl;
-
-		/*
-		 * This could be appended to an http URL and url-decoded;
-		 * check for malicious characters.
-		 */
-		decoded = url_decode(url);
-		has_nl = !!strchr(decoded, '\n');
-
-		free(decoded);
-		if (has_nl)
-			return -1;
-
-		/*
-		 * URLs which escape their root via "../" can overwrite
-		 * the host field and previous components, resolving to
-		 * URLs like https::example.com/submodule.git and
-		 * https:///example.com/submodule.git that were
-		 * susceptible to CVE-2020-11008.
-		 */
-		if (count_leading_dotdots(url, &next) > 0 &&
-		    (*next == ':' || *next == '/'))
-			return -1;
-	}
-
-	else if (url_to_curl_url(url, &curl_url)) {
-		struct credential c = CREDENTIAL_INIT;
-		int ret = 0;
-		if (credential_from_url_gently(&c, curl_url, 1) ||
-		    !*c.host)
-			ret = -1;
-		credential_clear(&c);
-		return ret;
-	}
-
-	return 0;
-}
-
 struct fsck_gitmodules_data {
 	const struct object_id *oid;
 	struct fsck_options *options;
 	int ret;
 };
 
-static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+static int fsck_gitmodules_fn(const char *var, const char *value,
+			      const struct config_context *ctx UNUSED,
+			      void *vdata)
 {
 	struct fsck_gitmodules_data *data = vdata;
 	const char *subsection, *key;
@@ -1233,7 +1129,8 @@
 		data.ret = 0;
 		config_opts.error_action = CONFIG_ERROR_SILENT;
 		if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
-					".gitmodules", buf, size, &data, &config_opts))
+					".gitmodules", buf, size, &data,
+					CONFIG_SCOPE_UNKNOWN, &config_opts))
 			data.ret |= report(options, oid, OBJ_BLOB,
 					FSCK_MSG_GITMODULES_PARSE,
 					"could not parse gitmodules blob");
@@ -1302,9 +1199,9 @@
 
 int fsck_error_function(struct fsck_options *o,
 			const struct object_id *oid,
-			enum object_type object_type,
+			enum object_type object_type UNUSED,
 			enum fsck_msg_type msg_type,
-			enum fsck_msg_id msg_id,
+			enum fsck_msg_id msg_id UNUSED,
 			const char *message)
 {
 	if (msg_type == FSCK_WARN) {
@@ -1332,7 +1229,7 @@
 		if (oidset_contains(blobs_done, oid))
 			continue;
 
-		buf = read_object_file(oid, &type, &size);
+		buf = repo_read_object_file(the_repository, oid, &type, &size);
 		if (!buf) {
 			if (is_promisor_object(oid))
 				continue;
@@ -1370,9 +1267,12 @@
 	return ret;
 }
 
-int git_fsck_config(const char *var, const char *value, void *cb)
+int git_fsck_config(const char *var, const char *value,
+		    const struct config_context *ctx, void *cb)
 {
 	struct fsck_options *options = cb;
+	const char *msg_id;
+
 	if (strcmp(var, "fsck.skiplist") == 0) {
 		const char *path;
 		struct strbuf sb = STRBUF_INIT;
@@ -1386,12 +1286,14 @@
 		return 0;
 	}
 
-	if (skip_prefix(var, "fsck.", &var)) {
-		fsck_set_msg_type(options, var, value);
+	if (skip_prefix(var, "fsck.", &msg_id)) {
+		if (!value)
+			return config_error_nonbool(var);
+		fsck_set_msg_type(options, msg_id, value);
 		return 0;
 	}
 
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
 
 /*
diff --git a/fsck.h b/fsck.h
index 6683308..e3adf9d 100644
--- a/fsck.h
+++ b/fsck.h
@@ -1,6 +1,7 @@
 #ifndef GIT_FSCK_H
 #define GIT_FSCK_H
 
+#include "object.h"
 #include "oidset.h"
 
 enum fsck_msg_type {
@@ -72,6 +73,7 @@
 	FUNC(NULL_SHA1, WARN) \
 	FUNC(ZERO_PADDED_FILEMODE, WARN) \
 	FUNC(NUL_IN_COMMIT, WARN) \
+	FUNC(LARGE_PATHNAME, WARN) \
 	/* infos (reported as warnings, but ignored by default) */ \
 	FUNC(BAD_FILEMODE, INFO) \
 	FUNC(GITMODULES_PARSE, INFO) \
@@ -232,10 +234,12 @@
 const char *fsck_describe_object(struct fsck_options *options,
 				 const struct object_id *oid);
 
+struct key_value_info;
 /*
  * git_config() callback for use by fsck-y tools that want to support
  * fsck.<msg> fsck.skipList etc.
  */
-int git_fsck_config(const char *var, const char *value, void *cb);
+int git_fsck_config(const char *var, const char *value,
+		    const struct config_context *ctx, void *cb);
 
 #endif
diff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h
index e24838f..5cbbec8 100644
--- a/fsmonitor--daemon.h
+++ b/fsmonitor--daemon.h
@@ -3,10 +3,7 @@
 
 #ifdef HAVE_FSMONITOR_DAEMON_BACKEND
 
-#include "cache.h"
-#include "dir.h"
-#include "run-command.h"
-#include "simple-ipc.h"
+#include "hashmap.h"
 #include "thread-utils.h"
 #include "fsmonitor-path-utils.h"
 
@@ -100,7 +97,7 @@
  * to only mean an external GITDIR referenced by a ".git" file.
  *
  * The platform FS event backends will receive watch-specific
- * relative paths (except for those OS's that always emit absolute
+ * relative paths (except for those OSes that always emit absolute
  * paths).  We use the following enum and routines to classify each
  * path so that we know how to handle it.  There is a slight asymmetry
  * here because ".git/" is inside the working directory and the
diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c
index 19d772f..45471b5 100644
--- a/fsmonitor-ipc.c
+++ b/fsmonitor-ipc.c
@@ -1,7 +1,8 @@
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "fsmonitor-ipc.h"
+#include "repository.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "trace2.h"
@@ -18,7 +19,7 @@
 	return 0;
 }
 
-const char *fsmonitor_ipc__get_path(struct repository *r)
+const char *fsmonitor_ipc__get_path(struct repository *r UNUSED)
 {
 	return NULL;
 }
@@ -28,14 +29,14 @@
 	return IPC_STATE__OTHER_ERROR;
 }
 
-int fsmonitor_ipc__send_query(const char *since_token,
-			      struct strbuf *answer)
+int fsmonitor_ipc__send_query(const char *since_token UNUSED,
+			      struct strbuf *answer UNUSED)
 {
 	return -1;
 }
 
-int fsmonitor_ipc__send_command(const char *command,
-				struct strbuf *answer)
+int fsmonitor_ipc__send_command(const char *command UNUSED,
+				struct strbuf *answer UNUSED)
 {
 	return -1;
 }
diff --git a/fsmonitor-ll.h b/fsmonitor-ll.h
new file mode 100644
index 0000000..0504ca0
--- /dev/null
+++ b/fsmonitor-ll.h
@@ -0,0 +1,52 @@
+#ifndef FSMONITOR_LL_H
+#define FSMONITOR_LL_H
+
+struct index_state;
+struct strbuf;
+
+extern struct trace_key trace_fsmonitor;
+
+/*
+ * Read the fsmonitor index extension and (if configured) restore the
+ * CE_FSMONITOR_VALID state.
+ */
+int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+
+/*
+ * Fill the fsmonitor_dirty ewah bits with their state from the index,
+ * before it is split during writing.
+ */
+void fill_fsmonitor_bitmap(struct index_state *istate);
+
+/*
+ * Write the CE_FSMONITOR_VALID state into the fsmonitor index
+ * extension.  Reads from the fsmonitor_dirty ewah in the index.
+ */
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension
+ */
+void add_fsmonitor(struct index_state *istate);
+void remove_fsmonitor(struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension as necessary based on the current
+ * core.fsmonitor setting.
+ */
+void tweak_fsmonitor(struct index_state *istate);
+
+/*
+ * Run the configured fsmonitor integration script and clear the
+ * CE_FSMONITOR_VALID bit for any files returned as dirty.  Also invalidate
+ * any corresponding untracked cache directory structures. Optimized to only
+ * run the first time it is called.
+ */
+void refresh_fsmonitor(struct index_state *istate);
+
+/*
+ * Does the received result contain the "trivial" response?
+ */
+int fsmonitor_is_trivial_response(const struct strbuf *query_result);
+
+#endif /* FSMONITOR_LL_H */
diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c
index 899bfe9..a6a9e6b 100644
--- a/fsmonitor-settings.c
+++ b/fsmonitor-settings.c
@@ -1,5 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
 #include "repository.h"
 #include "fsmonitor-ipc.h"
 #include "fsmonitor-settings.h"
@@ -61,7 +62,8 @@
 }
 #endif
 
-static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)
+static enum fsmonitor_reason check_for_incompatible(struct repository *r,
+						    int ipc MAYBE_UNUSED)
 {
 	if (!r->worktree) {
 		/*
diff --git a/fsmonitor.c b/fsmonitor.c
index a5b9e75..2b17d60 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -1,11 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "dir.h"
+#include "environment.h"
 #include "ewah/ewok.h"
 #include "fsmonitor.h"
 #include "fsmonitor-ipc.h"
+#include "name-hash.h"
 #include "run-command.h"
 #include "strbuf.h"
+#include "trace2.h"
 
 #define INDEX_EXTENSION_VERSION1	(1)
 #define INDEX_EXTENSION_VERSION2	(2)
@@ -181,79 +184,282 @@
 	return result;
 }
 
+/*
+ * Invalidate the FSM bit on this CE.  This is like mark_fsmonitor_invalid()
+ * but we've already handled the untracked-cache, so let's not repeat that
+ * work.  This also lets us have a different trace message so that we can
+ * see everything that was done as part of the refresh-callback.
+ */
+static void invalidate_ce_fsm(struct cache_entry *ce)
+{
+	if (ce->ce_flags & CE_FSMONITOR_VALID) {
+		trace_printf_key(&trace_fsmonitor,
+				 "fsmonitor_refresh_callback INV: '%s'",
+				 ce->name);
+		ce->ce_flags &= ~CE_FSMONITOR_VALID;
+	}
+}
+
+static size_t handle_path_with_trailing_slash(
+	struct index_state *istate, const char *name, int pos);
+
+/*
+ * Use the name-hash to do a case-insensitive cache-entry lookup with
+ * the pathname and invalidate the cache-entry.
+ *
+ * Returns the number of cache-entries that we invalidated.
+ */
+static size_t handle_using_name_hash_icase(
+	struct index_state *istate, const char *name)
+{
+	struct cache_entry *ce = NULL;
+
+	ce = index_file_exists(istate, name, strlen(name), 1);
+	if (!ce)
+		return 0;
+
+	/*
+	 * A case-insensitive search in the name-hash using the
+	 * observed pathname found a cache-entry, so the observed path
+	 * is case-incorrect.  Invalidate the cache-entry and use the
+	 * correct spelling from the cache-entry to invalidate the
+	 * untracked-cache.  Since we now have sparse-directories in
+	 * the index, the observed pathname may represent a regular
+	 * file or a sparse-index directory.
+	 *
+	 * Note that we should not have seen FSEvents for a
+	 * sparse-index directory, but we handle it just in case.
+	 *
+	 * Either way, we know that there are not any cache-entries for
+	 * children inside the cone of the directory, so we don't need to
+	 * do the usual scan.
+	 */
+	trace_printf_key(&trace_fsmonitor,
+			 "fsmonitor_refresh_callback MAP: '%s' '%s'",
+			 name, ce->name);
+
+	/*
+	 * NEEDSWORK: We used the name-hash to find the correct
+	 * case-spelling of the pathname in the cache-entry[], so
+	 * technically this is a tracked file or a sparse-directory.
+	 * It should not have any entries in the untracked-cache, so
+	 * we should not need to use the case-corrected spelling to
+	 * invalidate the the untracked-cache.  So we may not need to
+	 * do this.  For now, I'm going to be conservative and always
+	 * do it; we can revisit this later.
+	 */
+	untracked_cache_invalidate_trimmed_path(istate, ce->name, 0);
+
+	invalidate_ce_fsm(ce);
+	return 1;
+}
+
+/*
+ * Use the dir-name-hash to find the correct-case spelling of the
+ * directory.  Use the canonical spelling to invalidate all of the
+ * cache-entries within the matching cone.
+ *
+ * Returns the number of cache-entries that we invalidated.
+ */
+static size_t handle_using_dir_name_hash_icase(
+	struct index_state *istate, const char *name)
+{
+	struct strbuf canonical_path = STRBUF_INIT;
+	int pos;
+	size_t len = strlen(name);
+	size_t nr_in_cone;
+
+	if (name[len - 1] == '/')
+		len--;
+
+	if (!index_dir_find(istate, name, len, &canonical_path))
+		return 0; /* name is untracked */
+
+	if (!memcmp(name, canonical_path.buf, canonical_path.len)) {
+		strbuf_release(&canonical_path);
+		/*
+		 * NEEDSWORK: Our caller already tried an exact match
+		 * and failed to find one.  They called us to do an
+		 * ICASE match, so we should never get an exact match,
+		 * so we could promote this to a BUG() here if we
+		 * wanted to.  It doesn't hurt anything to just return
+		 * 0 and go on because we should never get here.  Or we
+		 * could just get rid of the memcmp() and this "if"
+		 * clause completely.
+		 */
+		BUG("handle_using_dir_name_hash_icase(%s) did not exact match",
+		    name);
+	}
+
+	trace_printf_key(&trace_fsmonitor,
+			 "fsmonitor_refresh_callback MAP: '%s' '%s'",
+			 name, canonical_path.buf);
+
+	/*
+	 * The dir-name-hash only tells us the corrected spelling of
+	 * the prefix.  We have to use this canonical path to do a
+	 * lookup in the cache-entry array so that we repeat the
+	 * original search using the case-corrected spelling.
+	 */
+	strbuf_addch(&canonical_path, '/');
+	pos = index_name_pos(istate, canonical_path.buf,
+			     canonical_path.len);
+	nr_in_cone = handle_path_with_trailing_slash(
+		istate, canonical_path.buf, pos);
+	strbuf_release(&canonical_path);
+	return nr_in_cone;
+}
+
+/*
+ * The daemon sent an observed pathname without a trailing slash.
+ * (This is the normal case.)  We do not know if it is a tracked or
+ * untracked file, a sparse-directory, or a populated directory (on a
+ * platform such as Windows where FSEvents are not qualified).
+ *
+ * The pathname contains the observed case reported by the FS. We
+ * do not know it is case-correct or -incorrect.
+ *
+ * Assume it is case-correct and try an exact match.
+ *
+ * Return the number of cache-entries that we invalidated.
+ */
+static size_t handle_path_without_trailing_slash(
+	struct index_state *istate, const char *name, int pos)
+{
+	/*
+	 * Mark the untracked cache dirty for this path (regardless of
+	 * whether or not we find an exact match for it in the index).
+	 * Since the path is unqualified (no trailing slash hint in the
+	 * FSEvent), it may refer to a file or directory. So we should
+	 * not assume one or the other and should always let the untracked
+	 * cache decide what needs to invalidated.
+	 */
+	untracked_cache_invalidate_trimmed_path(istate, name, 0);
+
+	if (pos >= 0) {
+		/*
+		 * An exact match on a tracked file. We assume that we
+		 * do not need to scan forward for a sparse-directory
+		 * cache-entry with the same pathname, nor for a cone
+		 * at that directory. (That is, assume no D/F conflicts.)
+		 */
+		invalidate_ce_fsm(istate->cache[pos]);
+		return 1;
+	} else {
+		size_t nr_in_cone;
+		struct strbuf work_path = STRBUF_INIT;
+
+		/*
+		 * The negative "pos" gives us the suggested insertion
+		 * point for the pathname (without the trailing slash).
+		 * We need to see if there is a directory with that
+		 * prefix, but there can be lots of pathnames between
+		 * "foo" and "foo/" like "foo-" or "foo-bar", so we
+		 * don't want to do our own scan.
+		 */
+		strbuf_add(&work_path, name, strlen(name));
+		strbuf_addch(&work_path, '/');
+		pos = index_name_pos(istate, work_path.buf, work_path.len);
+		nr_in_cone = handle_path_with_trailing_slash(
+			istate, work_path.buf, pos);
+		strbuf_release(&work_path);
+		return nr_in_cone;
+	}
+}
+
+/*
+ * The daemon can decorate directory events, such as a move or rename,
+ * by adding a trailing slash to the observed name.  Use this to
+ * explicitly invalidate the entire cone under that directory.
+ *
+ * The daemon can only reliably do that if the OS FSEvent contains
+ * sufficient information in the event.
+ *
+ * macOS FSEvents have enough information.
+ *
+ * Other platforms may or may not be able to do it (and it might
+ * depend on the type of event (for example, a daemon could lstat() an
+ * observed pathname after a rename, but not after a delete)).
+ *
+ * If we find an exact match in the index for a path with a trailing
+ * slash, it means that we matched a sparse-index directory in a
+ * cone-mode sparse-checkout (since that's the only time we have
+ * directories in the index).  We should never see this in practice
+ * (because sparse directories should not be present and therefore
+ * not generating FS events).  Either way, we can treat them in the
+ * same way and just invalidate the cache-entry and the untracked
+ * cache (and in this case, the forward cache-entry scan won't find
+ * anything and it doesn't hurt to let it run).
+ *
+ * Return the number of cache-entries that we invalidated.  We will
+ * use this later to determine if we need to attempt a second
+ * case-insensitive search on case-insensitive file systems.  That is,
+ * if the search using the observed-case in the FSEvent yields any
+ * results, we assume the prefix is case-correct.  If there are no
+ * matches, we still don't know if the observed path is simply
+ * untracked or case-incorrect.
+ */
+static size_t handle_path_with_trailing_slash(
+	struct index_state *istate, const char *name, int pos)
+{
+	int i;
+	size_t nr_in_cone = 0;
+
+	/*
+	 * Mark the untracked cache dirty for this directory path
+	 * (regardless of whether or not we find an exact match for it
+	 * in the index or find it to be proper prefix of one or more
+	 * files in the index), since the FSEvent is hinting that
+	 * there may be changes on or within the directory.
+	 */
+	untracked_cache_invalidate_trimmed_path(istate, name, 0);
+
+	if (pos < 0)
+		pos = -pos - 1;
+
+	/* Mark all entries for the folder invalid */
+	for (i = pos; i < istate->cache_nr; i++) {
+		if (!starts_with(istate->cache[i]->name, name))
+			break;
+		invalidate_ce_fsm(istate->cache[i]);
+		nr_in_cone++;
+	}
+
+	return nr_in_cone;
+}
+
 static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
 {
-	int i, len = strlen(name);
+	int len = strlen(name);
 	int pos = index_name_pos(istate, name, len);
+	size_t nr_in_cone;
 
 	trace_printf_key(&trace_fsmonitor,
 			 "fsmonitor_refresh_callback '%s' (pos %d)",
 			 name, pos);
 
-	if (name[len - 1] == '/') {
-		/*
-		 * The daemon can decorate directory events, such as
-		 * moves or renames, with a trailing slash if the OS
-		 * FS Event contains sufficient information, such as
-		 * MacOS.
-		 *
-		 * Use this to invalidate the entire cone under that
-		 * directory.
-		 *
-		 * We do not expect an exact match because the index
-		 * does not normally contain directory entries, so we
-		 * start at the insertion point and scan.
-		 */
-		if (pos < 0)
-			pos = -pos - 1;
-
-		/* Mark all entries for the folder invalid */
-		for (i = pos; i < istate->cache_nr; i++) {
-			if (!starts_with(istate->cache[i]->name, name))
-				break;
-			istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
-		}
-
-		/*
-		 * We need to remove the traling "/" from the path
-		 * for the untracked cache.
-		 */
-		name[len - 1] = '\0';
-	} else if (pos >= 0) {
-		/*
-		 * We have an exact match for this path and can just
-		 * invalidate it.
-		 */
-		istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
-	} else {
-		/*
-		 * The path is not a tracked file -or- it is a
-		 * directory event on a platform that cannot
-		 * distinguish between file and directory events in
-		 * the event handler, such as Windows.
-		 *
-		 * Scan as if it is a directory and invalidate the
-		 * cone under it.  (But remember to ignore items
-		 * between "name" and "name/", such as "name-" and
-		 * "name.".
-		 */
-		pos = -pos - 1;
-
-		for (i = pos; i < istate->cache_nr; i++) {
-			if (!starts_with(istate->cache[i]->name, name))
-				break;
-			if ((unsigned char)istate->cache[i]->name[len] > '/')
-				break;
-			if (istate->cache[i]->name[len] == '/')
-				istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
-		}
-	}
+	if (name[len - 1] == '/')
+		nr_in_cone = handle_path_with_trailing_slash(istate, name, pos);
+	else
+		nr_in_cone = handle_path_without_trailing_slash(istate, name, pos);
 
 	/*
-	 * Mark the untracked cache dirty even if it wasn't found in the index
-	 * as it could be a new untracked file.
+	 * If we did not find an exact match for this pathname or any
+	 * cache-entries with this directory prefix and we're on a
+	 * case-insensitive file system, try again using the name-hash
+	 * and dir-name-hash.
 	 */
-	untracked_cache_invalidate_path(istate, name, 0);
+	if (!nr_in_cone && ignore_case) {
+		nr_in_cone = handle_using_name_hash_icase(istate, name);
+		if (!nr_in_cone)
+			nr_in_cone = handle_using_dir_name_hash_icase(
+				istate, name);
+	}
+
+	if (nr_in_cone)
+		trace_printf_key(&trace_fsmonitor,
+				 "fsmonitor_refresh_callback CNT: %d",
+				 (int)nr_in_cone);
 }
 
 /*
diff --git a/fsmonitor.h b/fsmonitor.h
index edf7ce5..5195a86 100644
--- a/fsmonitor.h
+++ b/fsmonitor.h
@@ -1,54 +1,12 @@
 #ifndef FSMONITOR_H
 #define FSMONITOR_H
 
-#include "cache.h"
+#include "fsmonitor-ll.h"
 #include "dir.h"
 #include "fsmonitor-settings.h"
-
-extern struct trace_key trace_fsmonitor;
-
-/*
- * Read the fsmonitor index extension and (if configured) restore the
- * CE_FSMONITOR_VALID state.
- */
-int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
-
-/*
- * Fill the fsmonitor_dirty ewah bits with their state from the index,
- * before it is split during writing.
- */
-void fill_fsmonitor_bitmap(struct index_state *istate);
-
-/*
- * Write the CE_FSMONITOR_VALID state into the fsmonitor index
- * extension.  Reads from the fsmonitor_dirty ewah in the index.
- */
-void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
-
-/*
- * Add/remove the fsmonitor index extension
- */
-void add_fsmonitor(struct index_state *istate);
-void remove_fsmonitor(struct index_state *istate);
-
-/*
- * Add/remove the fsmonitor index extension as necessary based on the current
- * core.fsmonitor setting.
- */
-void tweak_fsmonitor(struct index_state *istate);
-
-/*
- * Run the configured fsmonitor integration script and clear the
- * CE_FSMONITOR_VALID bit for any files returned as dirty.  Also invalidate
- * any corresponding untracked cache directory structures. Optimized to only
- * run the first time it is called.
- */
-void refresh_fsmonitor(struct index_state *istate);
-
-/*
- * Does the received result contain the "trivial" response?
- */
-int fsmonitor_is_trivial_response(const struct strbuf *query_result);
+#include "object.h"
+#include "read-cache-ll.h"
+#include "trace.h"
 
 /*
  * Check if refresh_fsmonitor has been called at least once.
@@ -86,7 +44,7 @@
 	    !(ce->ce_flags & CE_FSMONITOR_VALID)) {
 		if (S_ISGITLINK(ce->ce_mode))
 			return;
-		istate->cache_changed = 1;
+		istate->cache_changed |= FSMONITOR_CHANGED;
 		ce->ce_flags |= CE_FSMONITOR_VALID;
 		trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
 	}
diff --git a/gettext.c b/gettext.c
index e81357f..57facbc 100644
--- a/gettext.c
+++ b/gettext.c
@@ -2,12 +2,12 @@
  * Copyright (c) 2010 Ævar Arnfjörð Bjarmason
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
 #include "exec-cmd.h"
 #include "gettext.h"
-#include "strbuf.h"
 #include "utf8.h"
-#include "config.h"
 
 #ifndef NO_GETTEXT
 #	include <libintl.h>
diff --git a/git-archimport.perl b/git-archimport.perl
index b7c173c..f5a317b 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -54,7 +54,7 @@
 
 =cut
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Std;
diff --git a/git-compat-util.h b/git-compat-util.h
index 4f0028c..044f874 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -218,6 +218,18 @@
 #define GIT_WINDOWS_NATIVE
 #endif
 
+#if defined(NO_UNIX_SOCKETS) || !defined(GIT_WINDOWS_NATIVE)
+static inline int _have_unix_sockets(void)
+{
+#if defined(NO_UNIX_SOCKETS)
+	return 0;
+#else
+	return 1;
+#endif
+}
+#define have_unix_sockets _have_unix_sockets
+#endif
+
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -225,6 +237,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <string.h>
 #ifdef HAVE_STRINGS_H
 #include <strings.h> /* for strcasecmp() */
@@ -339,6 +352,25 @@
 int compat_mkdir_wo_trailing_slash(const char*, mode_t);
 #endif
 
+#ifdef time
+#undef time
+#endif
+static inline time_t git_time(time_t *tloc)
+{
+	struct timeval tv;
+
+	/*
+	 * Avoid time(NULL), which can disagree with gettimeofday(2)
+	 * and filesystem timestamps.
+	 */
+	gettimeofday(&tv, NULL);
+
+	if (tloc)
+		*tloc = tv.tv_sec;
+	return tv.tv_sec;
+}
+#define time git_time
+
 #ifdef NO_STRUCT_ITIMERVAL
 struct itimerval {
 	struct timeval it_interval;
@@ -403,6 +435,10 @@
 #define PATH_MAX 4096
 #endif
 
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
 typedef uintmax_t timestamp_t;
 #define PRItime PRIuMAX
 #define parse_timestamp strtoumax
@@ -421,8 +457,10 @@
 #endif
 
 #ifndef platform_core_config
+struct config_context;
 static inline int noop_core_config(const char *var UNUSED,
 				   const char *value UNUSED,
+				   const struct config_context *ctx UNUSED,
 				   void *cb UNUSED)
 {
 	return 0;
@@ -606,9 +644,7 @@
 
 #include "compat/bswap.h"
 
-#include "wildmatch.h"
-
-struct strbuf;
+#include "wrapper.h"
 
 /* General helper functions */
 NORETURN void usage(const char *err);
@@ -660,15 +696,12 @@
 report_fn get_warn_routine(void);
 void set_die_is_recursing_routine(int (*routine)(void));
 
-int starts_with(const char *str, const char *prefix);
-int istarts_with(const char *str, const char *prefix);
-
 /*
- * If the string "str" begins with the string found in "prefix", return 1.
+ * 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).
  *
- * Otherwise, return 0 and leave "out" untouched.
+ * Otherwise, return false and leave "out" untouched.
  *
  * Examples:
  *
@@ -679,91 +712,63 @@
  *   [skip prefix if present, otherwise use whole string]
  *   skip_prefix(name, "refs/heads/", &name);
  */
-static inline int skip_prefix(const char *str, const char *prefix,
-			      const char **out)
+static inline bool skip_prefix(const char *str, const char *prefix,
+			       const char **out)
 {
 	do {
 		if (!*prefix) {
 			*out = str;
-			return 1;
+			return true;
 		}
 	} while (*str++ == *prefix++);
-	return 0;
-}
-
-/*
- * If the string "str" is the same as the string in "prefix", then the "arg"
- * parameter is set to the "def" parameter and 1 is returned.
- * If the string "str" begins with the string found in "prefix" and then a
- * "=" sign, then the "arg" parameter is set to "str + strlen(prefix) + 1"
- * (i.e., to the point in the string right after the prefix and the "=" sign),
- * and 1 is returned.
- *
- * Otherwise, return 0 and leave "arg" untouched.
- *
- * When we accept both a "--key" and a "--key=<val>" option, this function
- * can be used instead of !strcmp(arg, "--key") and then
- * skip_prefix(arg, "--key=", &arg) to parse such an option.
- */
-int skip_to_optional_arg_default(const char *str, const char *prefix,
-				 const char **arg, const char *def);
-
-static inline int skip_to_optional_arg(const char *str, const char *prefix,
-				       const char **arg)
-{
-	return skip_to_optional_arg_default(str, prefix, arg, "");
+	return false;
 }
 
 /*
  * Like skip_prefix, but promises never to read past "len" bytes of the input
  * buffer, and returns the remaining number of bytes in "out" via "outlen".
  */
-static inline int skip_prefix_mem(const char *buf, size_t len,
-				  const char *prefix,
-				  const char **out, size_t *outlen)
+static inline bool skip_prefix_mem(const char *buf, size_t len,
+				   const char *prefix,
+				   const char **out, size_t *outlen)
 {
 	size_t prefix_len = strlen(prefix);
 	if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
 		*out = buf + prefix_len;
 		*outlen = len - prefix_len;
-		return 1;
+		return true;
 	}
-	return 0;
+	return false;
 }
 
 /*
- * If buf ends with suffix, return 1 and subtract the length of the suffix
- * from *len. Otherwise, return 0 and leave *len untouched.
+ * If buf ends with suffix, return true and subtract the length of the suffix
+ * from *len. Otherwise, return false and leave *len untouched.
  */
-static inline int strip_suffix_mem(const char *buf, size_t *len,
-				   const char *suffix)
+static inline bool strip_suffix_mem(const char *buf, size_t *len,
+				    const char *suffix)
 {
 	size_t suflen = strlen(suffix);
 	if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
-		return 0;
+		return false;
 	*len -= suflen;
-	return 1;
+	return true;
 }
 
 /*
- * If str ends with suffix, return 1 and set *len to the size of the string
- * without the suffix. Otherwise, return 0 and set *len to the size of the
+ * If str ends with suffix, return true and set *len to the size of the string
+ * without the suffix. Otherwise, return false and set *len to the size of the
  * string.
  *
  * Note that we do _not_ NUL-terminate str to the new length.
  */
-static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+static inline bool strip_suffix(const char *str, const char *suffix,
+				size_t *len)
 {
 	*len = strlen(str);
 	return strip_suffix_mem(str, len, suffix);
 }
 
-static inline int ends_with(const char *str, const char *suffix)
-{
-	size_t len;
-	return strip_suffix(str, suffix, &len);
-}
-
 #define SWAP(a, b) do {						\
 	void *_swap_a_ptr = &(a);				\
 	void *_swap_b_ptr = &(b);				\
@@ -859,12 +864,6 @@
 #define pread git_pread
 ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
 #endif
-/*
- * Forward decl that will remind us if its twin in cache.h changes.
- * This function is used in compat/pread.c.  But we can't include
- * cache.h there.
- */
-ssize_t read_in_full(int fd, void *buf, size_t count);
 
 #ifdef NO_SETENV
 #define setenv gitsetenv
@@ -1028,6 +1027,15 @@
 	return (unsigned long)a;
 }
 
+static inline uint32_t cast_size_t_to_uint32_t(size_t a)
+{
+	if (a != (uint32_t)a)
+		die("object too large to read on this platform: %"
+		    PRIuMAX" is cut off to %u",
+		    (uintmax_t)a, (uint32_t)a);
+	return (uint32_t)a;
+}
+
 static inline int cast_size_t_to_int(size_t a)
 {
 	if (a > INT_MAX)
@@ -1066,36 +1074,6 @@
 # define xalloca(size)      (xmalloc(size))
 # define xalloca_free(p)    (free(p))
 #endif
-char *xstrdup(const char *str);
-void *xmalloc(size_t size);
-void *xmallocz(size_t size);
-void *xmallocz_gently(size_t size);
-void *xmemdupz(const void *data, size_t len);
-char *xstrndup(const char *str, size_t len);
-void *xrealloc(void *ptr, size_t size);
-void *xcalloc(size_t nmemb, size_t size);
-void xsetenv(const char *name, const char *value, int overwrite);
-void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-const char *mmap_os_err(void);
-void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-int xopen(const char *path, int flags, ...);
-ssize_t xread(int fd, void *buf, size_t len);
-ssize_t xwrite(int fd, const void *buf, size_t len);
-ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
-int xdup(int fd);
-FILE *xfopen(const char *path, const char *mode);
-FILE *xfdopen(int fd, const char *mode);
-int xmkstemp(char *temp_filename);
-int xmkstemp_mode(char *temp_filename, int mode);
-char *xgetcwd(void);
-FILE *fopen_for_writing(const char *path);
-FILE *fopen_or_warn(const char *path, const char *mode);
-
-/*
- * Like strncmp, but only return zero if s is NUL-terminated and exactly len
- * characters long.  If it is not, consider it greater than t.
- */
-int xstrncmpz(const char *s, const char *t, size_t len);
 
 /*
  * FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note
@@ -1185,6 +1163,81 @@
 #define FLEXPTR_ALLOC_STR(x, ptrname, str) \
 	FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
 
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/**
+ * Dynamically growing an array using realloc() is error prone and boring.
+ *
+ * Define your array with:
+ *
+ * - a pointer (`item`) that points at the array, initialized to `NULL`
+ *   (although please name the variable based on its contents, not on its
+ *   type);
+ *
+ * - an integer variable (`alloc`) that keeps track of how big the current
+ *   allocation is, initialized to `0`;
+ *
+ * - another integer variable (`nr`) to keep track of how many elements the
+ *   array currently has, initialized to `0`.
+ *
+ * Then before adding `n`th element to the item, call `ALLOC_GROW(item, n,
+ * alloc)`.  This ensures that the array can hold at least `n` elements by
+ * calling `realloc(3)` and adjusting `alloc` variable.
+ *
+ * ------------
+ * sometype *item;
+ * size_t nr;
+ * size_t alloc
+ *
+ * for (i = 0; i < nr; i++)
+ * 	if (we like item[i] already)
+ * 		return;
+ *
+ * // we did not like any existing one, so add one
+ * ALLOC_GROW(item, nr + 1, alloc);
+ * item[nr++] = value you like;
+ * ------------
+ *
+ * You are responsible for updating the `nr` variable.
+ *
+ * If you need to specify the number of elements to allocate explicitly
+ * then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
+ *
+ * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
+ * added niceties.
+ *
+ * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+	do { \
+		if ((nr) > alloc) { \
+			if (alloc_nr(alloc) < (nr)) \
+				alloc = (nr); \
+			else \
+				alloc = alloc_nr(alloc); \
+			REALLOC_ARRAY(x, alloc); \
+		} \
+	} while (0)
+
+/*
+ * Similar to ALLOC_GROW but handles updating of the nr value and
+ * zeroing the bytes of the newly-grown array elements.
+ *
+ * DO NOT USE any expression with side-effect for any of the
+ * arguments.
+ */
+#define ALLOC_GROW_BY(x, nr, increase, alloc) \
+	do { \
+		if (increase) { \
+			size_t new_nr = nr + (increase); \
+			if (new_nr < nr) \
+				BUG("negative growth in ALLOC_GROW_BY"); \
+			ALLOC_GROW(x, new_nr, alloc); \
+			memset((x) + nr, 0, sizeof(*(x)) * (increase)); \
+			nr = new_nr; \
+		} \
+	} while (0)
+
 static inline char *xstrdup_or_null(const char *str)
 {
 	return str ? xstrdup(str) : NULL;
@@ -1197,78 +1250,11 @@
 	return (size_t) len;
 }
 
-__attribute__((format (printf, 3, 4)))
-int xsnprintf(char *dst, size_t max, const char *fmt, ...);
-
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #endif
 
-int xgethostname(char *buf, size_t len);
-
-/* in ctype.c, for kwset users */
-extern const unsigned char tolower_trans_tbl[256];
-
-/* Sane ctype - no locale, and works with signed chars */
-#undef isascii
-#undef isspace
-#undef isdigit
-#undef isalpha
-#undef isalnum
-#undef isprint
-#undef islower
-#undef isupper
-#undef tolower
-#undef toupper
-#undef iscntrl
-#undef ispunct
-#undef isxdigit
-
-extern const unsigned char sane_ctype[256];
-#define GIT_SPACE 0x01
-#define GIT_DIGIT 0x02
-#define GIT_ALPHA 0x04
-#define GIT_GLOB_SPECIAL 0x08
-#define GIT_REGEX_SPECIAL 0x10
-#define GIT_PATHSPEC_MAGIC 0x20
-#define GIT_CNTRL 0x40
-#define GIT_PUNCT 0x80
-#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
-#define isascii(x) (((x) & ~0x7f) == 0)
-#define isspace(x) sane_istest(x,GIT_SPACE)
-#define isdigit(x) sane_istest(x,GIT_DIGIT)
-#define isalpha(x) sane_istest(x,GIT_ALPHA)
-#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
-#define isprint(x) ((x) >= 0x20 && (x) <= 0x7e)
-#define islower(x) sane_iscase(x, 1)
-#define isupper(x) sane_iscase(x, 0)
-#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
-#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
-#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
-#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
-		GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
-#define isxdigit(x) (hexval_table[(unsigned char)(x)] != -1)
-#define tolower(x) sane_case((unsigned char)(x), 0x20)
-#define toupper(x) sane_case((unsigned char)(x), 0)
-#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
-
-static inline int sane_case(int x, int high)
-{
-	if (sane_istest(x, GIT_ALPHA))
-		x = (x & ~0x20) | high;
-	return x;
-}
-
-static inline int sane_iscase(int x, int is_lower)
-{
-	if (!sane_istest(x, GIT_ALPHA))
-		return 0;
-
-	if (is_lower)
-		return (x & 0x20) != 0;
-	else
-		return (x & 0x20) == 0;
-}
+#include "sane-ctype.h"
 
 /*
  * Like skip_prefix, but compare case-insensitively. Note that the comparison
@@ -1287,6 +1273,25 @@
 	return 0;
 }
 
+/*
+ * Like skip_prefix_mem, but compare case-insensitively. Note that the
+ * comparison is done via tolower(), so it is strictly ASCII (no multi-byte
+ * characters or locale-specific conversions).
+ */
+static inline int skip_iprefix_mem(const char *buf, size_t len,
+				   const char *prefix,
+				   const char **out, size_t *outlen)
+{
+	do {
+		if (!*prefix) {
+			*out = buf;
+			*outlen = len;
+			return 1;
+		}
+	} while (len-- > 0 && tolower(*buf++) == tolower(*prefix++));
+	return 0;
+}
+
 static inline int strtoul_ui(char const *s, int base, unsigned int *result)
 {
 	unsigned long ul;
@@ -1426,72 +1431,6 @@
 #endif
 #endif
 
-enum fsync_action {
-	FSYNC_WRITEOUT_ONLY,
-	FSYNC_HARDWARE_FLUSH
-};
-
-/*
- * Issues an fsync against the specified file according to the specified mode.
- *
- * FSYNC_WRITEOUT_ONLY attempts to use interfaces available on some operating
- * systems to flush the OS cache without issuing a flush command to the storage
- * controller. If those interfaces are unavailable, the function fails with
- * ENOSYS.
- *
- * FSYNC_HARDWARE_FLUSH does an OS writeout and hardware flush to ensure that
- * changes are durable. It is not expected to fail.
- */
-int git_fsync(int fd, enum fsync_action action);
-
-/*
- * Writes out trace statistics for fsync using the trace2 API.
- */
-void trace_git_fsync_stats(void);
-
-/*
- * Preserves errno, prints a message, but gives no warning for ENOENT.
- * Returns 0 on success, which includes trying to unlink an object that does
- * not exist.
- */
-int unlink_or_warn(const char *path);
- /*
-  * Tries to unlink file.  Returns 0 if unlink succeeded
-  * or the file already didn't exist.  Returns -1 and
-  * appends a message to err suitable for
-  * 'error("%s", err->buf)' on error.
-  */
-int unlink_or_msg(const char *file, struct strbuf *err);
-/*
- * Preserves errno, prints a message, but gives no warning for ENOENT.
- * Returns 0 on success, which includes trying to remove a directory that does
- * not exist.
- */
-int rmdir_or_warn(const char *path);
-/*
- * Calls the correct function out of {unlink,rmdir}_or_warn based on
- * the supplied file mode.
- */
-int remove_or_warn(unsigned int mode, const char *path);
-
-/*
- * Call access(2), but warn for any error except "missing file"
- * (ENOENT or ENOTDIR).
- */
-#define ACCESS_EACCES_OK (1U << 0)
-int access_or_warn(const char *path, int mode, unsigned flag);
-int access_or_die(const char *path, int mode, unsigned flag);
-
-/* Warn on an inaccessible file if errno indicates this is an error */
-int warn_on_fopen_errors(const char *path);
-
-/*
- * Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
- * may be racy. Do not use this as protection against an attacker who can
- * simultaneously create paths.
- */
-int open_nofollow(const char *path, int flags);
-
 #ifndef SHELL_PATH
 # define SHELL_PATH "/bin/sh"
 #endif
@@ -1631,13 +1570,4 @@
 	((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))
 #endif /* !__GNUC__ */
 
-void sleep_millisec(int millisec);
-
-/*
- * Generate len bytes from the system cryptographically secure PRNG.
- * Returns 0 on success and -1 on error, setting errno.  The inability to
- * satisfy the full request is an error.
- */
-int csprng_bytes(void *buf, size_t len);
-
 #endif
diff --git a/git-curl-compat.h b/git-curl-compat.h
index fd96b3c..e1d0bdd 100644
--- a/git-curl-compat.h
+++ b/git-curl-compat.h
@@ -127,6 +127,15 @@
 #endif
 
 /**
+ * Versions before curl 7.66.0 (September 2019) required manually setting the
+ * transfer-encoding for a streaming POST; after that this is handled
+ * automatically.
+ */
+#if LIBCURL_VERSION_NUM < 0x074200
+#define GIT_CURL_NEED_TRANSFER_ENCODING_HEADER
+#endif
+
+/**
  * CURLOPT_PROTOCOLS_STR and CURLOPT_REDIR_PROTOCOLS_STR were added in 7.85.0,
  * released in August 2022.
  */
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 289d4bc..1e03ba9 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Std;
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 7bf3c12..211ec84 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -13,7 +13,7 @@
 # The head revision is on branch "origin" by default.
 # You can change that with the '-o' option.
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -329,7 +329,7 @@
 			# Use a HTTP Proxy. Only works for HTTP proxies that
 			# don't require user authentication
 			#
-			# See: http://www.ietf.org/rfc/rfc2817.txt
+			# See: https://www.ietf.org/rfc/rfc2817.txt
 
 			$s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
 			die "Socket to $proxyhost: $!\n" unless defined $s;
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 7b75736..124f598 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -15,7 +15,7 @@
 ####
 ####
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use bytes;
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index 992124c..dd0c9a5 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -75,6 +75,11 @@
 		merge_tool="$GIT_DIFF_TOOL"
 	else
 		merge_tool="$(get_merge_tool)"
+		subshell_exit_status=$?
+		if test $subshell_exit_status -gt 1
+		then
+			exit $subshell_exit_status
+		fi
 	fi
 fi
 
@@ -86,6 +91,19 @@
 	# ignore the error from the above --- run_merge_tool
 	# will diagnose unusable tool by itself
 	run_merge_tool "$merge_tool" false
+
+	status=$?
+	if test $status -ge 126
+	then
+		# Command not found (127), not executable (126) or
+		# exited via a signal (>= 128).
+		exit $status
+	fi
+
+	if test "$GIT_DIFFTOOL_TRUST_EXIT_CODE" = true
+	then
+		exit $status
+	fi
 else
 	# Launch the merge tool on each path provided by 'git diff'
 	while test $# -gt 6
diff --git a/git-gui/Makefile b/git-gui/Makefile
index a0d5a4b..3f80435 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -138,25 +138,10 @@
 GITGUI_RELATIVE :=
 GITGUI_MACOSXAPP :=
 
-ifeq ($(uname_O),Cygwin)
-	GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"`
-
-	# Is this a Cygwin Tcl/Tk binary?  If so it knows how to do
-	# POSIX path translation just like cygpath does and we must
-	# keep libdir in POSIX format so Cygwin packages of git-gui
-	# work no matter where the user installs them.
-	#
-	ifeq ($(shell echo 'puts [file normalize /]' | '$(TCL_PATH_SQ)'),$(shell cygpath --mixed --absolute /))
-		gg_libdir_sed_in := $(gg_libdir)
-	else
-		gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)")
-	endif
-else
-	ifeq ($(exedir),$(gg_libdir))
-		GITGUI_RELATIVE := 1
-	endif
-	gg_libdir_sed_in := $(gg_libdir)
+ifeq ($(exedir),$(gg_libdir))
+	GITGUI_RELATIVE := 1
 endif
+gg_libdir_sed_in := $(gg_libdir)
 ifeq ($(uname_S),Darwin)
 	ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y)
 		GITGUI_MACOSXAPP := YesPlease
diff --git a/git-gui/README.md b/git-gui/README.md
index 5ce2122..b460b64 100644
--- a/git-gui/README.md
+++ b/git-gui/README.md
@@ -88,7 +88,7 @@
 then send them via `git-send-email`.
 
 A pretty good guide to configuring and using `git-send-email` can be found
-[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/)
+[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/).
 
 ### Using your email client
 
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 201524c..507fb2b 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -24,7 +24,7 @@
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, see <http://www.gnu.org/licenses/>.}]
+along with this program; if not, see <https://www.gnu.org/licenses/>.}]
 
 ######################################################################
 ##
@@ -46,6 +46,132 @@
 
 ######################################################################
 ##
+## Enabling platform-specific code paths
+
+proc is_MacOSX {} {
+	if {[tk windowingsystem] eq {aqua}} {
+		return 1
+	}
+	return 0
+}
+
+proc is_Windows {} {
+	if {$::tcl_platform(platform) eq {windows}} {
+		return 1
+	}
+	return 0
+}
+
+set _iscygwin {}
+proc is_Cygwin {} {
+	global _iscygwin
+	if {$_iscygwin eq {}} {
+		if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
+			set _iscygwin 1
+		} else {
+			set _iscygwin 0
+		}
+	}
+	return $_iscygwin
+}
+
+######################################################################
+##
+## PATH lookup
+
+set _search_path {}
+proc _which {what args} {
+	global env _search_exe _search_path
+
+	if {$_search_path eq {}} {
+		if {[is_Windows]} {
+			set gitguidir [file dirname [info script]]
+			regsub -all ";" $gitguidir "\\;" gitguidir
+			set env(PATH) "$gitguidir;$env(PATH)"
+			set _search_path [split $env(PATH) {;}]
+			# Skip empty `PATH` elements
+			set _search_path [lsearch -all -inline -not -exact \
+				$_search_path ""]
+			set _search_exe .exe
+		} else {
+			set _search_path [split $env(PATH) :]
+			set _search_exe {}
+		}
+	}
+
+	if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
+		set suffix {}
+	} else {
+		set suffix $_search_exe
+	}
+
+	foreach p $_search_path {
+		set p [file join $p $what$suffix]
+		if {[file exists $p]} {
+			return [file normalize $p]
+		}
+	}
+	return {}
+}
+
+proc sanitize_command_line {command_line from_index} {
+	set i $from_index
+	while {$i < [llength $command_line]} {
+		set cmd [lindex $command_line $i]
+		if {[llength [file split $cmd]] < 2} {
+			set fullpath [_which $cmd]
+			if {$fullpath eq ""} {
+				throw {NOT-FOUND} "$cmd not found in PATH"
+			}
+			lset command_line $i $fullpath
+		}
+
+		# handle piped commands, e.g. `exec A | B`
+		for {incr i} {$i < [llength $command_line]} {incr i} {
+			if {[lindex $command_line $i] eq "|"} {
+				incr i
+				break
+			}
+		}
+	}
+	return $command_line
+}
+
+# Override `exec` to avoid unsafe PATH lookup
+
+rename exec real_exec
+
+proc exec {args} {
+	# skip options
+	for {set i 0} {$i < [llength $args]} {incr i} {
+		set arg [lindex $args $i]
+		if {$arg eq "--"} {
+			incr i
+			break
+		}
+		if {[string range $arg 0 0] ne "-"} {
+			break
+		}
+	}
+	set args [sanitize_command_line $args $i]
+	uplevel 1 real_exec $args
+}
+
+# Override `open` to avoid unsafe PATH lookup
+
+rename open real_open
+
+proc open {args} {
+	set arg0 [lindex $args 0]
+	if {[string range $arg0 0 0] eq "|"} {
+		set command_line [string trim [string range $arg0 1 end]]
+		lset args 0 "| [sanitize_command_line $command_line 0]"
+	}
+	uplevel 1 real_open $args
+}
+
+######################################################################
+##
 ## locate our library
 
 if { [info exists ::env(GIT_GUI_LIB_DIR) ] } {
@@ -163,8 +289,6 @@
 set _gitexec {}
 set _githtmldir {}
 set _reponame {}
-set _iscygwin {}
-set _search_path {}
 set _shellpath {@@SHELL_PATH@@}
 
 set _trace [lsearch -exact $argv --trace]
@@ -211,14 +335,7 @@
 		if {[catch {set _gitexec [git --exec-path]} err]} {
 			error "Git not installed?\n\n$err"
 		}
-		if {[is_Cygwin]} {
-			set _gitexec [exec cygpath \
-				--windows \
-				--absolute \
-				$_gitexec]
-		} else {
-			set _gitexec [file normalize $_gitexec]
-		}
+		set _gitexec [file normalize $_gitexec]
 	}
 	if {$args eq {}} {
 		return $_gitexec
@@ -233,14 +350,7 @@
 			# Git not installed or option not yet supported
 			return {}
 		}
-		if {[is_Cygwin]} {
-			set _githtmldir [exec cygpath \
-				--windows \
-				--absolute \
-				$_githtmldir]
-		} else {
-			set _githtmldir [file normalize $_githtmldir]
-		}
+		set _githtmldir [file normalize $_githtmldir]
 	}
 	if {$args eq {}} {
 		return $_githtmldir
@@ -252,40 +362,6 @@
 	return $::_reponame
 }
 
-proc is_MacOSX {} {
-	if {[tk windowingsystem] eq {aqua}} {
-		return 1
-	}
-	return 0
-}
-
-proc is_Windows {} {
-	if {$::tcl_platform(platform) eq {windows}} {
-		return 1
-	}
-	return 0
-}
-
-proc is_Cygwin {} {
-	global _iscygwin
-	if {$_iscygwin eq {}} {
-		if {$::tcl_platform(platform) eq {windows}} {
-			if {[catch {set p [exec cygpath --windir]} err]} {
-				set _iscygwin 0
-			} else {
-				set _iscygwin 1
-				# Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
-				if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
-					set _iscygwin 0
-				}
-			}
-		} else {
-			set _iscygwin 0
-		}
-	}
-	return $_iscygwin
-}
-
 proc is_enabled {option} {
 	global enabled_options
 	if {[catch {set on $enabled_options($option)}]} {return 0}
@@ -448,44 +524,6 @@
 	return $v
 }
 
-proc _which {what args} {
-	global env _search_exe _search_path
-
-	if {$_search_path eq {}} {
-		if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
-			set _search_path [split [exec cygpath \
-				--windows \
-				--path \
-				--absolute \
-				$env(PATH)] {;}]
-			set _search_exe .exe
-		} elseif {[is_Windows]} {
-			set gitguidir [file dirname [info script]]
-			regsub -all ";" $gitguidir "\\;" gitguidir
-			set env(PATH) "$gitguidir;$env(PATH)"
-			set _search_path [split $env(PATH) {;}]
-			set _search_exe .exe
-		} else {
-			set _search_path [split $env(PATH) :]
-			set _search_exe {}
-		}
-	}
-
-	if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
-		set suffix {}
-	} else {
-		set suffix $_search_exe
-	}
-
-	foreach p $_search_path {
-		set p [file join $p $what$suffix]
-		if {[file exists $p]} {
-			return [file normalize $p]
-		}
-	}
-	return {}
-}
-
 # Test a file for a hashbang to identify executable scripts on Windows.
 proc is_shellscript {filename} {
 	if {![file exists $filename]} {return 0}
@@ -623,31 +661,8 @@
 }
 
 proc githook_read {hook_name args} {
-	set pchook [gitdir hooks $hook_name]
-	lappend args 2>@1
-
-	# On Windows [file executable] might lie so we need to ask
-	# the shell if the hook is executable.  Yes that's annoying.
-	#
-	if {[is_Windows]} {
-		upvar #0 _sh interp
-		if {![info exists interp]} {
-			set interp [_which sh]
-		}
-		if {$interp eq {}} {
-			error "hook execution requires sh (not in PATH)"
-		}
-
-		set scr {if test -x "$1";then exec "$@";fi}
-		set sh_c [list $interp -c $scr $interp $pchook]
-		return [_open_stdout_stderr [concat $sh_c $args]]
-	}
-
-	if {[file executable $pchook]} {
-		return [_open_stdout_stderr [concat [list $pchook] $args]]
-	}
-
-	return {}
+	set cmd [concat git hook run --ignore-missing $hook_name -- $args 2>@1]
+	return [_open_stdout_stderr $cmd]
 }
 
 proc kill_file_process {fd} {
@@ -1259,9 +1274,6 @@
 	set _gitdir [pwd]
 }
 
-if {![file isdirectory $_gitdir] && [is_Cygwin]} {
-	catch {set _gitdir [exec cygpath --windows $_gitdir]}
-}
 if {![file isdirectory $_gitdir]} {
 	catch {wm withdraw .}
 	error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
@@ -1273,11 +1285,7 @@
 
 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
 if {[package vcompare $_git_version 1.7.0] >= 0} {
-	if { [is_Cygwin] } {
-		catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]}
-	} else {
-		set _gitworktree [git rev-parse --show-toplevel]
-	}
+	set _gitworktree [git rev-parse --show-toplevel]
 } else {
 	# try to set work tree from environment, core.worktree or use
 	# cdup to obtain a relative path to the top of the worktree. If
@@ -1502,24 +1510,8 @@
 	}
 }
 
-if {[is_Cygwin]} {
-	set is_git_info_exclude {}
-	proc have_info_exclude {} {
-		global is_git_info_exclude
-
-		if {$is_git_info_exclude eq {}} {
-			if {[catch {exec test -f [gitdir info exclude]}]} {
-				set is_git_info_exclude 0
-			} else {
-				set is_git_info_exclude 1
-			}
-		}
-		return $is_git_info_exclude
-	}
-} else {
-	proc have_info_exclude {} {
-		return [file readable [gitdir info exclude]]
-	}
+proc have_info_exclude {} {
+	return [file readable [gitdir info exclude]]
 }
 
 proc rescan_stage2 {fd after} {
@@ -2259,7 +2251,9 @@
 
 # Get the system-specific explorer app/command.
 proc get_explorer {} {
-	if {[is_Cygwin] || [is_Windows]} {
+	if {[is_Cygwin]} {
+		set explorer "/bin/cygstart.exe --explore"
+	} elseif {[is_Windows]} {
 		set explorer "explorer.exe"
 	} elseif {[is_MacOSX]} {
 		set explorer "open"
@@ -2373,7 +2367,7 @@
 	set ret_code $rc
 
 	# Briefly enable send again, working around Tk bug
-	# http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
+	# https://sourceforge.net/p/tktoolkit/bugs/2343/
 	tk appname [appname]
 
 	destroy .
@@ -3053,16 +3047,12 @@
 set doc_path [githtmldir]
 if {$doc_path ne {}} {
 	set doc_path [file join $doc_path index.html]
-
-	if {[is_Cygwin]} {
-		set doc_path [exec cygpath --mixed $doc_path]
-	}
 }
 
 if {[file isfile $doc_path]} {
 	set doc_url "file:$doc_path"
 } else {
-	set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
+	set doc_url {https://www.kernel.org/pub/software/scm/git/docs/}
 }
 
 proc start_browser {url} {
@@ -4028,60 +4018,6 @@
 wm title . "[appname] ([reponame]) [file normalize $_gitworktree]"
 focus -force $ui_comm
 
-# -- Warn the user about environmental problems.  Cygwin's Tcl
-#    does *not* pass its env array onto any processes it spawns.
-#    This means that git processes get none of our environment.
-#
-if {[is_Cygwin]} {
-	set ignored_env 0
-	set suggest_user {}
-	set msg [mc "Possible environment issues exist.
-
-The following environment variables are probably
-going to be ignored by any Git subprocess run
-by %s:
-
-" [appname]]
-	foreach name [array names env] {
-		switch -regexp -- $name {
-		{^GIT_INDEX_FILE$} -
-		{^GIT_OBJECT_DIRECTORY$} -
-		{^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
-		{^GIT_DIFF_OPTS$} -
-		{^GIT_EXTERNAL_DIFF$} -
-		{^GIT_PAGER$} -
-		{^GIT_TRACE$} -
-		{^GIT_CONFIG$} -
-		{^GIT_(AUTHOR|COMMITTER)_DATE$} {
-			append msg " - $name\n"
-			incr ignored_env
-		}
-		{^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
-			append msg " - $name\n"
-			incr ignored_env
-			set suggest_user $name
-		}
-		}
-	}
-	if {$ignored_env > 0} {
-		append msg [mc "
-This is due to a known issue with the
-Tcl binary distributed by Cygwin."]
-
-		if {$suggest_user ne {}} {
-			append msg [mc "
-
-A good replacement for %s
-is placing values for the user.name and
-user.email settings into your personal
-~/.gitconfig file.
-" $suggest_user]
-		}
-		warn_popup $msg
-	}
-	unset ignored_env msg suggest_user name
-}
-
 # -- Only initialize complex UI if we are going to stay running.
 #
 if {[is_enabled transport]} {
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index af1fee7..d23abed 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -174,9 +174,6 @@
 			-foreground blue \
 			-underline 1
 		set home $::env(HOME)
-		if {[is_Cygwin]} {
-			set home [exec cygpath --windows --absolute $home]
-		}
 		set home "[file normalize $home]/"
 		set hlen [string length $home]
 		foreach p $sorted_recent {
@@ -374,18 +371,6 @@
 		return $objdir
 	}
 
-	if {[is_Cygwin]} {
-		set objdir [file join $path .git objects.lnk]
-		if {[file isfile $objdir]} {
-			return [win32_read_lnk $objdir]
-		}
-
-		set objdir [file join $path objects.lnk]
-		if {[file isfile $objdir]} {
-			return [win32_read_lnk $objdir]
-		}
-	}
-
 	return {}
 }
 
@@ -623,12 +608,6 @@
 	}
 
 	set giturl $origin_url
-	if {[is_Cygwin] && [file isdirectory $giturl]} {
-		set giturl [exec cygpath --unix --absolute $giturl]
-		if {$clone_type eq {shared}} {
-			set objdir [exec cygpath --unix --absolute $objdir]
-		}
-	}
 
 	if {[file exists $local_path]} {
 		error_popup [mc "Location %s already exists." $local_path]
@@ -668,11 +647,7 @@
 				fconfigure $f_cp -translation binary -encoding binary
 				cd $objdir
 				while {[gets $f_in line] >= 0} {
-					if {[is_Cygwin]} {
-						puts $f_cp [exec cygpath --unix --absolute $line]
-					} else {
-						puts $f_cp [file normalize $line]
-					}
+					puts $f_cp [file normalize $line]
 				}
 				close $f_in
 				close $f_cp
diff --git a/git-gui/lib/encoding.tcl b/git-gui/lib/encoding.tcl
index 32668fc..d2e0fa6 100644
--- a/git-gui/lib/encoding.tcl
+++ b/git-gui/lib/encoding.tcl
@@ -3,7 +3,7 @@
 # (Copied from gitk, commit fd8ccbec4f0161)
 
 # This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
 # Not all of them are supported by Tcl.
 set encoding_aliases {
     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl
index 97d1d7a..674a41f 100644
--- a/git-gui/lib/shortcut.tcl
+++ b/git-gui/lib/shortcut.tcl
@@ -27,13 +27,10 @@
 }
 
 proc do_cygwin_shortcut {} {
-	global argv0 _gitworktree
+	global argv0 _gitworktree oguilib
 
 	if {[catch {
 		set desktop [exec cygpath \
-			--windows \
-			--absolute \
-			--long-name \
 			--desktop]
 		}]} {
 			set desktop .
@@ -48,19 +45,19 @@
 			set fn ${fn}.lnk
 		}
 		if {[catch {
-				set sh [exec cygpath \
-					--windows \
-					--absolute \
-					/bin/sh.exe]
-				set me [exec cygpath \
-					--unix \
-					--absolute \
-					$argv0]
-				win32_create_lnk $fn [list \
-					$sh -c \
-					"CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \
-					] \
-					[file normalize $_gitworktree]
+				set repodir [file normalize $_gitworktree]
+				set shargs {-c \
+					"CHERE_INVOKING=1 \
+					source /etc/profile; \
+					git gui"}
+				exec /bin/mkshortcut.exe \
+					--arguments $shargs \
+					--desc "git-gui on $repodir" \
+					--icon $oguilib/git-gui.ico \
+					--name $fn \
+					--show min \
+					--workingdir $repodir \
+					/bin/sh.exe
 			} err]} {
 			error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
 		}
diff --git a/git-gui/po/README b/git-gui/po/README
index 2514bc2..1162331 100644
--- a/git-gui/po/README
+++ b/git-gui/po/README
@@ -39,7 +39,7 @@
 If you do not know what your language should be named, you need to find
 it.  This currently follows ISO 639-1 two letter codes:
 
-	http://www.loc.gov/standards/iso639-2/php/code_list.php
+	https://www.loc.gov/standards/iso639-2/php/code_list.php
 
 For example, if you are preparing a translation for Afrikaans, the
 language code is "af".  If there already is a translation for your
diff --git a/git-instaweb.sh b/git-instaweb.sh
index c68f494..994431c 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -432,7 +432,7 @@
 # Mongoose web server configuration file.
 # Lines starting with '#' and empty lines are ignored.
 # For detailed description of every option, visit
-# http://code.google.com/p/mongoose/wiki/MongooseManual
+# https://code.google.com/p/mongoose/wiki/MongooseManual
 
 root		$root
 ports		$port
@@ -458,7 +458,7 @@
 #!$PERL
 
 # gitweb - simple web interface to track changes in git repositories
-#          PSGI wrapper and server starter (see http://plackperl.org)
+#          PSGI wrapper and server starter (see https://plackperl.org)
 
 use strict;
 
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index 9f99201..1ff2617 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -97,7 +97,42 @@
 	test "$TOOL_MODE" = merge
 }
 
+get_gui_default () {
+	if diff_mode
+	then
+		GUI_DEFAULT_KEY="difftool.guiDefault"
+	else
+		GUI_DEFAULT_KEY="mergetool.guiDefault"
+	fi
+	GUI_DEFAULT_CONFIG_LCASE=$(git config --default false --get "$GUI_DEFAULT_KEY" | tr 'A-Z' 'a-z')
+	if test "$GUI_DEFAULT_CONFIG_LCASE" = "auto"
+	then
+		if test -n "$DISPLAY"
+		then
+			GUI_DEFAULT=true
+		else
+			GUI_DEFAULT=false
+		fi
+	else
+		GUI_DEFAULT=$(git config --default false --bool --get "$GUI_DEFAULT_KEY")
+		subshell_exit_status=$?
+		if test $subshell_exit_status -ne 0
+		then
+			exit $subshell_exit_status
+		fi
+	fi
+	echo $GUI_DEFAULT
+}
+
 gui_mode () {
+	if test -z "$GIT_MERGETOOL_GUI"
+	then
+		GIT_MERGETOOL_GUI=$(get_gui_default)
+		if test $? -ne 0
+		then
+			exit 2
+		fi
+	fi
 	test "$GIT_MERGETOOL_GUI" = true
 }
 
@@ -467,6 +502,11 @@
 	is_guessed=false
 	# Check if a merge tool has been configured
 	merge_tool=$(get_configured_merge_tool)
+	subshell_exit_status=$?
+	if test $subshell_exit_status -gt "1"
+	then
+		exit $subshell_exit_status
+	fi
 	# Try to guess an appropriate merge tool if no tool has been set.
 	if test -z "$merge_tool"
 	then
diff --git a/git-mergetool.sh b/git-mergetool.sh
index f751d9c..8a92289 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -451,7 +451,7 @@
 
 main () {
 	prompt=$(git config --bool mergetool.prompt)
-	GIT_MERGETOOL_GUI=false
+	GIT_MERGETOOL_GUI=
 	guessed_merge_tool=false
 	orderfile=
 
@@ -511,9 +511,14 @@
 
 	if test -z "$merge_tool"
 	then
-		if ! merge_tool=$(get_merge_tool)
+		merge_tool=$(get_merge_tool)
+		subshell_exit_status=$?
+		if test $subshell_exit_status = 1
 		then
 			guessed_merge_tool=true
+		elif test $subshell_exit_status -gt 1
+		then
+			exit $subshell_exit_status
 		fi
 	fi
 	merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
diff --git a/git-p4.py b/git-p4.py
index d26a980..28ab12c 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -689,8 +689,8 @@
 
     if not isModeExec(mode):
         p4Type = getP4OpenedType(file)
-        p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
-        p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
+        p4Type = re.sub(r'^([cku]?)x(.*)', r'\1\2', p4Type)
+        p4Type = re.sub(r'(.*?\+.*?)x(.*?)', r'\1\2', p4Type)
         if p4Type[-1] == "+":
             p4Type = p4Type[0:-1]
 
@@ -701,7 +701,7 @@
     """Returns the perforce file type for the given file."""
 
     result = p4_read_pipe(["opened", wildcard_encode(file)])
-    match = re.match(".*\((.+)\)( \*exclusive\*)?\r?$", result)
+    match = re.match(r".*\((.+)\)( \*exclusive\*)?\r?$", result)
     if match:
         return match.group(1)
     else:
@@ -757,7 +757,7 @@
 
     global _diff_tree_pattern
     if not _diff_tree_pattern:
-        _diff_tree_pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
+        _diff_tree_pattern = re.compile(r':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
 
     match = _diff_tree_pattern.match(entry)
     if match:
@@ -918,9 +918,9 @@
             if len(result) > 0:
                 data = result[0].get('data')
                 if data:
-                    m = re.search('Too many rows scanned \(over (\d+)\)', data)
+                    m = re.search(r'Too many rows scanned \(over (\d+)\)', data)
                     if not m:
-                        m = re.search('Request too large \(over (\d+)\)', data)
+                        m = re.search(r'Request too large \(over (\d+)\)', data)
 
                     if m:
                         limit = int(m.group(1))
@@ -1452,7 +1452,7 @@
 
 
 def wildcard_present(path):
-    m = re.search("[*#@%]", path)
+    m = re.search(r"[*#@%]", path)
     return m is not None
 
 
@@ -1522,6 +1522,10 @@
            file is stored in the large file system and handles all necessary
            steps.
            """
+        # symlinks aren't processed by smudge/clean filters
+        if git_mode == "120000":
+            return (git_mode, contents)
+
         if self.exceedsLargeFileThreshold(relPath, contents) or self.hasLargeFileExtension(relPath):
             contentTempFile = self.generateTempFile(contents)
             pointer_git_mode, contents, localLargeFile = self.generatePointer(contentTempFile)
@@ -3044,7 +3048,7 @@
             # Preserve everything in relative path name except leading
             # //depot/; just look at first prefix as they all should
             # be in the same depot.
-            depot = re.sub("^(//[^/]+/).*", r'\1', prefixes[0])
+            depot = re.sub(r"^(//[^/]+/).*", r'\1', prefixes[0])
             if p4PathStartsWith(path, depot):
                 path = path[len(depot):]
 
@@ -3599,7 +3603,7 @@
                     commitFound = True
                 else:
                     gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
-                        "--reverse", ":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
+                        "--reverse", r":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
                     if len(gitCommit) == 0:
                         print("importing label %s: could not find git commit for changelist %d" % (name, changelist))
                     else:
@@ -4178,7 +4182,7 @@
                 if len(self.changesFile) == 0:
                     revision = "#head"
 
-            p = re.sub("\.\.\.$", "", p)
+            p = re.sub(r"\.\.\.$", "", p)
             if not p.endswith("/"):
                 p += "/"
 
@@ -4247,7 +4251,8 @@
         if self.tempBranches != []:
             for branch in self.tempBranches:
                 read_pipe(["git", "update-ref", "-d", branch])
-            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+            if len(read_pipe(["git", "for-each-ref", self.tempBranchLocation])) > 0:
+                   die("There are unexpected temporary branches")
 
         # Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
         # a convenient shortcut refname "p4".
@@ -4287,7 +4292,7 @@
             die("Cannot find upstream branchpoint for rebase")
 
         # the branchpoint may be p4/foo~3, so strip off the parent
-        upstream = re.sub("~[0-9]+$", "", upstream)
+        upstream = re.sub(r"~[0-9]+$", "", upstream)
 
         print("Rebasing the current branch onto %s" % upstream)
         oldHead = read_pipe(["git", "rev-parse", "HEAD"]).strip()
@@ -4316,8 +4321,8 @@
     def defaultDestination(self, args):
         # TODO: use common prefix of args?
         depotPath = args[0]
-        depotDir = re.sub("(@[^@]*)$", "", depotPath)
-        depotDir = re.sub("(#[^#]*)$", "", depotDir)
+        depotDir = re.sub(r"(@[^@]*)$", "", depotPath)
+        depotDir = re.sub(r"(#[^#]*)$", "", depotDir)
         depotDir = re.sub(r"\.\.\.$", "", depotDir)
         depotDir = re.sub(r"/$", "", depotDir)
         return os.path.split(depotDir)[1]
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index e3d3909..eb34cda 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -148,7 +148,7 @@
 	if [ -z "$dry_run" ] ; then
 		git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
 		tree=$(git write-tree) &&
-		commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
+		commit=$( { echo "$SUBJECT"; echo; cat "$tmp_msg"; } | git commit-tree $tree -p $commit) &&
 		git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
 	fi
 done 3<"$QUILT_SERIES"
diff --git a/git-send-email.perl b/git-send-email.perl
index 07f2a0c..821b2b3 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -16,7 +16,7 @@
 #    and second line is the subject of the message.
 #
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Getopt::Long;
@@ -26,22 +26,10 @@
 
 Getopt::Long::Configure qw/ pass_through /;
 
-package FakeTerm;
-sub new {
-	my ($class, $reason) = @_;
-	return bless \$reason, shift;
-}
-sub readline {
-	my $self = shift;
-	die "Cannot use readline on FakeTerm: $$self";
-}
-package main;
-
-
 sub usage {
 	print <<EOT;
-git send-email' [<options>] <file|directory>
-git send-email' [<options>] <format-patch options>
+git send-email [<options>] <file|directory>
+git send-email [<options>] <format-patch options>
 git send-email --dump-aliases
 
   Composing:
@@ -87,8 +75,10 @@
 
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
-    --to-cmd                <str>  * Email To: via `<str> \$patch_path`
-    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
+    --to-cmd                <str>  * Email To: via `<str> \$patch_path`.
+    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`.
+    --header-cmd            <str>  * Add headers via `<str> \$patch_path`.
+    --no-header-cmd                * Disable any header command in use.
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
     --[no-]cc-cover                * Email Cc: addresses in the cover letter.
     --[no-]to-cover                * Email To: addresses in the cover letter.
@@ -129,13 +119,16 @@
 
 	foreach my $key (keys %$original_opts) {
 		unless (exists $not_for_completion{$key}) {
-			$key =~ s/!$//;
+			my $negatable = ($key =~ s/!$//);
 
 			if ($key =~ /[:=][si]$/) {
 				$key =~ s/[:=][si]$//;
 				push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
 			} else {
 				push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
+				if ($negatable) {
+					push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));
+				}
 			}
 		}
 	}
@@ -202,7 +195,7 @@
 	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 # Things we either get from config, *or* are overridden on the
 # command-line.
-my ($no_cc, $no_to, $no_bcc, $no_identity);
+my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);
 my (@config_to, @getopt_to);
 my (@config_cc, @getopt_cc);
 my (@config_bcc, @getopt_bcc);
@@ -238,7 +231,7 @@
 	my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code);
 	if (defined $msg) {
 		# Quiet the 'redundant' warning category, except we
-		# need to support down to Perl 5.8, so we can't do a
+		# need to support down to Perl 5.8.1, so we can't do a
 		# "no warnings 'redundant'", since that category was
 		# introduced in perl 5.22, and asking for it will die
 		# on older perls.
@@ -269,7 +262,7 @@
 # Variables with corresponding config settings
 my ($suppress_from, $signed_off_by_cc);
 my ($cover_cc, $cover_to);
-my ($to_cmd, $cc_cmd);
+my ($to_cmd, $cc_cmd, $header_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
 my ($batch_size, $relogin_delay);
@@ -318,6 +311,7 @@
     "tocmd" => \$to_cmd,
     "cc" => \@config_cc,
     "cccmd" => \$cc_cmd,
+    "headercmd" => \$header_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@config_bcc,
     "suppresscc" => \@suppress_cc,
@@ -500,7 +494,6 @@
 		    "bcc=s" => \@getopt_bcc,
 		    "no-bcc" => \$no_bcc,
 		    "chain-reply-to!" => \$chain_reply_to,
-		    "no-chain-reply-to" => sub {$chain_reply_to = 0},
 		    "sendmail-cmd=s" => \$sendmail_cmd,
 		    "smtp-server=s" => \$smtp_server,
 		    "smtp-server-option=s" => \@smtp_server_options,
@@ -515,34 +508,27 @@
 		    "smtp-auth=s" => \$smtp_auth,
 		    "no-smtp-auth" => sub {$smtp_auth = 'none'},
 		    "annotate!" => \$annotate,
-		    "no-annotate" => sub {$annotate = 0},
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
+		    "header-cmd=s" => \$header_cmd,
+		    "no-header-cmd" => \$no_header_cmd,
 		    "suppress-from!" => \$suppress_from,
-		    "no-suppress-from" => sub {$suppress_from = 0},
 		    "suppress-cc=s" => \@suppress_cc,
 		    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
-		    "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0},
-		    "cc-cover|cc-cover!" => \$cover_cc,
-		    "no-cc-cover" => sub {$cover_cc = 0},
-		    "to-cover|to-cover!" => \$cover_to,
-		    "no-to-cover" => sub {$cover_to = 0},
+		    "cc-cover!" => \$cover_cc,
+		    "to-cover!" => \$cover_to,
 		    "confirm=s" => \$confirm,
 		    "dry-run" => \$dry_run,
 		    "envelope-sender=s" => \$envelope_sender,
 		    "thread!" => \$thread,
-		    "no-thread" => sub {$thread = 0},
 		    "validate!" => \$validate,
-		    "no-validate" => sub {$validate = 0},
 		    "transfer-encoding=s" => \$target_xfer_encoding,
 		    "format-patch!" => \$format_patch,
-		    "no-format-patch" => sub {$format_patch = 0},
 		    "8bit-encoding=s" => \$auto_8bit_encoding,
 		    "compose-encoding=s" => \$compose_encoding,
 		    "force" => \$force,
 		    "xmailer!" => \$use_xmailer,
-		    "no-xmailer" => sub {$use_xmailer = 0},
 		    "batch-size=i" => \$batch_size,
 		    "relogin-delay=i" => \$relogin_delay,
 		    "git-completion-helper" => \$git_completion_helper,
@@ -792,16 +778,22 @@
 				    @rev_list_opts);
 }
 
-@files = handle_backup_files(@files);
-
-if ($validate) {
-	foreach my $f (@files) {
-		unless (-p $f) {
-			validate_patch($f, $target_xfer_encoding);
-		}
-	}
+if (defined $sender) {
+	$sender =~ s/^\s+|\s+$//g;
+	($sender) = expand_aliases($sender);
+} else {
+	$sender = $repoauthor->() || $repocommitter->() || '';
 }
 
+# $sender could be an already sanitized address
+# (e.g. sendemail.from could be manually sanitized by user).
+# But it's a no-op to run sanitize_address on an already sanitized address.
+$sender = sanitize_address($sender);
+
+$time = time - scalar $#files;
+
+@files = handle_backup_files(@files);
+
 if (@files) {
 	unless ($quiet) {
 		print $_,"\n" for (@files);
@@ -838,6 +830,9 @@
 	my $tpl_subject = $initial_subject || '';
 	my $tpl_in_reply_to = $initial_in_reply_to || '';
 	my $tpl_reply_to = $reply_to || '';
+	my $tpl_to = join(',', @initial_to);
+	my $tpl_cc = join(',', @initial_cc);
+	my $tpl_bcc = join(', ', @initial_bcc);
 
 	print $c <<EOT1, Git::prefix_lines("GIT: ", __(<<EOT2)), <<EOT3;
 From $tpl_sender # This line is ignored.
@@ -849,6 +844,9 @@
 Clear the body content if you don't wish to send a summary.
 EOT2
 From: $tpl_sender
+To: $tpl_to
+Cc: $tpl_cc
+Bcc: $tpl_bcc
 Reply-To: $tpl_reply_to
 Subject: $tpl_subject
 In-Reply-To: $tpl_in_reply_to
@@ -865,88 +863,82 @@
 		do_edit($compose_filename);
 	}
 
+	open my $c2, ">", $compose_filename . ".final"
+		or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
+
 	open $c, "<", $compose_filename
 		or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
 
+	my $need_8bit_cte = file_has_nonascii($compose_filename);
+	my $in_body = 0;
+	my $summary_empty = 1;
 	if (!defined $compose_encoding) {
 		$compose_encoding = "UTF-8";
 	}
-
-	my %parsed_email;
-	while (my $line = <$c>) {
-		next if $line =~ m/^GIT:/;
-		parse_header_line($line, \%parsed_email);
-		if ($line =~ /^$/) {
-			$parsed_email{'body'} = filter_body($c);
+	while(<$c>) {
+		next if m/^GIT:/;
+		if ($in_body) {
+			$summary_empty = 0 unless (/^\n$/);
+		} elsif (/^\n$/) {
+			$in_body = 1;
+			if ($need_8bit_cte) {
+				print $c2 "MIME-Version: 1.0\n",
+					 "Content-Type: text/plain; ",
+					   "charset=$compose_encoding\n",
+					 "Content-Transfer-Encoding: 8bit\n";
+			}
+		} elsif (/^MIME-Version:/i) {
+			$need_8bit_cte = 0;
+		} elsif (/^Subject:\s*(.+)\s*$/i) {
+			$initial_subject = $1;
+			my $subject = $initial_subject;
+			$_ = "Subject: " .
+				quote_subject($subject, $compose_encoding) .
+				"\n";
+		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+			$initial_in_reply_to = $1;
+			next;
+		} elsif (/^Reply-To:\s*(.+)\s*$/i) {
+			$reply_to = $1;
+		} elsif (/^From:\s*(.+)\s*$/i) {
+			$sender = $1;
+			next;
+		} elsif (/^To:\s*(.+)\s*$/i) {
+			@initial_to = parse_address_line($1);
+			next;
+		} elsif (/^Cc:\s*(.+)\s*$/i) {
+			@initial_cc = parse_address_line($1);
+			next;
+		} elsif (/^Bcc:/i) {
+			@initial_bcc = parse_address_line($1);
+			next;
 		}
+		print $c2 $_;
 	}
 	close $c;
+	close $c2;
 
-	open my $c2, ">", $compose_filename . ".final"
-	or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
-
-
-	if ($parsed_email{'From'}) {
-		$sender = delete($parsed_email{'From'});
-	}
-	if ($parsed_email{'In-Reply-To'}) {
-		$initial_in_reply_to = delete($parsed_email{'In-Reply-To'});
-	}
-	if ($parsed_email{'Reply-To'}) {
-		$reply_to = delete($parsed_email{'Reply-To'});
-	}
-	if ($parsed_email{'Subject'}) {
-		$initial_subject = delete($parsed_email{'Subject'});
-		print $c2 "Subject: " .
-			quote_subject($initial_subject, $compose_encoding) .
-			"\n";
-	}
-
-	if ($parsed_email{'MIME-Version'}) {
-		print $c2 "MIME-Version: $parsed_email{'MIME-Version'}\n",
-				"Content-Type: $parsed_email{'Content-Type'};\n",
-				"Content-Transfer-Encoding: $parsed_email{'Content-Transfer-Encoding'}\n";
-		delete($parsed_email{'MIME-Version'});
-		delete($parsed_email{'Content-Type'});
-		delete($parsed_email{'Content-Transfer-Encoding'});
-	} elsif (file_has_nonascii($compose_filename)) {
-		my $content_type = (delete($parsed_email{'Content-Type'}) or
-			"text/plain; charset=$compose_encoding");
-		print $c2 "MIME-Version: 1.0\n",
-			"Content-Type: $content_type\n",
-			"Content-Transfer-Encoding: 8bit\n";
-	}
-	# Preserve unknown headers
-	foreach my $key (keys %parsed_email) {
-		next if $key eq 'body';
-		print $c2 "$key: $parsed_email{$key}";
-	}
-
-	if ($parsed_email{'body'}) {
-		print $c2 "\n$parsed_email{'body'}\n";
-		delete($parsed_email{'body'});
-	} else {
+	if ($summary_empty) {
 		print __("Summary email is empty, skipping it\n");
 		$compose = -1;
 	}
-
-	close $c2;
-
 } elsif ($annotate) {
 	do_edit(@files);
 }
 
-sub term {
-	my $term = eval {
+{
+	# Only instantiate one $term per program run, since some
+	# Term::ReadLine providers refuse to create a second instance.
+	my $term;
+	sub term {
 		require Term::ReadLine;
-		$ENV{"GIT_SEND_EMAIL_NOTTY"}
-			? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)
-			: Term::ReadLine->new('git-send-email');
-	};
-	if ($@) {
-		$term = FakeTerm->new("$@: going non-interactive");
+		if (!defined $term) {
+			$term = $ENV{"GIT_SEND_EMAIL_NOTTY"}
+				? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)
+				: Term::ReadLine->new('git-send-email');
+		}
+		return $term;
 	}
-	return $term;
 }
 
 sub ask {
@@ -984,32 +976,6 @@
 	return;
 }
 
-sub parse_header_line {
-	my $lines = shift;
-	my $parsed_line = shift;
-	my $addr_pat = join "|", qw(To Cc Bcc);
-
-	foreach (split(/\n/, $lines)) {
-		if (/^($addr_pat):\s*(.+)$/i) {
-		        $parsed_line->{$1} = [ parse_address_line($2) ];
-		} elsif (/^([^:]*):\s*(.+)\s*$/i) {
-		        $parsed_line->{$1} = $2;
-		}
-	}
-}
-
-sub filter_body {
-	my $c = shift;
-	my $body = "";
-	while (my $body_line = <$c>) {
-		if ($body_line !~ m/^GIT:/) {
-			$body .= $body_line;
-		}
-	}
-	return $body;
-}
-
-
 my %broken_encoding;
 
 sub file_declares_8bit_cte {
@@ -1050,18 +1016,6 @@
 	}
 }
 
-if (defined $sender) {
-	$sender =~ s/^\s+|\s+$//g;
-	($sender) = expand_aliases($sender);
-} else {
-	$sender = $repoauthor->() || $repocommitter->() || '';
-}
-
-# $sender could be an already sanitized address
-# (e.g. sendemail.from could be manually sanitized by user).
-# But it's a no-op to run sanitize_address on an already sanitized address.
-$sender = sanitize_address($sender);
-
 my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
@@ -1153,10 +1107,10 @@
 
 sub extract_valid_address_or_die {
 	my $address = shift;
-	$address = extract_valid_address($address);
+	my $valid_address = extract_valid_address($address);
 	die sprintf(__("error: unable to extract a valid address from: %s\n"), $address)
-		if !$address;
-	return $address;
+		if !$valid_address;
+	return $valid_address;
 }
 
 sub validate_address {
@@ -1221,10 +1175,6 @@
 	#print "new message id = $message_id\n"; # Was useful for debugging
 }
 
-
-
-$time = time - scalar $#files;
-
 sub unquote_rfc2047 {
 	local ($_) = @_;
 	my $charset;
@@ -1502,16 +1452,7 @@
 	return File::Spec::Functions::file_name_is_absolute($path);
 }
 
-# Prepares the email, then asks the user what to do.
-#
-# If the user chooses to send the email, it's sent and 1 is returned.
-# If the user chooses not to send the email, 0 is returned.
-# If the user decides they want to make further edits, -1 is returned and the
-# caller is expected to call send_message again after the edits are performed.
-#
-# If an error occurs sending the email, this just dies.
-
-sub send_message {
+sub gen_header {
 	my @recipients = unique_email_list(@to);
 	@cc = (grep { my $cc = extract_valid_address_or_die($_);
 		      not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
@@ -1537,7 +1478,7 @@
 To: $to${ccline}
 Subject: $subject
 Date: $date
-Message-Id: $message_id
+Message-ID: $message_id
 ";
 	if ($use_xmailer) {
 		$header .= "X-Mailer: git-send-email $gitversion\n";
@@ -1553,6 +1494,22 @@
 	if (@xh) {
 		$header .= join("\n", @xh) . "\n";
 	}
+	my $recipients_ref = \@recipients;
+	return ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header);
+}
+
+# Prepares the email, then asks the user what to do.
+#
+# If the user chooses to send the email, it's sent and 1 is returned.
+# If the user chooses not to send the email, 0 is returned.
+# If the user decides they want to make further edits, -1 is returned and the
+# caller is expected to call send_message again after the edits are performed.
+#
+# If an error occurs sending the email, this just dies.
+
+sub send_message {
+	my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
+	my @recipients = @$recipients_ref;
 
 	my @sendmail_parameters = ('-i', @recipients);
 	my $raw_from = $sender;
@@ -1738,15 +1695,8 @@
 	return 1;
 }
 
-$in_reply_to = $initial_in_reply_to;
-$references = $initial_in_reply_to || '';
-$message_num = 0;
-
-# Prepares the email, prompts the user, sends it out
-# Returns 0 if an edit was done and the function should be called again, or 1
-# otherwise.
-sub process_file {
-	my ($t) = @_;
+sub pre_process_file {
+	my ($t, $quiet) = @_;
 
 	open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t);
 
@@ -1765,16 +1715,17 @@
 	$subject = $initial_subject;
 	$message = "";
 	$message_num++;
-	# First unfold multiline header fields
+	undef $message_id;
+	# Retrieve and unfold header fields.
+	my @header_lines = ();
 	while(<$fh>) {
 		last if /^\s*$/;
-		if (/^\s+\S/ and @header) {
-			chomp($header[$#header]);
-			s/^\s+/ /;
-			$header[$#header] .= $_;
-	    } else {
-			push(@header, $_);
-		}
+		push(@header_lines, $_);
+	}
+	@header = unfold_headers(@header_lines);
+	# Add computed headers, if applicable.
+	unless ($no_header_cmd || ! $header_cmd) {
+		push @header, invoke_header_cmd($header_cmd, $t);
 	}
 	# Now parse the header
 	foreach(@header) {
@@ -1832,7 +1783,7 @@
 				$has_mime_version = 1;
 				push @xh, $_;
 			}
-			elsif (/^Message-Id: (.*)/i) {
+			elsif (/^Message-ID: (.*)/i) {
 				$message_id = $1;
 			}
 			elsif (/^Content-Transfer-Encoding: (.*)/i) {
@@ -1900,9 +1851,9 @@
 	}
 	close $fh;
 
-	push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
+	push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t, $quiet)
 		if defined $to_cmd;
-	push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
+	push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t, $quiet)
 		if defined $cc_cmd && !$suppress_cc{'cccmd'};
 
 	if ($broken_encoding{$t} && !$has_content_type) {
@@ -1961,6 +1912,15 @@
 			@initial_to = @to;
 		}
 	}
+}
+
+# Prepares the email, prompts the user, and sends it out
+# Returns 0 if an edit was done and the function should be called again, or 1
+# on the email being successfully sent out.
+sub process_file {
+	my ($t) = @_;
+
+        pre_process_file($t, $quiet);
 
 	my $message_was_sent = send_message();
 	if ($message_was_sent == -1) {
@@ -2000,21 +1960,102 @@
 	return 1;
 }
 
+sub initialize_modified_loop_vars {
+	$in_reply_to = $initial_in_reply_to;
+	$references = $initial_in_reply_to || '';
+	$message_num = 0;
+}
+
+if ($validate) {
+	# FIFOs can only be read once, exclude them from validation.
+	my @real_files = ();
+	foreach my $f (@files) {
+		unless (-p $f) {
+			push(@real_files, $f);
+		}
+	}
+
+	# Run the loop once again to avoid gaps in the counter due to FIFO
+	# arguments provided by the user.
+	my $num = 1;
+	my $num_files = scalar @real_files;
+	$ENV{GIT_SENDEMAIL_FILE_TOTAL} = "$num_files";
+	initialize_modified_loop_vars();
+	foreach my $r (@real_files) {
+		$ENV{GIT_SENDEMAIL_FILE_COUNTER} = "$num";
+		pre_process_file($r, 1);
+		validate_patch($r, $target_xfer_encoding);
+		$num += 1;
+	}
+	delete $ENV{GIT_SENDEMAIL_FILE_COUNTER};
+	delete $ENV{GIT_SENDEMAIL_FILE_TOTAL};
+}
+
+initialize_modified_loop_vars();
 foreach my $t (@files) {
 	while (!process_file($t)) {
 		# user edited the file
 	}
 }
 
+# Execute a command and return its output lines as an array.  Blank
+# lines which do not appear at the end of the output are reported as
+# errors.
+sub execute_cmd {
+	my ($prefix, $cmd, $file) = @_;
+	my @lines = ();
+	my $seen_blank_line = 0;
+	open my $fh, "-|", "$cmd \Q$file\E"
+		or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
+	while (my $line = <$fh>) {
+		die sprintf(__("(%s) Malformed output from '%s'"), $prefix, $cmd)
+		    if $seen_blank_line;
+		if ($line =~ /^$/) {
+			$seen_blank_line = $line =~ /^$/;
+			next;
+		}
+		push @lines, $line;
+	}
+	close $fh
+	    or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
+	return @lines;
+}
+
+# Process headers lines, unfolding multiline headers as defined by RFC
+# 2822.
+sub unfold_headers {
+	my @headers;
+	foreach(@_) {
+		last if /^\s*$/;
+		if (/^\s+\S/ and @headers) {
+			chomp($headers[$#headers]);
+			s/^\s+/ /;
+			$headers[$#headers] .= $_;
+		} else {
+			push(@headers, $_);
+		}
+	}
+	return @headers;
+}
+
+# Invoke the provided CMD with FILE as an argument, which should
+# output RFC 2822 email headers. Fold multiline headers and return the
+# headers as an array.
+sub invoke_header_cmd {
+	my ($cmd, $file) = @_;
+	my @lines = execute_cmd("header-cmd", $header_cmd, $file);
+	return unfold_headers(@lines);
+}
+
 # Execute a command (e.g. $to_cmd) to get a list of email addresses
 # and return a results array
 sub recipients_cmd {
-	my ($prefix, $what, $cmd, $file) = @_;
-
+	my ($prefix, $what, $cmd, $file, $quiet) = @_;
+	my @lines = ();
 	my @addresses = ();
-	open my $fh, "-|", "$cmd \Q$file\E"
-	    or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
-	while (my $address = <$fh>) {
+
+	@lines = execute_cmd($prefix, $cmd, $file);
+	for my $address (@lines) {
 		$address =~ s/^\s*//g;
 		$address =~ s/\s*$//g;
 		$address = sanitize_address($address);
@@ -2023,8 +2064,6 @@
 		printf(__("(%s) Adding %s: %s from: '%s'\n"),
 		       $prefix, $what, $address, $cmd) unless $quiet;
 		}
-	close $fh
-	    or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
 	return @addresses;
 }
 
@@ -2095,10 +2134,21 @@
 			chdir($repo->wc_path() or $repo->repo_path())
 				or die("chdir: $!");
 			local $ENV{"GIT_DIR"} = $repo->repo_path();
+
+			my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
+
+			require File::Temp;
+			my ($header_filehandle, $header_filename) = File::Temp::tempfile(
+                            TEMPLATE => ".gitsendemail.header.XXXXXX",
+                            DIR => $repo->repo_path(),
+                            UNLINK => 1,
+                        );
+			print $header_filehandle $header;
+
 			my @cmd = ("git", "hook", "run", "--ignore-missing",
 				    $hook_name, "--");
-			my @cmd_msg = (@cmd, "<patch>");
-			my @cmd_run = (@cmd, $target);
+			my @cmd_msg = (@cmd, "<patch>", "<header>");
+			my @cmd_run = (@cmd, $target, $header_filename);
 			$hook_error = system_or_msg(\@cmd_run, undef, "@cmd_msg");
 			chdir($cwd_save) or die("chdir: $!");
 		}
diff --git a/git-svn.perl b/git-svn.perl
index be987e3..b0d0a50 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
-use 5.008;
+use 5.008001;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use strict;
 use vars qw/	$AUTHOR $VERSION
@@ -297,28 +297,12 @@
 		{} ],
 );
 
-package FakeTerm;
-sub new {
-	my ($class, $reason) = @_;
-	return bless \$reason, shift;
-}
-sub readline {
-	my $self = shift;
-	die "Cannot use readline on FakeTerm: $$self";
-}
-package main;
-
 my $term;
 sub term_init {
-	$term = eval {
-		require Term::ReadLine;
-		$ENV{"GIT_SVN_NOTTY"}
+	require Term::ReadLine;
+	$term = $ENV{"GIT_SVN_NOTTY"}
 			? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT
 			: new Term::ReadLine 'git-svn';
-	};
-	if ($@) {
-		$term = new FakeTerm "$@: going non-interactive";
-	}
 }
 
 my $cmd;
diff --git a/zlib.c b/git-zlib.c
similarity index 98%
rename from zlib.c
rename to git-zlib.c
index d594cba..d43bbeb 100644
--- a/zlib.c
+++ b/git-zlib.c
@@ -2,7 +2,8 @@
  * zlib wrappers to make sure we don't silently miss errors
  * at init time.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "git-zlib.h"
 
 static const char *zerr_to_string(int status)
 {
diff --git a/git-zlib.h b/git-zlib.h
new file mode 100644
index 0000000..d8a670a
--- /dev/null
+++ b/git-zlib.h
@@ -0,0 +1,28 @@
+#ifndef GIT_ZLIB_H
+#define GIT_ZLIB_H
+
+typedef struct git_zstream {
+	z_stream z;
+	unsigned long avail_in;
+	unsigned long avail_out;
+	unsigned long total_in;
+	unsigned long total_out;
+	unsigned char *next_in;
+	unsigned char *next_out;
+} git_zstream;
+
+void git_inflate_init(git_zstream *);
+void git_inflate_init_gzip_only(git_zstream *);
+void git_inflate_end(git_zstream *);
+int git_inflate(git_zstream *, int flush);
+
+void git_deflate_init(git_zstream *, int level);
+void git_deflate_init_gzip(git_zstream *, int level);
+void git_deflate_init_raw(git_zstream *, int level);
+void git_deflate_end(git_zstream *);
+int git_deflate_abort(git_zstream *);
+int git_deflate_end_gently(git_zstream *);
+int git_deflate(git_zstream *, int flush);
+unsigned long git_deflate_bound(git_zstream *, unsigned long);
+
+#endif /* GIT_ZLIB_H */
diff --git a/git.c b/git.c
index 6171fd6..654d615 100644
--- a/git.c
+++ b/git.c
@@ -1,10 +1,20 @@
 #include "builtin.h"
 #include "config.h"
+#include "environment.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "help.h"
+#include "object-file.h"
+#include "pager.h"
+#include "read-cache-ll.h"
 #include "run-command.h"
 #include "alias.h"
+#include "replace-object.h"
+#include "setup.h"
+#include "attr.h"
 #include "shallow.h"
+#include "trace.h"
+#include "trace2.h"
 
 #define RUN_SETUP		(1<<0)
 #define RUN_SETUP_GENTLY	(1<<1)
@@ -177,8 +187,13 @@
 			use_pager = 0;
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
+			fetch_if_missing = 0;
+			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else if (!strcmp(cmd, "--no-replace-objects")) {
-			read_replace_refs = 0;
+			disable_replace_refs();
 			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
 			if (envchanged)
 				*envchanged = 1;
@@ -307,6 +322,21 @@
 			} else {
 				exit(list_cmds(cmd));
 			}
+		} else if (!strcmp(cmd, "--attr-source")) {
+			if (*argc < 2) {
+				fprintf(stderr, _("no attribute source given for --attr-source\n" ));
+				usage(git_usage_string);
+			}
+			setenv(GIT_ATTR_SOURCE_ENVIRONMENT, (*argv)[1], 1);
+			if (envchanged)
+				*envchanged = 1;
+			(*argv)++;
+			(*argc)--;
+		} else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
+			set_git_attr_source(cmd);
+			setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else {
 			fprintf(stderr, _("unknown option: %s\n"), cmd);
 			usage(git_usage_string);
@@ -349,8 +379,6 @@
 			strvec_pushv(&child.args, (*argv) + 1);
 
 			trace2_cmd_alias(alias_command, child.args.v);
-			trace2_cmd_list_config();
-			trace2_cmd_list_env_vars();
 			trace2_cmd_name("_run_shell_alias_");
 
 			ret = run_command(&child);
@@ -387,8 +415,6 @@
 		COPY_ARRAY(new_argv + count, *argv + 1, *argcp);
 
 		trace2_cmd_alias(alias_command, new_argv);
-		trace2_cmd_list_config();
-		trace2_cmd_list_env_vars();
 
 		*argv = new_argv;
 		*argcp += count - 1;
@@ -438,8 +464,6 @@
 
 	trace_argv_printf(argv, "trace: built-in: git");
 	trace2_cmd_name(p->cmd);
-	trace2_cmd_list_config();
-	trace2_cmd_list_env_vars();
 
 	validate_cache_entries(the_repository->index);
 	status = p->fn(argc, argv, prefix);
@@ -570,6 +594,7 @@
 	{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
 	{ "repack", cmd_repack, RUN_SETUP },
 	{ "replace", cmd_replace, RUN_SETUP },
+	{ "replay", cmd_replay, RUN_SETUP },
 	{ "rerere", cmd_rerere, RUN_SETUP },
 	{ "reset", cmd_reset, RUN_SETUP },
 	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
@@ -583,7 +608,7 @@
 	{ "show-branch", cmd_show_branch, RUN_SETUP },
 	{ "show-index", cmd_show_index, RUN_SETUP_GENTLY },
 	{ "show-ref", cmd_show_ref, RUN_SETUP },
-	{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
+	{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP },
 	{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
 	{ "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
 	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 0ae7d68..7a087f1 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -353,6 +353,16 @@
     return $ret
 }
 
+# Escapes a list of filter paths to be passed to git log via stdin. Note that
+# paths must not be quoted.
+proc escape_filter_paths {paths} {
+	set escaped [list]
+	foreach path $paths {
+		lappend escaped [string map {\\ \\\\ "\ " "\\\ "} $path]
+	}
+	return $escaped
+}
+
 # Start off a git log process and arrange to read its output
 proc start_rev_list {view} {
     global startmsecs commitidx viewcomplete curview
@@ -405,14 +415,17 @@
         if {$revs eq {}} {
             return 0
         }
-        set args [concat $vflags($view) $revs]
+        set args $vflags($view)
     } else {
+        set revs {}
         set args $vorigargs($view)
     }
 
     if {[catch {
         set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
-                        --parents --boundary $args "--" $files] r]
+                        --parents --boundary $args --stdin \
+                        "<<[join [concat $revs "--" \
+                                [escape_filter_paths $files]] "\\n"]"] r]
     } err]} {
         error_popup "[mc "Error executing git log:"] $err"
         return 0
@@ -554,13 +567,20 @@
             set revs $newrevs
             set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
         }
-        set args [concat $vflags($view) $revs --not $oldpos]
+        set args $vflags($view)
+        foreach r $oldpos {
+                lappend revs "^$r"
+        }
     } else {
+        set revs {}
         set args $vorigargs($view)
     }
     if {[catch {
         set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
-                        --parents --boundary $args "--" $vfilelimit($view)] r]
+                        --parents --boundary $args --stdin \
+                        "<<[join [concat $revs "--" \
+                                [escape_filter_paths \
+                                        $vfilelimit($view)]] "\\n"]"] r]
     } err]} {
         error_popup "[mc "Error executing git log:"] $err"
         return
@@ -10231,10 +10251,16 @@
             foreach id $seeds {
                 lappend ids "^$id"
             }
+            lappend ids "--"
         }
     }
     if {$ids ne {}} {
-        set fd [open [concat $cmd $ids] r]
+        if {$ids eq "--all"} {
+            set cmd [concat $cmd "--all"]
+        } else {
+            set cmd [concat $cmd --stdin "<<[join $ids "\\n"]"]
+        }
+        set fd [open $cmd r]
         fconfigure $fd -blocking 0
         incr allcommits
         nowbusy allcommits
@@ -11930,7 +11956,7 @@
 }
 
 # This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
 # Not all of them are supported by Tcl.
 set encoding_aliases {
     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
@@ -12446,7 +12472,7 @@
 
 catch {
     # follow the XDG base directory specification by default. See
-    # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+    # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
     if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} {
         # XDG_CONFIG_HOME environment variable is set
         set config_file [file join $env(XDG_CONFIG_HOME) git gitk]
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index a58e6b3..5bfa496 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -29,7 +29,7 @@
 ------------
 
  - Core git tools
- - Perl 5.8
+ - Perl 5.8.1
  - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
  - web server
 
@@ -203,7 +203,7 @@
    created.  [Default: /etc/gitweb.conf]
  * HIGHLIGHT_BIN
    Path to the highlight executable to use (must be the one from
-   http://www.andre-simon.de due to assumptions about parameters and output).
+   http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
    Useful if highlight is not installed on your webserver's PATH.
    [Default: highlight]
 
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index e66eb3d..ccd14e0 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -7,7 +7,7 @@
 #
 # This program is licensed under the GPLv2
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 # handle ACL in file access tests
@@ -122,9 +122,9 @@
 our $javascript = "++GITWEB_JS++";
 
 # URI and label (title) of GIT logo link
-#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
+#our $logo_url = "https://www.kernel.org/pub/software/scm/git/docs/";
 #our $logo_label = "git documentation";
-our $logo_url = "http://git-scm.com/";
+our $logo_url = "https://git-scm.com/";
 our $logo_label = "git homepage";
 
 # source of projects list
@@ -197,7 +197,7 @@
 our $prevent_xss = 0;
 
 # Path to the highlight executable to use (must be the one from
-# http://www.andre-simon.de due to assumptions about parameters and output).
+# http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
 # Useful if highlight is not installed on your webserver's PATH.
 # [Default: highlight]
 our $highlight_bin = "++HIGHLIGHT_BIN++";
@@ -269,7 +269,7 @@
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
-# configuration for 'highlight' (http://www.andre-simon.de/)
+# configuration for 'highlight' (http://andre-simon.de/doku/highlight/en/highlight.php)
 # match by basename
 our %highlight_basename = (
 	#'Program' => 'py',
@@ -728,9 +728,11 @@
 sub read_config_file {
 	my $filename = shift;
 	return unless defined $filename;
-	# die if there are errors parsing config file
 	if (-e $filename) {
 		do $filename;
+		# die if there is a problem accessing the file
+		die $! if $!;
+		# die if there are errors parsing config file
 		die $@ if $@;
 		return 1;
 	}
@@ -8193,7 +8195,7 @@
 	my $have_blame = gitweb_check_feature('blame');
 
 	# Atom: http://www.atomenabled.org/developers/syndication/
-	# RSS:  http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+	# RSS:  https://web.archive.org/web/20030729001534/http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
 	if ($format ne 'rss' && $format ne 'atom') {
 		die_error(400, "Unknown web feed format");
 	}
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index 3212601..48d2e51 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -667,7 +667,7 @@
 }
 
 
-/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+/* Style definition generated by highlight 2.4.5, http://andre-simon.de/doku/highlight/en/highlight.php */
 
 /* Highlighting theme definition: */
 
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index 018bbb7..99e3eb8 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -123,8 +123,8 @@
  * NOTE that there are limits and differences compared to native
  * getElementsByClassName as defined by e.g.:
  *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName
- *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
- *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
+ *   https://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
+ *   https://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
  *
  * Namely, this implementation supports only single class name as
  * argument and not set of space-separated tokens representing classes,
@@ -133,11 +133,11 @@
  * (via getElementsByTagName).
  *
  * Based on
- *   http://code.google.com/p/getelementsbyclassname/
+ *   https://code.google.com/p/getelementsbyclassname/
  *   http://www.dustindiaz.com/getelementsbyclass/
- *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
+ *   https://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
  *
- * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
+ * See also https://johnresig.com/blog/getelementsbyclassname-speed-comparison/
  *
  * @param {String} class: name of _single_ class to find
  * @param {String} [taghint] limit search to given tags
diff --git a/gpg-interface.c b/gpg-interface.c
index 5cd66d3..b599338 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1,14 +1,31 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "config.h"
+#include "date.h"
+#include "gettext.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "dir.h"
+#include "ident.h"
 #include "gpg-interface.h"
+#include "path.h"
 #include "sigchain.h"
 #include "tempfile.h"
 #include "alias.h"
 
+static int git_gpg_config(const char *, const char *,
+			  const struct config_context *, void *);
+
+static void gpg_interface_lazy_init(void)
+{
+	static int done;
+
+	if (done)
+		return;
+	done = 1;
+	git_config(git_gpg_config, NULL);
+}
+
 static char *configured_signing_key;
 static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
 static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
@@ -569,8 +586,8 @@
 		}
 	}
 
-	strbuf_stripspace(&ssh_keygen_out, 0);
-	strbuf_stripspace(&ssh_keygen_err, 0);
+	strbuf_stripspace(&ssh_keygen_out, NULL);
+	strbuf_stripspace(&ssh_keygen_err, NULL);
 	/* Add stderr outputs to show the user actual ssh-keygen errors */
 	strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
 	strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
@@ -632,8 +649,10 @@
 	struct gpg_format *fmt;
 	int status;
 
+	gpg_interface_lazy_init();
+
 	sigc->result = 'N';
-	sigc->trust_level = -1;
+	sigc->trust_level = TRUST_UNDEFINED;
 
 	fmt = get_format_by_sig(signature);
 	if (!fmt)
@@ -695,11 +714,15 @@
 
 void set_signing_key(const char *key)
 {
+	gpg_interface_lazy_init();
+
 	free(configured_signing_key);
 	configured_signing_key = xstrdup(key);
 }
 
-int git_gpg_config(const char *var, const char *value, void *cb UNUSED)
+static int git_gpg_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED,
+			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
 	char *fmtname = NULL;
@@ -738,23 +761,14 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
-		if (!value)
-			return config_error_nonbool(var);
+	if (!strcmp(var, "gpg.ssh.defaultkeycommand"))
 		return git_config_string(&ssh_default_key_command, var, value);
-	}
 
-	if (!strcmp(var, "gpg.ssh.allowedsignersfile")) {
-		if (!value)
-			return config_error_nonbool(var);
+	if (!strcmp(var, "gpg.ssh.allowedsignersfile"))
 		return git_config_pathname(&ssh_allowed_signers, var, value);
-	}
 
-	if (!strcmp(var, "gpg.ssh.revocationfile")) {
-		if (!value)
-			return config_error_nonbool(var);
+	if (!strcmp(var, "gpg.ssh.revocationfile"))
 		return git_config_pathname(&ssh_revocation_file, var, value);
-	}
 
 	if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
 		fmtname = "openpgp";
@@ -888,6 +902,8 @@
 /* Returns a textual but unique representation of the signing key */
 const char *get_signing_key_id(void)
 {
+	gpg_interface_lazy_init();
+
 	if (use_format->get_key_id) {
 		return use_format->get_key_id();
 	}
@@ -898,6 +914,8 @@
 
 const char *get_signing_key(void)
 {
+	gpg_interface_lazy_init();
+
 	if (configured_signing_key)
 		return configured_signing_key;
 	if (use_format->get_default_key) {
@@ -923,6 +941,8 @@
 
 int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
 {
+	gpg_interface_lazy_init();
+
 	return use_format->sign_buffer(buffer, signature, signing_key);
 }
 
@@ -1025,7 +1045,7 @@
 		ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL);
 	} else {
 		/* We assume a file */
-		ssh_signing_key_file = expand_user_path(signing_key, 1);
+		ssh_signing_key_file = interpolate_path(signing_key, 1);
 	}
 
 	buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX");
@@ -1058,7 +1078,7 @@
 		if (strstr(signer_stderr.buf, "usage:"))
 			error(_("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)"));
 
-		error("%s", signer_stderr.buf);
+		ret = error("%s", signer_stderr.buf);
 		goto out;
 	}
 
diff --git a/gpg-interface.h b/gpg-interface.h
index 8a9ef41..7cd9816 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -66,7 +66,7 @@
  * Create a detached signature for the contents of "buffer" and append
  * it after "signature"; "buffer" and "signature" can be the same
  * strbuf instance, which would cause the detached signature appended
- * at the end.
+ * at the end.  Returns 0 on success, non-zero on failure.
  */
 int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
 		const char *signing_key);
@@ -79,7 +79,6 @@
  */
 const char *gpg_trust_level_to_str(enum signature_trust_level level);
 
-int git_gpg_config(const char *, const char *, void *);
 void set_signing_key(const char *);
 const char *get_signing_key(void);
 
diff --git a/graph.c b/graph.c
index 568b6e7..1ca3477 100644
--- a/graph.c
+++ b/graph.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "config.h"
 #include "commit.h"
 #include "color.h"
@@ -338,7 +339,6 @@
 		diffopt->output_prefix = diff_output_prefix_callback;
 }
 
-
 struct git_graph *graph_init(struct rev_info *opt)
 {
 	struct git_graph *graph = xmalloc(sizeof(struct git_graph));
diff --git a/graph.h b/graph.h
index e88632a..3fd1dcb 100644
--- a/graph.h
+++ b/graph.h
@@ -130,7 +130,7 @@
  * This functions must be called BEFORE graph_init() is called.
  *
  * NOTE: This function isn't used in Git outside graph.c but it is used
- * by CGit (http://git.zx2c4.com/cgit/) to use HTML for colors.
+ * by CGit (https://git.zx2c4.com/cgit/) to use HTML for colors.
  */
 void graph_set_column_colors(const char **colors, unsigned short colors_max);
 
@@ -196,7 +196,7 @@
  * graph_update() is called.
  *
  * NOTE: This function isn't used in Git outside graph.c but it is used
- * by CGit (http://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
+ * by CGit (https://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
  */
 int graph_next_line(struct git_graph *graph, struct strbuf *sb);
 
diff --git a/grep.c b/grep.c
index cee44a7..ac34bfe 100644
--- a/grep.c
+++ b/grep.c
@@ -1,12 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
 #include "grep.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-store-ll.h"
+#include "pretty.h"
 #include "userdiff.h"
 #include "xdiff-interface.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "commit.h"
 #include "quote.h"
 #include "help.h"
 
@@ -14,7 +16,7 @@
 static int grep_source_is_binary(struct grep_source *gs,
 				 struct index_state *istate);
 
-static void std_output(struct grep_opt *opt, const void *buf, size_t size)
+static void std_output(struct grep_opt *opt UNUSED, const void *buf, size_t size)
 {
 	fwrite(buf, size, 1, stdout);
 }
@@ -52,7 +54,8 @@
  * Read the configuration file once and store it in
  * the grep_defaults template.
  */
-int grep_config(const char *var, const char *value, void *cb)
+int grep_config(const char *var, const char *value,
+		const struct config_context *ctx, void *cb)
 {
 	struct grep_opt *opt = cb;
 	const char *slot;
@@ -87,9 +90,9 @@
 	if (!strcmp(var, "color.grep"))
 		opt->color = git_config_colorbool(var, value);
 	if (!strcmp(var, "color.grep.match")) {
-		if (grep_config("color.grep.matchcontext", value, cb) < 0)
+		if (grep_config("color.grep.matchcontext", value, ctx, cb) < 0)
 			return -1;
-		if (grep_config("color.grep.matchselected", value, cb) < 0)
+		if (grep_config("color.grep.matchselected", value, ctx, cb) < 0)
 			return -1;
 	} else if (skip_prefix(var, "color.grep.", &slot)) {
 		int i = LOOKUP_CONFIG(color_grep_slots, slot);
@@ -320,6 +323,15 @@
 	if (!opt->ignore_locale && is_utf8_locale() && !literal)
 		options |= (PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_INVALID_UTF);
 
+#ifndef GIT_PCRE2_VERSION_10_35_OR_HIGHER
+	/*
+	 * Work around a JIT bug related to invalid Unicode character handling
+	 * fixed in 10.35:
+	 * https://github.com/PCRE2Project/pcre2/commit/c21bd977547d
+	 */
+	options &= ~PCRE2_UCP;
+#endif
+
 #ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
 	/* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
 	if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS))
@@ -439,18 +451,20 @@
 	pcre2_general_context_free(p->pcre2_general_context);
 }
 #else /* !USE_LIBPCRE2 */
-static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
+static void compile_pcre2_pattern(struct grep_pat *p UNUSED,
+				  const struct grep_opt *opt UNUSED)
 {
 	die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
 }
 
-static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
-		regmatch_t *match, int eflags)
+static int pcre2match(struct grep_pat *p UNUSED, const char *line UNUSED,
+		      const char *eol UNUSED, regmatch_t *match UNUSED,
+		      int eflags UNUSED)
 {
 	return 1;
 }
 
-static void free_pcre2_pattern(struct grep_pat *p)
+static void free_pcre2_pattern(struct grep_pat *p UNUSED)
 {
 }
 
@@ -607,7 +621,7 @@
 		*list = p->next;
 		x = compile_pattern_or(list);
 		if (!*list || (*list)->token != GREP_CLOSE_PAREN)
-			die("unmatched parenthesis");
+			die("unmatched ( for expression group");
 		*list = (*list)->next;
 		return x;
 	default:
@@ -778,7 +792,7 @@
 	if (p)
 		opt->pattern_expression = compile_pattern_expr(&p);
 	if (p)
-		die("incomplete pattern expression: %s", p->pattern);
+		die("incomplete pattern expression group: %s", p->pattern);
 
 	if (opt->no_body_match && opt->pattern_expression)
 		opt->pattern_expression = grep_not_expr(opt->pattern_expression);
diff --git a/grep.h b/grep.h
index 6075f99..926c087 100644
--- a/grep.h
+++ b/grep.h
@@ -7,6 +7,9 @@
 #if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 36) || PCRE2_MAJOR >= 11
 #define GIT_PCRE2_VERSION_10_36_OR_HIGHER
 #endif
+#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 35) || PCRE2_MAJOR >= 11
+#define GIT_PCRE2_VERSION_10_35_OR_HIGHER
+#endif
 #if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 34) || PCRE2_MAJOR >= 11
 #define GIT_PCRE2_VERSION_10_34_OR_HIGHER
 #endif
@@ -199,7 +202,9 @@
 	.output = std_output, \
 }
 
-int grep_config(const char *var, const char *value, void *);
+struct config_context;
+int grep_config(const char *var, const char *value,
+		const struct config_context *ctx, void *data);
 void grep_init(struct grep_opt *, struct repository *repo);
 
 void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
diff --git a/hash-ll.h b/hash-ll.h
new file mode 100644
index 0000000..2cfde63
--- /dev/null
+++ b/hash-ll.h
@@ -0,0 +1,310 @@
+#ifndef HASH_LL_H
+#define HASH_LL_H
+
+#if defined(SHA1_APPLE)
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(SHA1_OPENSSL)
+#  include <openssl/sha.h>
+#  if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+#    define SHA1_NEEDS_CLONE_HELPER
+#    include "sha1/openssl.h"
+#  endif
+#elif defined(SHA1_DC)
+#include "sha1dc_git.h"
+#else /* SHA1_BLK */
+#include "block-sha1/sha1.h"
+#endif
+
+#if defined(SHA256_NETTLE)
+#include "sha256/nettle.h"
+#elif defined(SHA256_GCRYPT)
+#define SHA256_NEEDS_CLONE_HELPER
+#include "sha256/gcrypt.h"
+#elif defined(SHA256_OPENSSL)
+#  include <openssl/sha.h>
+#  if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+#    define SHA256_NEEDS_CLONE_HELPER
+#    include "sha256/openssl.h"
+#  endif
+#else
+#include "sha256/block/sha256.h"
+#endif
+
+#ifndef platform_SHA_CTX
+/*
+ * platform's underlying implementation of SHA-1; could be OpenSSL,
+ * blk_SHA, Apple CommonCrypto, etc...  Note that the relevant
+ * SHA-1 header may have already defined platform_SHA_CTX for our
+ * own implementations like block-sha1, so we list
+ * the default for OpenSSL compatible SHA-1 implementations here.
+ */
+#define platform_SHA_CTX	SHA_CTX
+#define platform_SHA1_Init	SHA1_Init
+#define platform_SHA1_Update	SHA1_Update
+#define platform_SHA1_Final    	SHA1_Final
+#endif
+
+#define git_SHA_CTX		platform_SHA_CTX
+#define git_SHA1_Init		platform_SHA1_Init
+#define git_SHA1_Update		platform_SHA1_Update
+#define git_SHA1_Final		platform_SHA1_Final
+
+#ifdef platform_SHA1_Clone
+#define git_SHA1_Clone	platform_SHA1_Clone
+#endif
+
+#ifndef platform_SHA256_CTX
+#define platform_SHA256_CTX	SHA256_CTX
+#define platform_SHA256_Init	SHA256_Init
+#define platform_SHA256_Update	SHA256_Update
+#define platform_SHA256_Final	SHA256_Final
+#endif
+
+#define git_SHA256_CTX		platform_SHA256_CTX
+#define git_SHA256_Init		platform_SHA256_Init
+#define git_SHA256_Update	platform_SHA256_Update
+#define git_SHA256_Final	platform_SHA256_Final
+
+#ifdef platform_SHA256_Clone
+#define git_SHA256_Clone	platform_SHA256_Clone
+#endif
+
+#ifdef SHA1_MAX_BLOCK_SIZE
+#include "compat/sha1-chunked.h"
+#undef git_SHA1_Update
+#define git_SHA1_Update		git_SHA1_Update_Chunked
+#endif
+
+#ifndef SHA1_NEEDS_CLONE_HELPER
+static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
+{
+	memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+#ifndef SHA256_NEEDS_CLONE_HELPER
+static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
+{
+	memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+/*
+ * Note that these constants are suitable for indexing the hash_algos array and
+ * comparing against each other, but are otherwise arbitrary, so they should not
+ * be exposed to the user or serialized to disk.  To know whether a
+ * git_hash_algo struct points to some usable hash function, test the format_id
+ * field for being non-zero.  Use the name field for user-visible situations and
+ * the format_id field for fixed-length fields on disk.
+ */
+/* An unknown hash function. */
+#define GIT_HASH_UNKNOWN 0
+/* SHA-1 */
+#define GIT_HASH_SHA1 1
+/* SHA-256  */
+#define GIT_HASH_SHA256 2
+/* Number of algorithms supported (including unknown). */
+#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
+
+/* "sha1", big-endian */
+#define GIT_SHA1_FORMAT_ID 0x73686131
+
+/* The length in bytes and in hex digits of an object name (SHA-1 value). */
+#define GIT_SHA1_RAWSZ 20
+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* "s256", big-endian */
+#define GIT_SHA256_FORMAT_ID 0x73323536
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
+
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
+
+struct object_id {
+	unsigned char hash[GIT_MAX_RAWSZ];
+	int algo;	/* XXX requires 4-byte alignment */
+};
+
+#define GET_OID_QUIETLY           01
+#define GET_OID_COMMIT            02
+#define GET_OID_COMMITTISH        04
+#define GET_OID_TREE             010
+#define GET_OID_TREEISH          020
+#define GET_OID_BLOB             040
+#define GET_OID_FOLLOW_SYMLINKS 0100
+#define GET_OID_RECORD_PATH     0200
+#define GET_OID_ONLY_TO_DIE    04000
+#define GET_OID_REQUIRE_PATH  010000
+#define GET_OID_HASH_ANY      020000
+
+#define GET_OID_DISAMBIGUATORS \
+	(GET_OID_COMMIT | GET_OID_COMMITTISH | \
+	GET_OID_TREE | GET_OID_TREEISH | \
+	GET_OID_BLOB)
+
+enum get_oid_result {
+	FOUND = 0,
+	MISSING_OBJECT = -1, /* The requested object is missing */
+	SHORT_NAME_AMBIGUOUS = -2,
+	/* The following only apply when symlinks are followed */
+	DANGLING_SYMLINK = -4, /*
+				* The initial symlink is there, but
+				* (transitively) points to a missing
+				* in-tree file
+				*/
+	SYMLINK_LOOP = -5,
+	NOT_DIR = -6, /*
+		       * Somewhere along the symlink chain, a path is
+		       * requested which contains a file as a
+		       * non-final element.
+		       */
+};
+
+/* A suitably aligned type for stack allocations of hash contexts. */
+union git_hash_ctx {
+	git_SHA_CTX sha1;
+	git_SHA256_CTX sha256;
+};
+typedef union git_hash_ctx git_hash_ctx;
+
+typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
+typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
+typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
+typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
+typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
+
+struct git_hash_algo {
+	/*
+	 * The name of the algorithm, as appears in the config file and in
+	 * messages.
+	 */
+	const char *name;
+
+	/* A four-byte version identifier, used in pack indices. */
+	uint32_t format_id;
+
+	/* The length of the hash in binary. */
+	size_t rawsz;
+
+	/* The length of the hash in hex characters. */
+	size_t hexsz;
+
+	/* The block size of the hash. */
+	size_t blksz;
+
+	/* The hash initialization function. */
+	git_hash_init_fn init_fn;
+
+	/* The hash context cloning function. */
+	git_hash_clone_fn clone_fn;
+
+	/* The hash update function. */
+	git_hash_update_fn update_fn;
+
+	/* The hash finalization function. */
+	git_hash_final_fn final_fn;
+
+	/* The hash finalization function for object IDs. */
+	git_hash_final_oid_fn final_oid_fn;
+
+	/* The OID of the empty tree. */
+	const struct object_id *empty_tree;
+
+	/* The OID of the empty blob. */
+	const struct object_id *empty_blob;
+
+	/* The all-zeros OID. */
+	const struct object_id *null_oid;
+};
+extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
+
+/*
+ * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if
+ * the name doesn't match a known algorithm.
+ */
+int hash_algo_by_name(const char *name);
+/* Identical, except based on the format ID. */
+int hash_algo_by_id(uint32_t format_id);
+/* Identical, except based on the length. */
+int hash_algo_by_length(int len);
+/* Identical, except for a pointer to struct git_hash_algo. */
+static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
+{
+	return p - hash_algos;
+}
+
+const struct object_id *null_oid(void);
+
+static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+	/*
+	 * Teach the compiler that there are only two possibilities of hash size
+	 * here, so that it can optimize for this case as much as possible.
+	 */
+	if (algop->rawsz == GIT_MAX_RAWSZ)
+		return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+	return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+	/*
+	 * We write this here instead of deferring to hashcmp so that the
+	 * compiler can properly inline it and avoid calling memcmp.
+	 */
+	if (algop->rawsz == GIT_MAX_RAWSZ)
+		return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+	return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline void oidcpy(struct object_id *dst, const struct object_id *src)
+{
+	memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
+	dst->algo = src->algo;
+}
+
+static inline struct object_id *oiddup(const struct object_id *src)
+{
+	struct object_id *dst = xmalloc(sizeof(struct object_id));
+	oidcpy(dst, src);
+	return dst;
+}
+
+static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
+{
+	oid->algo = hash_algo_by_ptr(algop);
+}
+
+/*
+ * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
+ * for use in hash tables. Cryptographic hashes are supposed to have
+ * uniform distribution, so in contrast to `memhash()`, this just copies
+ * the first `sizeof(int)` bytes without shuffling any bits. Note that
+ * the results will be different on big-endian and little-endian
+ * platforms, so they should not be stored or transferred over the net.
+ */
+static inline unsigned int oidhash(const struct object_id *oid)
+{
+	/*
+	 * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
+	 * platforms that don't support unaligned reads.
+	 */
+	unsigned int hash;
+	memcpy(&hash, oid->hash, sizeof(hash));
+	return hash;
+}
+
+const char *empty_tree_oid_hex(void);
+const char *empty_blob_oid_hex(void);
+
+#endif
diff --git a/hash-lookup.c b/hash-lookup.c
index b98ed5e..9f0f95e 100644
--- a/hash-lookup.c
+++ b/hash-lookup.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash.h"
 #include "hash-lookup.h"
+#include "read-cache-ll.h"
 
 static uint32_t take2(const struct object_id *oid, size_t ofs)
 {
diff --git a/hash.h b/hash.h
index 36b6416..e064807 100644
--- a/hash.h
+++ b/hash.h
@@ -1,217 +1,11 @@
 #ifndef HASH_H
 #define HASH_H
 
-#include "git-compat-util.h"
+#include "hash-ll.h"
 #include "repository.h"
 
-#if defined(SHA1_APPLE)
-#include <CommonCrypto/CommonDigest.h>
-#elif defined(SHA1_OPENSSL)
-#include <openssl/sha.h>
-#elif defined(SHA1_DC)
-#include "sha1dc_git.h"
-#else /* SHA1_BLK */
-#include "block-sha1/sha1.h"
-#endif
-
-#if defined(SHA256_NETTLE)
-#include "sha256/nettle.h"
-#elif defined(SHA256_GCRYPT)
-#define SHA256_NEEDS_CLONE_HELPER
-#include "sha256/gcrypt.h"
-#elif defined(SHA256_OPENSSL)
-#include <openssl/sha.h>
-#else
-#include "sha256/block/sha256.h"
-#endif
-
-#ifndef platform_SHA_CTX
-/*
- * platform's underlying implementation of SHA-1; could be OpenSSL,
- * blk_SHA, Apple CommonCrypto, etc...  Note that the relevant
- * SHA-1 header may have already defined platform_SHA_CTX for our
- * own implementations like block-sha1, so we list
- * the default for OpenSSL compatible SHA-1 implementations here.
- */
-#define platform_SHA_CTX	SHA_CTX
-#define platform_SHA1_Init	SHA1_Init
-#define platform_SHA1_Update	SHA1_Update
-#define platform_SHA1_Final    	SHA1_Final
-#endif
-
-#define git_SHA_CTX		platform_SHA_CTX
-#define git_SHA1_Init		platform_SHA1_Init
-#define git_SHA1_Update		platform_SHA1_Update
-#define git_SHA1_Final		platform_SHA1_Final
-
-#ifndef platform_SHA256_CTX
-#define platform_SHA256_CTX	SHA256_CTX
-#define platform_SHA256_Init	SHA256_Init
-#define platform_SHA256_Update	SHA256_Update
-#define platform_SHA256_Final	SHA256_Final
-#endif
-
-#define git_SHA256_CTX		platform_SHA256_CTX
-#define git_SHA256_Init		platform_SHA256_Init
-#define git_SHA256_Update	platform_SHA256_Update
-#define git_SHA256_Final	platform_SHA256_Final
-
-#ifdef platform_SHA256_Clone
-#define git_SHA256_Clone	platform_SHA256_Clone
-#endif
-
-#ifdef SHA1_MAX_BLOCK_SIZE
-#include "compat/sha1-chunked.h"
-#undef git_SHA1_Update
-#define git_SHA1_Update		git_SHA1_Update_Chunked
-#endif
-
-static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-
-#ifndef SHA256_NEEDS_CLONE_HELPER
-static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
-{
-	memcpy(dst, src, sizeof(*dst));
-}
-#endif
-
-/*
- * Note that these constants are suitable for indexing the hash_algos array and
- * comparing against each other, but are otherwise arbitrary, so they should not
- * be exposed to the user or serialized to disk.  To know whether a
- * git_hash_algo struct points to some usable hash function, test the format_id
- * field for being non-zero.  Use the name field for user-visible situations and
- * the format_id field for fixed-length fields on disk.
- */
-/* An unknown hash function. */
-#define GIT_HASH_UNKNOWN 0
-/* SHA-1 */
-#define GIT_HASH_SHA1 1
-/* SHA-256  */
-#define GIT_HASH_SHA256 2
-/* Number of algorithms supported (including unknown). */
-#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
-
-/* "sha1", big-endian */
-#define GIT_SHA1_FORMAT_ID 0x73686131
-
-/* The length in bytes and in hex digits of an object name (SHA-1 value). */
-#define GIT_SHA1_RAWSZ 20
-#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
-/* The block size of SHA-1. */
-#define GIT_SHA1_BLKSZ 64
-
-/* "s256", big-endian */
-#define GIT_SHA256_FORMAT_ID 0x73323536
-
-/* The length in bytes and in hex digits of an object name (SHA-256 value). */
-#define GIT_SHA256_RAWSZ 32
-#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
-/* The block size of SHA-256. */
-#define GIT_SHA256_BLKSZ 64
-
-/* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
-/* The largest possible block size for any supported hash. */
-#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
-
-struct object_id {
-	unsigned char hash[GIT_MAX_RAWSZ];
-	int algo;	/* XXX requires 4-byte alignment */
-};
-
-/* A suitably aligned type for stack allocations of hash contexts. */
-union git_hash_ctx {
-	git_SHA_CTX sha1;
-	git_SHA256_CTX sha256;
-};
-typedef union git_hash_ctx git_hash_ctx;
-
-typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
-typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
-typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
-typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
-typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
-
-struct git_hash_algo {
-	/*
-	 * The name of the algorithm, as appears in the config file and in
-	 * messages.
-	 */
-	const char *name;
-
-	/* A four-byte version identifier, used in pack indices. */
-	uint32_t format_id;
-
-	/* The length of the hash in binary. */
-	size_t rawsz;
-
-	/* The length of the hash in hex characters. */
-	size_t hexsz;
-
-	/* The block size of the hash. */
-	size_t blksz;
-
-	/* The hash initialization function. */
-	git_hash_init_fn init_fn;
-
-	/* The hash context cloning function. */
-	git_hash_clone_fn clone_fn;
-
-	/* The hash update function. */
-	git_hash_update_fn update_fn;
-
-	/* The hash finalization function. */
-	git_hash_final_fn final_fn;
-
-	/* The hash finalization function for object IDs. */
-	git_hash_final_oid_fn final_oid_fn;
-
-	/* The OID of the empty tree. */
-	const struct object_id *empty_tree;
-
-	/* The OID of the empty blob. */
-	const struct object_id *empty_blob;
-
-	/* The all-zeros OID. */
-	const struct object_id *null_oid;
-};
-extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
-
-/*
- * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if
- * the name doesn't match a known algorithm.
- */
-int hash_algo_by_name(const char *name);
-/* Identical, except based on the format ID. */
-int hash_algo_by_id(uint32_t format_id);
-/* Identical, except based on the length. */
-int hash_algo_by_length(int len);
-/* Identical, except for a pointer to struct git_hash_algo. */
-static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
-{
-	return p - hash_algos;
-}
-
 #define the_hash_algo the_repository->hash_algo
 
-const struct object_id *null_oid(void);
-
-static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
-	/*
-	 * Teach the compiler that there are only two possibilities of hash size
-	 * here, so that it can optimize for this case as much as possible.
-	 */
-	if (algop->rawsz == GIT_MAX_RAWSZ)
-		return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
-	return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
 	return hashcmp_algop(sha1, sha2, the_hash_algo);
@@ -227,17 +21,6 @@
 	return hashcmp_algop(oid1->hash, oid2->hash, algop);
 }
 
-static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
-	/*
-	 * We write this here instead of deferring to hashcmp so that the
-	 * compiler can properly inline it and avoid calling memcmp.
-	 */
-	if (algop->rawsz == GIT_MAX_RAWSZ)
-		return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
-	return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
 static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 {
 	return hasheq_algop(sha1, sha2, the_hash_algo);
@@ -263,12 +46,6 @@
 	memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
 }
 
-static inline void oidcpy(struct object_id *dst, const struct object_id *src)
-{
-	memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
-	dst->algo = src->algo;
-}
-
 /* Like oidcpy() but zero-pads the unused bytes in dst's hash array. */
 static inline void oidcpy_with_padding(struct object_id *dst,
 				       const struct object_id *src)
@@ -285,13 +62,6 @@
 	dst->algo = src->algo;
 }
 
-static inline struct object_id *oiddup(const struct object_id *src)
-{
-	struct object_id *dst = xmalloc(sizeof(struct object_id));
-	oidcpy(dst, src);
-	return dst;
-}
-
 static inline void hashclr(unsigned char *hash)
 {
 	memset(hash, 0, the_hash_algo->rawsz);
@@ -303,10 +73,15 @@
 	oid->algo = hash_algo_by_ptr(the_hash_algo);
 }
 
+static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
+{
+	memcpy(oid->hash, hash, algop->rawsz);
+	oid->algo = hash_algo_by_ptr(algop);
+}
+
 static inline void oidread(struct object_id *oid, const unsigned char *hash)
 {
-	memcpy(oid->hash, hash, the_hash_algo->rawsz);
-	oid->algo = hash_algo_by_ptr(the_hash_algo);
+	oidread_algop(oid, hash, the_hash_algo);
 }
 
 static inline int is_empty_blob_sha1(const unsigned char *sha1)
@@ -329,12 +104,4 @@
 	return oideq(oid, the_hash_algo->empty_tree);
 }
 
-static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
-{
-	oid->algo = hash_algo_by_ptr(algop);
-}
-
-const char *empty_tree_oid_hex(void);
-const char *empty_blob_oid_hex(void);
-
 #endif
diff --git a/hashmap.c b/hashmap.c
index cf5fea8..ee45ef0 100644
--- a/hashmap.c
+++ b/hashmap.c
@@ -1,7 +1,7 @@
 /*
  * Generic implementation of hash-based key value mappings.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "hashmap.h"
 
 #define FNV32_BASE ((unsigned int) 0x811c9dc5)
diff --git a/hashmap.h b/hashmap.h
index 7251687..c8216e4 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -1,8 +1,6 @@
 #ifndef HASHMAP_H
 #define HASHMAP_H
 
-#include "hash.h"
-
 /*
  * Generic implementation of hash-based key-value mappings.
  *
@@ -121,25 +119,6 @@
 unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
 
 /*
- * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
- * for use in hash tables. Cryptographic hashes are supposed to have
- * uniform distribution, so in contrast to `memhash()`, this just copies
- * the first `sizeof(int)` bytes without shuffling any bits. Note that
- * the results will be different on big-endian and little-endian
- * platforms, so they should not be stored or transferred over the net.
- */
-static inline unsigned int oidhash(const struct object_id *oid)
-{
-	/*
-	 * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
-	 * platforms that don't support unaligned reads.
-	 */
-	unsigned int hash;
-	memcpy(&hash, oid->hash, sizeof(hash));
-	return hash;
-}
-
-/*
  * struct hashmap_entry is an opaque structure representing an entry in the
  * hash table.
  * Ideally it should be followed by an int-sized member to prevent unused
@@ -270,7 +249,7 @@
 #define hashmap_clear(map) hashmap_clear_(map, -1)
 
 /*
- * Similar to hashmap_clear(), except that the table is no deallocated; it
+ * Similar to hashmap_clear(), except that the table is not deallocated; it
  * is merely zeroed out but left the same size as before.  If the hashmap
  * will be reused, this avoids the overhead of deallocating and
  * reallocating map->table.  As with hashmap_clear(), you may need to free
diff --git a/help.c b/help.c
index 812af4c..2dbe57b 100644
--- a/help.c
+++ b/help.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "builtin.h"
 #include "exec-cmd.h"
 #include "run-command.h"
 #include "levenshtein.h"
+#include "gettext.h"
 #include "help.h"
 #include "command-list.h"
 #include "string-list.h"
@@ -307,7 +308,8 @@
 	exclude_cmds(other_cmds, main_cmds);
 }
 
-static int get_colopts(const char *var, const char *value, void *data)
+static int get_colopts(const char *var, const char *value,
+		       const struct config_context *ctx UNUSED, void *data)
 {
 	unsigned int *colopts = data;
 
@@ -457,12 +459,16 @@
 	putchar('\n');
 }
 
-static int get_alias(const char *var, const char *value, void *data)
+static int get_alias(const char *var, const char *value,
+		     const struct config_context *ctx UNUSED, void *data)
 {
 	struct string_list *list = data;
 
-	if (skip_prefix(var, "alias.", &var))
+	if (skip_prefix(var, "alias.", &var)) {
+		if (!value)
+			return config_error_nonbool(var);
 		string_list_append(list, var)->util = xstrdup(value);
+	}
 
 	return 0;
 }
@@ -540,7 +546,9 @@
 #define AUTOCORRECT_NEVER (-2)
 #define AUTOCORRECT_IMMEDIATELY (-1)
 
-static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
+static int git_unknown_cmd_config(const char *var, const char *value,
+				  const struct config_context *ctx,
+				  void *cb UNUSED)
 {
 	const char *p;
 
@@ -554,7 +562,7 @@
 		} else if (!strcmp(value, "prompt")) {
 			autocorrect = AUTOCORRECT_PROMPT;
 		} else {
-			int v = git_config_int(var, value);
+			int v = git_config_int(var, value, ctx->kvi);
 			autocorrect = (v < 0)
 				? AUTOCORRECT_IMMEDIATELY : v;
 		}
diff --git a/hex-ll.c b/hex-ll.c
new file mode 100644
index 0000000..4d7ece1
--- /dev/null
+++ b/hex-ll.c
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "hex-ll.h"
+
+const signed char hexval_table[256] = {
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 00-07 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 08-0f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 10-17 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 18-1f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 20-27 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 28-2f */
+	  0,  1,  2,  3,  4,  5,  6,  7,		/* 30-37 */
+	  8,  9, -1, -1, -1, -1, -1, -1,		/* 38-3f */
+	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 40-47 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 48-4f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 50-57 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 58-5f */
+	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 60-67 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 68-67 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 70-77 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 78-7f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 80-87 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 88-8f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 90-97 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 98-9f */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a0-a7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a8-af */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b0-b7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b8-bf */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c0-c7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c8-cf */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d0-d7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d8-df */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e0-e7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e8-ef */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f0-f7 */
+	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f8-ff */
+};
+
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
+{
+	for (; len; len--, hex += 2) {
+		unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
+		if (val & ~0xff)
+			return -1;
+		*binary++ = val;
+	}
+	return 0;
+}
diff --git a/hex-ll.h b/hex-ll.h
new file mode 100644
index 0000000..a381fa8
--- /dev/null
+++ b/hex-ll.h
@@ -0,0 +1,27 @@
+#ifndef HEX_LL_H
+#define HEX_LL_H
+
+extern const signed char hexval_table[256];
+static inline unsigned int hexval(unsigned char c)
+{
+	return hexval_table[c];
+}
+
+/*
+ * Convert two consecutive hexadecimal digits into a char.  Return a
+ * negative value on error.  Don't run over the end of short strings.
+ */
+static inline int hex2chr(const char *s)
+{
+	unsigned int val = hexval(s[0]);
+	return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
+}
+
+/*
+ * Read `len` pairs of hexadecimal digits from `hex` and write the
+ * values to `binary` as `len` bytes. Return 0 on success, or -1 if
+ * the input does not consist of hex digits).
+ */
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+
+#endif
diff --git a/hex.c b/hex.c
index 4f64d34..d42262b 100644
--- a/hex.c
+++ b/hex.c
@@ -1,51 +1,6 @@
-#include "cache.h"
-
-const signed char hexval_table[256] = {
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 00-07 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 08-0f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 10-17 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 18-1f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 20-27 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 28-2f */
-	  0,  1,  2,  3,  4,  5,  6,  7,		/* 30-37 */
-	  8,  9, -1, -1, -1, -1, -1, -1,		/* 38-3f */
-	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 40-47 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 48-4f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 50-57 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 58-5f */
-	 -1, 10, 11, 12, 13, 14, 15, -1,		/* 60-67 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 68-67 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 70-77 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 78-7f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 80-87 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 88-8f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 90-97 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* 98-9f */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a0-a7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* a8-af */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b0-b7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* b8-bf */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c0-c7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* c8-cf */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d0-d7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* d8-df */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e0-e7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* e8-ef */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f0-f7 */
-	 -1, -1, -1, -1, -1, -1, -1, -1,		/* f8-ff */
-};
-
-int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
-{
-	for (; len; len--, hex += 2) {
-		unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
-
-		if (val & ~0xff)
-			return -1;
-		*binary++ = val;
-	}
-	return 0;
-}
+#include "git-compat-util.h"
+#include "hash.h"
+#include "hex.h"
 
 static int get_hash_hex_algop(const char *hex, unsigned char *hash,
 			      const struct git_hash_algo *algop)
@@ -61,7 +16,7 @@
 	return 0;
 }
 
-int get_sha1_hex(const char *hex, unsigned char *sha1)
+int get_hash_hex(const char *hex, unsigned char *sha1)
 {
 	return get_hash_hex_algop(hex, sha1, the_hash_algo);
 }
diff --git a/hex.h b/hex.h
new file mode 100644
index 0000000..e0b83f7
--- /dev/null
+++ b/hex.h
@@ -0,0 +1,64 @@
+#ifndef HEX_H
+#define HEX_H
+
+#include "hash-ll.h"
+#include "hex-ll.h"
+
+/*
+ * Try to read a hash (specified by the_hash_algo) in hexadecimal
+ * format from the 40 (or whatever length the hash algorithm uses)
+ * characters starting at hex.  Write the 20-byte (or the length of
+ * the hash) result to hash in binary form.
+ * Return 0 on success.  Reading stops if a NUL is encountered in the
+ * input, so it is safe to pass this function an arbitrary
+ * null-terminated string.
+ */
+int get_hash_hex(const char *hex, unsigned char *hash);
+int get_oid_hex(const char *hex, struct object_id *oid);
+
+/* Like get_oid_hex, but for an arbitrary hash algorithm. */
+int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
+
+/*
+ * Convert a binary hash in "unsigned char []" or an object name in
+ * "struct object_id *" to its hex equivalent. The `_r` variant is reentrant,
+ * and writes the NUL-terminated output to the buffer `out`, which must be at
+ * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
+ * convenience.
+ *
+ * The non-`_r` variant returns a static buffer, but uses a ring of 4
+ * buffers, making it safe to make multiple calls for a single statement, like:
+ *
+ *   printf("%s -> %s", hash_to_hex(one), hash_to_hex(two));
+ *   printf("%s -> %s", oid_to_hex(one), oid_to_hex(two));
+ */
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
+char *oid_to_hex_r(char *out, const struct object_id *oid);
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);	/* static buffer result! */
+char *hash_to_hex(const unsigned char *hash);						/* same static buffer */
+char *oid_to_hex(const struct object_id *oid);						/* same static buffer */
+
+/*
+ * Parse a 40-character hexadecimal object ID starting from hex, updating the
+ * pointer specified by end when parsing stops.  The resulting object ID is
+ * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
+ * other invalid character.  end is only updated on success; otherwise, it is
+ * unmodified.
+ */
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
+/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
+int parse_oid_hex_algop(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
+ * a hex value for any algorithm. The algorithm is detected based on the length
+ * and the algorithm in use is returned. If this is not a hex object ID in any
+ * algorithm, returns GIT_HASH_UNKNOWN.
+ */
+int get_oid_hex_any(const char *hex, struct object_id *oid);
+int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
+
+#endif
diff --git a/hook.c b/hook.c
index 1a84831..f6306d7 100644
--- a/hook.c
+++ b/hook.c
@@ -1,7 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
+#include "gettext.h"
 #include "hook.h"
+#include "path.h"
 #include "run-command.h"
 #include "config.h"
+#include "strbuf.h"
 
 const char *find_hook(const char *name)
 {
@@ -43,9 +48,9 @@
 }
 
 static int pick_next_hook(struct child_process *cp,
-			  struct strbuf *out,
+			  struct strbuf *out UNUSED,
 			  void *pp_cb,
-			  void **pp_task_cb)
+			  void **pp_task_cb UNUSED)
 {
 	struct hook_cb_data *hook_cb = pp_cb;
 	const char *hook_path = hook_cb->hook_path;
@@ -77,9 +82,9 @@
 	return 1;
 }
 
-static int notify_start_failure(struct strbuf *out,
+static int notify_start_failure(struct strbuf *out UNUSED,
 				void *pp_cb,
-				void *pp_task_cp)
+				void *pp_task_cp UNUSED)
 {
 	struct hook_cb_data *hook_cb = pp_cb;
 
@@ -89,9 +94,9 @@
 }
 
 static int notify_hook_finished(int result,
-				struct strbuf *out,
+				struct strbuf *out UNUSED,
 				void *pp_cb,
-				void *pp_task_cb)
+				void *pp_task_cb UNUSED)
 {
 	struct hook_cb_data *hook_cb = pp_cb;
 	struct run_hooks_opt *opt = hook_cb->options;
diff --git a/http-backend.c b/http-backend.c
index 8ab58e5..1ed1e29 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -1,5 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "git-zlib.h"
+#include "hex.h"
+#include "path.h"
 #include "repository.h"
 #include "refs.h"
 #include "pkt-line.h"
@@ -11,9 +15,10 @@
 #include "url.h"
 #include "strvec.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "protocol.h"
 #include "date.h"
+#include "write-or-die.h"
 
 static const char content_type[] = "Content-Type";
 static const char content_length[] = "Content-Length";
@@ -33,6 +38,7 @@
 static struct rpc_service rpc_service[] = {
 	{ "upload-pack", "uploadpack", 1, 1 },
 	{ "receive-pack", "receivepack", 0, -1 },
+	{ "upload-archive", "uploadarchive", 0, -1 },
 };
 
 static struct string_list *get_parameters(void)
@@ -524,7 +530,7 @@
 	return 0;
 }
 
-static void get_info_refs(struct strbuf *hdr, char *arg)
+static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
 {
 	const char *service_name = get_parameter("service");
 	struct strbuf buf = STRBUF_INIT;
@@ -553,7 +559,7 @@
 
 	} else {
 		select_getanyfile(hdr);
-		for_each_namespaced_ref(show_text_ref, &buf);
+		for_each_namespaced_ref(NULL, show_text_ref, &buf);
 		send_strbuf(hdr, "text/plain", &buf);
 	}
 	strbuf_release(&buf);
@@ -578,7 +584,7 @@
 	return 0;
 }
 
-static void get_head(struct strbuf *hdr, char *arg)
+static void get_head(struct strbuf *hdr, char *arg UNUSED)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -588,7 +594,7 @@
 	strbuf_release(&buf);
 }
 
-static void get_info_packs(struct strbuf *hdr, char *arg)
+static void get_info_packs(struct strbuf *hdr, char *arg UNUSED)
 {
 	size_t objdirlen = strlen(get_object_directory());
 	struct strbuf buf = STRBUF_INIT;
@@ -634,10 +640,15 @@
 
 static void service_rpc(struct strbuf *hdr, char *service_name)
 {
-	const char *argv[] = {NULL, "--stateless-rpc", ".", NULL};
+	struct strvec argv = STRVEC_INIT;
 	struct rpc_service *svc = select_service(hdr, service_name);
 	struct strbuf buf = STRBUF_INIT;
 
+	strvec_push(&argv, svc->name);
+	if (strcmp(service_name, "git-upload-archive"))
+		strvec_push(&argv, "--stateless-rpc");
+	strvec_push(&argv, ".");
+
 	strbuf_reset(&buf);
 	strbuf_addf(&buf, "application/x-git-%s-request", svc->name);
 	check_content_type(hdr, buf.buf);
@@ -650,9 +661,9 @@
 
 	end_headers(hdr);
 
-	argv[0] = svc->name;
-	run_service(argv, svc->buffer_input);
+	run_service(argv.v, svc->buffer_input);
 	strbuf_release(&buf);
+	strvec_clear(&argv);
 }
 
 static int dead;
@@ -718,6 +729,7 @@
 	{"GET", "/objects/pack/pack-[0-9a-f]{64}\\.idx$", get_idx_file},
 
 	{"POST", "/git-upload-pack$", service_rpc},
+	{"POST", "/git-upload-archive$", service_rpc},
 	{"POST", "/git-receive-pack$", service_rpc}
 };
 
@@ -736,7 +748,7 @@
 	return 0;
 }
 
-int cmd_main(int argc, const char **argv)
+int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
 	char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
diff --git a/http-fetch.c b/http-fetch.c
index 258fec2..bec9498 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -1,9 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "exec-cmd.h"
+#include "gettext.h"
+#include "hex.h"
 #include "http.h"
 #include "walker.h"
+#include "setup.h"
 #include "strvec.h"
+#include "url.h"
 #include "urlmatch.h"
 #include "trace2.h"
 
diff --git a/http-push.c b/http-push.c
index 7f71316..1fe5122 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1,19 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "commit.h"
 #include "tag.h"
 #include "blob.h"
 #include "http.h"
-#include "refs.h"
 #include "diff.h"
 #include "revision.h"
-#include "exec-cmd.h"
 #include "remote.h"
 #include "list-objects.h"
+#include "setup.h"
 #include "sigchain.h"
 #include "strvec.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "url.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit-reach.h"
 
 #ifdef EXPAT_NEEDS_XMLPARSE_H
@@ -362,7 +366,8 @@
 	ssize_t size;
 	git_zstream stream;
 
-	unpacked = read_object_file(&request->obj->oid, &type, &len);
+	unpacked = repo_read_object_file(the_repository, &request->obj->oid,
+					 &type, &len);
 	hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
 
 	/* Set it up */
@@ -601,7 +606,7 @@
 }
 
 static int is_running_queue;
-static int fill_active_slot(void *unused)
+static int fill_active_slot(void *data UNUSED)
 {
 	struct transfer_request *request;
 
@@ -777,7 +782,7 @@
 static void one_remote_ref(const char *refname);
 
 static void
-xml_start_tag(void *userData, const char *name, const char **atts)
+xml_start_tag(void *userData, const char *name, const char **atts UNUSED)
 {
 	struct xml_ctx *ctx = (struct xml_ctx *)userData;
 	const char *c = strchr(name, ':');
@@ -1302,7 +1307,7 @@
 	obj->flags |= SEEN;
 	p = add_one_object(obj, p);
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry))
 		switch (object_type(entry.mode)) {
@@ -1331,7 +1336,8 @@
 	int count = 0;
 
 	while ((commit = get_revision(revs)) != NULL) {
-		p = process_tree(get_commit_tree(commit), p);
+		p = process_tree(repo_get_commit_tree(the_repository, commit),
+				 p);
 		commit->object.flags |= LOCAL;
 		if (!(commit->object.flags & UNINTERESTING))
 			count += add_send_request(&commit->object, lock);
@@ -1426,7 +1432,7 @@
 	 * Fetch a copy of the object if it doesn't exist locally - it
 	 * may be required for updating server info later.
 	 */
-	if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
+	if (repo->can_update_info_refs && !repo_has_object_file(the_repository, &ref->old_oid)) {
 		obj = lookup_unknown_object(the_repository, &ref->old_oid);
 		fprintf(stderr,	"  fetch %s for %s\n",
 			oid_to_hex(&ref->old_oid), refname);
@@ -1569,8 +1575,11 @@
 	struct commit *head = lookup_commit_or_die(head_oid, "HEAD");
 	struct commit *branch = lookup_commit_or_die(&remote->old_oid,
 						     remote->name);
+	int ret = repo_in_merge_bases(the_repository, branch, head);
 
-	return in_merge_bases(branch, head);
+	if (ret < 0)
+		exit(128);
+	return ret;
 }
 
 static int delete_remote_branch(const char *pattern, int force)
@@ -1627,14 +1636,14 @@
 			return error("Remote HEAD symrefs too deep");
 		if (is_null_oid(&head_oid))
 			return error("Unable to resolve remote HEAD");
-		if (!has_object_file(&head_oid))
+		if (!repo_has_object_file(the_repository, &head_oid))
 			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 */
 		if (is_null_oid(&remote_ref->old_oid))
 			return error("Unable to resolve remote branch %s",
 				     remote_ref->name);
-		if (!has_object_file(&remote_ref->old_oid))
+		if (!repo_has_object_file(the_repository, &remote_ref->old_oid))
 			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 */
@@ -1845,6 +1854,7 @@
 
 		if (oideq(&ref->old_oid, &ref->peer_ref->new_oid)) {
 			if (push_verbosely)
+				/* stable plumbing output; do not modify or localize */
 				fprintf(stderr, "'%s': up-to-date\n", ref->name);
 			if (helper_status)
 				printf("ok %s up to date\n", ref->name);
@@ -1854,7 +1864,7 @@
 		if (!force_all &&
 		    !is_null_oid(&ref->old_oid) &&
 		    !ref->force) {
-			if (!has_object_file(&ref->old_oid) ||
+			if (!repo_has_object_file(the_repository, &ref->old_oid) ||
 			    !ref_newer(&ref->peer_ref->new_oid,
 				       &ref->old_oid)) {
 				/*
@@ -1865,6 +1875,7 @@
 				 * commits at the remote end and likely
 				 * we were not up to date to begin with.
 				 */
+				/* stable plumbing output; do not modify or localize */
 				error("remote '%s' is not an ancestor of\n"
 				      "local '%s'.\n"
 				      "Maybe you are not up-to-date and "
diff --git a/http-walker.c b/http-walker.c
index b8f0f98..b395ef1 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -1,12 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "repository.h"
-#include "commit.h"
+#include "hex.h"
 #include "walker.h"
 #include "http.h"
 #include "list.h"
 #include "transport.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 struct alt_base {
 	char *base;
@@ -52,8 +52,7 @@
 
 static void process_object_response(void *callback_data);
 
-static void start_object_request(struct walker *walker,
-				 struct object_request *obj_req)
+static void start_object_request(struct object_request *obj_req)
 {
 	struct active_request_slot *slot;
 	struct http_object_request *req;
@@ -110,7 +109,7 @@
 			obj_req->repo =
 				obj_req->repo->next;
 			release_http_object_request(obj_req->req);
-			start_object_request(walker, obj_req);
+			start_object_request(obj_req);
 			return;
 		}
 	}
@@ -127,7 +126,7 @@
 	free(obj_req);
 }
 
-static int fill_active_slot(struct walker *walker)
+static int fill_active_slot(void *data UNUSED)
 {
 	struct object_request *obj_req;
 	struct list_head *pos, *tmp, *head = &object_queue_head;
@@ -135,10 +134,10 @@
 	list_for_each_safe(pos, tmp, head) {
 		obj_req = list_entry(pos, struct object_request, node);
 		if (obj_req->state == WAITING) {
-			if (has_object_file(&obj_req->oid))
+			if (repo_has_object_file(the_repository, &obj_req->oid))
 				obj_req->state = COMPLETE;
 			else {
-				start_object_request(walker, obj_req);
+				start_object_request(obj_req);
 				return 1;
 			}
 		}
@@ -492,7 +491,7 @@
 	if (!obj_req)
 		return error("Couldn't find request for %s in the queue", hex);
 
-	if (has_object_file(&obj_req->oid)) {
+	if (repo_has_object_file(the_repository, &obj_req->oid)) {
 		if (obj_req->req)
 			abort_http_object_request(obj_req->req);
 		abort_object_request(obj_req);
@@ -613,7 +612,7 @@
 	walker->cleanup = cleanup;
 	walker->data = data;
 
-	add_fill_function(walker, (int (*)(void *)) fill_active_slot);
+	add_fill_function(NULL, fill_active_slot);
 
 	return walker;
 }
diff --git a/http.c b/http.c
index c4b6dde..3d80bd6 100644
--- a/http.c
+++ b/http.c
@@ -1,9 +1,9 @@
 #include "git-compat-util.h"
 #include "git-curl-compat.h"
+#include "hex.h"
 #include "http.h"
 #include "config.h"
 #include "pack.h"
-#include "sideband.h"
 #include "run-command.h"
 #include "url.h"
 #include "urlmatch.h"
@@ -11,11 +11,12 @@
 #include "version.h"
 #include "pkt-line.h"
 #include "gettext.h"
+#include "trace.h"
 #include "transport.h"
 #include "packfile.h"
-#include "protocol.h"
 #include "string-list.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 
 static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
 static int trace_curl_data = 1;
@@ -39,6 +40,7 @@
 static int curl_ssl_try;
 static const char *curl_http_version = NULL;
 static const char *ssl_cert;
+static const char *ssl_cert_type;
 static const char *ssl_cipherlist;
 static const char *ssl_version;
 static struct {
@@ -58,6 +60,7 @@
 #endif
 };
 static const char *ssl_key;
+static const char *ssl_key_type;
 static const char *ssl_capath;
 static const char *curl_no_proxy;
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
@@ -181,7 +184,117 @@
 	return nmemb;
 }
 
-size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
+/*
+ * A folded header continuation line starts with any number of spaces or
+ * horizontal tab characters (SP or HTAB) as per RFC 7230 section 3.2.
+ * It is not a continuation line if the line starts with any other character.
+ */
+static inline int is_hdr_continuation(const char *ptr, const size_t size)
+{
+	return size && (*ptr == ' ' || *ptr == '\t');
+}
+
+static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p UNUSED)
+{
+	size_t size = eltsize * nmemb;
+	struct strvec *values = &http_auth.wwwauth_headers;
+	struct strbuf buf = STRBUF_INIT;
+	const char *val;
+	size_t val_len;
+
+	/*
+	 * Header lines may not come NULL-terminated from libcurl so we must
+	 * limit all scans to the maximum length of the header line, or leverage
+	 * strbufs for all operations.
+	 *
+	 * In addition, it is possible that header values can be split over
+	 * multiple lines as per RFC 7230. 'Line folding' has been deprecated
+	 * but older servers may still emit them. A continuation header field
+	 * value is identified as starting with a space or horizontal tab.
+	 *
+	 * The formal definition of a header field as given in RFC 7230 is:
+	 *
+	 * header-field   = field-name ":" OWS field-value OWS
+	 *
+	 * field-name     = token
+	 * field-value    = *( field-content / obs-fold )
+	 * field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+	 * field-vchar    = VCHAR / obs-text
+	 *
+	 * obs-fold       = CRLF 1*( SP / HTAB )
+	 *                ; obsolete line folding
+	 *                ; see Section 3.2.4
+	 */
+
+	/* Start of a new WWW-Authenticate header */
+	if (skip_iprefix_mem(ptr, size, "www-authenticate:", &val, &val_len)) {
+		strbuf_add(&buf, val, val_len);
+
+		/*
+		 * Strip the CRLF that should be present at the end of each
+		 * field as well as any trailing or leading whitespace from the
+		 * value.
+		 */
+		strbuf_trim(&buf);
+
+		strvec_push(values, buf.buf);
+		http_auth.header_is_last_match = 1;
+		goto exit;
+	}
+
+	/*
+	 * This line could be a continuation of the previously matched header
+	 * field. If this is the case then we should append this value to the
+	 * end of the previously consumed value.
+	 */
+	if (http_auth.header_is_last_match && is_hdr_continuation(ptr, size)) {
+		/*
+		 * Trim the CRLF and any leading or trailing from this line.
+		 */
+		strbuf_add(&buf, ptr, size);
+		strbuf_trim(&buf);
+
+		/*
+		 * At this point we should always have at least one existing
+		 * value, even if it is empty. Do not bother appending the new
+		 * value if this continuation header is itself empty.
+		 */
+		if (!values->nr) {
+			BUG("should have at least one existing header value");
+		} else if (buf.len) {
+			char *prev = xstrdup(values->v[values->nr - 1]);
+
+			/* Join two non-empty values with a single space. */
+			const char *const sp = *prev ? " " : "";
+
+			strvec_pop(values);
+			strvec_pushf(values, "%s%s%s", prev, sp, buf.buf);
+			free(prev);
+		}
+
+		goto exit;
+	}
+
+	/* Not a continuation of a previously matched auth header line. */
+	http_auth.header_is_last_match = 0;
+
+	/*
+	 * If this is a HTTP status line and not a header field, this signals
+	 * a different HTTP response. libcurl writes all the output of all
+	 * response headers of all responses, including redirects.
+	 * We only care about the last HTTP request response's headers so clear
+	 * the existing array.
+	 */
+	if (skip_iprefix_mem(ptr, size, "http/", &val, &val_len))
+		strvec_clear(values);
+
+exit:
+	strbuf_release(&buf);
+	return size;
+}
+
+size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
+		   void *data UNUSED)
 {
 	return nmemb;
 }
@@ -249,7 +362,8 @@
 	}
 }
 
-static int http_options(const char *var, const char *value, void *cb)
+static int http_options(const char *var, const char *value,
+			const struct config_context *ctx, void *data)
 {
 	if (!strcmp("http.version", var)) {
 		return git_config_string(&curl_http_version, var, value);
@@ -264,8 +378,12 @@
 		return git_config_string(&ssl_version, var, value);
 	if (!strcmp("http.sslcert", var))
 		return git_config_pathname(&ssl_cert, var, value);
+	if (!strcmp("http.sslcerttype", var))
+		return git_config_string(&ssl_cert_type, var, value);
 	if (!strcmp("http.sslkey", var))
 		return git_config_pathname(&ssl_key, var, value);
+	if (!strcmp("http.sslkeytype", var))
+		return git_config_string(&ssl_key_type, var, value);
 	if (!strcmp("http.sslcapath", var))
 		return git_config_pathname(&ssl_capath, var, value);
 	if (!strcmp("http.sslcainfo", var))
@@ -295,21 +413,21 @@
 	}
 
 	if (!strcmp("http.minsessions", var)) {
-		min_curl_sessions = git_config_int(var, value);
+		min_curl_sessions = git_config_int(var, value, ctx->kvi);
 		if (min_curl_sessions > 1)
 			min_curl_sessions = 1;
 		return 0;
 	}
 	if (!strcmp("http.maxrequests", var)) {
-		max_requests = git_config_int(var, value);
+		max_requests = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedlimit", var)) {
-		curl_low_speed_limit = (long)git_config_int(var, value);
+		curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp("http.lowspeedtime", var)) {
-		curl_low_speed_time = (long)git_config_int(var, value);
+		curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 
@@ -345,7 +463,7 @@
 	}
 
 	if (!strcmp("http.postbuffer", var)) {
-		http_post_buffer = git_config_ssize_t(var, value);
+		http_post_buffer = git_config_ssize_t(var, value, ctx->kvi);
 		if (http_post_buffer < 0)
 			warning(_("negative value for http.postBuffer; defaulting to %d"), LARGE_PACKET_MAX);
 		if (http_post_buffer < LARGE_PACKET_MAX)
@@ -416,7 +534,7 @@
 	}
 
 	/* Fall back on the default ones */
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, data);
 }
 
 static int curl_empty_auth_enabled(void)
@@ -618,17 +736,43 @@
 	return ret;
 }
 
+static int match_curl_h2_trace(const char *line, const char **out)
+{
+	const char *p;
+
+	/*
+	 * curl prior to 8.1.0 gives us:
+	 *
+	 *     h2h3 [<header-name>: <header-val>]
+	 *
+	 * Starting in 8.1.0, the first token became just "h2".
+	 */
+	if (skip_iprefix(line, "h2h3 [", out) ||
+	    skip_iprefix(line, "h2 [", out))
+		return 1;
+
+	/*
+	 * curl 8.3.0 uses:
+	 *   [HTTP/2] [<stream-id>] [<header-name>: <header-val>]
+	 * where <stream-id> is numeric.
+	 */
+	if (skip_iprefix(line, "[HTTP/2] [", &p)) {
+		while (isdigit(*p))
+			p++;
+		if (skip_prefix(p, "] [", out))
+			return 1;
+	}
+
+	return 0;
+}
+
 /* Redact headers in info */
 static void redact_sensitive_info_header(struct strbuf *header)
 {
 	const char *sensitive_header;
 
-	/*
-	 * curl's h2h3 prints headers in info, e.g.:
-	 *   h2h3 [<header-name>: <header-val>]
-	 */
 	if (trace_curl_redact &&
-	    skip_iprefix(header->buf, "h2h3 [", &sensitive_header)) {
+	    match_curl_h2_trace(header->buf, &sensitive_header)) {
 		if (redact_sensitive_header(header, sensitive_header - header->buf)) {
 			/* redaction ate our closing bracket */
 			strbuf_addch(header, ']');
@@ -701,7 +845,9 @@
 	strbuf_release(&buf);
 }
 
-static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp)
+static int curl_trace(CURL *handle UNUSED, curl_infotype type,
+		      char *data, size_t size,
+		      void *userp UNUSED)
 {
 	const char *text;
 	enum { NO_FILTER = 0, DO_FILTER = 1 };
@@ -904,10 +1050,14 @@
 
 	if (ssl_cert)
 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
+	if (ssl_cert_type)
+		curl_easy_setopt(result, CURLOPT_SSLCERTTYPE, ssl_cert_type);
 	if (has_cert_password())
 		curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
 	if (ssl_key)
 		curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
+	if (ssl_key_type)
+		curl_easy_setopt(result, CURLOPT_SSLKEYTYPE, ssl_key_type);
 	if (ssl_capath)
 		curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
@@ -1142,7 +1292,9 @@
 		curl_ssl_verify = 0;
 
 	set_from_env(&ssl_cert, "GIT_SSL_CERT");
+	set_from_env(&ssl_cert_type, "GIT_SSL_CERT_TYPE");
 	set_from_env(&ssl_key, "GIT_SSL_KEY");
+	set_from_env(&ssl_key_type, "GIT_SSL_KEY_TYPE");
 	set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
 	set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
@@ -1300,6 +1452,7 @@
 	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
 	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
 	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
+	curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, -1L);
 	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
 	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 	curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
@@ -1748,7 +1901,7 @@
 	 * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
 	 * that, q-value will be smaller than 0.001, the minimum q-value the
 	 * HTTP specification allows. See
-	 * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
+	 * https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1 for q-value.
 	 */
 	const int MAX_DECIMAL_PLACES = 3;
 	const int MAX_LANGUAGE_TAGS = 1000;
@@ -1895,6 +2048,8 @@
 					 fwrite_buffer);
 	}
 
+	curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION, fwrite_wwwauth);
+
 	accept_language = http_get_accept_language_header();
 
 	if (accept_language)
diff --git a/http.h b/http.h
index 77c0427..3af19a8 100644
--- a/http.h
+++ b/http.h
@@ -1,14 +1,15 @@
 #ifndef HTTP_H
 #define HTTP_H
 
-#include "cache.h"
+struct packed_git;
+
+#include "git-zlib.h"
 
 #include <curl/curl.h>
 #include <curl/easy.h>
 
 #include "strbuf.h"
 #include "remote.h"
-#include "url.h"
 
 #define DEFAULT_MAX_REQUESTS 5
 
diff --git a/ident.c b/ident.c
index 6de76f9..cc7afdb 100644
--- a/ident.c
+++ b/ident.c
@@ -5,10 +5,13 @@
  *
  * Copyright (C) 2005 Linus Torvalds
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "ident.h"
 #include "config.h"
 #include "date.h"
+#include "gettext.h"
 #include "mailmap.h"
+#include "strbuf.h"
 
 static struct strbuf git_default_name = STRBUF_INIT;
 static struct strbuf git_default_email = STRBUF_INIT;
@@ -200,7 +203,6 @@
 static int crud(unsigned char c)
 {
 	return  c <= 32  ||
-		c == '.' ||
 		c == ',' ||
 		c == ':' ||
 		c == ';' ||
@@ -668,7 +670,9 @@
 	return 0;
 }
 
-int git_ident_config(const char *var, const char *value, void *data UNUSED)
+int git_ident_config(const char *var, const char *value,
+		     const struct config_context *ctx UNUSED,
+		     void *data UNUSED)
 {
 	if (!strcmp(var, "user.useconfigonly")) {
 		ident_use_config_only = git_config_bool(var, value);
diff --git a/ident.h b/ident.h
new file mode 100644
index 0000000..6a79feb
--- /dev/null
+++ b/ident.h
@@ -0,0 +1,69 @@
+#ifndef IDENT_H
+#define IDENT_H
+
+#include "string-list.h"
+
+struct ident_split {
+	const char *name_begin;
+	const char *name_end;
+	const char *mail_begin;
+	const char *mail_end;
+	const char *date_begin;
+	const char *date_end;
+	const char *tz_begin;
+	const char *tz_end;
+};
+
+#define IDENT_STRICT	       1
+#define IDENT_NO_DATE	       2
+#define IDENT_NO_NAME	       4
+
+enum want_ident {
+	WANT_BLANK_IDENT,
+	WANT_AUTHOR_IDENT,
+	WANT_COMMITTER_IDENT
+};
+
+const char *ident_default_name(void);
+const char *ident_default_email(void);
+/*
+ * Prepare an ident to fall back on if the user didn't configure it.
+ */
+void prepare_fallback_ident(const char *name, const char *email);
+void reset_ident_date(void);
+/*
+ * Signals an success with 0, but time part of the result may be NULL
+ * if the input lacks timestamp and zone
+ */
+int split_ident_line(struct ident_split *, const char *, int);
+
+/*
+ * Given a commit or tag object buffer and the commit or tag headers, replaces
+ * the idents in the headers with their canonical versions using the mailmap mechanism.
+ */
+void apply_mailmap_to_header(struct strbuf *, const char **, struct string_list *);
+
+/*
+ * Compare split idents for equality or strict ordering. Note that we
+ * compare only the ident part of the line, ignoring any timestamp.
+ *
+ * Because there are two fields, we must choose one as the primary key; we
+ * currently arbitrarily pick the email.
+ */
+int ident_cmp(const struct ident_split *, const struct ident_split *);
+
+const char *git_author_info(int);
+const char *git_committer_info(int);
+const char *fmt_ident(const char *name, const char *email,
+		      enum want_ident whose_ident,
+		      const char *date_str, int);
+const char *fmt_name(enum want_ident);
+
+int committer_ident_sufficiently_given(void);
+int author_ident_sufficiently_given(void);
+
+struct config_context;
+int git_ident_config(const char *, const char *, const struct config_context *,
+		     void *);
+
+#endif
diff --git a/imap-send.c b/imap-send.c
index a50af56..4caa866 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -18,15 +18,17 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *  along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "credential.h"
-#include "exec-cmd.h"
+#include "gettext.h"
 #include "run-command.h"
 #include "parse-options.h"
+#include "setup.h"
+#include "strbuf.h"
 #if defined(NO_OPENSSL) && !defined(HAVE_OPENSSL_CSPRNG)
 typedef void *SSL;
 #endif
@@ -66,9 +68,6 @@
 
 static char *next_arg(char **);
 
-__attribute__((format (printf, 3, 4)))
-static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
-
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
 {
 	int len;
@@ -133,12 +132,10 @@
 };
 
 struct imap_cmd_cb {
-	int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
-	void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
+	int (*cont)(struct imap_store *ctx, const char *prompt);
 	void *ctx;
 	char *data;
 	int dlen;
-	int uid;
 };
 
 struct imap_cmd {
@@ -205,10 +202,14 @@
 		else
 			fprintf(stderr, "%s: unexpected EOF\n", func);
 	}
+	/* mark as used to appease -Wunused-parameter with NO_OPENSSL */
+	(void)sock;
 }
 
 #ifdef NO_OPENSSL
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock UNUSED,
+			      int use_tls_only UNUSED,
+			      int verify UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -496,19 +497,6 @@
 	return ret;
 }
 
-__attribute__((format (printf, 3, 4)))
-static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
-{
-	int ret;
-	va_list va;
-
-	va_start(va, fmt);
-	if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
-		BUG("buffer too small. Please report a bug.");
-	va_end(va);
-	return ret;
-}
-
 static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
 				       struct imap_cmd_cb *cb,
 				       const char *fmt, va_list ap)
@@ -531,11 +519,11 @@
 		get_cmd_result(ctx, NULL);
 
 	if (!cmd->cb.data)
-		bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
+		bufl = xsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
 	else
-		bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
-				  cmd->tag, cmd->cmd, cmd->cb.dlen,
-				  CAP(LITERALPLUS) ? "+" : "");
+		bufl = xsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
+				 cmd->tag, cmd->cmd, cmd->cb.dlen,
+				 CAP(LITERALPLUS) ? "+" : "");
 
 	if (0 < verbosity) {
 		if (imap->num_in_progress)
@@ -782,7 +770,7 @@
 				if (n != (int)cmdp->cb.dlen)
 					return RESP_BAD;
 			} else if (cmdp->cb.cont) {
-				if (cmdp->cb.cont(ctx, cmdp, cmd))
+				if (cmdp->cb.cont(ctx, cmd))
 					return RESP_BAD;
 			} else {
 				fprintf(stderr, "IMAP error: unexpected command continuation request\n");
@@ -824,8 +812,6 @@
 			}
 			if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
 				resp = resp2;
-			if (cmdp->cb.done)
-				cmdp->cb.done(ctx, cmdp, resp);
 			free(cmdp->cb.data);
 			free(cmdp->cmd);
 			free(cmdp);
@@ -857,7 +843,7 @@
 
 /*
  * hexchar() and cram() functions are based on the code from the isync
- * project (http://isync.sf.net/).
+ * project (https://isync.sourceforge.io/).
  */
 static char hexchar(unsigned int b)
 {
@@ -905,7 +891,9 @@
 
 #else
 
-static char *cram(const char *challenge_64, const char *user, const char *pass)
+static char *cram(const char *challenge_64 UNUSED,
+		  const char *user UNUSED,
+		  const char *pass UNUSED)
 {
 	die("If you want to use CRAM-MD5 authenticate method, "
 	    "you have to build git-imap-send with OpenSSL library.");
@@ -913,7 +901,7 @@
 
 #endif
 
-static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 {
 	int ret;
 	char *response;
@@ -1319,7 +1307,8 @@
 	return 1;
 }
 
-static int git_imap_config(const char *var, const char *val, void *cb)
+static int git_imap_config(const char *var, const char *val,
+			   const struct config_context *ctx, void *cb)
 {
 
 	if (!strcmp("imap.sslverify", var))
@@ -1337,10 +1326,10 @@
 	else if (!strcmp("imap.authmethod", var))
 		return git_config_string(&server.auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val);
+		server.port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
-			git_die_config("imap.host", "Missing value for 'imap.host'");
+			return config_error_nonbool(var);
 		} else {
 			if (starts_with(val, "imap:"))
 				val += 5;
@@ -1353,7 +1342,7 @@
 			server.host = xstrdup(val);
 		}
 	} else
-		return git_default_config(var, val, cb);
+		return git_default_config(var, val, ctx, cb);
 
 	return 0;
 }
@@ -1411,16 +1400,16 @@
 	if (!curl)
 		die("curl_easy_init failed");
 
-	server_fill_credential(&server, cred);
-	curl_easy_setopt(curl, CURLOPT_USERNAME, server.user);
-	curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);
+	server_fill_credential(srvc, cred);
+	curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
+	curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
 
-	strbuf_addstr(&path, server.use_ssl ? "imaps://" : "imap://");
-	strbuf_addstr(&path, server.host);
+	strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
+	strbuf_addstr(&path, srvc->host);
 	if (!path.len || path.buf[path.len - 1] != '/')
 		strbuf_addch(&path, '/');
 
-	uri_encoded_folder = curl_easy_escape(curl, server.folder, 0);
+	uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
 	if (!uri_encoded_folder)
 		die("failed to encode server folder");
 	strbuf_addstr(&path, uri_encoded_folder);
@@ -1428,25 +1417,25 @@
 
 	curl_easy_setopt(curl, CURLOPT_URL, path.buf);
 	strbuf_release(&path);
-	curl_easy_setopt(curl, CURLOPT_PORT, server.port);
+	curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
 
-	if (server.auth_method) {
+	if (srvc->auth_method) {
 #ifndef GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS
 		warning("No LOGIN_OPTIONS support in this cURL version");
 #else
 		struct strbuf auth = STRBUF_INIT;
 		strbuf_addstr(&auth, "AUTH=");
-		strbuf_addstr(&auth, server.auth_method);
+		strbuf_addstr(&auth, srvc->auth_method);
 		curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
 		strbuf_release(&auth);
 #endif
 	}
 
-	if (!server.use_ssl)
+	if (!srvc->use_ssl)
 		curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
 
-	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify);
-	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify);
+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
 
 	curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
 
diff --git a/json-writer.c b/json-writer.c
index f1cfd8f..005c820 100644
--- a/json-writer.c
+++ b/json-writer.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "json-writer.h"
 
 void jw_init(struct json_writer *jw)
diff --git a/json-writer.h b/json-writer.h
index 209355e..04413bd 100644
--- a/json-writer.h
+++ b/json-writer.h
@@ -3,8 +3,8 @@
 
 /*
  * JSON data structures are defined at:
- * [1] http://www.ietf.org/rfc/rfc7159.txt
- * [2] http://json.org/
+ * [1] https://www.ietf.org/rfc/rfc7159.txt
+ * [2] https://www.json.org/
  *
  * The JSON-writer API allows one to build JSON data structures using a
  * simple wrapper around a "struct strbuf" buffer.  It is intended as a
diff --git a/khash.h b/khash.h
index cb79bf8..ff88163 100644
--- a/khash.h
+++ b/khash.h
@@ -26,8 +26,7 @@
 #ifndef __AC_KHASH_H
 #define __AC_KHASH_H
 
-#include "cache.h"
-#include "hashmap.h"
+#include "hash.h"
 
 #define AC_VERSION_KHASH_H "0.2.8"
 
@@ -62,7 +61,7 @@
 static const double __ac_HASH_UPPER = 0.77;
 
 #define __KHASH_TYPE(name, khkey_t, khval_t) \
-	typedef struct { \
+	typedef struct kh_##name { \
 		khint_t n_buckets, size, n_occupied, upper_bound; \
 		khint32_t *flags; \
 		khkey_t *keys; \
diff --git a/kwset.c b/kwset.c
index 08aadf0..695e47b 100644
--- a/kwset.c
+++ b/kwset.c
@@ -18,7 +18,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>. */
+   along with this program; if not, see <https://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
@@ -32,7 +32,7 @@
    String Matching:  An Aid to Bibliographic Search," CACM June 1975,
    Vol. 18, No. 6, which describes the failure function used below. */
 
-#include "cache.h"
+#include "git-compat-util.h"
 
 #include "kwset.h"
 #include "compat/obstack.h"
@@ -49,6 +49,42 @@
 
 #define U(c) ((unsigned char) (c))
 
+/* For case-insensitive kwset */
+const unsigned char tolower_trans_tbl[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	 ' ',  '!',  '"',  '#',  '$',  '%',  '&', 0x27,
+	 '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+	 '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	 '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+	 'x',  'y',  'z',  '[', 0x5c,  ']',  '^',  '_',
+	 '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+	 'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+};
+
 /* Balanced tree of edges and labels leaving a given trie node. */
 struct tree
 {
diff --git a/kwset.h b/kwset.h
index f50ecae..c722664 100644
--- a/kwset.h
+++ b/kwset.h
@@ -20,12 +20,14 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>. */
+   along with this program; if not, see <https://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
    or (US mail) as Mike Haertel c/o Free Software Foundation. */
 
+extern const unsigned char tolower_trans_tbl[256];
+
 struct kwsmatch
 {
   int index;			/* Index number of matching keyword. */
diff --git a/levenshtein.c b/levenshtein.c
index d263269..fd8026f 100644
--- a/levenshtein.c
+++ b/levenshtein.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "levenshtein.h"
 
 /*
diff --git a/line-log.c b/line-log.c
index a7f3e7f..8ff6ccb 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1,21 +1,22 @@
 #include "git-compat-util.h"
+#include "diffcore.h"
 #include "line-range.h"
-#include "cache.h"
+#include "hex.h"
 #include "tag.h"
-#include "blob.h"
 #include "tree.h"
 #include "diff.h"
 #include "commit.h"
 #include "decorate.h"
+#include "repository.h"
 #include "revision.h"
 #include "xdiff-interface.h"
 #include "strbuf.h"
 #include "log-tree.h"
-#include "graph.h"
-#include "userdiff.h"
 #include "line-log.h"
+#include "setup.h"
 #include "strvec.h"
 #include "bloom.h"
+#include "tree-walk.h"
 
 static void range_set_grow(struct range_set *rs, size_t extra)
 {
@@ -1281,7 +1282,8 @@
 	return changed;
 }
 
-static enum rewrite_result line_log_rewrite_one(struct rev_info *rev, struct commit **pp)
+static enum rewrite_result line_log_rewrite_one(struct rev_info *rev UNUSED,
+						struct commit **pp)
 {
 	for (;;) {
 		struct commit *p = *pp;
@@ -1323,3 +1325,13 @@
 
 	return 0;
 }
+
+static void free_void_line_log_data(void *data)
+{
+	free_line_log_data(data);
+}
+
+void line_log_free(struct rev_info *rev)
+{
+	clear_decoration(&rev->line_log_data, free_void_line_log_data);
+}
diff --git a/line-log.h b/line-log.h
index 82ae8d9..e9dadbc 100644
--- a/line-log.h
+++ b/line-log.h
@@ -1,10 +1,9 @@
 #ifndef LINE_LOG_H
 #define LINE_LOG_H
 
-#include "diffcore.h"
-
 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. */
@@ -59,4 +58,6 @@
 
 int line_log_print(struct rev_info *rev, struct commit *commit);
 
+void line_log_free(struct rev_info *rev);
+
 #endif /* LINE_LOG_H */
diff --git a/line-range.c b/line-range.c
index 47bf0d6..60f0e5a 100644
--- a/line-range.c
+++ b/line-range.c
@@ -1,7 +1,6 @@
 #include "git-compat-util.h"
 #include "line-range.h"
 #include "xdiff-interface.h"
-#include "strbuf.h"
 #include "userdiff.h"
 
 /*
diff --git a/linear-assignment.c b/linear-assignment.c
index ecffc09..5416cbc 100644
--- a/linear-assignment.c
+++ b/linear-assignment.c
@@ -3,7 +3,7 @@
  * algorithm for dense and sparse linear assignment problems</i>. Computing,
  * 38(4), 325-340.
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "linear-assignment.h"
 
 #define COST(column, row) cost[(column) + column_count * (row)]
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index ee01bcd..c5f363c 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -1,14 +1,11 @@
-#include "cache.h"
-#include "commit.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "revision.h"
-#include "strvec.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
+#include "gettext.h"
 #include "list-objects-filter-options.h"
 #include "promisor-remote.h"
 #include "trace.h"
 #include "url.h"
+#include "parse-options.h"
 
 static int parse_combine_filter(
 	struct list_objects_filter_options *filter_options,
@@ -341,7 +338,7 @@
 	char *filter_name;
 
 	/* Check if it is already registered */
-	if ((promisor_remote = promisor_remote_find(remote))) {
+	if ((promisor_remote = repo_promisor_remote_find(the_repository, remote))) {
 		if (promisor_remote->partial_clone_filter)
 			/*
 			 * Remote is already registered and a filter is already
@@ -369,14 +366,15 @@
 	free(filter_name);
 
 	/* Make sure the config info are reset */
-	promisor_remote_reinit();
+	repo_promisor_remote_reinit(the_repository);
 }
 
 void partial_clone_get_default_filter_spec(
 	struct list_objects_filter_options *filter_options,
 	const char *remote)
 {
-	struct promisor_remote *promisor = promisor_remote_find(remote);
+	struct promisor_remote *promisor = repo_promisor_remote_find(the_repository,
+								     remote);
 	struct strbuf errbuf = STRBUF_INIT;
 
 	/*
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index 1fe393f..55fab85 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -1,9 +1,11 @@
 #ifndef LIST_OBJECTS_FILTER_OPTIONS_H
 #define LIST_OBJECTS_FILTER_OPTIONS_H
 
-#include "cache.h"
-#include "parse-options.h"
-#include "string-list.h"
+#include "gettext.h"
+#include "object.h"
+#include "strbuf.h"
+
+struct option;
 
 /*
  * The list of defined filters for list-objects.
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 7ed21cb..4346f8d 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -1,18 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
-#include "tag.h"
+#include "gettext.h"
+#include "hex.h"
 #include "commit.h"
-#include "tree.h"
-#include "blob.h"
 #include "diff.h"
-#include "tree-walk.h"
 #include "revision.h"
-#include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "oidmap.h"
 #include "oidset.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 
 /* Remember to update object flag allocation in object.h */
 /*
@@ -713,15 +711,6 @@
 	free(d);
 }
 
-static void add_all(struct oidset *dest, struct oidset *src) {
-	struct oidset_iter iter;
-	struct object_id *src_oid;
-
-	oidset_iter_init(src, &iter);
-	while ((src_oid = oidset_iter_next(&iter)) != NULL)
-		oidset_insert(dest, src_oid);
-}
-
 static void filter_combine__finalize_omits(
 	struct oidset *omits,
 	void *filter_data)
@@ -730,7 +719,7 @@
 	size_t sub;
 
 	for (sub = 0; sub < d->nr; sub++) {
-		add_all(omits, &d->sub[sub].omits);
+		oidset_insert_from_set(omits, &d->sub[sub].omits);
 		oidset_clear(&d->sub[sub].omits);
 	}
 }
diff --git a/list-objects.c b/list-objects.c
index 7528fe1..11ad8be 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tag.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tree.h"
 #include "blob.h"
 #include "diff.h"
@@ -10,8 +12,9 @@
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "trace.h"
+#include "environment.h"
 
 struct traversal_context {
 	struct rev_info *revs;
@@ -19,6 +22,7 @@
 	show_commit_fn show_commit;
 	void *show_data;
 	struct filter *filter;
+	int depth;
 };
 
 static void show_commit(struct traversal_context *ctx,
@@ -35,6 +39,9 @@
 {
 	if (!ctx->show_object)
 		return;
+	if (ctx->revs->unpacked && has_object_pack(&object->oid))
+		return;
+
 	ctx->show_object(object, name, ctx->show_data);
 }
 
@@ -64,7 +71,7 @@
 	 * of missing objects.
 	 */
 	if (ctx->revs->exclude_promisor_objects &&
-	    !has_object_file(&obj->oid) &&
+	    !repo_has_object_file(the_repository, &obj->oid) &&
 	    is_promisor_object(&obj->oid))
 		return;
 
@@ -95,12 +102,12 @@
 	enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
 		all_entries_interesting : entry_not_interesting;
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
 		if (match != all_entries_interesting) {
 			match = tree_entry_interesting(ctx->revs->repo->index,
-						       &entry, base, 0,
+						       &entry, base,
 						       &ctx->revs->diffopt.pathspec);
 			if (match == all_entries_not_interesting)
 				break;
@@ -116,7 +123,9 @@
 				    entry.path, oid_to_hex(&tree->object.oid));
 			}
 			t->object.flags |= NOT_USER_GIVEN;
+			ctx->depth++;
 			process_tree(ctx, t, base, entry.path);
+			ctx->depth--;
 		}
 		else if (S_ISGITLINK(entry.mode))
 			; /* ignore gitlink */
@@ -154,6 +163,9 @@
 	    !revs->include_check_obj(&tree->object, revs->include_check_data))
 		return;
 
+	if (ctx->depth > max_allowed_tree_depth)
+		die("exceeded maximum allowed tree depth");
+
 	failed_parse = parse_tree_gently(tree, 1);
 	if (failed_parse) {
 		if (revs->ignore_missing_links)
@@ -168,7 +180,7 @@
 		    is_promisor_object(&obj->oid))
 			return;
 
-		if (!revs->do_not_die_on_missing_tree)
+		if (!revs->do_not_die_on_missing_objects)
 			die("bad tree object %s", oid_to_hex(&obj->oid));
 	}
 
@@ -227,7 +239,8 @@
 		struct commit *parent = parents->item;
 		if (!(parent->object.flags & UNINTERESTING))
 			continue;
-		mark_tree_uninteresting(revs->repo, get_commit_tree(parent));
+		mark_tree_uninteresting(revs->repo,
+					repo_get_commit_tree(the_repository, parent));
 		if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
 			parent->object.flags |= SHOWN;
 			show_edge(parent);
@@ -244,7 +257,8 @@
 
 	for (parents = commit->parents; parents; parents = parents->next) {
 		struct commit *parent = parents->item;
-		struct tree *tree = get_commit_tree(parent);
+		struct tree *tree = repo_get_commit_tree(the_repository,
+							 parent);
 
 		if (!tree)
 			continue;
@@ -275,7 +289,8 @@
 
 		for (list = revs->commits; list; list = list->next) {
 			struct commit *commit = list->item;
-			struct tree *tree = get_commit_tree(commit);
+			struct tree *tree = repo_get_commit_tree(the_repository,
+								 commit);
 
 			if (commit->object.flags & UNINTERESTING)
 				tree->object.flags |= UNINTERESTING;
@@ -291,7 +306,7 @@
 			struct commit *commit = list->item;
 			if (commit->object.flags & UNINTERESTING) {
 				mark_tree_uninteresting(revs->repo,
-							get_commit_tree(commit));
+							repo_get_commit_tree(the_repository, commit));
 				if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
 					commit->object.flags |= SHOWN;
 					show_edge(commit);
@@ -309,7 +324,7 @@
 			if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
 				continue;
 			mark_tree_uninteresting(revs->repo,
-						get_commit_tree(commit));
+						repo_get_commit_tree(the_repository, commit));
 			if (!(obj->flags & SHOWN)) {
 				obj->flags |= SHOWN;
 				show_edge(commit);
@@ -344,6 +359,7 @@
 		if (!path)
 			path = "";
 		if (obj->type == OBJ_TREE) {
+			ctx->depth = 0;
 			process_tree(ctx, (struct tree *)obj, base, path);
 			continue;
 		}
@@ -376,8 +392,12 @@
 		 */
 		if (!ctx->revs->tree_objects)
 			; /* do not bother loading tree */
-		else if (get_commit_tree(commit)) {
-			struct tree *tree = get_commit_tree(commit);
+		else if (ctx->revs->do_not_die_on_missing_objects &&
+			 oidset_contains(&ctx->revs->missing_commits, &commit->object.oid))
+			;
+		else if (repo_get_commit_tree(the_repository, commit)) {
+			struct tree *tree = repo_get_commit_tree(the_repository,
+								 commit);
 			tree->object.flags |= NOT_USER_GIVEN;
 			add_pending_tree(ctx->revs, tree);
 		} else if (commit->object.parsed) {
diff --git a/list.h b/list.h
index 362a4cd..9842801 100644
--- a/list.h
+++ b/list.h
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, see
- * <http://www.gnu.org/licenses/>.
+ * <https://www.gnu.org/licenses/>.
  */
 
 #ifndef LIST_H
diff --git a/lockfile.c b/lockfile.c
index cc9a4b8..1d5ed01 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -2,7 +2,9 @@
  * Copyright (c) 2005, Junio C Hamano
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "gettext.h"
 #include "lockfile.h"
 
 /*
diff --git a/lockfile.h b/lockfile.h
index 90af4e6..1bb9926 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -321,11 +321,11 @@
  * Roll back `lk`: close the file descriptor and/or file pointer and
  * remove the lockfile. It is a NOOP to call `rollback_lock_file()`
  * for a `lock_file` object that has already been committed or rolled
- * back.
+ * back. No error will be returned in this case.
  */
-static inline void rollback_lock_file(struct lock_file *lk)
+static inline int rollback_lock_file(struct lock_file *lk)
 {
-	delete_tempfile(&lk->tempfile);
+	return delete_tempfile(&lk->tempfile);
 }
 
 #endif /* LOCKFILE_H */
diff --git a/log-tree.c b/log-tree.c
index 1dd5fcb..59eeaef 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1,8 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit-reach.h"
 #include "config.h"
 #include "diff.h"
-#include "object-store.h"
+#include "diffcore.h"
+#include "environment.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "repository.h"
 #include "tmp-objdir.h"
 #include "commit.h"
@@ -12,6 +16,8 @@
 #include "merge-ort.h"
 #include "reflog-walk.h"
 #include "refs.h"
+#include "replace-object.h"
+#include "revision.h"
 #include "string-list.h"
 #include "color.h"
 #include "gpg-interface.h"
@@ -20,6 +26,9 @@
 #include "help.h"
 #include "range-diff.h"
 #include "strmap.h"
+#include "tree.h"
+#include "wildmatch.h"
+#include "write-or-die.h"
 
 static struct decoration name_decoration = { "object names" };
 static int decoration_loaded;
@@ -150,7 +159,7 @@
 
 	if (starts_with(refname, git_replace_ref_base)) {
 		struct object_id original_oid;
-		if (!read_replace_refs)
+		if (!replace_refs_enabled(the_repository))
 			return 0;
 		if (get_oid_hex(refname + strlen(git_replace_ref_base),
 				&original_oid)) {
@@ -196,7 +205,8 @@
 	return 0;
 }
 
-static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
+static int add_graft_decoration(const struct commit_graft *graft,
+				void *cb_data UNUSED)
 {
 	struct commit *commit = lookup_commit(the_repository, &graft->oid);
 	if (!commit)
@@ -233,7 +243,8 @@
 	struct commit_list *p;
 	for (p = commit->parents; p ; p = p->next) {
 		struct commit *parent = p->item;
-		fprintf(file, " %s", find_unique_abbrev(&parent->object.oid, abbrev));
+		fprintf(file, " %s",
+			repo_find_unique_abbrev(the_repository, &parent->object.oid, abbrev));
 	}
 }
 
@@ -241,7 +252,8 @@
 {
 	struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
 	for ( ; p; p = p->next) {
-		fprintf(opt->diffopt.file, " %s", find_unique_abbrev(&p->item->object.oid, abbrev));
+		fprintf(opt->diffopt.file, " %s",
+			repo_find_unique_abbrev(the_repository, &p->item->object.oid, abbrev));
 	}
 }
 
@@ -292,26 +304,43 @@
 
 /*
  * The caller makes sure there is no funny color before calling.
- * format_decorations_extended makes sure the same after return.
+ * format_decorations ensures the same after return.
  */
-void format_decorations_extended(struct strbuf *sb,
+void format_decorations(struct strbuf *sb,
 			const struct commit *commit,
 			int use_color,
-			const char *prefix,
-			const char *separator,
-			const char *suffix)
+			const struct decoration_options *opts)
 {
 	const struct name_decoration *decoration;
 	const struct name_decoration *current_and_HEAD;
-	const char *color_commit =
-		diff_get_color(use_color, DIFF_COMMIT);
-	const char *color_reset =
-		decorate_get_color(use_color, DECORATION_NONE);
+	const char *color_commit, *color_reset;
+
+	const char *prefix = " (";
+	const char *suffix = ")";
+	const char *separator = ", ";
+	const char *pointer = " -> ";
+	const char *tag = "tag: ";
 
 	decoration = get_name_decoration(&commit->object);
 	if (!decoration)
 		return;
 
+	if (opts) {
+		if (opts->prefix)
+			prefix = opts->prefix;
+		if (opts->suffix)
+			suffix = opts->suffix;
+		if (opts->separator)
+			separator = opts->separator;
+		if (opts->pointer)
+			pointer = opts->pointer;
+		if (opts->tag)
+			tag = opts->tag;
+	}
+
+	color_commit = diff_get_color(use_color, DIFF_COMMIT);
+	color_reset = decorate_get_color(use_color, DECORATION_NONE);
+
 	current_and_HEAD = current_pointed_by_HEAD(decoration);
 	while (decoration) {
 		/*
@@ -320,31 +349,44 @@
 		 * appeared, skipping the entry for current.
 		 */
 		if (decoration != current_and_HEAD) {
-			strbuf_addstr(sb, color_commit);
-			strbuf_addstr(sb, prefix);
-			strbuf_addstr(sb, color_reset);
-			strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
-			if (decoration->type == DECORATION_REF_TAG)
-				strbuf_addstr(sb, "tag: ");
+			const char *color =
+				decorate_get_color(use_color, decoration->type);
 
+			if (*prefix) {
+				strbuf_addstr(sb, color_commit);
+				strbuf_addstr(sb, prefix);
+				strbuf_addstr(sb, color_reset);
+			}
+
+			if (*tag && decoration->type == DECORATION_REF_TAG) {
+				strbuf_addstr(sb, color);
+				strbuf_addstr(sb, tag);
+				strbuf_addstr(sb, color_reset);
+			}
+
+			strbuf_addstr(sb, color);
 			show_name(sb, decoration);
+			strbuf_addstr(sb, color_reset);
 
 			if (current_and_HEAD &&
 			    decoration->type == DECORATION_REF_HEAD) {
-				strbuf_addstr(sb, " -> ");
+				strbuf_addstr(sb, color_commit);
+				strbuf_addstr(sb, pointer);
 				strbuf_addstr(sb, color_reset);
 				strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
 				show_name(sb, current_and_HEAD);
+				strbuf_addstr(sb, color_reset);
 			}
-			strbuf_addstr(sb, color_reset);
 
 			prefix = separator;
 		}
 		decoration = decoration->next;
 	}
-	strbuf_addstr(sb, color_commit);
-	strbuf_addstr(sb, suffix);
-	strbuf_addstr(sb, color_reset);
+	if (*suffix) {
+		strbuf_addstr(sb, color_commit);
+		strbuf_addstr(sb, suffix);
+		strbuf_addstr(sb, color_reset);
+	}
 }
 
 void show_decorations(struct rev_info *opt, struct commit *commit)
@@ -359,7 +401,7 @@
 	}
 	if (!opt->show_decorations)
 		return;
-	format_decorations(&sb, commit, opt->diffopt.use_color);
+	format_decorations(&sb, commit, opt->diffopt.use_color, NULL);
 	fputs(sb.buf, opt->diffopt.file);
 	strbuf_release(&sb);
 }
@@ -405,7 +447,8 @@
 	struct pretty_print_context ctx = {0};
 	struct strbuf subject = STRBUF_INIT;
 
-	format_commit_message(commit, "%f", &subject, &ctx);
+	repo_format_commit_message(the_repository, commit, "%f", &subject,
+				   &ctx);
 	fmt_output_subject(filename, subject.buf, info);
 	strbuf_release(&subject);
 }
@@ -427,20 +470,23 @@
 }
 
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-			     const char **extra_headers_p,
+			     char **extra_headers_p,
 			     int *need_8bit_cte_p,
 			     int maybe_multipart)
 {
-	const char *extra_headers = opt->extra_headers;
+	struct strbuf headers = STRBUF_INIT;
 	const char *name = oid_to_hex(opt->zero_commit ?
 				      null_oid() : &commit->object.oid);
 
 	*need_8bit_cte_p = 0; /* unknown */
 
+	if (opt->extra_headers && *opt->extra_headers)
+		strbuf_addstr(&headers, opt->extra_headers);
+
 	fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
 	graph_show_oneline(opt->graph);
 	if (opt->message_id) {
-		fprintf(opt->diffopt.file, "Message-Id: <%s>\n", opt->message_id);
+		fprintf(opt->diffopt.file, "Message-ID: <%s>\n", opt->message_id);
 		graph_show_oneline(opt->graph);
 	}
 	if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
@@ -453,16 +499,13 @@
 		graph_show_oneline(opt->graph);
 	}
 	if (opt->mime_boundary && maybe_multipart) {
-		static struct strbuf subject_buffer = STRBUF_INIT;
 		static struct strbuf buffer = STRBUF_INIT;
 		struct strbuf filename =  STRBUF_INIT;
 		*need_8bit_cte_p = -1; /* NEVER */
 
-		strbuf_reset(&subject_buffer);
 		strbuf_reset(&buffer);
 
-		strbuf_addf(&subject_buffer,
-			 "%s"
+		strbuf_addf(&headers,
 			 "MIME-Version: 1.0\n"
 			 "Content-Type: multipart/mixed;"
 			 " boundary=\"%s%s\"\n"
@@ -473,10 +516,8 @@
 			 "Content-Type: text/plain; "
 			 "charset=UTF-8; format=fixed\n"
 			 "Content-Transfer-Encoding: 8bit\n\n",
-			 extra_headers ? extra_headers : "",
 			 mime_boundary_leader, opt->mime_boundary,
 			 mime_boundary_leader, opt->mime_boundary);
-		extra_headers = subject_buffer.buf;
 
 		if (opt->numbered_files)
 			strbuf_addf(&filename, "%d", opt->nr);
@@ -496,7 +537,7 @@
 		opt->diffopt.stat_sep = buffer.buf;
 		strbuf_release(&filename);
 	}
-	*extra_headers_p = extra_headers;
+	*extra_headers_p = headers.len ? strbuf_detach(&headers, NULL) : NULL;
 }
 
 static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
@@ -635,7 +676,6 @@
 	struct log_info *log = opt->loginfo;
 	struct commit *commit = log->commit, *parent = log->parent;
 	int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
-	const char *extra_headers = opt->extra_headers;
 	struct pretty_print_context ctx = {0};
 
 	opt->loginfo = NULL;
@@ -644,7 +684,8 @@
 
 		if (!opt->graph)
 			put_revision_mark(opt, commit);
-		fputs(find_unique_abbrev(&commit->object.oid, abbrev_commit), opt->diffopt.file);
+		fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, abbrev_commit),
+		      opt->diffopt.file);
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit, opt->diffopt.file);
 		if (opt->children.name)
@@ -695,10 +736,9 @@
 	 */
 
 	if (cmit_fmt_is_mail(opt->commit_format)) {
-		log_write_email_headers(opt, commit, &extra_headers,
+		log_write_email_headers(opt, commit, &ctx.after_subject,
 					&ctx.need_8bit_cte, 1);
 		ctx.rev = opt;
-		ctx.print_email_subject = 1;
 	} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
 		if (opt->commit_format != CMIT_FMT_ONELINE)
@@ -706,8 +746,8 @@
 
 		if (!opt->graph)
 			put_revision_mark(opt, commit);
-		fputs(find_unique_abbrev(&commit->object.oid,
-					 abbrev_commit),
+		fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid,
+					      abbrev_commit),
 		      opt->diffopt.file);
 		if (opt->print_parents)
 			show_parents(commit, abbrev_commit, opt->diffopt.file);
@@ -715,7 +755,7 @@
 			show_children(opt, commit, abbrev_commit);
 		if (parent)
 			fprintf(opt->diffopt.file, " (from %s)",
-			       find_unique_abbrev(&parent->object.oid, abbrev_commit));
+			       repo_find_unique_abbrev(the_repository, &parent->object.oid, abbrev_commit));
 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);
 		show_decorations(opt, commit);
 		if (opt->commit_format == CMIT_FMT_ONELINE) {
@@ -764,7 +804,6 @@
 	ctx.date_mode = opt->date_mode;
 	ctx.date_mode_explicit = opt->date_mode_explicit;
 	ctx.abbrev = opt->diffopt.abbrev;
-	ctx.after_subject = extra_headers;
 	ctx.preserve_subject = opt->preserve_subject;
 	ctx.encode_email_headers = opt->encode_email_headers;
 	ctx.reflog_info = opt->reflog_info;
@@ -813,6 +852,7 @@
 
 	strbuf_release(&msgbuf);
 	free(ctx.notes_message);
+	free(ctx.after_subject);
 
 	if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
 		struct diff_queue_struct dq;
@@ -846,7 +886,7 @@
 		 * Pass minimum required diff-options to range-diff; others
 		 * can be added later if deemed desirable.
 		 */
-		diff_setup(&opts);
+		repo_diff_setup(the_repository, &opts);
 		opts.file = opt->diffopt.file;
 		opts.use_color = opt->diffopt.use_color;
 		diff_setup_done(&opts);
@@ -967,7 +1007,7 @@
 			   struct object_id *oid)
 {
 	struct merge_options o;
-	struct commit_list *bases;
+	struct commit_list *bases = NULL;
 	struct merge_result res = {0};
 	struct pretty_print_context ctx = {0};
 	struct commit *parent1 = parents->item;
@@ -982,15 +1022,18 @@
 	o.msg_header_prefix = "remerge";
 
 	ctx.abbrev = DEFAULT_ABBREV;
-	format_commit_message(parent1, "%h (%s)", &parent1_desc, &ctx);
-	format_commit_message(parent2, "%h (%s)", &parent2_desc, &ctx);
+	repo_format_commit_message(the_repository, parent1, "%h (%s)",
+				   &parent1_desc, &ctx);
+	repo_format_commit_message(the_repository, parent2, "%h (%s)",
+				   &parent2_desc, &ctx);
 	o.branch1 = parent1_desc.buf;
 	o.branch2 = parent2_desc.buf;
 
 	/* Parse the relevant commits and get the merge bases */
 	parse_commit_or_die(parent1);
 	parse_commit_or_die(parent2);
-	bases = get_merge_bases(parent1, parent2);
+	if (repo_get_merge_bases(the_repository, parent1, parent2, &bases) < 0)
+		exit(128);
 
 	/* Re-merge the parents */
 	merge_incore_recursive(&o, bases, parent1, parent2, &res);
diff --git a/log-tree.h b/log-tree.h
index e7e4641..94978e2 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -1,7 +1,7 @@
 #ifndef LOG_TREE_H
 #define LOG_TREE_H
 
-#include "revision.h"
+struct rev_info;
 
 struct log_info {
 	struct commit *commit, *parent;
@@ -13,20 +13,23 @@
 	struct string_list *exclude_ref_config_pattern;
 };
 
+struct decoration_options {
+	char *prefix;
+	char *suffix;
+	char *separator;
+	char *pointer;
+	char *tag;
+};
+
 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
 void show_log(struct rev_info *opt);
-void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
-			     int use_color,
-			     const char *prefix,
-			     const char *separator,
-			     const char *suffix);
-#define format_decorations(strbuf, commit, color) \
-			     format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")")
+void format_decorations(struct strbuf *sb, const struct commit *commit,
+			int use_color, const struct decoration_options *opts);
 void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-			     const char **extra_headers_p,
+			     char **extra_headers_p,
 			     int *need_8bit_cte_p,
 			     int maybe_multipart);
 void load_ref_decorations(struct decoration_filter *filter, int flags);
diff --git a/loose.c b/loose.c
new file mode 100644
index 0000000..f6faa62
--- /dev/null
+++ b/loose.c
@@ -0,0 +1,259 @@
+#include "git-compat-util.h"
+#include "hash.h"
+#include "path.h"
+#include "object-store.h"
+#include "hex.h"
+#include "wrapper.h"
+#include "gettext.h"
+#include "loose.h"
+#include "lockfile.h"
+#include "oidtree.h"
+
+static const char *loose_object_header = "# loose-object-idx\n";
+
+static inline int should_use_loose_object_map(struct repository *repo)
+{
+	return repo->compat_hash_algo && repo->gitdir;
+}
+
+void loose_object_map_init(struct loose_object_map **map)
+{
+	struct loose_object_map *m;
+	m = xmalloc(sizeof(**map));
+	m->to_compat = kh_init_oid_map();
+	m->to_storage = kh_init_oid_map();
+	*map = m;
+}
+
+static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
+{
+	khiter_t pos;
+	int ret;
+	struct object_id *stored;
+
+	pos = kh_put_oid_map(map, *key, &ret);
+
+	/* This item already exists in the map. */
+	if (ret == 0)
+		return 0;
+
+	stored = xmalloc(sizeof(*stored));
+	oidcpy(stored, value);
+	kh_value(map, pos) = stored;
+	return 1;
+}
+
+static int insert_loose_map(struct object_directory *odb,
+			    const struct object_id *oid,
+			    const struct object_id *compat_oid)
+{
+	struct loose_object_map *map = odb->loose_map;
+	int inserted = 0;
+
+	inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
+	inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
+	if (inserted)
+		oidtree_insert(odb->loose_objects_cache, compat_oid);
+
+	return inserted;
+}
+
+static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
+{
+	struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+	FILE *fp;
+
+	if (!dir->loose_map)
+		loose_object_map_init(&dir->loose_map);
+	if (!dir->loose_objects_cache) {
+		ALLOC_ARRAY(dir->loose_objects_cache, 1);
+		oidtree_init(dir->loose_objects_cache);
+	}
+
+	insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
+	insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
+	insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
+
+	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	fp = fopen(path.buf, "rb");
+	if (!fp) {
+		strbuf_release(&path);
+		return 0;
+	}
+
+	errno = 0;
+	if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
+		goto err;
+	while (!strbuf_getline_lf(&buf, fp)) {
+		const char *p;
+		struct object_id oid, compat_oid;
+		if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
+		    *p++ != ' ' ||
+		    parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
+		    p != buf.buf + buf.len)
+			goto err;
+		insert_loose_map(dir, &oid, &compat_oid);
+	}
+
+	strbuf_release(&buf);
+	strbuf_release(&path);
+	return errno ? -1 : 0;
+err:
+	strbuf_release(&buf);
+	strbuf_release(&path);
+	return -1;
+}
+
+int repo_read_loose_object_map(struct repository *repo)
+{
+	struct object_directory *dir;
+
+	if (!should_use_loose_object_map(repo))
+		return 0;
+
+	prepare_alt_odb(repo);
+
+	for (dir = repo->objects->odb; dir; dir = dir->next) {
+		if (load_one_loose_object_map(repo, dir) < 0) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int repo_write_loose_object_map(struct repository *repo)
+{
+	kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
+	struct lock_file lock;
+	int fd;
+	khiter_t iter;
+	struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+	if (!should_use_loose_object_map(repo))
+		return 0;
+
+	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+	iter = kh_begin(map);
+	if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+		goto errout;
+
+	for (; iter != kh_end(map); iter++) {
+		if (kh_exist(map, iter)) {
+			if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
+			    oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
+				continue;
+			strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
+			if (write_in_full(fd, buf.buf, buf.len) < 0)
+				goto errout;
+			strbuf_reset(&buf);
+		}
+	}
+	strbuf_release(&buf);
+	if (commit_lock_file(&lock) < 0) {
+		error_errno(_("could not write loose object index %s"), path.buf);
+		strbuf_release(&path);
+		return -1;
+	}
+	strbuf_release(&path);
+	return 0;
+errout:
+	rollback_lock_file(&lock);
+	strbuf_release(&buf);
+	error_errno(_("failed to write loose object index %s\n"), path.buf);
+	strbuf_release(&path);
+	return -1;
+}
+
+static int write_one_object(struct repository *repo, const struct object_id *oid,
+			    const struct object_id *compat_oid)
+{
+	struct lock_file lock;
+	int fd;
+	struct stat st;
+	struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+	strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+	hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+
+	fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
+	if (fd < 0)
+		goto errout;
+	if (fstat(fd, &st) < 0)
+		goto errout;
+	if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+		goto errout;
+
+	strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
+	if (write_in_full(fd, buf.buf, buf.len) < 0)
+		goto errout;
+	if (close(fd))
+		goto errout;
+	adjust_shared_perm(path.buf);
+	rollback_lock_file(&lock);
+	strbuf_release(&buf);
+	strbuf_release(&path);
+	return 0;
+errout:
+	error_errno(_("failed to write loose object index %s\n"), path.buf);
+	close(fd);
+	rollback_lock_file(&lock);
+	strbuf_release(&buf);
+	strbuf_release(&path);
+	return -1;
+}
+
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+			      const struct object_id *compat_oid)
+{
+	int inserted = 0;
+
+	if (!should_use_loose_object_map(repo))
+		return 0;
+
+	inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
+	if (inserted)
+		return write_one_object(repo, oid, compat_oid);
+	return 0;
+}
+
+int repo_loose_object_map_oid(struct repository *repo,
+			      const struct object_id *src,
+			      const struct git_hash_algo *to,
+			      struct object_id *dest)
+{
+	struct object_directory *dir;
+	kh_oid_map_t *map;
+	khiter_t pos;
+
+	for (dir = repo->objects->odb; dir; dir = dir->next) {
+		struct loose_object_map *loose_map = dir->loose_map;
+		if (!loose_map)
+			continue;
+		map = (to == repo->compat_hash_algo) ?
+			loose_map->to_compat :
+			loose_map->to_storage;
+		pos = kh_get_oid_map(map, *src);
+		if (pos < kh_end(map)) {
+			oidcpy(dest, kh_value(map, pos));
+			return 0;
+		}
+	}
+	return -1;
+}
+
+void loose_object_map_clear(struct loose_object_map **map)
+{
+	struct loose_object_map *m = *map;
+	struct object_id *oid;
+
+	if (!m)
+		return;
+
+	kh_foreach_value(m->to_compat, oid, free(oid));
+	kh_foreach_value(m->to_storage, oid, free(oid));
+	kh_destroy_oid_map(m->to_compat);
+	kh_destroy_oid_map(m->to_storage);
+	free(m);
+	*map = NULL;
+}
diff --git a/loose.h b/loose.h
new file mode 100644
index 0000000..2c29570
--- /dev/null
+++ b/loose.h
@@ -0,0 +1,22 @@
+#ifndef LOOSE_H
+#define LOOSE_H
+
+#include "khash.h"
+
+struct loose_object_map {
+	kh_oid_map_t *to_compat;
+	kh_oid_map_t *to_storage;
+};
+
+void loose_object_map_init(struct loose_object_map **map);
+void loose_object_map_clear(struct loose_object_map **map);
+int repo_loose_object_map_oid(struct repository *repo,
+			      const struct object_id *src,
+			      const struct git_hash_algo *dest_algo,
+			      struct object_id *dest);
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+			      const struct object_id *compat_oid);
+int repo_read_loose_object_map(struct repository *repo);
+int repo_write_loose_object_map(struct repository *repo);
+
+#endif
diff --git a/ls-refs.c b/ls-refs.c
index 697d4be..819cbef 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -1,45 +1,42 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "repository.h"
 #include "refs.h"
-#include "remote.h"
 #include "strvec.h"
 #include "ls-refs.h"
 #include "pkt-line.h"
 #include "config.h"
 #include "string-list.h"
 
-static int config_read;
-static int advertise_unborn;
-static int allow_unborn;
-
-static void ensure_config_read(void)
+static enum {
+	UNBORN_IGNORE = 0,
+	UNBORN_ALLOW,
+	UNBORN_ADVERTISE /* implies ALLOW */
+} unborn_config(struct repository *r)
 {
 	const char *str = NULL;
 
-	if (config_read)
-		return;
-
-	if (repo_config_get_string_tmp(the_repository, "lsrefs.unborn", &str)) {
+	if (repo_config_get_string_tmp(r, "lsrefs.unborn", &str)) {
 		/*
 		 * If there is no such config, advertise and allow it by
 		 * default.
 		 */
-		advertise_unborn = 1;
-		allow_unborn = 1;
+		return UNBORN_ADVERTISE;
 	} else {
 		if (!strcmp(str, "advertise")) {
-			advertise_unborn = 1;
-			allow_unborn = 1;
+			return UNBORN_ADVERTISE;
 		} else if (!strcmp(str, "allow")) {
-			allow_unborn = 1;
+			return UNBORN_ALLOW;
 		} else if (!strcmp(str, "ignore")) {
-			/* do nothing */
+			return UNBORN_IGNORE;
 		} else {
 			die(_("invalid value for '%s': '%s'"),
 			    "lsrefs.unborn", str);
 		}
 	}
-	config_read = 1;
 }
 
 /*
@@ -74,7 +71,7 @@
 	unsigned symrefs;
 	struct strvec prefixes;
 	struct strbuf buf;
-	struct string_list hidden_refs;
+	struct strvec hidden_refs;
 	unsigned unborn : 1;
 };
 
@@ -139,6 +136,7 @@
 }
 
 static int ls_refs_config(const char *var, const char *value,
+			  const struct config_context *ctx UNUSED,
 			  void *cb_data)
 {
 	struct ls_refs_data *data = cb_data;
@@ -157,9 +155,8 @@
 	memset(&data, 0, sizeof(data));
 	strvec_init(&data.prefixes);
 	strbuf_init(&data.buf, 0);
-	string_list_init_dup(&data.hidden_refs);
+	strvec_init(&data.hidden_refs);
 
-	ensure_config_read();
 	git_config(ls_refs_config, &data);
 
 	while (packet_reader_read(request) == PACKET_READ_NORMAL) {
@@ -175,7 +172,7 @@
 				strvec_push(&data.prefixes, out);
 		}
 		else if (!strcmp("unborn", arg))
-			data.unborn = allow_unborn;
+			data.unborn = !!unborn_config(r);
 		else
 			die(_("unexpected line: '%s'"), arg);
 	}
@@ -196,21 +193,19 @@
 		strvec_push(&data.prefixes, "");
 	refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
 					  get_git_namespace(), data.prefixes.v,
+					  hidden_refs_to_excludes(&data.hidden_refs),
 					  send_ref, &data);
 	packet_fflush(stdout);
 	strvec_clear(&data.prefixes);
 	strbuf_release(&data.buf);
-	string_list_clear(&data.hidden_refs, 0);
+	strvec_clear(&data.hidden_refs);
 	return 0;
 }
 
 int ls_refs_advertise(struct repository *r, struct strbuf *value)
 {
-	if (value) {
-		ensure_config_read();
-		if (advertise_unborn)
-			strbuf_addstr(value, "unborn");
-	}
+	if (value && unborn_config(r) == UNBORN_ADVERTISE)
+		strbuf_addstr(value, "unborn");
 
 	return 1;
 }
diff --git a/mailinfo.c b/mailinfo.c
index 833d286..94b9b0a 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "gettext.h"
+#include "hex-ll.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "mailinfo.h"
@@ -56,12 +58,13 @@
 
 static const char *unquote_comment(struct strbuf *outbuf, const char *in)
 {
-	int c;
 	int take_next_literally = 0;
+	int depth = 1;
 
 	strbuf_addch(outbuf, '(');
 
-	while ((c = *in++) != 0) {
+	while (*in) {
+		int c = *in++;
 		if (take_next_literally == 1) {
 			take_next_literally = 0;
 		} else {
@@ -70,11 +73,14 @@
 				take_next_literally = 1;
 				continue;
 			case '(':
-				in = unquote_comment(outbuf, in);
+				strbuf_addch(outbuf, '(');
+				depth++;
 				continue;
 			case ')':
 				strbuf_addch(outbuf, ')');
-				return in;
+				if (!--depth)
+					return in;
+				continue;
 			}
 		}
 
@@ -86,10 +92,10 @@
 
 static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in)
 {
-	int c;
 	int take_next_literally = 0;
 
-	while ((c = *in++) != 0) {
+	while (*in) {
+		int c = *in++;
 		if (take_next_literally == 1) {
 			take_next_literally = 0;
 		} else {
@@ -597,7 +603,7 @@
 		ret = 1;
 		goto check_header_out;
 	}
-	if (parse_header(line, "Message-Id", mi, &sb)) {
+	if (parse_header(line, "Message-ID", mi, &sb)) {
 		if (mi->add_message_id)
 			mi->message_id = strbuf_detach(&sb, NULL);
 		ret = 1;
@@ -829,7 +835,7 @@
 	if (patchbreak(line)) {
 		if (mi->message_id)
 			strbuf_addf(&mi->log_message,
-				    "Message-Id: %s\n", mi->message_id);
+				    "Message-ID: %s\n", mi->message_id);
 		return 1;
 	}
 
@@ -1239,17 +1245,20 @@
 	return 0;
 }
 
-static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+static int git_mailinfo_config(const char *var, const char *value,
+			       const struct config_context *ctx, void *mi_)
 {
 	struct mailinfo *mi = mi_;
 
 	if (!starts_with(var, "mailinfo."))
-		return git_default_config(var, value, NULL);
+		return git_default_config(var, value, ctx, NULL);
 	if (!strcmp(var, "mailinfo.scissors")) {
 		mi->use_scissors = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "mailinfo.quotedcr")) {
+		if (!value)
+			return config_error_nonbool(var);
 		if (mailinfo_parse_quoted_cr_action(value, &mi->quoted_cr) != 0)
 			return error(_("bad action '%s' for '%s'"), value, var);
 		return 0;
diff --git a/mailmap.c b/mailmap.c
index da2589b..3d6a5e9 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -1,17 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
 #include "string-list.h"
 #include "mailmap.h"
-#include "object-store.h"
-
-#define DEBUG_MAILMAP 0
-#if DEBUG_MAILMAP
-#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
-#define debug_str(X) ((X) ? (X) : "(none)")
-#else
-__attribute__((format (printf, 1, 2)))
-static inline void debug_mm(const char *format, ...) {}
-static inline const char *debug_str(const char *s) { return s; }
-#endif
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "setup.h"
 
 const char *git_mailmap_file;
 const char *git_mailmap_blob;
@@ -30,23 +23,17 @@
 	struct string_list namemap;
 };
 
-static void free_mailmap_info(void *p, const char *s)
+static void free_mailmap_info(void *p, const char *s UNUSED)
 {
 	struct mailmap_info *mi = (struct mailmap_info *)p;
-	debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
-		 s, debug_str(mi->name), debug_str(mi->email));
 	free(mi->name);
 	free(mi->email);
 	free(mi);
 }
 
-static void free_mailmap_entry(void *p, const char *s)
+static void free_mailmap_entry(void *p, const char *s UNUSED)
 {
 	struct mailmap_entry *me = (struct mailmap_entry *)p;
-	debug_mm("mailmap: removing entries for <%s>, with %"PRIuMAX" sub-entries\n",
-		 s, (uintmax_t)me->namemap.nr);
-	debug_mm("mailmap: - simple: '%s' <%s>\n",
-		 debug_str(me->name), debug_str(me->email));
 
 	free(me->name);
 	free(me->email);
@@ -93,8 +80,6 @@
 	}
 
 	if (!old_name) {
-		debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email);
-
 		/* Replace current name and new email for simple entry */
 		if (new_name) {
 			free(me->name);
@@ -106,15 +91,10 @@
 		}
 	} else {
 		struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
-		debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
 		mi->name = xstrdup_or_null(new_name);
 		mi->email = xstrdup_or_null(new_email);
 		string_list_insert(&me->namemap, old_name)->util = mi;
 	}
-
-	debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
-		 debug_str(old_name), old_email,
-		 debug_str(new_name), debug_str(new_email));
 }
 
 static char *parse_name_and_email(char *buffer, char **name,
@@ -213,10 +193,10 @@
 
 	if (!name)
 		return 0;
-	if (get_oid(name, &oid) < 0)
+	if (repo_get_oid(the_repository, name, &oid) < 0)
 		return 0;
 
-	buf = read_object_file(&oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &oid, &type, &size);
 	if (!buf)
 		return error("unable to read mailmap object at %s", name);
 	if (type != OBJ_BLOB)
@@ -250,11 +230,8 @@
 
 void clear_mailmap(struct string_list *map)
 {
-	debug_mm("mailmap: clearing %"PRIuMAX" entries...\n",
-		 (uintmax_t)map->nr);
 	map->strdup_strings = 1;
 	string_list_clear_func(map, free_mailmap_entry);
-	debug_mm("mailmap: cleared\n");
 }
 
 /*
@@ -315,10 +292,6 @@
 	struct string_list_item *item;
 	struct mailmap_entry *me;
 
-	debug_mm("map_user: map '%.*s' <%.*s>\n",
-		 (int)*namelen, debug_str(*name),
-		 (int)*emaillen, debug_str(*email));
-
 	item = lookup_prefix(map, *email, *emaillen);
 	if (item) {
 		me = (struct mailmap_entry *)item->util;
@@ -336,10 +309,8 @@
 	}
 	if (item) {
 		struct mailmap_info *mi = (struct mailmap_info *)item->util;
-		if (mi->name == NULL && mi->email == NULL) {
-			debug_mm("map_user:  -- (no simple mapping)\n");
+		if (mi->name == NULL && mi->email == NULL)
 			return 0;
-		}
 		if (mi->email) {
 				*email = mi->email;
 				*emaillen = strlen(*email);
@@ -348,11 +319,7 @@
 				*name = mi->name;
 				*namelen = strlen(*name);
 		}
-		debug_mm("map_user:  to '%.*s' <%.*s>\n",
-			 (int)*namelen, debug_str(*name),
-			 (int)*emaillen, debug_str(*email));
 		return 1;
 	}
-	debug_mm("map_user:  --\n");
 	return 0;
 }
diff --git a/mailmap.h b/mailmap.h
index 7e99fcc..0f8fd2c 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -3,6 +3,9 @@
 
 struct string_list;
 
+extern const char *git_mailmap_file;
+extern const char *git_mailmap_blob;
+
 int read_mailmap(struct string_list *map);
 void clear_mailmap(struct string_list *map);
 
diff --git a/match-trees.c b/match-trees.c
index 49398e5..3412b6a 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex.h"
+#include "match-trees.h"
+#include "strbuf.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static int score_missing(unsigned mode)
 {
@@ -55,12 +58,12 @@
 	enum object_type type;
 	unsigned long size;
 
-	buffer = read_object_file(hash, &type, &size);
+	buffer = repo_read_object_file(the_repository, hash, &type, &size);
 	if (!buffer)
 		die("unable to read tree (%s)", oid_to_hex(hash));
 	if (type != OBJ_TREE)
 		die("%s is not a tree", oid_to_hex(hash));
-	init_tree_desc(desc, buffer, size);
+	init_tree_desc(desc, hash, buffer, size);
 	return buffer;
 }
 
@@ -188,10 +191,10 @@
 	if (*subpath)
 		subpath++;
 
-	buf = read_object_file(oid1, &type, &sz);
+	buf = repo_read_object_file(the_repository, oid1, &type, &sz);
 	if (!buf)
 		die("cannot read tree %s", oid_to_hex(oid1));
-	init_tree_desc(&desc, buf, sz);
+	init_tree_desc(&desc, oid1, buf, sz);
 
 	rewrite_here = NULL;
 	while (desc.size) {
diff --git a/match-trees.h b/match-trees.h
new file mode 100644
index 0000000..e3877ac
--- /dev/null
+++ b/match-trees.h
@@ -0,0 +1,10 @@
+#ifndef MATCH_TREES_H
+#define MATCH_TREES_H
+
+struct object_id;
+struct repository;
+
+void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
+void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
+
+#endif /* MATCH_TREES_H */
diff --git a/mem-pool.c b/mem-pool.c
index 599d8e8..3065b12 100644
--- a/mem-pool.c
+++ b/mem-pool.c
@@ -2,7 +2,7 @@
  * Memory Pool implementation logic.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "mem-pool.h"
 
 #define BLOCK_GROWTH_SIZE (1024 * 1024 - sizeof(struct mp_block))
@@ -89,9 +89,7 @@
 	struct mp_block *p = NULL;
 	void *r;
 
-	/* round up to a 'GIT_MAX_ALIGNMENT' alignment */
-	if (len & (GIT_MAX_ALIGNMENT - 1))
-		len += GIT_MAX_ALIGNMENT - (len & (GIT_MAX_ALIGNMENT - 1));
+	len = DIV_ROUND_UP(len, GIT_MAX_ALIGNMENT) * GIT_MAX_ALIGNMENT;
 
 	if (pool->mp_block &&
 	    pool->mp_block->end - pool->mp_block->next_free >= len)
@@ -99,9 +97,9 @@
 
 	if (!p) {
 		if (len >= (pool->block_alloc / 2))
-			return mem_pool_alloc_block(pool, len, pool->mp_block);
-
-		p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
+			p = mem_pool_alloc_block(pool, len, pool->mp_block);
+		else
+			p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
 	}
 
 	r = p->next_free;
@@ -109,6 +107,47 @@
 	return r;
 }
 
+static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt,
+			      va_list ap)
+{
+	struct mp_block *block = pool->mp_block;
+	char *next_free = block ? block->next_free : NULL;
+	size_t available = block ? block->end - block->next_free : 0;
+	va_list cp;
+	int len, len2;
+	size_t size;
+	char *ret;
+
+	va_copy(cp, ap);
+	len = vsnprintf(next_free, available, fmt, cp);
+	va_end(cp);
+	if (len < 0)
+		BUG("your vsnprintf is broken (returned %d)", len);
+
+	size = st_add(len, 1); /* 1 for NUL */
+	ret = mem_pool_alloc(pool, size);
+
+	/* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */
+	if (ret == next_free)
+		return ret;
+
+	len2 = vsnprintf(ret, size, fmt, ap);
+	if (len2 != len)
+		BUG("your vsnprintf is broken (returns inconsistent lengths)");
+	return ret;
+}
+
+char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...)
+{
+	va_list ap;
+	char *ret;
+
+	va_start(ap, fmt);
+	ret = mem_pool_strvfmt(pool, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
 void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size)
 {
 	size_t len = st_mult(count, size);
diff --git a/mem-pool.h b/mem-pool.h
index fe7507f..d1c6641 100644
--- a/mem-pool.h
+++ b/mem-pool.h
@@ -48,6 +48,11 @@
 char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len);
 
 /*
+ * Allocate memory from the memory pool and format a string into it.
+ */
+char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...);
+
+/*
  * Move the memory associated with the 'src' pool to the 'dst' pool. The 'src'
  * pool will be empty and not contain any memory. It still needs to be free'd
  * with a call to `mem_pool_discard`.
diff --git a/merge-blobs.c b/merge-blobs.c
index 8138090..2f659fd 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -1,10 +1,8 @@
-#include "cache.h"
-#include "run-command.h"
-#include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "git-compat-util.h"
+#include "merge-ll.h"
 #include "blob.h"
 #include "merge-blobs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
 {
@@ -12,7 +10,8 @@
 	unsigned long size;
 	enum object_type type;
 
-	buf = read_object_file(&obj->object.oid, &type, &size);
+	buf = repo_read_object_file(the_repository, &obj->object.oid, &type,
+				    &size);
 	if (!buf)
 		return -1;
 	if (type != OBJ_BLOB) {
@@ -78,7 +77,8 @@
 			return NULL;
 		if (!our)
 			our = their;
-		return read_object_file(&our->object.oid, &type, size);
+		return repo_read_object_file(the_repository, &our->object.oid,
+					     &type, size);
 	}
 
 	if (fill_mmfile_blob(&f1, our) < 0)
diff --git a/ll-merge.c b/merge-ll.c
similarity index 84%
rename from ll-merge.c
rename to merge-ll.c
index 130d265..bf1077a 100644
--- a/ll-merge.c
+++ b/merge-ll.c
@@ -4,13 +4,15 @@
  * Copyright (c) 2007 Junio C Hamano
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "convert.h"
 #include "attr.h"
 #include "xdiff-interface.h"
 #include "run-command.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "quote.h"
+#include "strbuf.h"
 
 struct ll_merge_driver;
 
@@ -126,7 +128,9 @@
 	xmp.level = XDL_MERGE_ZEALOUS;
 	xmp.favor = opts->variant;
 	xmp.xpp.flags = opts->xdl_opts;
-	if (git_xmerge_style >= 0)
+	if (opts->conflict_style >= 0)
+		xmp.style = opts->conflict_style;
+	else if (git_xmerge_style >= 0)
 		xmp.style = git_xmerge_style;
 	if (marker_size > 0)
 		xmp.marker_size = marker_size;
@@ -183,30 +187,21 @@
 static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
 			mmbuffer_t *result,
 			const char *path,
-			mmfile_t *orig, const char *orig_name UNUSED,
-			mmfile_t *src1, const char *name1 UNUSED,
-			mmfile_t *src2, const char *name2 UNUSED,
+			mmfile_t *orig, const char *orig_name,
+			mmfile_t *src1, const char *name1,
+			mmfile_t *src2, const char *name2,
 			const struct ll_merge_options *opts,
 			int marker_size)
 {
-	char temp[4][50];
+	char temp[3][50];
 	struct strbuf cmd = STRBUF_INIT;
-	struct strbuf_expand_dict_entry dict[6];
-	struct strbuf path_sq = STRBUF_INIT;
+	const char *format = fn->cmdline;
 	struct child_process child = CHILD_PROCESS_INIT;
 	int status, fd, i;
 	struct stat st;
 	enum ll_merge_result ret;
 	assert(opts);
 
-	sq_quote_buf(&path_sq, path);
-	dict[0].placeholder = "O"; dict[0].value = temp[0];
-	dict[1].placeholder = "A"; dict[1].value = temp[1];
-	dict[2].placeholder = "B"; dict[2].value = temp[2];
-	dict[3].placeholder = "L"; dict[3].value = temp[3];
-	dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
-	dict[5].placeholder = NULL; dict[5].value = NULL;
-
 	if (!fn->cmdline)
 		die("custom merge driver %s lacks command line.", fn->name);
 
@@ -215,9 +210,29 @@
 	create_temp(orig, temp[0], sizeof(temp[0]));
 	create_temp(src1, temp[1], sizeof(temp[1]));
 	create_temp(src2, temp[2], sizeof(temp[2]));
-	xsnprintf(temp[3], sizeof(temp[3]), "%d", marker_size);
 
-	strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
+	while (strbuf_expand_step(&cmd, &format)) {
+		if (skip_prefix(format, "%", &format))
+			strbuf_addch(&cmd, '%');
+		else if (skip_prefix(format, "O", &format))
+			strbuf_addstr(&cmd, temp[0]);
+		else if (skip_prefix(format, "A", &format))
+			strbuf_addstr(&cmd, temp[1]);
+		else if (skip_prefix(format, "B", &format))
+			strbuf_addstr(&cmd, temp[2]);
+		else if (skip_prefix(format, "L", &format))
+			strbuf_addf(&cmd, "%d", marker_size);
+		else if (skip_prefix(format, "P", &format))
+			sq_quote_buf(&cmd, path);
+		else if (skip_prefix(format, "S", &format))
+			sq_quote_buf(&cmd, orig_name ? orig_name : "");
+		else if (skip_prefix(format, "X", &format))
+			sq_quote_buf(&cmd, name1 ? name1 : "");
+		else if (skip_prefix(format, "Y", &format))
+			sq_quote_buf(&cmd, name2 ? name2 : "");
+		else
+			strbuf_addch(&cmd, '%');
+	}
 
 	child.use_shell = 1;
 	strvec_push(&child.args, cmd.buf);
@@ -239,8 +254,13 @@
 	for (i = 0; i < 3; i++)
 		unlink_or_warn(temp[i]);
 	strbuf_release(&cmd);
-	strbuf_release(&path_sq);
-	ret = (status > 0) ? LL_MERGE_CONFLICT : status;
+	if (!status)
+		ret = LL_MERGE_OK;
+	else if (status <= 128)
+		ret = LL_MERGE_CONFLICT;
+	else
+		/* died due to a signal: WTERMSIG(status) + 128 */
+		ret = LL_MERGE_ERROR;
 	return ret;
 }
 
@@ -251,6 +271,7 @@
 static const char *default_ll_merge;
 
 static int read_merge_config(const char *var, const char *value,
+			     const struct config_context *ctx UNUSED,
 			     void *cb UNUSED)
 {
 	struct ll_merge_driver *fn;
@@ -273,7 +294,7 @@
 	 * after seeing merge.<name>.var1.
 	 */
 	for (fn = ll_user_merge; fn; fn = fn->next)
-		if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+		if (!xstrncmpz(fn->name, name, namelen))
 			break;
 	if (!fn) {
 		CALLOC_ARRAY(fn, 1);
@@ -288,7 +309,7 @@
 
 	if (!strcmp("driver", key)) {
 		if (!value)
-			return error("%s: lacks value", var);
+			return config_error_nonbool(var);
 		/*
 		 * merge.<name>.driver specifies the command line:
 		 *
@@ -302,7 +323,12 @@
 		 *    %B - temporary file name for the other branches' version.
 		 *    %L - conflict marker length
 		 *    %P - the original path (safely quoted for the shell)
+		 *    %S - the revision for the merge base
+		 *    %X - the revision for our version
+		 *    %Y - the revision for their version
 		 *
+		 * If the file is not named indentically in all versions, then each
+		 * revision is joined with the corresponding path, separated by a colon.
 		 * The external merge driver should write the results in the
 		 * file named by %A, and signal that it has done with zero exit
 		 * status.
@@ -377,7 +403,7 @@
 	     const struct ll_merge_options *opts)
 {
 	struct attr_check *check = load_merge_attributes();
-	static const struct ll_merge_options default_opts;
+	static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT;
 	const char *ll_driver_name = NULL;
 	int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 	const struct ll_merge_driver *driver;
@@ -391,7 +417,7 @@
 		normalize_file(theirs, path, istate);
 	}
 
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	ll_driver_name = check->items[0].value;
 	if (check->items[1].value) {
 		marker_size = atoi(check->items[1].value);
@@ -419,7 +445,7 @@
 
 	if (!check)
 		check = attr_check_initl("conflict-marker-size", NULL);
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 	if (check->items[0].value) {
 		marker_size = atoi(check->items[0].value);
 		if (marker_size <= 0)
diff --git a/ll-merge.h b/merge-ll.h
similarity index 96%
rename from ll-merge.h
rename to merge-ll.h
index e4a20e8..d038ee0 100644
--- a/ll-merge.h
+++ b/merge-ll.h
@@ -78,10 +78,15 @@
 	 */
 	unsigned extra_marker_size;
 
+	/* Override the global conflict style. */
+	int conflict_style;
+
 	/* Extra xpparam_t flags as defined in xdiff/xdiff.h. */
 	long xdl_opts;
 };
 
+#define LL_MERGE_OPTIONS_INIT { .conflict_style = -1 }
+
 enum ll_merge_result {
 	LL_MERGE_ERROR = -1,
 	LL_MERGE_OK = 0,
diff --git a/merge-ort-wrappers.c b/merge-ort-wrappers.c
index 748924a..4acedf3 100644
--- a/merge-ort-wrappers.c
+++ b/merge-ort-wrappers.c
@@ -1,6 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
 #include "merge-ort.h"
 #include "merge-ort-wrappers.h"
+#include "read-cache-ll.h"
+#include "tree.h"
 
 #include "commit.h"
 
diff --git a/merge-ort.c b/merge-ort.c
index d1611ca..eaede6c 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -14,26 +14,36 @@
  * "cale", "peedy", or "ins" instead of "ort"?)
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "merge-ort.h"
 
 #include "alloc.h"
+#include "advice.h"
 #include "attr.h"
-#include "blob.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "commit-reach.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "entry.h"
-#include "ll-merge.h"
-#include "object-store.h"
+#include "merge-ll.h"
+#include "match-trees.h"
+#include "mem-pool.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "path.h"
 #include "promisor-remote.h"
+#include "read-cache-ll.h"
+#include "refs.h"
 #include "revision.h"
+#include "sparse-index.h"
 #include "strmap.h"
-#include "submodule-config.h"
-#include "submodule.h"
+#include "trace2.h"
 #include "tree.h"
 #include "unpack-trees.h"
 #include "xdiff-interface.h"
@@ -533,6 +543,7 @@
 	CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
 	CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
 	CONFLICT_SUBMODULE_NULL_MERGE_BASE,
+	CONFLICT_SUBMODULE_CORRUPT,
 
 	/* Keep this entry _last_ in the list */
 	NB_CONFLICT_TYPES,
@@ -585,7 +596,9 @@
 	[CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
 		"CONFLICT (submodule may have rewinds)",
 	[CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
-		"CONFLICT (submodule lacks merge base)"
+		"CONFLICT (submodule lacks merge base)",
+	[CONFLICT_SUBMODULE_CORRUPT] =
+		"CONFLICT (submodule corrupt)"
 };
 
 struct logical_conflict_info {
@@ -710,23 +723,6 @@
 	renames->callback_data_nr = renames->callback_data_alloc = 0;
 }
 
-__attribute__((format (printf, 2, 3)))
-static int err(struct merge_options *opt, const char *err, ...)
-{
-	va_list params;
-	struct strbuf sb = STRBUF_INIT;
-
-	strbuf_addstr(&sb, "error: ");
-	va_start(params, err);
-	strbuf_vaddf(&sb, err, params);
-	va_end(params);
-
-	error("%s", sb.buf);
-	strbuf_release(&sb);
-
-	return -1;
-}
-
 static void format_commit(struct strbuf *sb,
 			  int indent,
 			  struct repository *repo,
@@ -1665,12 +1661,14 @@
 	info.data = opt;
 	info.show_all_errors = 1;
 
-	parse_tree(merge_base);
-	parse_tree(side1);
-	parse_tree(side2);
-	init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
-	init_tree_desc(t + 1, side1->buffer, side1->size);
-	init_tree_desc(t + 2, side2->buffer, side2->size);
+	if (parse_tree(merge_base) < 0 ||
+	    parse_tree(side1) < 0 ||
+	    parse_tree(side2) < 0)
+		return -1;
+	init_tree_desc(t + 0, &merge_base->object.oid,
+		       merge_base->buffer, merge_base->size);
+	init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size);
+	init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size);
 
 	trace2_region_enter("merge", "traverse_trees", opt->repo);
 	ret = traverse_trees(NULL, 3, t, &info);
@@ -1716,7 +1714,14 @@
 		die("revision walk setup failed");
 	while ((commit = get_revision(&revs)) != NULL) {
 		struct object *o = &(commit->object);
-		if (repo_in_merge_bases(repo, b, commit))
+		int ret = repo_in_merge_bases(repo, b, commit);
+
+		if (ret < 0) {
+			object_array_clear(&merges);
+			release_revisions(&revs);
+			return ret;
+		}
+		if (ret > 0)
 			add_object_array(o, NULL, &merges);
 	}
 	reset_revision_walk();
@@ -1731,9 +1736,17 @@
 		contains_another = 0;
 		for (j = 0; j < merges.nr; j++) {
 			struct commit *m2 = (struct commit *) merges.objects[j].item;
-			if (i != j && repo_in_merge_bases(repo, m2, m1)) {
-				contains_another = 1;
-				break;
+			if (i != j) {
+				int ret = repo_in_merge_bases(repo, m2, m1);
+				if (ret < 0) {
+					object_array_clear(&merges);
+					release_revisions(&revs);
+					return ret;
+				}
+				if (ret > 0) {
+					contains_another = 1;
+					break;
+				}
 			}
 		}
 
@@ -1755,7 +1768,7 @@
 {
 	struct repository subrepo;
 	struct strbuf sb = STRBUF_INIT;
-	int ret = 0;
+	int ret = 0, ret2;
 	struct commit *commit_o, *commit_a, *commit_b;
 	int parent_count;
 	struct object_array merges;
@@ -1802,8 +1815,28 @@
 	}
 
 	/* check whether both changes are forward */
-	if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
-	    !repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
+	ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
+	if (ret2 < 0) {
+		path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s "
+			   "(repository corrupt)"),
+			 path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (ret2 > 0)
+		ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
+	if (ret2 < 0) {
+		path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s "
+			   "(repository corrupt)"),
+			 path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (!ret2) {
 		path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
 			 path, NULL, NULL, NULL,
 			 _("Failed to merge submodule %s "
@@ -1813,7 +1846,17 @@
 	}
 
 	/* Case #1: a is contained in b or vice versa */
-	if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+	ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+	if (ret2 < 0) {
+		path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s "
+			   "(repository corrupt)"),
+			 path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (ret2 > 0) {
 		oidcpy(result, b);
 		path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
 			 path, NULL, NULL, NULL,
@@ -1822,7 +1865,17 @@
 		ret = 1;
 		goto cleanup;
 	}
-	if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+	ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+	if (ret2 < 0) {
+		path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s "
+			   "(repository corrupt)"),
+			 path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (ret2 > 0) {
 		oidcpy(result, a);
 		path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
 			 path, NULL, NULL, NULL,
@@ -1847,6 +1900,14 @@
 	parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
 					 &merges);
 	switch (parent_count) {
+	case -1:
+		path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+			 path, NULL, NULL, NULL,
+			 _("Failed to merge submodule %s "
+			   "(repository corrupt)"),
+			 path);
+		ret = -1;
+		break;
 	case 0:
 		path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
 			 path, NULL, NULL, NULL,
@@ -1908,6 +1969,7 @@
 	struct index_state *attr_index = &opt->priv->attr_index;
 	struct cache_entry *ce;
 
+	attr_index->repo = opt->repo;
 	attr_index->initialized = 1;
 
 	if (!opt->renormalize)
@@ -1963,7 +2025,7 @@
 		      mmbuffer_t *result_buf)
 {
 	mmfile_t orig, src1, src2;
-	struct ll_merge_options ll_opts = {0};
+	struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
 	char *base, *name1, *name2;
 	enum ll_merge_result merge_status;
 
@@ -1973,6 +2035,7 @@
 	ll_opts.renormalize = opt->renormalize;
 	ll_opts.extra_marker_size = extra_marker_size;
 	ll_opts.xdl_opts = opt->xdl_opts;
+	ll_opts.conflict_style = opt->conflict_style;
 
 	if (opt->priv->call_depth) {
 		ll_opts.virtual_ancestor = 1;
@@ -2042,7 +2105,7 @@
 	 * the three blobs to merge on various sides of history.
 	 *
 	 * extra_marker_size is the amount to extend conflict markers in
-	 * ll_merge; this is neeed if we have content merges of content
+	 * ll_merge; this is needed if we have content merges of content
 	 * merges, which happens for example with rename/rename(2to1) and
 	 * rename/add conflicts.
 	 */
@@ -2111,13 +2174,12 @@
 					  &result_buf);
 
 		if ((merge_status < 0) || !result_buf.ptr)
-			ret = err(opt, _("Failed to execute internal merge"));
+			ret = error(_("failed to execute internal merge"));
 
 		if (!ret &&
 		    write_object_file(result_buf.ptr, result_buf.size,
 				      OBJ_BLOB, &result->oid))
-			ret = err(opt, _("Unable to add %s to database"),
-				  path);
+			ret = error(_("unable to add %s to database"), path);
 
 		free(result_buf.ptr);
 		if (ret)
@@ -2650,7 +2712,7 @@
 			oidcpy(&ci->stages[i].oid, null_oid());
 		}
 
-		// Now we want to focus on new_ci, so reassign ci to it
+		/* Now we want to focus on new_ci, so reassign ci to it. */
 		ci = new_ci;
 	}
 
@@ -3331,10 +3393,7 @@
 	return clean;
 }
 
-static int detect_and_process_renames(struct merge_options *opt,
-				      struct tree *merge_base,
-				      struct tree *side1,
-				      struct tree *side2)
+static int detect_and_process_renames(struct merge_options *opt)
 {
 	struct diff_queue_struct combined = { 0 };
 	struct rename_info *renames = &opt->priv->renames;
@@ -3498,19 +3557,18 @@
 		return c1 - c2;
 }
 
-static int read_oid_strbuf(struct merge_options *opt,
-			   const struct object_id *oid,
+static int read_oid_strbuf(const struct object_id *oid,
 			   struct strbuf *dst)
 {
 	void *buf;
 	enum object_type type;
 	unsigned long size;
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf)
-		return err(opt, _("cannot read object %s"), oid_to_hex(oid));
+		return error(_("cannot read object %s"), oid_to_hex(oid));
 	if (type != OBJ_BLOB) {
 		free(buf);
-		return err(opt, _("object %s is not a blob"), oid_to_hex(oid));
+		return error(_("object %s is not a blob"), oid_to_hex(oid));
 	}
 	strbuf_attach(dst, buf, size, size + 1);
 	return 0;
@@ -3534,8 +3592,8 @@
 	if (oideq(&base->oid, &side->oid))
 		return 1;
 
-	if (read_oid_strbuf(opt, &base->oid, &basebuf) ||
-	    read_oid_strbuf(opt, &side->oid, &sidebuf))
+	if (read_oid_strbuf(&base->oid, &basebuf) ||
+	    read_oid_strbuf(&side->oid, &sidebuf))
 		goto error_return;
 	/*
 	 * Note: binary | is used so that both renormalizations are
@@ -4216,7 +4274,7 @@
 	struct string_list_item *e;
 	struct oid_array to_fetch = OID_ARRAY_INIT;
 
-	if (opt->repo != the_repository || !has_promisor_remote())
+	if (opt->repo != the_repository || !repo_has_promisor_remote(the_repository))
 		return;
 
 	for (e = &plist->items[plist->nr-1]; e >= plist->items; --e) {
@@ -4388,10 +4446,12 @@
 	unpack_opts.verbose_update = (opt->verbosity > 2);
 	unpack_opts.fn = twoway_merge;
 	unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
-	parse_tree(prev);
-	init_tree_desc(&trees[0], prev->buffer, prev->size);
-	parse_tree(next);
-	init_tree_desc(&trees[1], next->buffer, next->size);
+	if (parse_tree(prev) < 0)
+		return -1;
+	init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
+	if (parse_tree(next) < 0)
+		return -1;
+	init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
 
 	ret = unpack_trees(2, trees, &unpack_opts);
 	clear_unpack_trees_porcelain(&unpack_opts);
@@ -4568,7 +4628,7 @@
 		      " - commit the resulting index in the superproject\n"),
 		    tmp.buf, subs.buf);
 
-	printf("%s", msg.buf);
+	advise_if_enabled(ADVICE_SUBMODULE_MERGE_CONFLICT, "%s", msg.buf);
 
 	strbuf_release(&subs);
 	strbuf_release(&tmp);
@@ -4672,9 +4732,6 @@
 {
 	assert(opt->priv == NULL);
 	if (result->clean >= 0 && update_worktree_and_index) {
-		const char *filename;
-		FILE *fp;
-
 		trace2_region_enter("merge", "checkout", opt->repo);
 		if (checkout(opt, head, result->tree)) {
 			/* failure to function */
@@ -4700,10 +4757,17 @@
 		trace2_region_leave("merge", "record_conflicted", opt->repo);
 
 		trace2_region_enter("merge", "write_auto_merge", opt->repo);
-		filename = git_path_auto_merge(opt->repo);
-		fp = xfopen(filename, "w");
-		fprintf(fp, "%s\n", oid_to_hex(&result->tree->object.oid));
-		fclose(fp);
+		if (refs_update_ref(get_main_ref_store(opt->repo), "", "AUTO_MERGE",
+				    &result->tree->object.oid, NULL, REF_NO_DEREF,
+				    UPDATE_REFS_MSG_ON_ERR)) {
+			/* failure to function */
+			opt->priv = NULL;
+			result->clean = -1;
+			merge_finalize(opt, result);
+			trace2_region_leave("merge", "write_auto_merge",
+					    opt->repo);
+			return;
+		}
 		trace2_region_leave("merge", "write_auto_merge", opt->repo);
 	}
 	if (display_update_msgs)
@@ -4715,14 +4779,14 @@
 void merge_finalize(struct merge_options *opt,
 		    struct merge_result *result)
 {
-	struct merge_options_internal *opti = result->priv;
-
 	if (opt->renormalize)
 		git_attr_set_direction(GIT_ATTR_CHECKIN);
 	assert(opt->priv == NULL);
 
-	clear_or_reinit_internal_opts(opti, 0);
-	FREE_AND_NULL(opti);
+	if (result->priv) {
+		clear_or_reinit_internal_opts(result->priv, 0);
+		FREE_AND_NULL(result->priv);
+	}
 }
 
 /*** Function Grouping: helper functions for merge_incore_*() ***/
@@ -4891,8 +4955,7 @@
 	trace2_region_leave("merge", "allocate/init", opt->repo);
 }
 
-static void merge_check_renames_reusable(struct merge_options *opt,
-					 struct merge_result *result,
+static void merge_check_renames_reusable(struct merge_result *result,
 					 struct tree *merge_base,
 					 struct tree *side1,
 					 struct tree *side2)
@@ -4962,7 +5025,7 @@
 		 * TRANSLATORS: The %s arguments are: 1) tree hash of a merge
 		 * base, and 2-3) the trees for the two trees we're merging.
 		 */
-		err(opt, _("collecting merge info failed for trees %s, %s, %s"),
+		error(_("collecting merge info failed for trees %s, %s, %s"),
 		    oid_to_hex(&merge_base->object.oid),
 		    oid_to_hex(&side1->object.oid),
 		    oid_to_hex(&side2->object.oid));
@@ -4972,8 +5035,7 @@
 	trace2_region_leave("merge", "collect_merge_info", opt->repo);
 
 	trace2_region_enter("merge", "renames", opt->repo);
-	result->clean = detect_and_process_renames(opt, merge_base,
-						   side1, side2);
+	result->clean = detect_and_process_renames(opt);
 	trace2_region_leave("merge", "renames", opt->repo);
 	if (opt->priv->renames.redo_after_renames == 2) {
 		trace2_region_enter("merge", "reset_maps", opt->repo);
@@ -4992,6 +5054,9 @@
 
 	if (result->clean >= 0) {
 		result->tree = parse_tree_indirect(&working_tree_oid);
+		if (!result->tree)
+			die(_("unable to read tree (%s)"),
+			    oid_to_hex(&working_tree_oid));
 		/* existence of conflicted entries implies unclean */
 		result->clean &= strmap_empty(&opt->priv->conflicted);
 	}
@@ -5017,7 +5082,11 @@
 	struct strbuf merge_base_abbrev = STRBUF_INIT;
 
 	if (!merge_bases) {
-		merge_bases = get_merge_bases(h1, h2);
+		if (repo_get_merge_bases(the_repository, h1, h2,
+					 &merge_bases) < 0) {
+			result->clean = -1;
+			return;
+		}
 		/* See merge-ort.h:merge_incore_recursive() declaration NOTE */
 		merge_bases = reverse_commit_list(merge_bases);
 	}
@@ -5095,7 +5164,7 @@
 
 	trace2_region_enter("merge", "merge_start", opt->repo);
 	assert(opt->ancestor != NULL);
-	merge_check_renames_reusable(opt, result, merge_base, side1, side2);
+	merge_check_renames_reusable(result, merge_base, side1, side2);
 	merge_start(opt, result);
 	/*
 	 * Record the trees used in this merge, so if there's a next merge in
diff --git a/merge-ort.h b/merge-ort.h
index a994c9a..ce56ec1 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -2,7 +2,7 @@
 #define MERGE_ORT_H
 
 #include "merge-recursive.h"
-#include "hash.h"
+#include "hash-ll.h"
 
 struct commit;
 struct tree;
diff --git a/merge-recursive.c b/merge-recursive.c
index ae469f8..8ff29ed 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,14 +3,10 @@
  * Fredrik Kuivinen.
  * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "merge-recursive.h"
 
-#include "advice.h"
 #include "alloc.h"
-#include "attr.h"
-#include "blob.h"
-#include "builtin.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "commit-reach.h"
@@ -18,14 +14,22 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "dir.h"
-#include "ll-merge.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "merge-ll.h"
 #include "lockfile.h"
-#include "object-store.h"
+#include "match-trees.h"
+#include "name-hash.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
 #include "revision.h"
+#include "sparse-index.h"
 #include "string-list.h"
-#include "submodule-config.h"
-#include "submodule.h"
+#include "symlinks.h"
 #include "tag.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
@@ -401,8 +405,9 @@
 
 static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
 {
-	parse_tree(tree);
-	init_tree_desc(desc, tree->buffer, tree->size);
+	if (parse_tree(tree) < 0)
+		exit(128);
+	init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
 }
 
 static int unpack_trees_start(struct merge_options *opt,
@@ -951,7 +956,8 @@
 			goto update_index;
 		}
 
-		buf = read_object_file(&contents->oid, &type, &size);
+		buf = repo_read_object_file(the_repository, &contents->oid,
+					    &type, &size);
 		if (!buf) {
 			ret = err(opt, _("cannot read object %s '%s'"),
 				  oid_to_hex(&contents->oid), path);
@@ -1042,13 +1048,14 @@
 		      const int extra_marker_size)
 {
 	mmfile_t orig, src1, src2;
-	struct ll_merge_options ll_opts = {0};
+	struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
 	char *base, *name1, *name2;
 	enum ll_merge_result merge_status;
 
 	ll_opts.renormalize = opt->renormalize;
 	ll_opts.extra_marker_size = extra_marker_size;
 	ll_opts.xdl_opts = opt->xdl_opts;
+	ll_opts.conflict_style = opt->conflict_style;
 
 	if (opt->priv->call_depth) {
 		ll_opts.virtual_ancestor = 1;
@@ -1134,7 +1141,13 @@
 		die("revision walk setup failed");
 	while ((commit = get_revision(&revs)) != NULL) {
 		struct object *o = &(commit->object);
-		if (repo_in_merge_bases(repo, b, commit))
+		int ret = repo_in_merge_bases(repo, b, commit);
+		if (ret < 0) {
+			object_array_clear(&merges);
+			release_revisions(&revs);
+			return ret;
+		}
+		if (ret)
 			add_object_array(o, NULL, &merges);
 	}
 	reset_revision_walk();
@@ -1149,9 +1162,17 @@
 		contains_another = 0;
 		for (j = 0; j < merges.nr; j++) {
 			struct commit *m2 = (struct commit *) merges.objects[j].item;
-			if (i != j && repo_in_merge_bases(repo, m2, m1)) {
-				contains_another = 1;
-				break;
+			if (i != j) {
+				int ret = repo_in_merge_bases(repo, m2, m1);
+				if (ret < 0) {
+					object_array_clear(&merges);
+					release_revisions(&revs);
+					return ret;
+				}
+				if (ret > 0) {
+					contains_another = 1;
+					break;
+				}
 			}
 		}
 
@@ -1187,7 +1208,7 @@
 			   const struct object_id *b)
 {
 	struct repository subrepo;
-	int ret = 0;
+	int ret = 0, ret2;
 	struct commit *commit_base, *commit_a, *commit_b;
 	int parent_count;
 	struct object_array merges;
@@ -1224,14 +1245,32 @@
 	}
 
 	/* check whether both changes are forward */
-	if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) ||
-	    !repo_in_merge_bases(&subrepo, commit_base, commit_b)) {
+	ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
+	if (ret2 < 0) {
+		output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (ret2 > 0)
+		ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b);
+	if (ret2 < 0) {
+		output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (!ret2) {
 		output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
 		goto cleanup;
 	}
 
 	/* Case #1: a is contained in b or vice versa */
-	if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+	ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+	if (ret2 < 0) {
+		output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (ret2) {
 		oidcpy(result, b);
 		if (show(opt, 3)) {
 			output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1244,7 +1283,13 @@
 		ret = 1;
 		goto cleanup;
 	}
-	if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+	ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+	if (ret2 < 0) {
+		output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+		ret = -1;
+		goto cleanup;
+	}
+	if (ret2) {
 		oidcpy(result, a);
 		if (show(opt, 3)) {
 			output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1273,6 +1318,10 @@
 	parent_count = find_first_merges(&subrepo, &merges, path,
 					 commit_a, commit_b);
 	switch (parent_count) {
+	case -1:
+		output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
+		ret = -1;
+		break;
 	case 0:
 		output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
 		break;
@@ -1373,12 +1422,12 @@
 						  extra_marker_size);
 
 			if ((merge_status < 0) || !result_buf.ptr)
-				ret = err(opt, _("Failed to execute internal merge"));
+				ret = err(opt, _("failed to execute internal merge"));
 
 			if (!ret &&
 			    write_object_file(result_buf.ptr, result_buf.size,
 					      OBJ_BLOB, &result->blob.oid))
-				ret = err(opt, _("Unable to add %s to database"),
+				ret = err(opt, _("unable to add %s to database"),
 					  a->path);
 
 			free(result_buf.ptr);
@@ -1387,11 +1436,14 @@
 			/* FIXME: bug, what if modes didn't match? */
 			result->clean = (merge_status == 0);
 		} else if (S_ISGITLINK(a->mode)) {
-			result->clean = merge_submodule(opt, &result->blob.oid,
-							o->path,
-							&o->oid,
-							&a->oid,
-							&b->oid);
+			int clean = merge_submodule(opt, &result->blob.oid,
+						    o->path,
+						    &o->oid,
+						    &a->oid,
+						    &b->oid);
+			if (clean < 0)
+				return -1;
+			result->clean = clean;
 		} else if (S_ISLNK(a->mode)) {
 			switch (opt->recursive_variant) {
 			case MERGE_VARIANT_NORMAL:
@@ -3021,7 +3073,7 @@
 	void *buf;
 	enum object_type type;
 	unsigned long size;
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf)
 		return err(opt, _("cannot read object %s"), oid_to_hex(oid));
 	if (type != OBJ_BLOB) {
@@ -3592,7 +3644,9 @@
 	}
 
 	if (!merge_bases) {
-		merge_bases = get_merge_bases(h1, h2);
+		if (repo_get_merge_bases(the_repository, h1, h2,
+					 &merge_bases) < 0)
+			return -1;
 		merge_bases = reverse_commit_list(merge_bases);
 	}
 
@@ -3797,7 +3851,7 @@
 		return make_virtual_commit(repo, (struct tree*)object, name);
 	if (object->type != OBJ_COMMIT)
 		return NULL;
-	if (parse_commit((struct commit *)object))
+	if (repo_parse_commit(repo, (struct commit *)object))
 		return NULL;
 	return (struct commit *)object;
 }
@@ -3894,6 +3948,8 @@
 
 	opt->renormalize = 0;
 
+	opt->conflict_style = -1;
+
 	merge_recursive_config(opt);
 	merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
 	if (merge_verbosity)
@@ -3902,6 +3958,22 @@
 		opt->buffer_output = 0;
 }
 
+/*
+ * For now, members of merge_options do not need deep copying, but
+ * it may change in the future, in which case we would need to update
+ * this, and also make a matching change to clear_merge_options() to
+ * release the resources held by a copied instance.
+ */
+void copy_merge_options(struct merge_options *dst, struct merge_options *src)
+{
+	*dst = *src;
+}
+
+void clear_merge_options(struct merge_options *opt UNUSED)
+{
+	; /* no-op as our copy is shallow right now */
+}
+
 int parse_merge_opt(struct merge_options *opt, const char *s)
 {
 	const char *arg;
diff --git a/merge-recursive.h b/merge-recursive.h
index b88000e..e67d38c 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -31,6 +31,7 @@
 
 	/* xdiff-related options (patience, ignore whitespace, ours/theirs) */
 	long xdl_opts;
+	int conflict_style;
 	enum {
 		MERGE_VARIANT_NORMAL = 0,
 		MERGE_VARIANT_OURS,
@@ -55,6 +56,9 @@
 
 void init_merge_options(struct merge_options *opt, struct repository *repo);
 
+void copy_merge_options(struct merge_options *dst, struct merge_options *src);
+void clear_merge_options(struct merge_options *opt);
+
 /* parse the option in s and update the relevant field of opt */
 int parse_merge_opt(struct merge_options *opt, const char *s);
 
diff --git a/merge.c b/merge.c
index 445b4f1..752a937 100644
--- a/merge.c
+++ b/merge.c
@@ -1,13 +1,16 @@
-#include "cache.h"
-#include "diff.h"
-#include "diffcore.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "lockfile.h"
+#include "merge.h"
 #include "commit.h"
+#include "repository.h"
 #include "run-command.h"
 #include "resolve-undo.h"
+#include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
-#include "dir.h"
 
 static const char *merge_argument(struct commit *commit)
 {
@@ -74,8 +77,12 @@
 		return -1;
 	}
 	for (i = 0; i < nr_trees; i++) {
-		parse_tree(trees[i]);
-		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+		if (parse_tree(trees[i]) < 0) {
+			rollback_lock_file(&lock_file);
+			return -1;
+		}
+		init_tree_desc(t+i, &trees[i]->object.oid,
+			       trees[i]->buffer, trees[i]->size);
 	}
 
 	memset(&opts, 0, sizeof(opts));
diff --git a/merge.h b/merge.h
new file mode 100644
index 0000000..21ac7ef
--- /dev/null
+++ b/merge.h
@@ -0,0 +1,17 @@
+#ifndef MERGE_H
+#define MERGE_H
+
+struct commit_list;
+struct object_id;
+struct repository;
+
+int try_merge_command(struct repository *r,
+		const char *strategy, size_t xopts_nr,
+		const char **xopts, struct commit_list *common,
+		const char *head_arg, struct commit_list *remotes);
+int checkout_fast_forward(struct repository *r,
+			  const struct object_id *from,
+			  const struct object_id *to,
+			  int overwrite_ignore);
+
+#endif /* MERGE_H */
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 06937ac..97e3763 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -371,9 +371,17 @@
 
 
 merge_cmd () {
-	layout=$(git config mergetool.vimdiff.layout)
+	TOOL=$1
 
-	case "$1" in
+	layout=$(git config "mergetool.$TOOL.layout")
+
+	# backward compatibility:
+	if test -z "$layout"
+	then
+		layout=$(git config mergetool.vimdiff.layout)
+	fi
+
+	case "$TOOL" in
 	*vimdiff)
 		if test -z "$layout"
 		then
diff --git a/midx-write.c b/midx-write.c
new file mode 100644
index 0000000..65e69d2
--- /dev/null
+++ b/midx-write.c
@@ -0,0 +1,1525 @@
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "config.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "packfile.h"
+#include "object-file.h"
+#include "hash-lookup.h"
+#include "midx.h"
+#include "progress.h"
+#include "trace2.h"
+#include "run-command.h"
+#include "chunk-format.h"
+#include "pack-bitmap.h"
+#include "refs.h"
+#include "revision.h"
+#include "list-objects.h"
+
+#define PACK_EXPIRED UINT_MAX
+#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
+#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
+#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
+
+extern int midx_checksum_valid(struct multi_pack_index *m);
+extern void clear_midx_files_ext(const char *object_dir, const char *ext,
+				 unsigned char *keep_hash);
+extern int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+				const char *idx_name);
+
+static size_t write_midx_header(struct hashfile *f,
+				unsigned char num_chunks,
+				uint32_t num_packs)
+{
+	hashwrite_be32(f, MIDX_SIGNATURE);
+	hashwrite_u8(f, MIDX_VERSION);
+	hashwrite_u8(f, oid_version(the_hash_algo));
+	hashwrite_u8(f, num_chunks);
+	hashwrite_u8(f, 0); /* unused */
+	hashwrite_be32(f, num_packs);
+
+	return MIDX_HEADER_SIZE;
+}
+
+struct pack_info {
+	uint32_t orig_pack_int_id;
+	char *pack_name;
+	struct packed_git *p;
+
+	uint32_t bitmap_pos;
+	uint32_t bitmap_nr;
+
+	unsigned expired : 1;
+};
+
+static void fill_pack_info(struct pack_info *info,
+			   struct packed_git *p, const char *pack_name,
+			   uint32_t orig_pack_int_id)
+{
+	memset(info, 0, sizeof(struct pack_info));
+
+	info->orig_pack_int_id = orig_pack_int_id;
+	info->pack_name = xstrdup(pack_name);
+	info->p = p;
+	info->bitmap_pos = BITMAP_POS_UNKNOWN;
+}
+
+static int pack_info_compare(const void *_a, const void *_b)
+{
+	struct pack_info *a = (struct pack_info *)_a;
+	struct pack_info *b = (struct pack_info *)_b;
+	return strcmp(a->pack_name, b->pack_name);
+}
+
+static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
+{
+	const char *pack_name = _va;
+	const struct pack_info *compar = _vb;
+
+	return cmp_idx_or_pack_name(pack_name, compar->pack_name);
+}
+
+struct write_midx_context {
+	struct pack_info *info;
+	size_t nr;
+	size_t alloc;
+	struct multi_pack_index *m;
+	struct progress *progress;
+	unsigned pack_paths_checked;
+
+	struct pack_midx_entry *entries;
+	size_t entries_nr;
+
+	uint32_t *pack_perm;
+	uint32_t *pack_order;
+	unsigned large_offsets_needed:1;
+	uint32_t num_large_offsets;
+
+	int preferred_pack_idx;
+
+	struct string_list *to_include;
+};
+
+static void add_pack_to_midx(const char *full_path, size_t full_path_len,
+			     const char *file_name, void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct packed_git *p;
+
+	if (ends_with(file_name, ".idx")) {
+		display_progress(ctx->progress, ++ctx->pack_paths_checked);
+		/*
+		 * Note that at most one of ctx->m and ctx->to_include are set,
+		 * so we are testing midx_contains_pack() and
+		 * string_list_has_string() independently (guarded by the
+		 * appropriate NULL checks).
+		 *
+		 * We could support passing to_include while reusing an existing
+		 * MIDX, but don't currently since the reuse process drags
+		 * forward all packs from an existing MIDX (without checking
+		 * whether or not they appear in the to_include list).
+		 *
+		 * If we added support for that, these next two conditional
+		 * should be performed independently (likely checking
+		 * to_include before the existing MIDX).
+		 */
+		if (ctx->m && midx_contains_pack(ctx->m, file_name))
+			return;
+		else if (ctx->to_include &&
+			 !string_list_has_string(ctx->to_include, file_name))
+			return;
+
+		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
+
+		p = add_packed_git(full_path, full_path_len, 0);
+		if (!p) {
+			warning(_("failed to add packfile '%s'"),
+				full_path);
+			return;
+		}
+
+		if (open_pack_index(p)) {
+			warning(_("failed to open pack-index '%s'"),
+				full_path);
+			close_pack(p);
+			free(p);
+			return;
+		}
+
+		fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
+		ctx->nr++;
+	}
+}
+
+struct pack_midx_entry {
+	struct object_id oid;
+	uint32_t pack_int_id;
+	time_t pack_mtime;
+	uint64_t offset;
+	unsigned preferred : 1;
+};
+
+static int midx_oid_compare(const void *_a, const void *_b)
+{
+	const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
+	const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
+	int cmp = oidcmp(&a->oid, &b->oid);
+
+	if (cmp)
+		return cmp;
+
+	/* Sort objects in a preferred pack first when multiple copies exist. */
+	if (a->preferred > b->preferred)
+		return -1;
+	if (a->preferred < b->preferred)
+		return 1;
+
+	if (a->pack_mtime > b->pack_mtime)
+		return -1;
+	else if (a->pack_mtime < b->pack_mtime)
+		return 1;
+
+	return a->pack_int_id - b->pack_int_id;
+}
+
+static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
+				      struct pack_midx_entry *e,
+				      uint32_t pos)
+{
+	if (pos >= m->num_objects)
+		return 1;
+
+	nth_midxed_object_oid(&e->oid, m, pos);
+	e->pack_int_id = nth_midxed_pack_int_id(m, pos);
+	e->offset = nth_midxed_offset(m, pos);
+
+	/* consider objects in midx to be from "old" packs */
+	e->pack_mtime = 0;
+	return 0;
+}
+
+static void fill_pack_entry(uint32_t pack_int_id,
+			    struct packed_git *p,
+			    uint32_t cur_object,
+			    struct pack_midx_entry *entry,
+			    int preferred)
+{
+	if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
+		die(_("failed to locate object %d in packfile"), cur_object);
+
+	entry->pack_int_id = pack_int_id;
+	entry->pack_mtime = p->mtime;
+
+	entry->offset = nth_packed_object_offset(p, cur_object);
+	entry->preferred = !!preferred;
+}
+
+struct midx_fanout {
+	struct pack_midx_entry *entries;
+	size_t nr, alloc;
+};
+
+static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
+{
+	if (nr < fanout->nr)
+		BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
+		    (uintmax_t)nr, (uintmax_t)fanout->nr);
+	ALLOC_GROW(fanout->entries, nr, fanout->alloc);
+}
+
+static void midx_fanout_sort(struct midx_fanout *fanout)
+{
+	QSORT(fanout->entries, fanout->nr, midx_oid_compare);
+}
+
+static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
+					struct multi_pack_index *m,
+					uint32_t cur_fanout,
+					int preferred_pack)
+{
+	uint32_t start = 0, end;
+	uint32_t cur_object;
+
+	if (cur_fanout)
+		start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
+	end = ntohl(m->chunk_oid_fanout[cur_fanout]);
+
+	for (cur_object = start; cur_object < end; cur_object++) {
+		if ((preferred_pack > -1) &&
+		    (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
+			/*
+			 * Objects from preferred packs are added
+			 * separately.
+			 */
+			continue;
+		}
+
+		midx_fanout_grow(fanout, fanout->nr + 1);
+		nth_midxed_pack_midx_entry(m,
+					   &fanout->entries[fanout->nr],
+					   cur_object);
+		fanout->entries[fanout->nr].preferred = 0;
+		fanout->nr++;
+	}
+}
+
+static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
+					struct pack_info *info,
+					uint32_t cur_pack,
+					int preferred,
+					uint32_t cur_fanout)
+{
+	struct packed_git *pack = info[cur_pack].p;
+	uint32_t start = 0, end;
+	uint32_t cur_object;
+
+	if (cur_fanout)
+		start = get_pack_fanout(pack, cur_fanout - 1);
+	end = get_pack_fanout(pack, cur_fanout);
+
+	for (cur_object = start; cur_object < end; cur_object++) {
+		midx_fanout_grow(fanout, fanout->nr + 1);
+		fill_pack_entry(cur_pack,
+				info[cur_pack].p,
+				cur_object,
+				&fanout->entries[fanout->nr],
+				preferred);
+		fanout->nr++;
+	}
+}
+
+/*
+ * It is possible to artificially get into a state where there are many
+ * duplicate copies of objects. That can create high memory pressure if
+ * we are to create a list of all objects before de-duplication. To reduce
+ * this memory pressure without a significant performance drop, automatically
+ * group objects by the first byte of their object id. Use the IDX fanout
+ * tables to group the data, copy to a local array, then sort.
+ *
+ * Copy only the de-duplicated entries (selected by most-recent modified time
+ * of a packfile containing the object).
+ */
+static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
+						  struct pack_info *info,
+						  uint32_t nr_packs,
+						  size_t *nr_objects,
+						  int preferred_pack)
+{
+	uint32_t cur_fanout, cur_pack, cur_object;
+	size_t alloc_objects, total_objects = 0;
+	struct midx_fanout fanout = { 0 };
+	struct pack_midx_entry *deduplicated_entries = NULL;
+	uint32_t start_pack = m ? m->num_packs : 0;
+
+	for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
+		total_objects = st_add(total_objects,
+				       info[cur_pack].p->num_objects);
+
+	/*
+	 * As we de-duplicate by fanout value, we expect the fanout
+	 * slices to be evenly distributed, with some noise. Hence,
+	 * allocate slightly more than one 256th.
+	 */
+	alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
+
+	ALLOC_ARRAY(fanout.entries, fanout.alloc);
+	ALLOC_ARRAY(deduplicated_entries, alloc_objects);
+	*nr_objects = 0;
+
+	for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
+		fanout.nr = 0;
+
+		if (m)
+			midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
+						    preferred_pack);
+
+		for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
+			int preferred = cur_pack == preferred_pack;
+			midx_fanout_add_pack_fanout(&fanout,
+						    info, cur_pack,
+						    preferred, cur_fanout);
+		}
+
+		if (-1 < preferred_pack && preferred_pack < start_pack)
+			midx_fanout_add_pack_fanout(&fanout, info,
+						    preferred_pack, 1,
+						    cur_fanout);
+
+		midx_fanout_sort(&fanout);
+
+		/*
+		 * The batch is now sorted by OID and then mtime (descending).
+		 * Take only the first duplicate.
+		 */
+		for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
+			if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
+						&fanout.entries[cur_object].oid))
+				continue;
+
+			ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+				   alloc_objects);
+			memcpy(&deduplicated_entries[*nr_objects],
+			       &fanout.entries[cur_object],
+			       sizeof(struct pack_midx_entry));
+			(*nr_objects)++;
+		}
+	}
+
+	free(fanout.entries);
+	return deduplicated_entries;
+}
+
+static int write_midx_pack_names(struct hashfile *f, void *data)
+{
+	struct write_midx_context *ctx = data;
+	uint32_t i;
+	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
+	size_t written = 0;
+
+	for (i = 0; i < ctx->nr; i++) {
+		size_t writelen;
+
+		if (ctx->info[i].expired)
+			continue;
+
+		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
+			BUG("incorrect pack-file order: %s before %s",
+			    ctx->info[i - 1].pack_name,
+			    ctx->info[i].pack_name);
+
+		writelen = strlen(ctx->info[i].pack_name) + 1;
+		hashwrite(f, ctx->info[i].pack_name, writelen);
+		written += writelen;
+	}
+
+	/* add padding to be aligned */
+	i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
+	if (i < MIDX_CHUNK_ALIGNMENT) {
+		memset(padding, 0, sizeof(padding));
+		hashwrite(f, padding, i);
+	}
+
+	return 0;
+}
+
+static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
+{
+	struct write_midx_context *ctx = data;
+	size_t i;
+
+	for (i = 0; i < ctx->nr; i++) {
+		struct pack_info *pack = &ctx->info[i];
+		if (pack->expired)
+			continue;
+
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
+			BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
+			    pack->pack_name, pack->bitmap_nr);
+
+		hashwrite_be32(f, pack->bitmap_pos);
+		hashwrite_be32(f, pack->bitmap_nr);
+	}
+	return 0;
+}
+
+static int write_midx_oid_fanout(struct hashfile *f,
+				 void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
+	uint32_t count = 0;
+	uint32_t i;
+
+	/*
+	* Write the first-level table (the list is sorted,
+	* but we use a 256-entry lookup to be able to avoid
+	* having to do eight extra binary search iterations).
+	*/
+	for (i = 0; i < 256; i++) {
+		struct pack_midx_entry *next = list;
+
+		while (next < last && next->oid.hash[0] == i) {
+			count++;
+			next++;
+		}
+
+		hashwrite_be32(f, count);
+		list = next;
+	}
+
+	return 0;
+}
+
+static int write_midx_oid_lookup(struct hashfile *f,
+				 void *data)
+{
+	struct write_midx_context *ctx = data;
+	unsigned char hash_len = the_hash_algo->rawsz;
+	struct pack_midx_entry *list = ctx->entries;
+	uint32_t i;
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *obj = list++;
+
+		if (i < ctx->entries_nr - 1) {
+			struct pack_midx_entry *next = list;
+			if (oidcmp(&obj->oid, &next->oid) >= 0)
+				BUG("OIDs not in order: %s >= %s",
+				    oid_to_hex(&obj->oid),
+				    oid_to_hex(&next->oid));
+		}
+
+		hashwrite(f, obj->oid.hash, (int)hash_len);
+	}
+
+	return 0;
+}
+
+static int write_midx_object_offsets(struct hashfile *f,
+				     void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	uint32_t i, nr_large_offset = 0;
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *obj = list++;
+
+		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
+			BUG("object %s is in an expired pack with int-id %d",
+			    oid_to_hex(&obj->oid),
+			    obj->pack_int_id);
+
+		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
+
+		if (ctx->large_offsets_needed && obj->offset >> 31)
+			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
+		else if (!ctx->large_offsets_needed && obj->offset >> 32)
+			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
+			    oid_to_hex(&obj->oid),
+			    obj->offset);
+		else
+			hashwrite_be32(f, (uint32_t)obj->offset);
+	}
+
+	return 0;
+}
+
+static int write_midx_large_offsets(struct hashfile *f,
+				    void *data)
+{
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
+	uint32_t nr_large_offset = ctx->num_large_offsets;
+
+	while (nr_large_offset) {
+		struct pack_midx_entry *obj;
+		uint64_t offset;
+
+		if (list >= end)
+			BUG("too many large-offset objects");
+
+		obj = list++;
+		offset = obj->offset;
+
+		if (!(offset >> 31))
+			continue;
+
+		hashwrite_be64(f, offset);
+
+		nr_large_offset--;
+	}
+
+	return 0;
+}
+
+static int write_midx_revindex(struct hashfile *f,
+			       void *data)
+{
+	struct write_midx_context *ctx = data;
+	uint32_t i;
+
+	for (i = 0; i < ctx->entries_nr; i++)
+		hashwrite_be32(f, ctx->pack_order[i]);
+
+	return 0;
+}
+
+struct midx_pack_order_data {
+	uint32_t nr;
+	uint32_t pack;
+	off_t offset;
+};
+
+static int midx_pack_order_cmp(const void *va, const void *vb)
+{
+	const struct midx_pack_order_data *a = va, *b = vb;
+	if (a->pack < b->pack)
+		return -1;
+	else if (a->pack > b->pack)
+		return 1;
+	else if (a->offset < b->offset)
+		return -1;
+	else if (a->offset > b->offset)
+		return 1;
+	else
+		return 0;
+}
+
+static uint32_t *midx_pack_order(struct write_midx_context *ctx)
+{
+	struct midx_pack_order_data *data;
+	uint32_t *pack_order;
+	uint32_t i;
+
+	trace2_region_enter("midx", "midx_pack_order", the_repository);
+
+	ALLOC_ARRAY(data, ctx->entries_nr);
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *e = &ctx->entries[i];
+		data[i].nr = i;
+		data[i].pack = ctx->pack_perm[e->pack_int_id];
+		if (!e->preferred)
+			data[i].pack |= (1U << 31);
+		data[i].offset = e->offset;
+	}
+
+	QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
+
+	ALLOC_ARRAY(pack_order, ctx->entries_nr);
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *e = &ctx->entries[data[i].nr];
+		struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+			pack->bitmap_pos = i;
+		pack->bitmap_nr++;
+		pack_order[i] = data[i].nr;
+	}
+	for (i = 0; i < ctx->nr; i++) {
+		struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
+		if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+			pack->bitmap_pos = 0;
+	}
+	free(data);
+
+	trace2_region_leave("midx", "midx_pack_order", the_repository);
+
+	return pack_order;
+}
+
+static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
+				     struct write_midx_context *ctx)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *tmp_file;
+
+	trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
+
+	strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
+
+	tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
+					midx_hash, WRITE_REV);
+
+	if (finalize_object_file(tmp_file, buf.buf))
+		die(_("cannot store reverse index file"));
+
+	strbuf_release(&buf);
+
+	trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
+}
+
+static void prepare_midx_packing_data(struct packing_data *pdata,
+				      struct write_midx_context *ctx)
+{
+	uint32_t i;
+
+	trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
+
+	memset(pdata, 0, sizeof(struct packing_data));
+	prepare_packing_data(the_repository, pdata);
+
+	for (i = 0; i < ctx->entries_nr; i++) {
+		struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
+		struct object_entry *to = packlist_alloc(pdata, &from->oid);
+
+		oe_set_in_pack(pdata, to,
+			       ctx->info[ctx->pack_perm[from->pack_int_id]].p);
+	}
+
+	trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
+}
+
+static int add_ref_to_pending(const char *refname,
+			      const struct object_id *oid,
+			      int flag, void *cb_data)
+{
+	struct rev_info *revs = (struct rev_info*)cb_data;
+	struct object_id peeled;
+	struct object *object;
+
+	if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
+		warning("symbolic ref is dangling: %s", refname);
+		return 0;
+	}
+
+	if (!peel_iterated_oid(oid, &peeled))
+		oid = &peeled;
+
+	object = parse_object_or_die(oid, refname);
+	if (object->type != OBJ_COMMIT)
+		return 0;
+
+	add_pending_object(revs, object, "");
+	if (bitmap_is_preferred_refname(revs->repo, refname))
+		object->flags |= NEEDS_BITMAP;
+	return 0;
+}
+
+struct bitmap_commit_cb {
+	struct commit **commits;
+	size_t commits_nr, commits_alloc;
+
+	struct write_midx_context *ctx;
+};
+
+static const struct object_id *bitmap_oid_access(size_t index,
+						 const void *_entries)
+{
+	const struct pack_midx_entry *entries = _entries;
+	return &entries[index].oid;
+}
+
+static void bitmap_show_commit(struct commit *commit, void *_data)
+{
+	struct bitmap_commit_cb *data = _data;
+	int pos = oid_pos(&commit->object.oid, data->ctx->entries,
+			  data->ctx->entries_nr,
+			  bitmap_oid_access);
+	if (pos < 0)
+		return;
+
+	ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
+	data->commits[data->commits_nr++] = commit;
+}
+
+static int read_refs_snapshot(const char *refs_snapshot,
+			      struct rev_info *revs)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id oid;
+	FILE *f = xfopen(refs_snapshot, "r");
+
+	while (strbuf_getline(&buf, f) != EOF) {
+		struct object *object;
+		int preferred = 0;
+		char *hex = buf.buf;
+		const char *end = NULL;
+
+		if (buf.len && *buf.buf == '+') {
+			preferred = 1;
+			hex = &buf.buf[1];
+		}
+
+		if (parse_oid_hex(hex, &oid, &end) < 0)
+			die(_("could not parse line: %s"), buf.buf);
+		if (*end)
+			die(_("malformed line: %s"), buf.buf);
+
+		object = parse_object_or_die(&oid, NULL);
+		if (preferred)
+			object->flags |= NEEDS_BITMAP;
+
+		add_pending_object(revs, object, "");
+	}
+
+	fclose(f);
+	strbuf_release(&buf);
+	return 0;
+}
+static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
+						    const char *refs_snapshot,
+						    struct write_midx_context *ctx)
+{
+	struct rev_info revs;
+	struct bitmap_commit_cb cb = {0};
+
+	trace2_region_enter("midx", "find_commits_for_midx_bitmap",
+			    the_repository);
+
+	cb.ctx = ctx;
+
+	repo_init_revisions(the_repository, &revs, NULL);
+	if (refs_snapshot) {
+		read_refs_snapshot(refs_snapshot, &revs);
+	} else {
+		setup_revisions(0, NULL, &revs, NULL);
+		for_each_ref(add_ref_to_pending, &revs);
+	}
+
+	/*
+	 * Skipping promisor objects here is intentional, since it only excludes
+	 * them from the list of reachable commits that we want to select from
+	 * when computing the selection of MIDX'd commits to receive bitmaps.
+	 *
+	 * Reachability bitmaps do require that their objects be closed under
+	 * reachability, but fetching any objects missing from promisors at this
+	 * point is too late. But, if one of those objects can be reached from
+	 * an another object that is included in the bitmap, then we will
+	 * complain later that we don't have reachability closure (and fail
+	 * appropriately).
+	 */
+	fetch_if_missing = 0;
+	revs.exclude_promisor_objects = 1;
+
+	if (prepare_revision_walk(&revs))
+		die(_("revision walk setup failed"));
+
+	traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
+	if (indexed_commits_nr_p)
+		*indexed_commits_nr_p = cb.commits_nr;
+
+	release_revisions(&revs);
+
+	trace2_region_leave("midx", "find_commits_for_midx_bitmap",
+			    the_repository);
+
+	return cb.commits;
+}
+
+static int write_midx_bitmap(const char *midx_name,
+			     const unsigned char *midx_hash,
+			     struct packing_data *pdata,
+			     struct commit **commits,
+			     uint32_t commits_nr,
+			     uint32_t *pack_order,
+			     unsigned flags)
+{
+	int ret, i;
+	uint16_t options = 0;
+	struct pack_idx_entry **index;
+	char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
+					hash_to_hex(midx_hash));
+
+	trace2_region_enter("midx", "write_midx_bitmap", the_repository);
+
+	if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
+		options |= BITMAP_OPT_HASH_CACHE;
+
+	if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
+		options |= BITMAP_OPT_LOOKUP_TABLE;
+
+	/*
+	 * Build the MIDX-order index based on pdata.objects (which is already
+	 * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
+	 * this order).
+	 */
+	ALLOC_ARRAY(index, pdata->nr_objects);
+	for (i = 0; i < pdata->nr_objects; i++)
+		index[i] = &pdata->objects[i].idx;
+
+	bitmap_writer_show_progress(flags & MIDX_PROGRESS);
+	bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
+
+	/*
+	 * bitmap_writer_finish expects objects in lex order, but pack_order
+	 * gives us exactly that. use it directly instead of re-sorting the
+	 * array.
+	 *
+	 * This changes the order of objects in 'index' between
+	 * bitmap_writer_build_type_index and bitmap_writer_finish.
+	 *
+	 * The same re-ordering takes place in the single-pack bitmap code via
+	 * write_idx_file(), which is called by finish_tmp_packfile(), which
+	 * happens between bitmap_writer_build_type_index() and
+	 * bitmap_writer_finish().
+	 */
+	for (i = 0; i < pdata->nr_objects; i++)
+		index[pack_order[i]] = &pdata->objects[i].idx;
+
+	bitmap_writer_select_commits(commits, commits_nr, -1);
+	ret = bitmap_writer_build(pdata);
+	if (ret < 0)
+		goto cleanup;
+
+	bitmap_writer_set_checksum(midx_hash);
+	bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
+
+cleanup:
+	free(index);
+	free(bitmap_name);
+
+	trace2_region_leave("midx", "write_midx_bitmap", the_repository);
+
+	return ret;
+}
+
+static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
+							const char *object_dir)
+{
+	struct multi_pack_index *result = NULL;
+	struct multi_pack_index *cur;
+	char *obj_dir_real = real_pathdup(object_dir, 1);
+	struct strbuf cur_path_real = STRBUF_INIT;
+
+	/* Ensure the given object_dir is local, or a known alternate. */
+	find_odb(r, obj_dir_real);
+
+	for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
+		strbuf_realpath(&cur_path_real, cur->object_dir, 1);
+		if (!strcmp(obj_dir_real, cur_path_real.buf)) {
+			result = cur;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	free(obj_dir_real);
+	strbuf_release(&cur_path_real);
+	return result;
+}
+
+static int write_midx_internal(const char *object_dir,
+			       struct string_list *packs_to_include,
+			       struct string_list *packs_to_drop,
+			       const char *preferred_pack_name,
+			       const char *refs_snapshot,
+			       unsigned flags)
+{
+	struct strbuf midx_name = STRBUF_INIT;
+	unsigned char midx_hash[GIT_MAX_RAWSZ];
+	uint32_t i;
+	struct hashfile *f = NULL;
+	struct lock_file lk;
+	struct write_midx_context ctx = { 0 };
+	int bitmapped_packs_concat_len = 0;
+	int pack_name_concat_len = 0;
+	int dropped_packs = 0;
+	int result = 0;
+	struct chunkfile *cf;
+
+	trace2_region_enter("midx", "write_midx_internal", the_repository);
+
+	get_midx_filename(&midx_name, object_dir);
+	if (safe_create_leading_directories(midx_name.buf))
+		die_errno(_("unable to create leading directories of %s"),
+			  midx_name.buf);
+
+	if (!packs_to_include) {
+		/*
+		 * Only reference an existing MIDX when not filtering which
+		 * packs to include, since all packs and objects are copied
+		 * blindly from an existing MIDX if one is present.
+		 */
+		ctx.m = lookup_multi_pack_index(the_repository, object_dir);
+	}
+
+	if (ctx.m && !midx_checksum_valid(ctx.m)) {
+		warning(_("ignoring existing multi-pack-index; checksum mismatch"));
+		ctx.m = NULL;
+	}
+
+	ctx.nr = 0;
+	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
+	ctx.info = NULL;
+	ALLOC_ARRAY(ctx.info, ctx.alloc);
+
+	if (ctx.m) {
+		for (i = 0; i < ctx.m->num_packs; i++) {
+			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
+
+			if (flags & MIDX_WRITE_REV_INDEX) {
+				/*
+				 * If generating a reverse index, need to have
+				 * packed_git's loaded to compare their
+				 * mtimes and object count.
+				 */
+				if (prepare_midx_pack(the_repository, ctx.m, i)) {
+					error(_("could not load pack"));
+					result = 1;
+					goto cleanup;
+				}
+
+				if (open_pack_index(ctx.m->packs[i]))
+					die(_("could not open index for %s"),
+					    ctx.m->packs[i]->pack_name);
+			}
+
+			fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
+				       ctx.m->pack_names[i], i);
+		}
+	}
+
+	ctx.pack_paths_checked = 0;
+	if (flags & MIDX_PROGRESS)
+		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
+	else
+		ctx.progress = NULL;
+
+	ctx.to_include = packs_to_include;
+
+	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
+	stop_progress(&ctx.progress);
+
+	if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
+	    !(packs_to_include || packs_to_drop)) {
+		struct bitmap_index *bitmap_git;
+		int bitmap_exists;
+		int want_bitmap = flags & MIDX_WRITE_BITMAP;
+
+		bitmap_git = prepare_midx_bitmap_git(ctx.m);
+		bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
+		free_bitmap_index(bitmap_git);
+
+		if (bitmap_exists || !want_bitmap) {
+			/*
+			 * The correct MIDX already exists, and so does a
+			 * corresponding bitmap (or one wasn't requested).
+			 */
+			if (!want_bitmap)
+				clear_midx_files_ext(object_dir, ".bitmap",
+						     NULL);
+			goto cleanup;
+		}
+	}
+
+	if (preferred_pack_name) {
+		ctx.preferred_pack_idx = -1;
+
+		for (i = 0; i < ctx.nr; i++) {
+			if (!cmp_idx_or_pack_name(preferred_pack_name,
+						  ctx.info[i].pack_name)) {
+				ctx.preferred_pack_idx = i;
+				break;
+			}
+		}
+
+		if (ctx.preferred_pack_idx == -1)
+			warning(_("unknown preferred pack: '%s'"),
+				preferred_pack_name);
+	} else if (ctx.nr &&
+		   (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
+		struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
+		ctx.preferred_pack_idx = 0;
+
+		if (packs_to_drop && packs_to_drop->nr)
+			BUG("cannot write a MIDX bitmap during expiration");
+
+		/*
+		 * set a preferred pack when writing a bitmap to ensure that
+		 * the pack from which the first object is selected in pseudo
+		 * pack-order has all of its objects selected from that pack
+		 * (and not another pack containing a duplicate)
+		 */
+		for (i = 1; i < ctx.nr; i++) {
+			struct packed_git *p = ctx.info[i].p;
+
+			if (!oldest->num_objects || p->mtime < oldest->mtime) {
+				oldest = p;
+				ctx.preferred_pack_idx = i;
+			}
+		}
+
+		if (!oldest->num_objects) {
+			/*
+			 * If all packs are empty; unset the preferred index.
+			 * This is acceptable since there will be no duplicate
+			 * objects to resolve, so the preferred value doesn't
+			 * matter.
+			 */
+			ctx.preferred_pack_idx = -1;
+		}
+	} else {
+		/*
+		 * otherwise don't mark any pack as preferred to avoid
+		 * interfering with expiration logic below
+		 */
+		ctx.preferred_pack_idx = -1;
+	}
+
+	if (ctx.preferred_pack_idx > -1) {
+		struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
+		if (!preferred->num_objects) {
+			error(_("cannot select preferred pack %s with no objects"),
+			      preferred->pack_name);
+			result = 1;
+			goto cleanup;
+		}
+	}
+
+	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
+					 ctx.preferred_pack_idx);
+
+	ctx.large_offsets_needed = 0;
+	for (i = 0; i < ctx.entries_nr; i++) {
+		if (ctx.entries[i].offset > 0x7fffffff)
+			ctx.num_large_offsets++;
+		if (ctx.entries[i].offset > 0xffffffff)
+			ctx.large_offsets_needed = 1;
+	}
+
+	QSORT(ctx.info, ctx.nr, pack_info_compare);
+
+	if (packs_to_drop && packs_to_drop->nr) {
+		int drop_index = 0;
+		int missing_drops = 0;
+
+		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
+			int cmp = strcmp(ctx.info[i].pack_name,
+					 packs_to_drop->items[drop_index].string);
+
+			if (!cmp) {
+				drop_index++;
+				ctx.info[i].expired = 1;
+			} else if (cmp > 0) {
+				error(_("did not see pack-file %s to drop"),
+				      packs_to_drop->items[drop_index].string);
+				drop_index++;
+				missing_drops++;
+				i--;
+			} else {
+				ctx.info[i].expired = 0;
+			}
+		}
+
+		if (missing_drops) {
+			result = 1;
+			goto cleanup;
+		}
+	}
+
+	/*
+	 * pack_perm stores a permutation between pack-int-ids from the
+	 * previous multi-pack-index to the new one we are writing:
+	 *
+	 * pack_perm[old_id] = new_id
+	 */
+	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired) {
+			dropped_packs++;
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
+		} else {
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
+		}
+	}
+
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired)
+			continue;
+		pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+		bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
+	}
+
+	/* Check that the preferred pack wasn't expired (if given). */
+	if (preferred_pack_name) {
+		struct pack_info *preferred = bsearch(preferred_pack_name,
+						      ctx.info, ctx.nr,
+						      sizeof(*ctx.info),
+						      idx_or_pack_name_cmp);
+		if (preferred) {
+			uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
+			if (perm == PACK_EXPIRED)
+				warning(_("preferred pack '%s' is expired"),
+					preferred_pack_name);
+		}
+	}
+
+	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
+		pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
+					(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
+
+	hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
+	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
+
+	if (ctx.nr - dropped_packs == 0) {
+		error(_("no pack files to index."));
+		result = 1;
+		goto cleanup;
+	}
+
+	if (!ctx.entries_nr) {
+		if (flags & MIDX_WRITE_BITMAP)
+			warning(_("refusing to write multi-pack .bitmap without any objects"));
+		flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
+	}
+
+	cf = init_chunkfile(f);
+
+	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
+		  write_midx_pack_names);
+	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
+		  write_midx_oid_fanout);
+	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
+		  st_mult(ctx.entries_nr, the_hash_algo->rawsz),
+		  write_midx_oid_lookup);
+	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
+		  st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
+		  write_midx_object_offsets);
+
+	if (ctx.large_offsets_needed)
+		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
+			st_mult(ctx.num_large_offsets,
+				MIDX_CHUNK_LARGE_OFFSET_WIDTH),
+			write_midx_large_offsets);
+
+	if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
+		ctx.pack_order = midx_pack_order(&ctx);
+		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
+			  st_mult(ctx.entries_nr, sizeof(uint32_t)),
+			  write_midx_revindex);
+		add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+			  bitmapped_packs_concat_len,
+			  write_midx_bitmapped_packs);
+	}
+
+	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
+	write_chunkfile(cf, &ctx);
+
+	finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
+			  CSUM_FSYNC | CSUM_HASH_IN_STREAM);
+	free_chunkfile(cf);
+
+	if (flags & MIDX_WRITE_REV_INDEX &&
+	    git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
+		write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
+
+	if (flags & MIDX_WRITE_BITMAP) {
+		struct packing_data pdata;
+		struct commit **commits;
+		uint32_t commits_nr;
+
+		if (!ctx.entries_nr)
+			BUG("cannot write a bitmap without any objects");
+
+		prepare_midx_packing_data(&pdata, &ctx);
+
+		commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
+
+		/*
+		 * The previous steps translated the information from
+		 * 'entries' into information suitable for constructing
+		 * bitmaps. We no longer need that array, so clear it to
+		 * reduce memory pressure.
+		 */
+		FREE_AND_NULL(ctx.entries);
+		ctx.entries_nr = 0;
+
+		if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
+				      commits, commits_nr, ctx.pack_order,
+				      flags) < 0) {
+			error(_("could not write multi-pack bitmap"));
+			result = 1;
+			clear_packing_data(&pdata);
+			free(commits);
+			goto cleanup;
+		}
+
+		clear_packing_data(&pdata);
+		free(commits);
+	}
+	/*
+	 * NOTE: Do not use ctx.entries beyond this point, since it might
+	 * have been freed in the previous if block.
+	 */
+
+	if (ctx.m)
+		close_object_store(the_repository->objects);
+
+	if (commit_lock_file(&lk) < 0)
+		die_errno(_("could not write multi-pack-index"));
+
+	clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
+	clear_midx_files_ext(object_dir, ".rev", midx_hash);
+
+cleanup:
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].p) {
+			close_pack(ctx.info[i].p);
+			free(ctx.info[i].p);
+		}
+		free(ctx.info[i].pack_name);
+	}
+
+	free(ctx.info);
+	free(ctx.entries);
+	free(ctx.pack_perm);
+	free(ctx.pack_order);
+	strbuf_release(&midx_name);
+
+	trace2_region_leave("midx", "write_midx_internal", the_repository);
+
+	return result;
+}
+
+int write_midx_file(const char *object_dir,
+		    const char *preferred_pack_name,
+		    const char *refs_snapshot,
+		    unsigned flags)
+{
+	return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
+				   refs_snapshot, flags);
+}
+
+int write_midx_file_only(const char *object_dir,
+			 struct string_list *packs_to_include,
+			 const char *preferred_pack_name,
+			 const char *refs_snapshot,
+			 unsigned flags)
+{
+	return write_midx_internal(object_dir, packs_to_include, NULL,
+				   preferred_pack_name, refs_snapshot, flags);
+}
+
+int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
+{
+	uint32_t i, *count, result = 0;
+	struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
+	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
+	struct progress *progress = NULL;
+
+	if (!m)
+		return 0;
+
+	CALLOC_ARRAY(count, m->num_packs);
+
+	if (flags & MIDX_PROGRESS)
+		progress = start_delayed_progress(_("Counting referenced objects"),
+					  m->num_objects);
+	for (i = 0; i < m->num_objects; i++) {
+		int pack_int_id = nth_midxed_pack_int_id(m, i);
+		count[pack_int_id]++;
+		display_progress(progress, i + 1);
+	}
+	stop_progress(&progress);
+
+	if (flags & MIDX_PROGRESS)
+		progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
+					  m->num_packs);
+	for (i = 0; i < m->num_packs; i++) {
+		char *pack_name;
+		display_progress(progress, i + 1);
+
+		if (count[i])
+			continue;
+
+		if (prepare_midx_pack(r, m, i))
+			continue;
+
+		if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
+			continue;
+
+		pack_name = xstrdup(m->packs[i]->pack_name);
+		close_pack(m->packs[i]);
+
+		string_list_insert(&packs_to_drop, m->pack_names[i]);
+		unlink_pack_path(pack_name, 0);
+		free(pack_name);
+	}
+	stop_progress(&progress);
+
+	free(count);
+
+	if (packs_to_drop.nr)
+		result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
+
+	string_list_clear(&packs_to_drop, 0);
+
+	return result;
+}
+
+struct repack_info {
+	timestamp_t mtime;
+	uint32_t referenced_objects;
+	uint32_t pack_int_id;
+};
+
+static int compare_by_mtime(const void *a_, const void *b_)
+{
+	const struct repack_info *a, *b;
+
+	a = (const struct repack_info *)a_;
+	b = (const struct repack_info *)b_;
+
+	if (a->mtime < b->mtime)
+		return -1;
+	if (a->mtime > b->mtime)
+		return 1;
+	return 0;
+}
+
+static int want_included_pack(struct repository *r,
+			      struct multi_pack_index *m,
+			      int pack_kept_objects,
+			      uint32_t pack_int_id)
+{
+	struct packed_git *p;
+	if (prepare_midx_pack(r, m, pack_int_id))
+		return 0;
+	p = m->packs[pack_int_id];
+	if (!pack_kept_objects && p->pack_keep)
+		return 0;
+	if (p->is_cruft)
+		return 0;
+	if (open_pack_index(p) || !p->num_objects)
+		return 0;
+	return 1;
+}
+
+static void fill_included_packs_all(struct repository *r,
+				    struct multi_pack_index *m,
+				    unsigned char *include_pack)
+{
+	uint32_t i;
+	int pack_kept_objects = 0;
+
+	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+	for (i = 0; i < m->num_packs; i++) {
+		if (!want_included_pack(r, m, pack_kept_objects, i))
+			continue;
+
+		include_pack[i] = 1;
+	}
+}
+
+static void fill_included_packs_batch(struct repository *r,
+				      struct multi_pack_index *m,
+				      unsigned char *include_pack,
+				      size_t batch_size)
+{
+	uint32_t i;
+	size_t total_size;
+	struct repack_info *pack_info;
+	int pack_kept_objects = 0;
+
+	CALLOC_ARRAY(pack_info, m->num_packs);
+
+	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+	for (i = 0; i < m->num_packs; i++) {
+		pack_info[i].pack_int_id = i;
+
+		if (prepare_midx_pack(r, m, i))
+			continue;
+
+		pack_info[i].mtime = m->packs[i]->mtime;
+	}
+
+	for (i = 0; i < m->num_objects; i++) {
+		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
+		pack_info[pack_int_id].referenced_objects++;
+	}
+
+	QSORT(pack_info, m->num_packs, compare_by_mtime);
+
+	total_size = 0;
+	for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
+		int pack_int_id = pack_info[i].pack_int_id;
+		struct packed_git *p = m->packs[pack_int_id];
+		size_t expected_size;
+
+		if (!want_included_pack(r, m, pack_kept_objects, pack_int_id))
+			continue;
+
+		expected_size = st_mult(p->pack_size,
+					pack_info[i].referenced_objects);
+		expected_size /= p->num_objects;
+
+		if (expected_size >= batch_size)
+			continue;
+
+		total_size += expected_size;
+		include_pack[pack_int_id] = 1;
+	}
+
+	free(pack_info);
+}
+
+int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
+{
+	int result = 0;
+	uint32_t i, packs_to_repack = 0;
+	unsigned char *include_pack;
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	FILE *cmd_in;
+	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
+
+	/*
+	 * When updating the default for these configuration
+	 * variables in builtin/repack.c, these must be adjusted
+	 * to match.
+	 */
+	int delta_base_offset = 1;
+	int use_delta_islands = 0;
+
+	if (!m)
+		return 0;
+
+	CALLOC_ARRAY(include_pack, m->num_packs);
+
+	if (batch_size)
+		fill_included_packs_batch(r, m, include_pack, batch_size);
+	else
+		fill_included_packs_all(r, m, include_pack);
+
+	for (i = 0; i < m->num_packs; i++) {
+		if (include_pack[i])
+			packs_to_repack++;
+	}
+	if (packs_to_repack <= 1)
+		goto cleanup;
+
+	repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
+	repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
+
+	strvec_pushl(&cmd.args, "pack-objects", "--stdin-packs", "--non-empty",
+		     NULL);
+
+	strvec_pushf(&cmd.args, "%s/pack/pack", object_dir);
+
+	if (delta_base_offset)
+		strvec_push(&cmd.args, "--delta-base-offset");
+	if (use_delta_islands)
+		strvec_push(&cmd.args, "--delta-islands");
+
+	if (flags & MIDX_PROGRESS)
+		strvec_push(&cmd.args, "--progress");
+	else
+		strvec_push(&cmd.args, "-q");
+
+	cmd.git_cmd = 1;
+	cmd.in = cmd.out = -1;
+
+	if (start_command(&cmd)) {
+		error(_("could not start pack-objects"));
+		result = 1;
+		goto cleanup;
+	}
+
+	cmd_in = xfdopen(cmd.in, "w");
+	for (i = 0; i < m->num_packs; i++) {
+		struct packed_git *p = m->packs[i];
+		if (!p)
+			continue;
+
+		if (include_pack[i])
+			fprintf(cmd_in, "%s\n", pack_basename(p));
+		else
+			fprintf(cmd_in, "^%s\n", pack_basename(p));
+	}
+	fclose(cmd_in);
+
+	if (finish_command(&cmd)) {
+		error(_("could not finish pack-objects"));
+		result = 1;
+		goto cleanup;
+	}
+
+	result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
+
+cleanup:
+	free(include_pack);
+	return result;
+}
diff --git a/midx.c b/midx.c
index 7cfad04..ae3b491 100644
--- a/midx.c
+++ b/midx.c
@@ -1,45 +1,22 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "csum-file.h"
 #include "dir.h"
-#include "lockfile.h"
+#include "hex.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
 #include "hash-lookup.h"
 #include "midx.h"
 #include "progress.h"
 #include "trace2.h"
-#include "run-command.h"
-#include "repository.h"
 #include "chunk-format.h"
-#include "pack.h"
 #include "pack-bitmap.h"
-#include "refs.h"
-#include "revision.h"
-#include "list-objects.h"
+#include "pack-revindex.h"
 
-#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
-#define MIDX_VERSION 1
-#define MIDX_BYTE_FILE_VERSION 4
-#define MIDX_BYTE_HASH_VERSION 5
-#define MIDX_BYTE_NUM_CHUNKS 6
-#define MIDX_BYTE_NUM_PACKS 8
-#define MIDX_HEADER_SIZE 12
-#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
-
-#define MIDX_CHUNK_ALIGNMENT 4
-#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
-#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
-#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
-#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
-#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
-#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
-#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
-#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
-#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
-#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
-
-#define PACK_EXPIRED UINT_MAX
+int midx_checksum_valid(struct multi_pack_index *m);
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+			  unsigned char *keep_hash);
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+			 const char *idx_name);
 
 const unsigned char *get_midx_checksum(struct multi_pack_index *m)
 {
@@ -60,6 +37,7 @@
 static int midx_read_oid_fanout(const unsigned char *chunk_start,
 				size_t chunk_size, void *data)
 {
+	int i;
 	struct multi_pack_index *m = data;
 	m->chunk_oid_fanout = (uint32_t *)chunk_start;
 
@@ -67,9 +45,48 @@
 		error(_("multi-pack-index OID fanout is of the wrong size"));
 		return 1;
 	}
+	for (i = 0; i < 255; i++) {
+		uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
+		uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i+1]);
+
+		if (oid_fanout1 > oid_fanout2) {
+			error(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
+			      i, oid_fanout1, oid_fanout2, i + 1);
+			return 1;
+		}
+	}
+	m->num_objects = ntohl(m->chunk_oid_fanout[255]);
 	return 0;
 }
 
+static int midx_read_oid_lookup(const unsigned char *chunk_start,
+				size_t chunk_size, void *data)
+{
+	struct multi_pack_index *m = data;
+	m->chunk_oid_lookup = chunk_start;
+
+	if (chunk_size != st_mult(m->hash_len, m->num_objects)) {
+		error(_("multi-pack-index OID lookup chunk is the wrong size"));
+		return 1;
+	}
+	return 0;
+}
+
+static int midx_read_object_offsets(const unsigned char *chunk_start,
+				    size_t chunk_size, void *data)
+{
+	struct multi_pack_index *m = data;
+	m->chunk_object_offsets = chunk_start;
+
+	if (chunk_size != st_mult(m->num_objects, MIDX_CHUNK_OFFSET_WIDTH)) {
+		error(_("multi-pack-index object offset chunk is the wrong size"));
+		return 1;
+	}
+	return 0;
+}
+
+#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
+
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local)
 {
 	struct multi_pack_index *m = NULL;
@@ -133,36 +150,49 @@
 
 	m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS);
 
+	m->preferred_pack_idx = -1;
+
 	cf = init_chunkfile(NULL);
 
 	if (read_table_of_contents(cf, m->data, midx_size,
-				   MIDX_HEADER_SIZE, m->num_chunks))
+				   MIDX_HEADER_SIZE, m->num_chunks,
+				   MIDX_CHUNK_ALIGNMENT))
 		goto cleanup_fail;
 
-	if (pair_chunk(cf, MIDX_CHUNKID_PACKNAMES, &m->chunk_pack_names) == CHUNK_NOT_FOUND)
-		die(_("multi-pack-index missing required pack-name chunk"));
-	if (read_chunk(cf, MIDX_CHUNKID_OIDFANOUT, midx_read_oid_fanout, m) == CHUNK_NOT_FOUND)
-		die(_("multi-pack-index missing required OID fanout chunk"));
-	if (pair_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, &m->chunk_oid_lookup) == CHUNK_NOT_FOUND)
-		die(_("multi-pack-index missing required OID lookup chunk"));
-	if (pair_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, &m->chunk_object_offsets) == CHUNK_NOT_FOUND)
-		die(_("multi-pack-index missing required object offsets chunk"));
+	if (pair_chunk(cf, MIDX_CHUNKID_PACKNAMES, &m->chunk_pack_names, &m->chunk_pack_names_len))
+		die(_("multi-pack-index required pack-name chunk missing or corrupted"));
+	if (read_chunk(cf, MIDX_CHUNKID_OIDFANOUT, midx_read_oid_fanout, m))
+		die(_("multi-pack-index required OID fanout chunk missing or corrupted"));
+	if (read_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, midx_read_oid_lookup, m))
+		die(_("multi-pack-index required OID lookup chunk missing or corrupted"));
+	if (read_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, midx_read_object_offsets, m))
+		die(_("multi-pack-index required object offsets chunk missing or corrupted"));
 
-	pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets);
+	pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets,
+		   &m->chunk_large_offsets_len);
+	pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+		   (const unsigned char **)&m->chunk_bitmapped_packs,
+		   &m->chunk_bitmapped_packs_len);
 
 	if (git_env_bool("GIT_TEST_MIDX_READ_RIDX", 1))
-		pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex);
-
-	m->num_objects = ntohl(m->chunk_oid_fanout[255]);
+		pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex,
+			   &m->chunk_revindex_len);
 
 	CALLOC_ARRAY(m->pack_names, m->num_packs);
 	CALLOC_ARRAY(m->packs, m->num_packs);
 
 	cur_pack_name = (const char *)m->chunk_pack_names;
 	for (i = 0; i < m->num_packs; i++) {
+		const char *end;
+		size_t avail = m->chunk_pack_names_len -
+				(cur_pack_name - (const char *)m->chunk_pack_names);
+
 		m->pack_names[i] = cur_pack_name;
 
-		cur_pack_name += strlen(cur_pack_name) + 1;
+		end = memchr(cur_pack_name, '\0', avail);
+		if (!end)
+			die(_("multi-pack-index pack-name chunk is too short"));
+		cur_pack_name = end + 1;
 
 		if (i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0)
 			die(_("multi-pack-index pack names out of order: '%s' before '%s'"),
@@ -236,6 +266,28 @@
 	return 0;
 }
 
+#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
+
+int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
+		       struct bitmapped_pack *bp, uint32_t pack_int_id)
+{
+	if (!m->chunk_bitmapped_packs)
+		return error(_("MIDX does not contain the BTMP chunk"));
+
+	if (prepare_midx_pack(r, m, pack_int_id))
+		return error(_("could not load bitmapped pack %"PRIu32), pack_int_id);
+
+	bp->p = m->packs[pack_int_id];
+	bp->bitmap_pos = get_be32((char *)m->chunk_bitmapped_packs +
+				  MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * pack_int_id);
+	bp->bitmap_nr = get_be32((char *)m->chunk_bitmapped_packs +
+				 MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * pack_int_id +
+				 sizeof(uint32_t));
+	bp->pack_int_id = pack_int_id;
+
+	return 0;
+}
+
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result)
 {
 	return bsearch_hash(oid->hash, m->chunk_oid_fanout, m->chunk_oid_lookup,
@@ -249,7 +301,7 @@
 	if (n >= m->num_objects)
 		return NULL;
 
-	oidread(oid, m->chunk_oid_lookup + m->hash_len * n);
+	oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n));
 	return oid;
 }
 
@@ -266,6 +318,8 @@
 			die(_("multi-pack-index stores a 64-bit offset, but off_t is too small"));
 
 		offset32 ^= MIDX_LARGE_OFFSET_NEEDED;
+		if (offset32 >= m->chunk_large_offsets_len / sizeof(uint64_t))
+			die(_("multi-pack-index large offset out of bounds"));
 		return get_be64(m->chunk_large_offsets + sizeof(uint64_t) * offset32);
 	}
 
@@ -320,8 +374,8 @@
 }
 
 /* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */
-static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
-				const char *idx_name)
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+			 const char *idx_name)
 {
 	/* Skip past any initial matching prefix. */
 	while (*idx_name && *idx_name == *idx_or_pack_name) {
@@ -351,7 +405,8 @@
 	return strcmp(idx_or_pack_name, idx_name);
 }
 
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
+int midx_locate_pack(struct multi_pack_index *m, const char *idx_or_pack_name,
+		     uint32_t *pos)
 {
 	uint32_t first = 0, last = m->num_packs;
 
@@ -362,8 +417,11 @@
 
 		current = m->pack_names[mid];
 		cmp = cmp_idx_or_pack_name(idx_or_pack_name, current);
-		if (!cmp)
+		if (!cmp) {
+			if (pos)
+				*pos = mid;
 			return 1;
+		}
 		if (cmp > 0) {
 			first = mid + 1;
 			continue;
@@ -374,6 +432,28 @@
 	return 0;
 }
 
+int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
+{
+	return midx_locate_pack(m, idx_or_pack_name, NULL);
+}
+
+int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id)
+{
+	if (m->preferred_pack_idx == -1) {
+		if (load_midx_revindex(m) < 0) {
+			m->preferred_pack_idx = -2;
+			return -1;
+		}
+
+		m->preferred_pack_idx =
+			nth_midxed_pack_int_id(m, pack_pos_to_midx(m, 0));
+	} else if (m->preferred_pack_idx == -2)
+		return -1; /* no revindex */
+
+	*pack_int_id = m->preferred_pack_idx;
+	return 0;
+}
+
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local)
 {
 	struct multi_pack_index *m;
@@ -402,1212 +482,17 @@
 	return 0;
 }
 
-static size_t write_midx_header(struct hashfile *f,
-				unsigned char num_chunks,
-				uint32_t num_packs)
-{
-	hashwrite_be32(f, MIDX_SIGNATURE);
-	hashwrite_u8(f, MIDX_VERSION);
-	hashwrite_u8(f, oid_version(the_hash_algo));
-	hashwrite_u8(f, num_chunks);
-	hashwrite_u8(f, 0); /* unused */
-	hashwrite_be32(f, num_packs);
-
-	return MIDX_HEADER_SIZE;
-}
-
-struct pack_info {
-	uint32_t orig_pack_int_id;
-	char *pack_name;
-	struct packed_git *p;
-	unsigned expired : 1;
-};
-
-static int pack_info_compare(const void *_a, const void *_b)
-{
-	struct pack_info *a = (struct pack_info *)_a;
-	struct pack_info *b = (struct pack_info *)_b;
-	return strcmp(a->pack_name, b->pack_name);
-}
-
-static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
-{
-	const char *pack_name = _va;
-	const struct pack_info *compar = _vb;
-
-	return cmp_idx_or_pack_name(pack_name, compar->pack_name);
-}
-
-struct write_midx_context {
-	struct pack_info *info;
-	uint32_t nr;
-	uint32_t alloc;
-	struct multi_pack_index *m;
-	struct progress *progress;
-	unsigned pack_paths_checked;
-
-	struct pack_midx_entry *entries;
-	uint32_t entries_nr;
-
-	uint32_t *pack_perm;
-	uint32_t *pack_order;
-	unsigned large_offsets_needed:1;
-	uint32_t num_large_offsets;
-
-	int preferred_pack_idx;
-
-	struct string_list *to_include;
-};
-
-static void add_pack_to_midx(const char *full_path, size_t full_path_len,
-			     const char *file_name, void *data)
-{
-	struct write_midx_context *ctx = data;
-
-	if (ends_with(file_name, ".idx")) {
-		display_progress(ctx->progress, ++ctx->pack_paths_checked);
-		/*
-		 * Note that at most one of ctx->m and ctx->to_include are set,
-		 * so we are testing midx_contains_pack() and
-		 * string_list_has_string() independently (guarded by the
-		 * appropriate NULL checks).
-		 *
-		 * We could support passing to_include while reusing an existing
-		 * MIDX, but don't currently since the reuse process drags
-		 * forward all packs from an existing MIDX (without checking
-		 * whether or not they appear in the to_include list).
-		 *
-		 * If we added support for that, these next two conditional
-		 * should be performed independently (likely checking
-		 * to_include before the existing MIDX).
-		 */
-		if (ctx->m && midx_contains_pack(ctx->m, file_name))
-			return;
-		else if (ctx->to_include &&
-			 !string_list_has_string(ctx->to_include, file_name))
-			return;
-
-		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
-
-		ctx->info[ctx->nr].p = add_packed_git(full_path,
-						      full_path_len,
-						      0);
-
-		if (!ctx->info[ctx->nr].p) {
-			warning(_("failed to add packfile '%s'"),
-				full_path);
-			return;
-		}
-
-		if (open_pack_index(ctx->info[ctx->nr].p)) {
-			warning(_("failed to open pack-index '%s'"),
-				full_path);
-			close_pack(ctx->info[ctx->nr].p);
-			FREE_AND_NULL(ctx->info[ctx->nr].p);
-			return;
-		}
-
-		ctx->info[ctx->nr].pack_name = xstrdup(file_name);
-		ctx->info[ctx->nr].orig_pack_int_id = ctx->nr;
-		ctx->info[ctx->nr].expired = 0;
-		ctx->nr++;
-	}
-}
-
-struct pack_midx_entry {
-	struct object_id oid;
-	uint32_t pack_int_id;
-	time_t pack_mtime;
-	uint64_t offset;
-	unsigned preferred : 1;
-};
-
-static int midx_oid_compare(const void *_a, const void *_b)
-{
-	const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
-	const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
-	int cmp = oidcmp(&a->oid, &b->oid);
-
-	if (cmp)
-		return cmp;
-
-	/* Sort objects in a preferred pack first when multiple copies exist. */
-	if (a->preferred > b->preferred)
-		return -1;
-	if (a->preferred < b->preferred)
-		return 1;
-
-	if (a->pack_mtime > b->pack_mtime)
-		return -1;
-	else if (a->pack_mtime < b->pack_mtime)
-		return 1;
-
-	return a->pack_int_id - b->pack_int_id;
-}
-
-static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
-				      struct pack_midx_entry *e,
-				      uint32_t pos)
-{
-	if (pos >= m->num_objects)
-		return 1;
-
-	nth_midxed_object_oid(&e->oid, m, pos);
-	e->pack_int_id = nth_midxed_pack_int_id(m, pos);
-	e->offset = nth_midxed_offset(m, pos);
-
-	/* consider objects in midx to be from "old" packs */
-	e->pack_mtime = 0;
-	return 0;
-}
-
-static void fill_pack_entry(uint32_t pack_int_id,
-			    struct packed_git *p,
-			    uint32_t cur_object,
-			    struct pack_midx_entry *entry,
-			    int preferred)
-{
-	if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
-		die(_("failed to locate object %d in packfile"), cur_object);
-
-	entry->pack_int_id = pack_int_id;
-	entry->pack_mtime = p->mtime;
-
-	entry->offset = nth_packed_object_offset(p, cur_object);
-	entry->preferred = !!preferred;
-}
-
-struct midx_fanout {
-	struct pack_midx_entry *entries;
-	uint32_t nr;
-	uint32_t alloc;
-};
-
-static void midx_fanout_grow(struct midx_fanout *fanout, uint32_t nr)
-{
-	ALLOC_GROW(fanout->entries, nr, fanout->alloc);
-}
-
-static void midx_fanout_sort(struct midx_fanout *fanout)
-{
-	QSORT(fanout->entries, fanout->nr, midx_oid_compare);
-}
-
-static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
-					struct multi_pack_index *m,
-					uint32_t cur_fanout,
-					int preferred_pack)
-{
-	uint32_t start = 0, end;
-	uint32_t cur_object;
-
-	if (cur_fanout)
-		start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
-	end = ntohl(m->chunk_oid_fanout[cur_fanout]);
-
-	for (cur_object = start; cur_object < end; cur_object++) {
-		if ((preferred_pack > -1) &&
-		    (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
-			/*
-			 * Objects from preferred packs are added
-			 * separately.
-			 */
-			continue;
-		}
-
-		midx_fanout_grow(fanout, fanout->nr + 1);
-		nth_midxed_pack_midx_entry(m,
-					   &fanout->entries[fanout->nr],
-					   cur_object);
-		fanout->entries[fanout->nr].preferred = 0;
-		fanout->nr++;
-	}
-}
-
-static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
-					struct pack_info *info,
-					uint32_t cur_pack,
-					int preferred,
-					uint32_t cur_fanout)
-{
-	struct packed_git *pack = info[cur_pack].p;
-	uint32_t start = 0, end;
-	uint32_t cur_object;
-
-	if (cur_fanout)
-		start = get_pack_fanout(pack, cur_fanout - 1);
-	end = get_pack_fanout(pack, cur_fanout);
-
-	for (cur_object = start; cur_object < end; cur_object++) {
-		midx_fanout_grow(fanout, fanout->nr + 1);
-		fill_pack_entry(cur_pack,
-				info[cur_pack].p,
-				cur_object,
-				&fanout->entries[fanout->nr],
-				preferred);
-		fanout->nr++;
-	}
-}
-
-/*
- * It is possible to artificially get into a state where there are many
- * duplicate copies of objects. That can create high memory pressure if
- * we are to create a list of all objects before de-duplication. To reduce
- * this memory pressure without a significant performance drop, automatically
- * group objects by the first byte of their object id. Use the IDX fanout
- * tables to group the data, copy to a local array, then sort.
- *
- * Copy only the de-duplicated entries (selected by most-recent modified time
- * of a packfile containing the object).
- */
-static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
-						  struct pack_info *info,
-						  uint32_t nr_packs,
-						  uint32_t *nr_objects,
-						  int preferred_pack)
-{
-	uint32_t cur_fanout, cur_pack, cur_object;
-	uint32_t alloc_objects, total_objects = 0;
-	struct midx_fanout fanout = { 0 };
-	struct pack_midx_entry *deduplicated_entries = NULL;
-	uint32_t start_pack = m ? m->num_packs : 0;
-
-	for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
-		total_objects += info[cur_pack].p->num_objects;
-
-	/*
-	 * As we de-duplicate by fanout value, we expect the fanout
-	 * slices to be evenly distributed, with some noise. Hence,
-	 * allocate slightly more than one 256th.
-	 */
-	alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
-
-	ALLOC_ARRAY(fanout.entries, fanout.alloc);
-	ALLOC_ARRAY(deduplicated_entries, alloc_objects);
-	*nr_objects = 0;
-
-	for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
-		fanout.nr = 0;
-
-		if (m)
-			midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
-						    preferred_pack);
-
-		for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
-			int preferred = cur_pack == preferred_pack;
-			midx_fanout_add_pack_fanout(&fanout,
-						    info, cur_pack,
-						    preferred, cur_fanout);
-		}
-
-		if (-1 < preferred_pack && preferred_pack < start_pack)
-			midx_fanout_add_pack_fanout(&fanout, info,
-						    preferred_pack, 1,
-						    cur_fanout);
-
-		midx_fanout_sort(&fanout);
-
-		/*
-		 * The batch is now sorted by OID and then mtime (descending).
-		 * Take only the first duplicate.
-		 */
-		for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
-			if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
-						&fanout.entries[cur_object].oid))
-				continue;
-
-			ALLOC_GROW(deduplicated_entries, *nr_objects + 1, alloc_objects);
-			memcpy(&deduplicated_entries[*nr_objects],
-			       &fanout.entries[cur_object],
-			       sizeof(struct pack_midx_entry));
-			(*nr_objects)++;
-		}
-	}
-
-	free(fanout.entries);
-	return deduplicated_entries;
-}
-
-static int write_midx_pack_names(struct hashfile *f, void *data)
-{
-	struct write_midx_context *ctx = data;
-	uint32_t i;
-	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
-	size_t written = 0;
-
-	for (i = 0; i < ctx->nr; i++) {
-		size_t writelen;
-
-		if (ctx->info[i].expired)
-			continue;
-
-		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
-			BUG("incorrect pack-file order: %s before %s",
-			    ctx->info[i - 1].pack_name,
-			    ctx->info[i].pack_name);
-
-		writelen = strlen(ctx->info[i].pack_name) + 1;
-		hashwrite(f, ctx->info[i].pack_name, writelen);
-		written += writelen;
-	}
-
-	/* add padding to be aligned */
-	i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
-	if (i < MIDX_CHUNK_ALIGNMENT) {
-		memset(padding, 0, sizeof(padding));
-		hashwrite(f, padding, i);
-	}
-
-	return 0;
-}
-
-static int write_midx_oid_fanout(struct hashfile *f,
-				 void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
-	uint32_t count = 0;
-	uint32_t i;
-
-	/*
-	* Write the first-level table (the list is sorted,
-	* but we use a 256-entry lookup to be able to avoid
-	* having to do eight extra binary search iterations).
-	*/
-	for (i = 0; i < 256; i++) {
-		struct pack_midx_entry *next = list;
-
-		while (next < last && next->oid.hash[0] == i) {
-			count++;
-			next++;
-		}
-
-		hashwrite_be32(f, count);
-		list = next;
-	}
-
-	return 0;
-}
-
-static int write_midx_oid_lookup(struct hashfile *f,
-				 void *data)
-{
-	struct write_midx_context *ctx = data;
-	unsigned char hash_len = the_hash_algo->rawsz;
-	struct pack_midx_entry *list = ctx->entries;
-	uint32_t i;
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *obj = list++;
-
-		if (i < ctx->entries_nr - 1) {
-			struct pack_midx_entry *next = list;
-			if (oidcmp(&obj->oid, &next->oid) >= 0)
-				BUG("OIDs not in order: %s >= %s",
-				    oid_to_hex(&obj->oid),
-				    oid_to_hex(&next->oid));
-		}
-
-		hashwrite(f, obj->oid.hash, (int)hash_len);
-	}
-
-	return 0;
-}
-
-static int write_midx_object_offsets(struct hashfile *f,
-				     void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	uint32_t i, nr_large_offset = 0;
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *obj = list++;
-
-		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
-			BUG("object %s is in an expired pack with int-id %d",
-			    oid_to_hex(&obj->oid),
-			    obj->pack_int_id);
-
-		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
-
-		if (ctx->large_offsets_needed && obj->offset >> 31)
-			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
-		else if (!ctx->large_offsets_needed && obj->offset >> 32)
-			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
-			    oid_to_hex(&obj->oid),
-			    obj->offset);
-		else
-			hashwrite_be32(f, (uint32_t)obj->offset);
-	}
-
-	return 0;
-}
-
-static int write_midx_large_offsets(struct hashfile *f,
-				    void *data)
-{
-	struct write_midx_context *ctx = data;
-	struct pack_midx_entry *list = ctx->entries;
-	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
-	uint32_t nr_large_offset = ctx->num_large_offsets;
-
-	while (nr_large_offset) {
-		struct pack_midx_entry *obj;
-		uint64_t offset;
-
-		if (list >= end)
-			BUG("too many large-offset objects");
-
-		obj = list++;
-		offset = obj->offset;
-
-		if (!(offset >> 31))
-			continue;
-
-		hashwrite_be64(f, offset);
-
-		nr_large_offset--;
-	}
-
-	return 0;
-}
-
-static int write_midx_revindex(struct hashfile *f,
-			       void *data)
-{
-	struct write_midx_context *ctx = data;
-	uint32_t i;
-
-	for (i = 0; i < ctx->entries_nr; i++)
-		hashwrite_be32(f, ctx->pack_order[i]);
-
-	return 0;
-}
-
-struct midx_pack_order_data {
-	uint32_t nr;
-	uint32_t pack;
-	off_t offset;
-};
-
-static int midx_pack_order_cmp(const void *va, const void *vb)
-{
-	const struct midx_pack_order_data *a = va, *b = vb;
-	if (a->pack < b->pack)
-		return -1;
-	else if (a->pack > b->pack)
-		return 1;
-	else if (a->offset < b->offset)
-		return -1;
-	else if (a->offset > b->offset)
-		return 1;
-	else
-		return 0;
-}
-
-static uint32_t *midx_pack_order(struct write_midx_context *ctx)
-{
-	struct midx_pack_order_data *data;
-	uint32_t *pack_order;
-	uint32_t i;
-
-	trace2_region_enter("midx", "midx_pack_order", the_repository);
-
-	ALLOC_ARRAY(data, ctx->entries_nr);
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *e = &ctx->entries[i];
-		data[i].nr = i;
-		data[i].pack = ctx->pack_perm[e->pack_int_id];
-		if (!e->preferred)
-			data[i].pack |= (1U << 31);
-		data[i].offset = e->offset;
-	}
-
-	QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
-
-	ALLOC_ARRAY(pack_order, ctx->entries_nr);
-	for (i = 0; i < ctx->entries_nr; i++)
-		pack_order[i] = data[i].nr;
-	free(data);
-
-	trace2_region_leave("midx", "midx_pack_order", the_repository);
-
-	return pack_order;
-}
-
-static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
-				     struct write_midx_context *ctx)
-{
-	struct strbuf buf = STRBUF_INIT;
-	const char *tmp_file;
-
-	trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
-
-	strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
-
-	tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
-					midx_hash, WRITE_REV);
-
-	if (finalize_object_file(tmp_file, buf.buf))
-		die(_("cannot store reverse index file"));
-
-	strbuf_release(&buf);
-
-	trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
-}
-
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
-				 unsigned char *keep_hash);
-
-static int midx_checksum_valid(struct multi_pack_index *m)
+int midx_checksum_valid(struct multi_pack_index *m)
 {
 	return hashfile_checksum_valid(m->data, m->data_len);
 }
 
-static void prepare_midx_packing_data(struct packing_data *pdata,
-				      struct write_midx_context *ctx)
-{
-	uint32_t i;
-
-	trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
-
-	memset(pdata, 0, sizeof(struct packing_data));
-	prepare_packing_data(the_repository, pdata);
-
-	for (i = 0; i < ctx->entries_nr; i++) {
-		struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
-		struct object_entry *to = packlist_alloc(pdata, &from->oid);
-
-		oe_set_in_pack(pdata, to,
-			       ctx->info[ctx->pack_perm[from->pack_int_id]].p);
-	}
-
-	trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
-}
-
-static int add_ref_to_pending(const char *refname,
-			      const struct object_id *oid,
-			      int flag, void *cb_data)
-{
-	struct rev_info *revs = (struct rev_info*)cb_data;
-	struct object_id peeled;
-	struct object *object;
-
-	if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
-		warning("symbolic ref is dangling: %s", refname);
-		return 0;
-	}
-
-	if (!peel_iterated_oid(oid, &peeled))
-		oid = &peeled;
-
-	object = parse_object_or_die(oid, refname);
-	if (object->type != OBJ_COMMIT)
-		return 0;
-
-	add_pending_object(revs, object, "");
-	if (bitmap_is_preferred_refname(revs->repo, refname))
-		object->flags |= NEEDS_BITMAP;
-	return 0;
-}
-
-struct bitmap_commit_cb {
-	struct commit **commits;
-	size_t commits_nr, commits_alloc;
-
-	struct write_midx_context *ctx;
-};
-
-static const struct object_id *bitmap_oid_access(size_t index,
-						 const void *_entries)
-{
-	const struct pack_midx_entry *entries = _entries;
-	return &entries[index].oid;
-}
-
-static void bitmap_show_commit(struct commit *commit, void *_data)
-{
-	struct bitmap_commit_cb *data = _data;
-	int pos = oid_pos(&commit->object.oid, data->ctx->entries,
-			  data->ctx->entries_nr,
-			  bitmap_oid_access);
-	if (pos < 0)
-		return;
-
-	ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
-	data->commits[data->commits_nr++] = commit;
-}
-
-static int read_refs_snapshot(const char *refs_snapshot,
-			      struct rev_info *revs)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct object_id oid;
-	FILE *f = xfopen(refs_snapshot, "r");
-
-	while (strbuf_getline(&buf, f) != EOF) {
-		struct object *object;
-		int preferred = 0;
-		char *hex = buf.buf;
-		const char *end = NULL;
-
-		if (buf.len && *buf.buf == '+') {
-			preferred = 1;
-			hex = &buf.buf[1];
-		}
-
-		if (parse_oid_hex(hex, &oid, &end) < 0)
-			die(_("could not parse line: %s"), buf.buf);
-		if (*end)
-			die(_("malformed line: %s"), buf.buf);
-
-		object = parse_object_or_die(&oid, NULL);
-		if (preferred)
-			object->flags |= NEEDS_BITMAP;
-
-		add_pending_object(revs, object, "");
-	}
-
-	fclose(f);
-	strbuf_release(&buf);
-	return 0;
-}
-
-static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
-						    const char *refs_snapshot,
-						    struct write_midx_context *ctx)
-{
-	struct rev_info revs;
-	struct bitmap_commit_cb cb = {0};
-
-	trace2_region_enter("midx", "find_commits_for_midx_bitmap",
-			    the_repository);
-
-	cb.ctx = ctx;
-
-	repo_init_revisions(the_repository, &revs, NULL);
-	if (refs_snapshot) {
-		read_refs_snapshot(refs_snapshot, &revs);
-	} else {
-		setup_revisions(0, NULL, &revs, NULL);
-		for_each_ref(add_ref_to_pending, &revs);
-	}
-
-	/*
-	 * Skipping promisor objects here is intentional, since it only excludes
-	 * them from the list of reachable commits that we want to select from
-	 * when computing the selection of MIDX'd commits to receive bitmaps.
-	 *
-	 * Reachability bitmaps do require that their objects be closed under
-	 * reachability, but fetching any objects missing from promisors at this
-	 * point is too late. But, if one of those objects can be reached from
-	 * an another object that is included in the bitmap, then we will
-	 * complain later that we don't have reachability closure (and fail
-	 * appropriately).
-	 */
-	fetch_if_missing = 0;
-	revs.exclude_promisor_objects = 1;
-
-	if (prepare_revision_walk(&revs))
-		die(_("revision walk setup failed"));
-
-	traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
-	if (indexed_commits_nr_p)
-		*indexed_commits_nr_p = cb.commits_nr;
-
-	release_revisions(&revs);
-
-	trace2_region_leave("midx", "find_commits_for_midx_bitmap",
-			    the_repository);
-
-	return cb.commits;
-}
-
-static int write_midx_bitmap(const char *midx_name,
-			     const unsigned char *midx_hash,
-			     struct packing_data *pdata,
-			     struct commit **commits,
-			     uint32_t commits_nr,
-			     uint32_t *pack_order,
-			     unsigned flags)
-{
-	int ret, i;
-	uint16_t options = 0;
-	struct pack_idx_entry **index;
-	char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
-					hash_to_hex(midx_hash));
-
-	trace2_region_enter("midx", "write_midx_bitmap", the_repository);
-
-	if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
-		options |= BITMAP_OPT_HASH_CACHE;
-
-	if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
-		options |= BITMAP_OPT_LOOKUP_TABLE;
-
-	/*
-	 * Build the MIDX-order index based on pdata.objects (which is already
-	 * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
-	 * this order).
-	 */
-	ALLOC_ARRAY(index, pdata->nr_objects);
-	for (i = 0; i < pdata->nr_objects; i++)
-		index[i] = &pdata->objects[i].idx;
-
-	bitmap_writer_show_progress(flags & MIDX_PROGRESS);
-	bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
-
-	/*
-	 * bitmap_writer_finish expects objects in lex order, but pack_order
-	 * gives us exactly that. use it directly instead of re-sorting the
-	 * array.
-	 *
-	 * This changes the order of objects in 'index' between
-	 * bitmap_writer_build_type_index and bitmap_writer_finish.
-	 *
-	 * The same re-ordering takes place in the single-pack bitmap code via
-	 * write_idx_file(), which is called by finish_tmp_packfile(), which
-	 * happens between bitmap_writer_build_type_index() and
-	 * bitmap_writer_finish().
-	 */
-	for (i = 0; i < pdata->nr_objects; i++)
-		index[pack_order[i]] = &pdata->objects[i].idx;
-
-	bitmap_writer_select_commits(commits, commits_nr, -1);
-	ret = bitmap_writer_build(pdata);
-	if (ret < 0)
-		goto cleanup;
-
-	bitmap_writer_set_checksum(midx_hash);
-	bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
-
-cleanup:
-	free(index);
-	free(bitmap_name);
-
-	trace2_region_leave("midx", "write_midx_bitmap", the_repository);
-
-	return ret;
-}
-
-static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
-							const char *object_dir)
-{
-	struct multi_pack_index *result = NULL;
-	struct multi_pack_index *cur;
-	char *obj_dir_real = real_pathdup(object_dir, 1);
-	struct strbuf cur_path_real = STRBUF_INIT;
-
-	/* Ensure the given object_dir is local, or a known alternate. */
-	find_odb(r, obj_dir_real);
-
-	for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
-		strbuf_realpath(&cur_path_real, cur->object_dir, 1);
-		if (!strcmp(obj_dir_real, cur_path_real.buf)) {
-			result = cur;
-			goto cleanup;
-		}
-	}
-
-cleanup:
-	free(obj_dir_real);
-	strbuf_release(&cur_path_real);
-	return result;
-}
-
-static int write_midx_internal(const char *object_dir,
-			       struct string_list *packs_to_include,
-			       struct string_list *packs_to_drop,
-			       const char *preferred_pack_name,
-			       const char *refs_snapshot,
-			       unsigned flags)
-{
-	struct strbuf midx_name = STRBUF_INIT;
-	unsigned char midx_hash[GIT_MAX_RAWSZ];
-	uint32_t i;
-	struct hashfile *f = NULL;
-	struct lock_file lk;
-	struct write_midx_context ctx = { 0 };
-	int pack_name_concat_len = 0;
-	int dropped_packs = 0;
-	int result = 0;
-	struct chunkfile *cf;
-
-	trace2_region_enter("midx", "write_midx_internal", the_repository);
-
-	get_midx_filename(&midx_name, object_dir);
-	if (safe_create_leading_directories(midx_name.buf))
-		die_errno(_("unable to create leading directories of %s"),
-			  midx_name.buf);
-
-	if (!packs_to_include) {
-		/*
-		 * Only reference an existing MIDX when not filtering which
-		 * packs to include, since all packs and objects are copied
-		 * blindly from an existing MIDX if one is present.
-		 */
-		ctx.m = lookup_multi_pack_index(the_repository, object_dir);
-	}
-
-	if (ctx.m && !midx_checksum_valid(ctx.m)) {
-		warning(_("ignoring existing multi-pack-index; checksum mismatch"));
-		ctx.m = NULL;
-	}
-
-	ctx.nr = 0;
-	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
-	ctx.info = NULL;
-	ALLOC_ARRAY(ctx.info, ctx.alloc);
-
-	if (ctx.m) {
-		for (i = 0; i < ctx.m->num_packs; i++) {
-			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
-
-			ctx.info[ctx.nr].orig_pack_int_id = i;
-			ctx.info[ctx.nr].pack_name = xstrdup(ctx.m->pack_names[i]);
-			ctx.info[ctx.nr].p = ctx.m->packs[i];
-			ctx.info[ctx.nr].expired = 0;
-
-			if (flags & MIDX_WRITE_REV_INDEX) {
-				/*
-				 * If generating a reverse index, need to have
-				 * packed_git's loaded to compare their
-				 * mtimes and object count.
-				 */
-				if (prepare_midx_pack(the_repository, ctx.m, i)) {
-					error(_("could not load pack"));
-					result = 1;
-					goto cleanup;
-				}
-
-				if (open_pack_index(ctx.m->packs[i]))
-					die(_("could not open index for %s"),
-					    ctx.m->packs[i]->pack_name);
-				ctx.info[ctx.nr].p = ctx.m->packs[i];
-			}
-
-			ctx.nr++;
-		}
-	}
-
-	ctx.pack_paths_checked = 0;
-	if (flags & MIDX_PROGRESS)
-		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
-	else
-		ctx.progress = NULL;
-
-	ctx.to_include = packs_to_include;
-
-	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
-	stop_progress(&ctx.progress);
-
-	if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
-	    !(packs_to_include || packs_to_drop)) {
-		struct bitmap_index *bitmap_git;
-		int bitmap_exists;
-		int want_bitmap = flags & MIDX_WRITE_BITMAP;
-
-		bitmap_git = prepare_midx_bitmap_git(ctx.m);
-		bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
-		free_bitmap_index(bitmap_git);
-
-		if (bitmap_exists || !want_bitmap) {
-			/*
-			 * The correct MIDX already exists, and so does a
-			 * corresponding bitmap (or one wasn't requested).
-			 */
-			if (!want_bitmap)
-				clear_midx_files_ext(object_dir, ".bitmap",
-						     NULL);
-			goto cleanup;
-		}
-	}
-
-	if (preferred_pack_name) {
-		int found = 0;
-		for (i = 0; i < ctx.nr; i++) {
-			if (!cmp_idx_or_pack_name(preferred_pack_name,
-						  ctx.info[i].pack_name)) {
-				ctx.preferred_pack_idx = i;
-				found = 1;
-				break;
-			}
-		}
-
-		if (!found)
-			warning(_("unknown preferred pack: '%s'"),
-				preferred_pack_name);
-	} else if (ctx.nr &&
-		   (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
-		struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
-		ctx.preferred_pack_idx = 0;
-
-		if (packs_to_drop && packs_to_drop->nr)
-			BUG("cannot write a MIDX bitmap during expiration");
-
-		/*
-		 * set a preferred pack when writing a bitmap to ensure that
-		 * the pack from which the first object is selected in pseudo
-		 * pack-order has all of its objects selected from that pack
-		 * (and not another pack containing a duplicate)
-		 */
-		for (i = 1; i < ctx.nr; i++) {
-			struct packed_git *p = ctx.info[i].p;
-
-			if (!oldest->num_objects || p->mtime < oldest->mtime) {
-				oldest = p;
-				ctx.preferred_pack_idx = i;
-			}
-		}
-
-		if (!oldest->num_objects) {
-			/*
-			 * If all packs are empty; unset the preferred index.
-			 * This is acceptable since there will be no duplicate
-			 * objects to resolve, so the preferred value doesn't
-			 * matter.
-			 */
-			ctx.preferred_pack_idx = -1;
-		}
-	} else {
-		/*
-		 * otherwise don't mark any pack as preferred to avoid
-		 * interfering with expiration logic below
-		 */
-		ctx.preferred_pack_idx = -1;
-	}
-
-	if (ctx.preferred_pack_idx > -1) {
-		struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
-		if (!preferred->num_objects) {
-			error(_("cannot select preferred pack %s with no objects"),
-			      preferred->pack_name);
-			result = 1;
-			goto cleanup;
-		}
-	}
-
-	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
-					 ctx.preferred_pack_idx);
-
-	ctx.large_offsets_needed = 0;
-	for (i = 0; i < ctx.entries_nr; i++) {
-		if (ctx.entries[i].offset > 0x7fffffff)
-			ctx.num_large_offsets++;
-		if (ctx.entries[i].offset > 0xffffffff)
-			ctx.large_offsets_needed = 1;
-	}
-
-	QSORT(ctx.info, ctx.nr, pack_info_compare);
-
-	if (packs_to_drop && packs_to_drop->nr) {
-		int drop_index = 0;
-		int missing_drops = 0;
-
-		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
-			int cmp = strcmp(ctx.info[i].pack_name,
-					 packs_to_drop->items[drop_index].string);
-
-			if (!cmp) {
-				drop_index++;
-				ctx.info[i].expired = 1;
-			} else if (cmp > 0) {
-				error(_("did not see pack-file %s to drop"),
-				      packs_to_drop->items[drop_index].string);
-				drop_index++;
-				missing_drops++;
-				i--;
-			} else {
-				ctx.info[i].expired = 0;
-			}
-		}
-
-		if (missing_drops) {
-			result = 1;
-			goto cleanup;
-		}
-	}
-
-	/*
-	 * pack_perm stores a permutation between pack-int-ids from the
-	 * previous multi-pack-index to the new one we are writing:
-	 *
-	 * pack_perm[old_id] = new_id
-	 */
-	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].expired) {
-			dropped_packs++;
-			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
-		} else {
-			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
-		}
-	}
-
-	for (i = 0; i < ctx.nr; i++) {
-		if (!ctx.info[i].expired)
-			pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
-	}
-
-	/* Check that the preferred pack wasn't expired (if given). */
-	if (preferred_pack_name) {
-		struct pack_info *preferred = bsearch(preferred_pack_name,
-						      ctx.info, ctx.nr,
-						      sizeof(*ctx.info),
-						      idx_or_pack_name_cmp);
-		if (preferred) {
-			uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
-			if (perm == PACK_EXPIRED)
-				warning(_("preferred pack '%s' is expired"),
-					preferred_pack_name);
-		}
-	}
-
-	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
-		pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
-					(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
-
-	hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
-	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
-
-	if (ctx.nr - dropped_packs == 0) {
-		error(_("no pack files to index."));
-		result = 1;
-		goto cleanup;
-	}
-
-	if (!ctx.entries_nr) {
-		if (flags & MIDX_WRITE_BITMAP)
-			warning(_("refusing to write multi-pack .bitmap without any objects"));
-		flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
-	}
-
-	cf = init_chunkfile(f);
-
-	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
-		  write_midx_pack_names);
-	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
-		  write_midx_oid_fanout);
-	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
-		  (size_t)ctx.entries_nr * the_hash_algo->rawsz,
-		  write_midx_oid_lookup);
-	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
-		  (size_t)ctx.entries_nr * MIDX_CHUNK_OFFSET_WIDTH,
-		  write_midx_object_offsets);
-
-	if (ctx.large_offsets_needed)
-		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
-			(size_t)ctx.num_large_offsets * MIDX_CHUNK_LARGE_OFFSET_WIDTH,
-			write_midx_large_offsets);
-
-	if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
-		ctx.pack_order = midx_pack_order(&ctx);
-		add_chunk(cf, MIDX_CHUNKID_REVINDEX,
-			  ctx.entries_nr * sizeof(uint32_t),
-			  write_midx_revindex);
-	}
-
-	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
-	write_chunkfile(cf, &ctx);
-
-	finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
-			  CSUM_FSYNC | CSUM_HASH_IN_STREAM);
-	free_chunkfile(cf);
-
-	if (flags & MIDX_WRITE_REV_INDEX &&
-	    git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
-		write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
-
-	if (flags & MIDX_WRITE_BITMAP) {
-		struct packing_data pdata;
-		struct commit **commits;
-		uint32_t commits_nr;
-
-		if (!ctx.entries_nr)
-			BUG("cannot write a bitmap without any objects");
-
-		prepare_midx_packing_data(&pdata, &ctx);
-
-		commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
-
-		/*
-		 * The previous steps translated the information from
-		 * 'entries' into information suitable for constructing
-		 * bitmaps. We no longer need that array, so clear it to
-		 * reduce memory pressure.
-		 */
-		FREE_AND_NULL(ctx.entries);
-		ctx.entries_nr = 0;
-
-		if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
-				      commits, commits_nr, ctx.pack_order,
-				      flags) < 0) {
-			error(_("could not write multi-pack bitmap"));
-			result = 1;
-			goto cleanup;
-		}
-	}
-	/*
-	 * NOTE: Do not use ctx.entries beyond this point, since it might
-	 * have been freed in the previous if block.
-	 */
-
-	if (ctx.m)
-		close_object_store(the_repository->objects);
-
-	if (commit_lock_file(&lk) < 0)
-		die_errno(_("could not write multi-pack-index"));
-
-	clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
-	clear_midx_files_ext(object_dir, ".rev", midx_hash);
-
-cleanup:
-	for (i = 0; i < ctx.nr; i++) {
-		if (ctx.info[i].p) {
-			close_pack(ctx.info[i].p);
-			free(ctx.info[i].p);
-		}
-		free(ctx.info[i].pack_name);
-	}
-
-	free(ctx.info);
-	free(ctx.entries);
-	free(ctx.pack_perm);
-	free(ctx.pack_order);
-	strbuf_release(&midx_name);
-
-	trace2_region_leave("midx", "write_midx_internal", the_repository);
-
-	return result;
-}
-
-int write_midx_file(const char *object_dir,
-		    const char *preferred_pack_name,
-		    const char *refs_snapshot,
-		    unsigned flags)
-{
-	return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
-				   refs_snapshot, flags);
-}
-
-int write_midx_file_only(const char *object_dir,
-			 struct string_list *packs_to_include,
-			 const char *preferred_pack_name,
-			 const char *refs_snapshot,
-			 unsigned flags)
-{
-	return write_midx_internal(object_dir, packs_to_include, NULL,
-				   preferred_pack_name, refs_snapshot, flags);
-}
-
 struct clear_midx_data {
 	char *keep;
 	const char *ext;
 };
 
-static void clear_midx_file_ext(const char *full_path, size_t full_path_len,
+static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUSED,
 				const char *file_name, void *_data)
 {
 	struct clear_midx_data *data = _data;
@@ -1622,8 +507,8 @@
 		die_errno(_("failed to remove %s"), full_path);
 }
 
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
-				 unsigned char *keep_hash)
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+			  unsigned char *keep_hash)
 {
 	struct clear_midx_data data;
 	memset(&data, 0, sizeof(struct clear_midx_data));
@@ -1736,15 +621,6 @@
 	}
 	stop_progress(&progress);
 
-	for (i = 0; i < 255; i++) {
-		uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
-		uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i + 1]);
-
-		if (oid_fanout1 > oid_fanout2)
-			midx_report(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
-				    i, oid_fanout1, oid_fanout2, i + 1);
-	}
-
 	if (m->num_objects == 0) {
 		midx_report(_("the midx contains no oid"));
 		/*
@@ -1835,256 +711,3 @@
 
 	return verify_midx_error;
 }
-
-int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
-{
-	uint32_t i, *count, result = 0;
-	struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
-	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
-	struct progress *progress = NULL;
-
-	if (!m)
-		return 0;
-
-	CALLOC_ARRAY(count, m->num_packs);
-
-	if (flags & MIDX_PROGRESS)
-		progress = start_delayed_progress(_("Counting referenced objects"),
-					  m->num_objects);
-	for (i = 0; i < m->num_objects; i++) {
-		int pack_int_id = nth_midxed_pack_int_id(m, i);
-		count[pack_int_id]++;
-		display_progress(progress, i + 1);
-	}
-	stop_progress(&progress);
-
-	if (flags & MIDX_PROGRESS)
-		progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
-					  m->num_packs);
-	for (i = 0; i < m->num_packs; i++) {
-		char *pack_name;
-		display_progress(progress, i + 1);
-
-		if (count[i])
-			continue;
-
-		if (prepare_midx_pack(r, m, i))
-			continue;
-
-		if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
-			continue;
-
-		pack_name = xstrdup(m->packs[i]->pack_name);
-		close_pack(m->packs[i]);
-
-		string_list_insert(&packs_to_drop, m->pack_names[i]);
-		unlink_pack_path(pack_name, 0);
-		free(pack_name);
-	}
-	stop_progress(&progress);
-
-	free(count);
-
-	if (packs_to_drop.nr)
-		result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
-
-	string_list_clear(&packs_to_drop, 0);
-
-	return result;
-}
-
-struct repack_info {
-	timestamp_t mtime;
-	uint32_t referenced_objects;
-	uint32_t pack_int_id;
-};
-
-static int compare_by_mtime(const void *a_, const void *b_)
-{
-	const struct repack_info *a, *b;
-
-	a = (const struct repack_info *)a_;
-	b = (const struct repack_info *)b_;
-
-	if (a->mtime < b->mtime)
-		return -1;
-	if (a->mtime > b->mtime)
-		return 1;
-	return 0;
-}
-
-static int fill_included_packs_all(struct repository *r,
-				   struct multi_pack_index *m,
-				   unsigned char *include_pack)
-{
-	uint32_t i, count = 0;
-	int pack_kept_objects = 0;
-
-	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
-
-	for (i = 0; i < m->num_packs; i++) {
-		if (prepare_midx_pack(r, m, i))
-			continue;
-		if (!pack_kept_objects && m->packs[i]->pack_keep)
-			continue;
-		if (m->packs[i]->is_cruft)
-			continue;
-
-		include_pack[i] = 1;
-		count++;
-	}
-
-	return count < 2;
-}
-
-static int fill_included_packs_batch(struct repository *r,
-				     struct multi_pack_index *m,
-				     unsigned char *include_pack,
-				     size_t batch_size)
-{
-	uint32_t i, packs_to_repack;
-	size_t total_size;
-	struct repack_info *pack_info;
-	int pack_kept_objects = 0;
-
-	CALLOC_ARRAY(pack_info, m->num_packs);
-
-	repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
-
-	for (i = 0; i < m->num_packs; i++) {
-		pack_info[i].pack_int_id = i;
-
-		if (prepare_midx_pack(r, m, i))
-			continue;
-
-		pack_info[i].mtime = m->packs[i]->mtime;
-	}
-
-	for (i = 0; i < m->num_objects; i++) {
-		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
-		pack_info[pack_int_id].referenced_objects++;
-	}
-
-	QSORT(pack_info, m->num_packs, compare_by_mtime);
-
-	total_size = 0;
-	packs_to_repack = 0;
-	for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
-		int pack_int_id = pack_info[i].pack_int_id;
-		struct packed_git *p = m->packs[pack_int_id];
-		size_t expected_size;
-
-		if (!p)
-			continue;
-		if (!pack_kept_objects && p->pack_keep)
-			continue;
-		if (p->is_cruft)
-			continue;
-		if (open_pack_index(p) || !p->num_objects)
-			continue;
-
-		expected_size = (size_t)(p->pack_size
-					 * pack_info[i].referenced_objects);
-		expected_size /= p->num_objects;
-
-		if (expected_size >= batch_size)
-			continue;
-
-		packs_to_repack++;
-		total_size += expected_size;
-		include_pack[pack_int_id] = 1;
-	}
-
-	free(pack_info);
-
-	if (packs_to_repack < 2)
-		return 1;
-
-	return 0;
-}
-
-int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
-{
-	int result = 0;
-	uint32_t i;
-	unsigned char *include_pack;
-	struct child_process cmd = CHILD_PROCESS_INIT;
-	FILE *cmd_in;
-	struct strbuf base_name = STRBUF_INIT;
-	struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
-
-	/*
-	 * When updating the default for these configuration
-	 * variables in builtin/repack.c, these must be adjusted
-	 * to match.
-	 */
-	int delta_base_offset = 1;
-	int use_delta_islands = 0;
-
-	if (!m)
-		return 0;
-
-	CALLOC_ARRAY(include_pack, m->num_packs);
-
-	if (batch_size) {
-		if (fill_included_packs_batch(r, m, include_pack, batch_size))
-			goto cleanup;
-	} else if (fill_included_packs_all(r, m, include_pack))
-		goto cleanup;
-
-	repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
-	repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
-
-	strvec_push(&cmd.args, "pack-objects");
-
-	strbuf_addstr(&base_name, object_dir);
-	strbuf_addstr(&base_name, "/pack/pack");
-	strvec_push(&cmd.args, base_name.buf);
-
-	if (delta_base_offset)
-		strvec_push(&cmd.args, "--delta-base-offset");
-	if (use_delta_islands)
-		strvec_push(&cmd.args, "--delta-islands");
-
-	if (flags & MIDX_PROGRESS)
-		strvec_push(&cmd.args, "--progress");
-	else
-		strvec_push(&cmd.args, "-q");
-
-	strbuf_release(&base_name);
-
-	cmd.git_cmd = 1;
-	cmd.in = cmd.out = -1;
-
-	if (start_command(&cmd)) {
-		error(_("could not start pack-objects"));
-		result = 1;
-		goto cleanup;
-	}
-
-	cmd_in = xfdopen(cmd.in, "w");
-
-	for (i = 0; i < m->num_objects; i++) {
-		struct object_id oid;
-		uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
-
-		if (!include_pack[pack_int_id])
-			continue;
-
-		nth_midxed_object_oid(&oid, m, i);
-		fprintf(cmd_in, "%s\n", oid_to_hex(&oid));
-	}
-	fclose(cmd_in);
-
-	if (finish_command(&cmd)) {
-		error(_("could not finish pack-objects"));
-		result = 1;
-		goto cleanup;
-	}
-
-	result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
-
-cleanup:
-	free(include_pack);
-	return result;
-}
diff --git a/midx.h b/midx.h
index 5578cd7..dc477df 100644
--- a/midx.h
+++ b/midx.h
@@ -1,12 +1,31 @@
 #ifndef MIDX_H
 #define MIDX_H
 
-#include "repository.h"
 #include "string-list.h"
 
 struct object_id;
 struct pack_entry;
 struct repository;
+struct bitmapped_pack;
+
+#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
+#define MIDX_VERSION 1
+#define MIDX_BYTE_FILE_VERSION 4
+#define MIDX_BYTE_HASH_VERSION 5
+#define MIDX_BYTE_NUM_CHUNKS 6
+#define MIDX_BYTE_NUM_PACKS 8
+#define MIDX_HEADER_SIZE 12
+
+#define MIDX_CHUNK_ALIGNMENT 4
+#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
+#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
+#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
+#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
+#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
+#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
+#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
+#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
+#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
 
 #define GIT_TEST_MULTI_PACK_INDEX "GIT_TEST_MULTI_PACK_INDEX"
 #define GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP \
@@ -28,15 +47,21 @@
 	unsigned char num_chunks;
 	uint32_t num_packs;
 	uint32_t num_objects;
+	int preferred_pack_idx;
 
 	int local;
 
 	const unsigned char *chunk_pack_names;
+	size_t chunk_pack_names_len;
+	const uint32_t *chunk_bitmapped_packs;
+	size_t chunk_bitmapped_packs_len;
 	const uint32_t *chunk_oid_fanout;
 	const unsigned char *chunk_oid_lookup;
 	const unsigned char *chunk_object_offsets;
 	const unsigned char *chunk_large_offsets;
+	size_t chunk_large_offsets_len;
 	const unsigned char *chunk_revindex;
+	size_t chunk_revindex_len;
 
 	const char **pack_names;
 	struct packed_git **packs;
@@ -55,6 +80,8 @@
 
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
 int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
+int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
+		       struct bitmapped_pack *bp, uint32_t pack_int_id);
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result);
 off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos);
 uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos);
@@ -62,7 +89,11 @@
 					struct multi_pack_index *m,
 					uint32_t n);
 int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e, struct multi_pack_index *m);
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
+int midx_contains_pack(struct multi_pack_index *m,
+		       const char *idx_or_pack_name);
+int midx_locate_pack(struct multi_pack_index *m, const char *idx_or_pack_name,
+		     uint32_t *pos);
+int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id);
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
 
 /*
diff --git a/name-hash.c b/name-hash.c
index cd009c7..3a58ce0 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -5,8 +5,14 @@
  *
  * Copyright (C) 2008 Linus Torvalds
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "object.h"
+#include "read-cache-ll.h"
 #include "thread-utils.h"
+#include "trace.h"
 #include "trace2.h"
 #include "sparse-index.h"
 
@@ -679,13 +685,20 @@
 	return slow_same_name(name, namelen, ce->name, len);
 }
 
-int index_dir_exists(struct index_state *istate, const char *name, int namelen)
+int index_dir_find(struct index_state *istate, const char *name, int namelen,
+		   struct strbuf *canonical_path)
 {
 	struct dir_entry *dir;
 
 	lazy_init_name_hash(istate);
 	expand_to_path(istate, name, namelen, 0);
 	dir = find_dir_entry(istate, name, namelen);
+
+	if (canonical_path && dir && dir->nr) {
+		strbuf_reset(canonical_path);
+		strbuf_add(canonical_path, dir->name, dir->namelen);
+	}
+
 	return dir && dir->nr;
 }
 
diff --git a/name-hash.h b/name-hash.h
new file mode 100644
index 0000000..0cbfc42
--- /dev/null
+++ b/name-hash.h
@@ -0,0 +1,21 @@
+#ifndef NAME_HASH_H
+#define NAME_HASH_H
+
+struct cache_entry;
+struct index_state;
+
+
+int index_dir_find(struct index_state *istate, const char *name, int namelen,
+		   struct strbuf *canonical_path);
+
+#define index_dir_exists(i, n, l) index_dir_find((i), (n), (l), NULL)
+
+void adjust_dirname_case(struct index_state *istate, char *name);
+struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+
+int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
+void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
+void free_name_hash(struct index_state *istate);
+
+#endif /* NAME_HASH_H */
diff --git a/negotiator/default.c b/negotiator/default.c
index b7e79fe..9a5b696 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "default.h"
 #include "../commit.h"
 #include "../fetch-negotiator.h"
 #include "../prio-queue.h"
 #include "../refs.h"
+#include "../repository.h"
 #include "../tag.h"
 
 /* Remember to update object flag allocation in object.h */
@@ -25,7 +26,7 @@
 	if (!(commit->object.flags & mark)) {
 		commit->object.flags |= mark;
 
-		if (parse_commit(commit))
+		if (repo_parse_commit(the_repository, commit))
 			return;
 
 		prio_queue_put(&ns->rev_list, commit);
@@ -55,30 +56,49 @@
 static void mark_common(struct negotiation_state *ns, struct commit *commit,
 		int ancestors_only, int dont_parse)
 {
-	if (commit != NULL && !(commit->object.flags & COMMON)) {
-		struct object *o = (struct object *)commit;
+	struct prio_queue queue = { NULL };
 
-		if (!ancestors_only)
-			o->flags |= COMMON;
+	if (!commit || (commit->object.flags & COMMON))
+		return;
+
+	prio_queue_put(&queue, commit);
+	if (!ancestors_only) {
+		commit->object.flags |= COMMON;
+
+		if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED))
+			ns->non_common_revs--;
+	}
+	while ((commit = prio_queue_get(&queue))) {
+		struct object *o = (struct object *)commit;
 
 		if (!(o->flags & SEEN))
 			rev_list_push(ns, commit, SEEN);
 		else {
 			struct commit_list *parents;
 
-			if (!ancestors_only && !(o->flags & POPPED))
-				ns->non_common_revs--;
 			if (!o->parsed && !dont_parse)
-				if (parse_commit(commit))
-					return;
+				if (repo_parse_commit(the_repository, commit))
+					continue;
 
 			for (parents = commit->parents;
 					parents;
-					parents = parents->next)
-				mark_common(ns, parents->item, 0,
-					    dont_parse);
+					parents = parents->next) {
+				struct commit *p = parents->item;
+
+				if (p->object.flags & COMMON)
+					continue;
+
+				p->object.flags |= COMMON;
+
+				if ((p->object.flags & SEEN) && !(p->object.flags & POPPED))
+					ns->non_common_revs--;
+
+				prio_queue_put(&queue, parents->item);
+			}
 		}
 	}
+
+	clear_prio_queue(&queue);
 }
 
 /*
@@ -96,7 +116,7 @@
 			return NULL;
 
 		commit = prio_queue_get(&ns->rev_list);
-		parse_commit(commit);
+		repo_parse_commit(the_repository, commit);
 		parents = commit->parents;
 
 		commit->object.flags |= POPPED;
diff --git a/negotiator/noop.c b/negotiator/noop.c
index 60569b8..65e3c20 100644
--- a/negotiator/noop.c
+++ b/negotiator/noop.c
@@ -1,24 +1,25 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "noop.h"
-#include "../commit.h"
 #include "../fetch-negotiator.h"
 
-static void known_common(struct fetch_negotiator *n, struct commit *c)
+static void known_common(struct fetch_negotiator *n UNUSED,
+			 struct commit *c UNUSED)
 {
 	/* do nothing */
 }
 
-static void add_tip(struct fetch_negotiator *n, struct commit *c)
+static void add_tip(struct fetch_negotiator *n UNUSED,
+		    struct commit *c UNUSED)
 {
 	/* do nothing */
 }
 
-static const struct object_id *next(struct fetch_negotiator *n)
+static const struct object_id *next(struct fetch_negotiator *n UNUSED)
 {
 	return NULL;
 }
 
-static int ack(struct fetch_negotiator *n, struct commit *c)
+static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED)
 {
 	/*
 	 * This negotiator does not emit any commits, so there is no commit to
@@ -28,7 +29,7 @@
 	return 0;
 }
 
-static void release(struct fetch_negotiator *n)
+static void release(struct fetch_negotiator *n UNUSED)
 {
 	/* nothing to release */
 }
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 0f5ac48..5b91520 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "skipping.h"
 #include "../commit.h"
 #include "../fetch-negotiator.h"
+#include "../hex.h"
 #include "../prio-queue.h"
 #include "../refs.h"
+#include "../repository.h"
 #include "../tag.h"
 
 /* Remember to update object flag allocation in object.h */
@@ -50,7 +52,7 @@
 	int non_common_revs;
 };
 
-static int compare(const void *a_, const void *b_, void *unused)
+static int compare(const void *a_, const void *b_, void *data UNUSED)
 {
 	const struct entry *a = a_;
 	const struct entry *b = b_;
@@ -84,29 +86,37 @@
 }
 
 /*
- * Mark this SEEN commit and all its SEEN ancestors as COMMON.
+ * Mark this SEEN commit and all its parsed SEEN ancestors as COMMON.
  */
 static void mark_common(struct data *data, struct commit *seen_commit)
 {
 	struct prio_queue queue = { NULL };
 	struct commit *c;
 
+	if (seen_commit->object.flags & COMMON)
+		return;
+
 	prio_queue_put(&queue, seen_commit);
+	seen_commit->object.flags |= COMMON;
 	while ((c = prio_queue_get(&queue))) {
 		struct commit_list *p;
-		if (c->object.flags & COMMON)
-			return;
-		c->object.flags |= COMMON;
+
 		if (!(c->object.flags & POPPED))
 			data->non_common_revs--;
 
 		if (!c->object.parsed)
-			return;
+			continue;
 		for (p = c->parents; p; p = p->next) {
-			if (p->item->object.flags & SEEN)
-				prio_queue_put(&queue, p->item);
+			if (!(p->item->object.flags & SEEN) ||
+			    (p->item->object.flags & COMMON))
+				continue;
+
+			p->item->object.flags |= COMMON;
+			prio_queue_put(&queue, p->item);
 		}
 	}
+
+	clear_prio_queue(&queue);
 }
 
 /*
@@ -183,7 +193,7 @@
 		if (!(commit->object.flags & COMMON) && !entry->ttl)
 			to_send = commit;
 
-		parse_commit(commit);
+		repo_parse_commit(the_repository, commit);
 		for (p = commit->parents; p; p = p->next)
 			parent_pushed |= push_parent(data, entry, p->item);
 
diff --git a/notes-cache.c b/notes-cache.c
index 9dfd251..0e1d5b1 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "notes-cache.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "pretty.h"
 #include "repository.h"
 #include "commit.h"
 #include "refs.h"
+#include "strbuf.h"
 
 static int notes_cache_match_validity(struct repository *r,
 				      const char *ref,
@@ -23,7 +25,8 @@
 		return 0;
 
 	memset(&pretty_ctx, 0, sizeof(pretty_ctx));
-	format_commit_message(commit, "%s", &msg, &pretty_ctx);
+	repo_format_commit_message(r, commit, "%s", &msg,
+				   &pretty_ctx);
 	strbuf_trim(&msg);
 
 	ret = !strcmp(msg.buf, validity);
@@ -81,7 +84,7 @@
 	value_oid = get_note(&c->tree, key_oid);
 	if (!value_oid)
 		return NULL;
-	value = read_object_file(value_oid, &type, &size);
+	value = repo_read_object_file(the_repository, value_oid, &type, &size);
 
 	*outsize = size;
 	return value;
diff --git a/notes-merge.c b/notes-merge.c
index b4cc594..5128293 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -1,16 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "commit.h"
+#include "gettext.h"
 #include "refs.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "hex.h"
 #include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
 #include "dir.h"
 #include "notes.h"
 #include "notes-merge.h"
 #include "strbuf.h"
+#include "trace.h"
 #include "notes-utils.h"
 #include "commit-reach.h"
 
@@ -326,7 +333,7 @@
 {
 	enum object_type type;
 	unsigned long size;
-	void *buf = read_object_file(note, &type, &size);
+	void *buf = repo_read_object_file(the_repository, note, &type, &size);
 
 	if (!buf)
 		die("cannot read note %s for object %s",
@@ -566,7 +573,7 @@
 	trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid));
 
 	/* Dereference o->remote_ref into remote_oid */
-	if (get_oid(o->remote_ref, &remote_oid)) {
+	if (repo_get_oid(the_repository, o->remote_ref, &remote_oid)) {
 		/*
 		 * Failed to get remote_oid. If o->remote_ref looks like an
 		 * unborn ref, perform the merge using an empty notes tree.
@@ -600,7 +607,8 @@
 	assert(local && remote);
 
 	/* Find merge bases */
-	bases = get_merge_bases(local, remote);
+	if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
+		exit(128);
 	if (!bases) {
 		base_oid = null_oid();
 		base_tree_oid = the_hash_algo->empty_tree;
@@ -678,7 +686,8 @@
 	DIR *dir;
 	struct dirent *e;
 	struct strbuf path = STRBUF_INIT;
-	const char *buffer = get_commit_buffer(partial_commit, NULL);
+	const char *buffer = repo_get_commit_buffer(the_repository,
+						    partial_commit, NULL);
 	const char *msg = strstr(buffer, "\n\n");
 	int baselen;
 
@@ -725,7 +734,7 @@
 
 	create_notes_commit(o->repo, partial_tree, partial_commit->parents, msg,
 			    strlen(msg), result_oid);
-	unuse_commit_buffer(partial_commit, buffer);
+	repo_unuse_commit_buffer(the_repository, partial_commit, buffer);
 	if (o->verbosity >= 4)
 		printf("Finalized notes merge commit: %s\n",
 			oid_to_hex(result_oid));
diff --git a/notes-utils.c b/notes-utils.c
index d7d18e3..6197a5a 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
 #include "refs.h"
 #include "notes-utils.h"
-#include "repository.h"
+#include "strbuf.h"
 
 void create_notes_commit(struct repository *r,
 			 struct notes_tree *t,
@@ -23,7 +25,7 @@
 		struct object_id parent_oid;
 		if (!read_ref(t->ref, &parent_oid)) {
 			struct commit *parent = lookup_commit(r, &parent_oid);
-			if (parse_commit(parent))
+			if (repo_parse_commit(r, parent))
 				die("Failed to find/parse commit %s", t->ref);
 			commit_list_insert(parent, &parents);
 		}
@@ -91,7 +93,9 @@
 		return NULL;
 }
 
-static int notes_rewrite_config(const char *k, const char *v, void *cb)
+static int notes_rewrite_config(const char *k, const char *v,
+				const struct config_context *ctx UNUSED,
+				void *cb)
 {
 	struct notes_rewrite_cfg *c = cb;
 	if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
@@ -107,6 +111,8 @@
 		}
 		return 0;
 	} else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+		if (!v)
+			return config_error_nonbool(k);
 		/* note that a refs/ prefix is implied in the
 		 * underlying for_each_glob_ref */
 		if (starts_with(v, "refs/notes/"))
diff --git a/notes.c b/notes.c
index f2805d5..fed1eda 100644
--- a/notes.c
+++ b/notes.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "hex.h"
 #include "notes.h"
-#include "object-store.h"
-#include "blob.h"
-#include "tree.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "tree-walk.h"
@@ -752,7 +753,7 @@
 	return 0;
 }
 
-static int write_each_note(const struct object_id *object_oid,
+static int write_each_note(const struct object_id *object_oid UNUSED,
 		const struct object_id *note_oid, char *note_path,
 		void *cb_data)
 {
@@ -780,13 +781,14 @@
 };
 
 static int prune_notes_helper(const struct object_id *object_oid,
-		const struct object_id *note_oid, char *note_path,
-		void *cb_data)
+			      const struct object_id *note_oid UNUSED,
+			      char *note_path UNUSED,
+			      void *cb_data)
 {
 	struct note_delete_list **l = (struct note_delete_list **) cb_data;
 	struct note_delete_list *n;
 
-	if (has_object_file(object_oid))
+	if (repo_has_object_file(the_repository, object_oid))
 		return 0; /* nothing to do for this note */
 
 	/* failed to find object => prune this note */
@@ -807,13 +809,15 @@
 
 	/* read in both note blob objects */
 	if (!is_null_oid(new_oid))
-		new_msg = read_object_file(new_oid, &new_type, &new_len);
+		new_msg = repo_read_object_file(the_repository, new_oid,
+						&new_type, &new_len);
 	if (!new_msg || !new_len || new_type != OBJ_BLOB) {
 		free(new_msg);
 		return 0;
 	}
 	if (!is_null_oid(cur_oid))
-		cur_msg = read_object_file(cur_oid, &cur_type, &cur_len);
+		cur_msg = repo_read_object_file(the_repository, cur_oid,
+						&cur_type, &cur_len);
 	if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) {
 		free(cur_msg);
 		free(new_msg);
@@ -848,8 +852,8 @@
 	return 0;
 }
 
-int combine_notes_ignore(struct object_id *cur_oid,
-			 const struct object_id *new_oid)
+int combine_notes_ignore(struct object_id *cur_oid UNUSED,
+			 const struct object_id *new_oid UNUSED)
 {
 	return 0;
 }
@@ -869,7 +873,7 @@
 		return 0;
 
 	/* read_sha1_file NUL-terminates */
-	data = read_object_file(oid, &t, &len);
+	data = repo_read_object_file(the_repository, oid, &t, &len);
 	if (t != OBJ_BLOB || !data || !len) {
 		free(data);
 		return t != OBJ_BLOB || !data;
@@ -944,7 +948,7 @@
 		for_each_glob_ref(string_list_add_one_ref, glob, list);
 	} else {
 		struct object_id oid;
-		if (get_oid(glob, &oid))
+		if (repo_get_oid(the_repository, glob, &oid))
 			warning("notes ref %s is invalid", glob);
 		if (!unsorted_string_list_has_string(list, glob))
 			string_list_append(list, glob);
@@ -958,7 +962,7 @@
 	char *globs_copy = xstrdup(globs);
 	int i;
 
-	string_list_split_in_place(&split, globs_copy, ':', -1);
+	string_list_split_in_place(&split, globs_copy, ":", -1);
 	string_list_remove_empty_items(&split, 0);
 
 	for (i = 0; i < split.nr; i++)
@@ -968,7 +972,9 @@
 	free(globs_copy);
 }
 
-static int notes_display_config(const char *k, const char *v, void *cb)
+static int notes_display_config(const char *k, const char *v,
+				const struct config_context *ctx UNUSED,
+				void *cb)
 {
 	int *load_refs = cb;
 
@@ -1014,14 +1020,14 @@
 	t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node));
 	t->first_non_note = NULL;
 	t->prev_non_note = NULL;
-	t->ref = xstrdup_or_null(notes_ref);
+	t->ref = xstrdup(notes_ref);
 	t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL;
 	t->combine_notes = combine_notes;
 	t->initialized = 1;
 	t->dirty = 0;
 
-	if (flags & NOTES_INIT_EMPTY || !notes_ref ||
-	    get_oid_treeish(notes_ref, &object_oid))
+	if (flags & NOTES_INIT_EMPTY ||
+	    repo_get_oid_treeish(the_repository, notes_ref, &object_oid))
 		return;
 	if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))
 		die("Cannot use notes ref %s", notes_ref);
@@ -1264,7 +1270,7 @@
 	if (!oid)
 		return;
 
-	if (!(msg = read_object_file(oid, &type, &msglen)) || type != OBJ_BLOB) {
+	if (!(msg = repo_read_object_file(the_repository, oid, &type, &msglen)) || type != OBJ_BLOB) {
 		free(msg);
 		return;
 	}
@@ -1348,7 +1354,7 @@
 {
 	struct object_id object;
 
-	if (get_oid(sb->buf, &object)) {
+	if (repo_get_oid(the_repository, sb->buf, &object)) {
 		/* fallback to expand_notes_ref */
 		expand_notes_ref(sb);
 	}
diff --git a/notes.h b/notes.h
index c1682c3..064fd71 100644
--- a/notes.h
+++ b/notes.h
@@ -256,7 +256,17 @@
 struct string_list;
 
 struct display_notes_opt {
+	/*
+	 * Less than `0` is "unset", which means that the default notes
+	 * are shown iff no other notes are given. Otherwise,
+	 * treat it like a boolean.
+	 */
 	int use_default_notes;
+
+	/*
+	 * A list of globs (in the same style as notes.displayRef) where
+	 * notes should be loaded from.
+	 */
 	struct string_list extra_notes_refs;
 };
 
@@ -283,14 +293,7 @@
 /*
  * Load the notes machinery for displaying several notes trees.
  *
- * If 'opt' is not NULL, then it specifies additional settings for the
- * displaying:
- *
- * - suppress_default_notes indicates that the notes from
- *   core.notesRef and notes.displayRef should not be loaded.
- *
- * - extra_notes_refs may contain a list of globs (in the same style
- *   as notes.displayRef) where notes should be loaded from.
+ * 'opt' may be NULL.
  */
 void load_display_notes(struct display_notes_opt *opt);
 
diff --git a/object-file-convert.c b/object-file-convert.c
new file mode 100644
index 0000000..4f61890
--- /dev/null
+++ b/object-file-convert.c
@@ -0,0 +1,277 @@
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "hex.h"
+#include "repository.h"
+#include "hash-ll.h"
+#include "hash.h"
+#include "object.h"
+#include "loose.h"
+#include "commit.h"
+#include "gpg-interface.h"
+#include "object-file-convert.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+		      const struct git_hash_algo *to, struct object_id *dest)
+{
+	/*
+	 * If the source algorithm is not set, then we're using the
+	 * default hash algorithm for that object.
+	 */
+	const struct git_hash_algo *from =
+		src->algo ? &hash_algos[src->algo] : repo->hash_algo;
+
+	if (from == to) {
+		if (src != dest)
+			oidcpy(dest, src);
+		return 0;
+	}
+	if (repo_loose_object_map_oid(repo, src, to, dest)) {
+		/*
+		 * We may have loaded the object map at repo initialization but
+		 * another process (perhaps upstream of a pipe from us) may have
+		 * written a new object into the map.  If the object is missing,
+		 * let's reload the map to see if the object has appeared.
+		 */
+		repo_read_loose_object_map(repo);
+		if (repo_loose_object_map_oid(repo, src, to, dest))
+			return -1;
+	}
+	return 0;
+}
+
+static int decode_tree_entry_raw(struct object_id *oid, const char **path,
+				 size_t *len, const struct git_hash_algo *algo,
+				 const char *buf, unsigned long size)
+{
+	uint16_t mode;
+	const unsigned hashsz = algo->rawsz;
+
+	if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
+		return -1;
+	}
+
+	*path = parse_mode(buf, &mode);
+	if (!*path || !**path)
+		return -1;
+	*len = strlen(*path) + 1;
+
+	oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+	return 0;
+}
+
+static int convert_tree_object(struct strbuf *out,
+			       const struct git_hash_algo *from,
+			       const struct git_hash_algo *to,
+			       const char *buffer, size_t size)
+{
+	const char *p = buffer, *end = buffer + size;
+
+	while (p < end) {
+		struct object_id entry_oid, mapped_oid;
+		const char *path = NULL;
+		size_t pathlen;
+
+		if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
+					  end - p))
+			return error(_("failed to decode tree entry"));
+		if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
+			return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
+		strbuf_add(out, p, path - p);
+		strbuf_add(out, path, pathlen);
+		strbuf_add(out, mapped_oid.hash, to->rawsz);
+		p = path + pathlen + from->rawsz;
+	}
+	return 0;
+}
+
+static int convert_tag_object(struct strbuf *out,
+			      const struct git_hash_algo *from,
+			      const struct git_hash_algo *to,
+			      const char *buffer, size_t size)
+{
+	struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
+	const int entry_len = from->hexsz + 7;
+	size_t payload_size;
+	struct object_id oid, mapped_oid;
+	const char *p;
+
+	/* Consume the object line */
+	if ((entry_len >= size) ||
+	    memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
+		return error("bogus tag object");
+	if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
+		return error("bad tag object ID");
+	if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+		return error("unable to map tree %s in tag object",
+			     oid_to_hex(&oid));
+	size -= ((p + 1) - buffer);
+	buffer = p + 1;
+
+	/* Is there a signature for our algorithm? */
+	payload_size = parse_signed_buffer(buffer, size);
+	if (payload_size != size) {
+		/* Yes, there is. */
+		strbuf_add(&oursig, buffer + payload_size, size - payload_size);
+	}
+
+	/* Now, is there a signature for the other algorithm? */
+	parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
+	/*
+	 * Our payload is now in payload and we may have up to two signatrures
+	 * in oursig and othersig.
+	 */
+
+	/* Add some slop for longer signature header in the new algorithm. */
+	strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
+	strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
+	strbuf_addbuf(out, &payload);
+	if (oursig.len)
+		add_header_signature(out, &oursig, from);
+	strbuf_addbuf(out, &othersig);
+
+	strbuf_release(&payload);
+	strbuf_release(&othersig);
+	strbuf_release(&oursig);
+	return 0;
+}
+
+static int convert_commit_object(struct strbuf *out,
+				 const struct git_hash_algo *from,
+				 const struct git_hash_algo *to,
+				 const char *buffer, size_t size)
+{
+	const char *tail = buffer;
+	const char *bufptr = buffer;
+	const int tree_entry_len = from->hexsz + 5;
+	const int parent_entry_len = from->hexsz + 7;
+	struct object_id oid, mapped_oid;
+	const char *p, *eol;
+
+	tail += size;
+
+	while ((bufptr < tail) && (*bufptr != '\n')) {
+		eol = memchr(bufptr, '\n', tail - bufptr);
+		if (!eol)
+			return error(_("bad %s in commit"), "line");
+
+		if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
+		{
+			if (((bufptr + tree_entry_len) != eol) ||
+			    parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
+			    (p != eol))
+				return error(_("bad %s in commit"), "tree");
+
+			if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+				return error(_("unable to map %s %s in commit object"),
+					     "tree", oid_to_hex(&oid));
+			strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
+		}
+		else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
+		{
+			if (((bufptr + parent_entry_len) != eol) ||
+			    parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
+			    (p != eol))
+				return error(_("bad %s in commit"), "parent");
+
+			if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+				return error(_("unable to map %s %s in commit object"),
+					     "parent", oid_to_hex(&oid));
+
+			strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
+		}
+		else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
+		{
+			struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
+
+			/* Recover the tag object from the mergetag */
+			strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
+
+			bufptr = eol + 1;
+			while ((bufptr < tail) && (*bufptr == ' ')) {
+				eol = memchr(bufptr, '\n', tail - bufptr);
+				if (!eol) {
+					strbuf_release(&tag);
+					return error(_("bad %s in commit"), "mergetag continuation");
+				}
+				strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
+				bufptr = eol + 1;
+			}
+
+			/* Compute the new tag object */
+			if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
+				strbuf_release(&tag);
+				strbuf_release(&new_tag);
+				return -1;
+			}
+
+			/* Write the new mergetag */
+			strbuf_addstr(out, "mergetag");
+			strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
+			strbuf_release(&tag);
+			strbuf_release(&new_tag);
+		}
+		else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
+			strbuf_add(out, bufptr, (eol - bufptr) + 1);
+		else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
+			strbuf_add(out, bufptr, (eol - bufptr) + 1);
+		else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
+			strbuf_add(out, bufptr, (eol - bufptr) + 1);
+		else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
+			strbuf_add(out, bufptr, (eol - bufptr) + 1);
+		else {
+			/* Unknown line fail it might embed an oid */
+			return -1;
+		}
+		/* Consume any trailing continuation lines */
+		bufptr = eol + 1;
+		while ((bufptr < tail) && (*bufptr == ' ')) {
+			eol = memchr(bufptr, '\n', tail - bufptr);
+			if (!eol)
+				return error(_("bad %s in commit"), "continuation");
+			strbuf_add(out, bufptr, (eol - bufptr) + 1);
+			bufptr = eol + 1;
+		}
+	}
+	if (bufptr < tail)
+		strbuf_add(out, bufptr, tail - bufptr);
+	return 0;
+}
+
+int convert_object_file(struct strbuf *outbuf,
+			const struct git_hash_algo *from,
+			const struct git_hash_algo *to,
+			const void *buf, size_t len,
+			enum object_type type,
+			int gentle)
+{
+	int ret;
+
+	/* Don't call this function when no conversion is necessary */
+	if ((from == to) || (type == OBJ_BLOB))
+		BUG("Refusing noop object file conversion");
+
+	switch (type) {
+	case OBJ_COMMIT:
+		ret = convert_commit_object(outbuf, from, to, buf, len);
+		break;
+	case OBJ_TREE:
+		ret = convert_tree_object(outbuf, from, to, buf, len);
+		break;
+	case OBJ_TAG:
+		ret = convert_tag_object(outbuf, from, to, buf, len);
+		break;
+	default:
+		/* Not implemented yet, so fail. */
+		ret = -1;
+		break;
+	}
+	if (!ret)
+		return 0;
+	if (gentle) {
+		strbuf_release(outbuf);
+		return ret;
+	}
+	die(_("Failed to convert object from %s to %s"),
+		from->name, to->name);
+}
diff --git a/object-file-convert.h b/object-file-convert.h
new file mode 100644
index 0000000..a4f802a
--- /dev/null
+++ b/object-file-convert.h
@@ -0,0 +1,24 @@
+#ifndef OBJECT_CONVERT_H
+#define OBJECT_CONVERT_H
+
+struct repository;
+struct object_id;
+struct git_hash_algo;
+struct strbuf;
+#include "object.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+		      const struct git_hash_algo *to, struct object_id *dest);
+
+/*
+ * Convert an object file from one hash algorithm to another algorithm.
+ * Return -1 on failure, 0 on success.
+ */
+int convert_object_file(struct strbuf *outbuf,
+			const struct git_hash_algo *from,
+			const struct git_hash_algo *to,
+			const void *buf, size_t len,
+			enum object_type type,
+			int gentle);
+
+#endif /* OBJECT_CONVERT_H */
diff --git a/object-file.c b/object-file.c
index 939865c..610b1f4 100644
--- a/object-file.c
+++ b/object-file.c
@@ -6,34 +6,37 @@
  * This handles basic git object files - packing, unpacking,
  * creation etc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "convert.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "string-list.h"
 #include "lockfile.h"
-#include "delta.h"
 #include "pack.h"
-#include "blob.h"
 #include "commit.h"
 #include "run-command.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
 #include "refs.h"
-#include "pack-revindex.h"
-#include "hash-lookup.h"
 #include "bulk-checkin.h"
 #include "repository.h"
 #include "replace-object.h"
 #include "streaming.h"
 #include "dir.h"
 #include "list.h"
-#include "mergesort.h"
 #include "quote.h"
 #include "packfile.h"
+#include "object-file.h"
 #include "object-store.h"
+#include "oidtree.h"
+#include "path.h"
 #include "promisor-remote.h"
+#include "setup.h"
 #include "submodule.h"
 #include "fsck.h"
+#include "loose.h"
+#include "object-file-convert.h"
 
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
@@ -267,7 +270,7 @@
 
 /*
  * This is meant to hold a *small* number of objects that you would
- * want read_object_file() to be able to return, but yet you do not want
+ * want repo_read_object_file() to be able to return, but yet you do not want
  * to write them into the object store (e.g. a browse-only
  * application).
  */
@@ -944,6 +947,12 @@
 	r->objects->loaded_alternates = 1;
 }
 
+int has_alt_odb(struct repository *r)
+{
+	prepare_alt_odb(r);
+	return !!r->objects->odb->next;
+}
+
 /* Returns 1 if we have successfully freshened the file, 0 otherwise. */
 static int freshen_file(const char *fn)
 {
@@ -1077,9 +1086,11 @@
 			   void *buf, unsigned long size,
 			   enum object_type type)
 {
+	const struct git_hash_algo *algo =
+		oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
 	struct object_id real_oid;
 
-	hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+	hash_object_file(algo, buf, size, type, &real_oid);
 
 	return !oideq(oid, &real_oid) ? -1 : 0;
 }
@@ -1645,10 +1656,101 @@
 	return 0;
 }
 
+static int oid_object_info_convert(struct repository *r,
+				   const struct object_id *input_oid,
+				   struct object_info *input_oi, unsigned flags)
+{
+	const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+	int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+	struct strbuf type_name = STRBUF_INIT;
+	struct object_id oid, delta_base_oid;
+	struct object_info new_oi, *oi;
+	unsigned long size;
+	void *content;
+	int ret;
+
+	if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+		if (do_die)
+			die(_("missing mapping of %s to %s"),
+			    oid_to_hex(input_oid), the_hash_algo->name);
+		return -1;
+	}
+
+	/* Is new_oi needed? */
+	oi = input_oi;
+	if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+			 input_oi->contentp)) {
+		new_oi = *input_oi;
+		/* Does delta_base_oid need to be converted? */
+		if (input_oi->delta_base_oid)
+			new_oi.delta_base_oid = &delta_base_oid;
+		/* Will the attributes differ when converted? */
+		if (input_oi->sizep || input_oi->contentp) {
+			new_oi.contentp = &content;
+			new_oi.sizep = &size;
+			new_oi.type_name = &type_name;
+		}
+		oi = &new_oi;
+	}
+
+	ret = oid_object_info_extended(r, &oid, oi, flags);
+	if (ret)
+		return -1;
+	if (oi == input_oi)
+		return ret;
+
+	if (new_oi.contentp) {
+		struct strbuf outbuf = STRBUF_INIT;
+		enum object_type type;
+
+		type = type_from_string_gently(type_name.buf, type_name.len,
+					       !do_die);
+		if (type == -1)
+			return -1;
+		if (type != OBJ_BLOB) {
+			ret = convert_object_file(&outbuf,
+						  the_hash_algo, input_algo,
+						  content, size, type, !do_die);
+			if (ret == -1)
+				return -1;
+			free(content);
+			size = outbuf.len;
+			content = strbuf_detach(&outbuf, NULL);
+		}
+		if (input_oi->sizep)
+			*input_oi->sizep = size;
+		if (input_oi->contentp)
+			*input_oi->contentp = content;
+		else
+			free(content);
+		if (input_oi->type_name)
+			*input_oi->type_name = type_name;
+		else
+			strbuf_release(&type_name);
+	}
+	if (new_oi.delta_base_oid == &delta_base_oid) {
+		if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+				 input_oi->delta_base_oid)) {
+			if (do_die)
+				die(_("missing mapping of %s to %s"),
+				    oid_to_hex(&delta_base_oid),
+				    input_algo->name);
+			return -1;
+		}
+	}
+	input_oi->whence = new_oi.whence;
+	input_oi->u = new_oi.u;
+	return ret;
+}
+
 int oid_object_info_extended(struct repository *r, const struct object_id *oid,
 			     struct object_info *oi, unsigned flags)
 {
 	int ret;
+
+	if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+		return oid_object_info_convert(r, oid, oi, flags);
+
 	obj_read_lock();
 	ret = do_oid_object_info_extended(r, oid, oi, flags);
 	obj_read_unlock();
@@ -1678,7 +1780,7 @@
 	struct cached_object *co;
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
-	if (has_object_file_with_flags(oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
+	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
 	    find_cached_object(oid))
 		return 0;
 	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
@@ -1937,9 +2039,12 @@
 				     const char *filename, unsigned flags,
 				     git_zstream *stream,
 				     unsigned char *buf, size_t buflen,
-				     git_hash_ctx *c,
+				     git_hash_ctx *c, git_hash_ctx *compat_c,
 				     char *hdr, int hdrlen)
 {
+	struct repository *repo = the_repository;
+	const struct git_hash_algo *algo = repo->hash_algo;
+	const struct git_hash_algo *compat = repo->compat_hash_algo;
 	int fd;
 
 	fd = create_tmpfile(tmp_file, filename);
@@ -1959,14 +2064,18 @@
 	git_deflate_init(stream, zlib_compression_level);
 	stream->next_out = buf;
 	stream->avail_out = buflen;
-	the_hash_algo->init_fn(c);
+	algo->init_fn(c);
+	if (compat && compat_c)
+		compat->init_fn(compat_c);
 
 	/*  Start to feed header to zlib stream */
 	stream->next_in = (unsigned char *)hdr;
 	stream->avail_in = hdrlen;
 	while (git_deflate(stream, 0) == Z_OK)
 		; /* nothing */
-	the_hash_algo->update_fn(c, hdr, hdrlen);
+	algo->update_fn(c, hdr, hdrlen);
+	if (compat && compat_c)
+		compat->update_fn(compat_c, hdr, hdrlen);
 
 	return fd;
 }
@@ -1975,16 +2084,21 @@
  * Common steps for the inner git_deflate() loop for writing loose
  * objects. Returns what git_deflate() returns.
  */
-static int write_loose_object_common(git_hash_ctx *c,
+static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
 				     git_zstream *stream, const int flush,
 				     unsigned char *in0, const int fd,
 				     unsigned char *compressed,
 				     const size_t compressed_len)
 {
+	struct repository *repo = the_repository;
+	const struct git_hash_algo *algo = repo->hash_algo;
+	const struct git_hash_algo *compat = repo->compat_hash_algo;
 	int ret;
 
 	ret = git_deflate(stream, flush ? Z_FINISH : 0);
-	the_hash_algo->update_fn(c, in0, stream->next_in - in0);
+	algo->update_fn(c, in0, stream->next_in - in0);
+	if (compat && compat_c)
+		compat->update_fn(compat_c, in0, stream->next_in - in0);
 	if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
 		die_errno(_("unable to write loose object file"));
 	stream->next_out = compressed;
@@ -1999,15 +2113,21 @@
  * - End the compression of zlib stream.
  * - Get the calculated oid to "oid".
  */
-static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream,
-				   struct object_id *oid)
+static int end_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
+				   git_zstream *stream, struct object_id *oid,
+				   struct object_id *compat_oid)
 {
+	struct repository *repo = the_repository;
+	const struct git_hash_algo *algo = repo->hash_algo;
+	const struct git_hash_algo *compat = repo->compat_hash_algo;
 	int ret;
 
 	ret = git_deflate_end_gently(stream);
 	if (ret != Z_OK)
 		return ret;
-	the_hash_algo->final_oid_fn(oid, c);
+	algo->final_oid_fn(oid, c);
+	if (compat && compat_c)
+		compat->final_oid_fn(compat_oid, compat_c);
 
 	return Z_OK;
 }
@@ -2031,7 +2151,7 @@
 
 	fd = start_loose_object_common(&tmp_file, filename.buf, flags,
 				       &stream, compressed, sizeof(compressed),
-				       &c, hdr, hdrlen);
+				       &c, NULL, hdr, hdrlen);
 	if (fd < 0)
 		return -1;
 
@@ -2041,14 +2161,14 @@
 	do {
 		unsigned char *in0 = stream.next_in;
 
-		ret = write_loose_object_common(&c, &stream, 1, in0, fd,
+		ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd,
 						compressed, sizeof(compressed));
 	} while (ret == Z_OK);
 
 	if (ret != Z_STREAM_END)
 		die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
 		    ret);
-	ret = end_loose_object_common(&c, &stream, &parano_oid);
+	ret = end_loose_object_common(&c, NULL, &stream, &parano_oid, NULL);
 	if (ret != Z_OK)
 		die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
 		    ret);
@@ -2093,10 +2213,12 @@
 int stream_loose_object(struct input_stream *in_stream, size_t len,
 			struct object_id *oid)
 {
+	const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+	struct object_id compat_oid;
 	int fd, ret, err = 0, flush = 0;
 	unsigned char compressed[4096];
 	git_zstream stream;
-	git_hash_ctx c;
+	git_hash_ctx c, compat_c;
 	struct strbuf tmp_file = STRBUF_INIT;
 	struct strbuf filename = STRBUF_INIT;
 	int dirlen;
@@ -2120,7 +2242,7 @@
 	 */
 	fd = start_loose_object_common(&tmp_file, filename.buf, 0,
 				       &stream, compressed, sizeof(compressed),
-				       &c, hdr, hdrlen);
+				       &c, &compat_c, hdr, hdrlen);
 	if (fd < 0) {
 		err = -1;
 		goto cleanup;
@@ -2138,7 +2260,7 @@
 			if (in_stream->is_finished)
 				flush = 1;
 		}
-		ret = write_loose_object_common(&c, &stream, flush, in0, fd,
+		ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd,
 						compressed, sizeof(compressed));
 		/*
 		 * Unlike write_loose_object(), we do not have the entire
@@ -2161,7 +2283,7 @@
 	 */
 	if (ret != Z_STREAM_END)
 		die(_("unable to stream deflate new object (%d)"), ret);
-	ret = end_loose_object_common(&c, &stream, oid);
+	ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid);
 	if (ret != Z_OK)
 		die(_("deflateEnd on stream object failed (%d)"), ret);
 	close_loose_object(fd, tmp_file.buf);
@@ -2188,6 +2310,8 @@
 	}
 
 	err = finalize_object_file(tmp_file.buf, filename.buf);
+	if (!err && compat)
+		err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
 cleanup:
 	strbuf_release(&tmp_file);
 	strbuf_release(&filename);
@@ -2196,19 +2320,42 @@
 
 int write_object_file_flags(const void *buf, unsigned long len,
 			    enum object_type type, struct object_id *oid,
-			    unsigned flags)
+			    struct object_id *compat_oid_in, unsigned flags)
 {
+	struct repository *repo = the_repository;
+	const struct git_hash_algo *algo = repo->hash_algo;
+	const struct git_hash_algo *compat = repo->compat_hash_algo;
+	struct object_id compat_oid;
 	char hdr[MAX_HEADER_LEN];
 	int hdrlen = sizeof(hdr);
 
+	/* Generate compat_oid */
+	if (compat) {
+		if (compat_oid_in)
+			oidcpy(&compat_oid, compat_oid_in);
+		else if (type == OBJ_BLOB)
+			hash_object_file(compat, buf, len, type, &compat_oid);
+		else {
+			struct strbuf converted = STRBUF_INIT;
+			convert_object_file(&converted, algo, compat,
+					    buf, len, type, 0);
+			hash_object_file(compat, converted.buf, converted.len,
+					 type, &compat_oid);
+			strbuf_release(&converted);
+		}
+	}
+
 	/* Normally if we have it in the pack then we do not bother writing
 	 * it out into .git/objects/??/?{38} file.
 	 */
-	write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr,
-				  &hdrlen);
+	write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
 	if (freshen_packed_object(oid) || freshen_loose_object(oid))
 		return 0;
-	return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
+	if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags))
+		return -1;
+	if (compat)
+		return repo_add_loose_object_map(repo, oid, &compat_oid);
+	return 0;
 }
 
 int write_object_file_literally(const void *buf, unsigned long len,
@@ -2216,7 +2363,27 @@
 				unsigned flags)
 {
 	char *header;
+	struct repository *repo = the_repository;
+	const struct git_hash_algo *algo = repo->hash_algo;
+	const struct git_hash_algo *compat = repo->compat_hash_algo;
+	struct object_id compat_oid;
 	int hdrlen, status = 0;
+	int compat_type = -1;
+
+	if (compat) {
+		compat_type = type_from_string_gently(type, -1, 1);
+		if (compat_type == OBJ_BLOB)
+			hash_object_file(compat, buf, len, compat_type,
+					 &compat_oid);
+		else if (compat_type != -1) {
+			struct strbuf converted = STRBUF_INIT;
+			convert_object_file(&converted, algo, compat,
+					    buf, len, compat_type, 0);
+			hash_object_file(compat, converted.buf, converted.len,
+					 compat_type, &compat_oid);
+			strbuf_release(&converted);
+		}
+	}
 
 	/* type string, SP, %lu of the length plus NUL must fit this */
 	hdrlen = strlen(type) + MAX_HEADER_LEN;
@@ -2229,6 +2396,8 @@
 	if (freshen_packed_object(oid) || freshen_loose_object(oid))
 		goto cleanup;
 	status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
+	if (compat_type != -1)
+		return repo_add_loose_object_map(repo, oid, &compat_oid);
 
 cleanup:
 	free(header);
@@ -2237,9 +2406,12 @@
 
 int force_object_loose(const struct object_id *oid, time_t mtime)
 {
+	struct repository *repo = the_repository;
+	const struct git_hash_algo *compat = repo->compat_hash_algo;
 	void *buf;
 	unsigned long len;
 	struct object_info oi = OBJECT_INFO_INIT;
+	struct object_id compat_oid;
 	enum object_type type;
 	char hdr[MAX_HEADER_LEN];
 	int hdrlen;
@@ -2252,8 +2424,15 @@
 	oi.contentp = &buf;
 	if (oid_object_info_extended(the_repository, oid, &oi, 0))
 		return error(_("cannot read object for %s"), oid_to_hex(oid));
+	if (compat) {
+		if (repo_oid_to_algop(repo, oid, compat, &compat_oid))
+			return error(_("cannot map object %s to %s"),
+				     oid_to_hex(oid), compat->name);
+	}
 	hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
 	ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
+	if (!ret && compat)
+		ret = repo_add_loose_object_map(the_repository, oid, &compat_oid);
 	free(buf);
 
 	return ret;
@@ -2291,11 +2470,11 @@
  * report the minimal fsck error here, and rely on the caller to
  * give more context.
  */
-static int hash_format_check_report(struct fsck_options *opts,
-				     const struct object_id *oid,
-				     enum object_type object_type,
-				     enum fsck_msg_type msg_type,
-				     enum fsck_msg_id msg_id,
+static int hash_format_check_report(struct fsck_options *opts UNUSED,
+				     const struct object_id *oid UNUSED,
+				     enum object_type object_type UNUSED,
+				     enum fsck_msg_type msg_type UNUSED,
+				     enum fsck_msg_id msg_id UNUSED,
 				     const char *message)
 {
 	error(_("object fails fsck: %s"), message);
@@ -2431,11 +2610,11 @@
  * 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_stream(struct object_id *oid, int fd, size_t size,
-			enum object_type type, const char *path,
-			unsigned flags)
+static int index_blob_stream(struct object_id *oid, int fd, size_t size,
+			     const char *path,
+			     unsigned flags)
 {
-	return index_bulk_checkin(oid, fd, size, type, path, flags);
+	return index_blob_bulk_checkin(oid, fd, size, path, flags);
 }
 
 int index_fd(struct index_state *istate, struct object_id *oid,
@@ -2457,8 +2636,8 @@
 		ret = index_core(istate, oid, fd, xsize_t(st->st_size),
 				 type, path, flags);
 	else
-		ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
-				   flags);
+		ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,
+					flags);
 	close(fd);
 	return ret;
 }
@@ -2644,7 +2823,8 @@
 	return 0;
 }
 
-static int append_loose_object(const struct object_id *oid, const char *path,
+static int append_loose_object(const struct object_id *oid,
+			       const char *path UNUSED,
 			       void *data)
 {
 	oidtree_insert(data, oid);
diff --git a/object-file.h b/object-file.h
new file mode 100644
index 0000000..d641461
--- /dev/null
+++ b/object-file.h
@@ -0,0 +1,131 @@
+#ifndef OBJECT_FILE_H
+#define OBJECT_FILE_H
+
+#include "git-zlib.h"
+#include "object.h"
+
+struct index_state;
+
+/*
+ * Set this to 0 to prevent oid_object_info_extended() from fetching missing
+ * blobs. This has a difference only if extensions.partialClone is set.
+ *
+ * Its default value is 1.
+ */
+extern int fetch_if_missing;
+
+#define HASH_WRITE_OBJECT 1
+#define HASH_FORMAT_CHECK 2
+#define HASH_RENORMALIZE  4
+#define HASH_SILENT 8
+int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
+
+/*
+ * Create the directory containing the named path, using care to be
+ * somewhat safe against races. Return one of the scld_error values to
+ * indicate success/failure. On error, set errno to describe the
+ * problem.
+ *
+ * SCLD_VANISHED indicates that one of the ancestor directories of the
+ * path existed at one point during the function call and then
+ * suddenly vanished, probably because another process pruned the
+ * directory while we were working.  To be robust against this kind of
+ * race, callers might want to try invoking the function again when it
+ * returns SCLD_VANISHED.
+ *
+ * safe_create_leading_directories() temporarily changes path while it
+ * is working but restores it before returning.
+ * safe_create_leading_directories_const() doesn't modify path, even
+ * temporarily. Both these variants adjust the permissions of the
+ * created directories to honor core.sharedRepository, so they are best
+ * suited for files inside the git dir. For working tree files, use
+ * safe_create_leading_directories_no_share() instead, as it ignores
+ * the core.sharedRepository setting.
+ */
+enum scld_error {
+	SCLD_OK = 0,
+	SCLD_FAILED = -1,
+	SCLD_PERMS = -2,
+	SCLD_EXISTS = -3,
+	SCLD_VANISHED = -4
+};
+enum scld_error safe_create_leading_directories(char *path);
+enum scld_error safe_create_leading_directories_const(const char *path);
+enum scld_error safe_create_leading_directories_no_share(char *path);
+
+int mkdir_in_gitdir(const char *path);
+
+int git_open_cloexec(const char *name, int flags);
+#define git_open(name) git_open_cloexec(name, O_RDONLY)
+
+/**
+ * unpack_loose_header() initializes the data stream needed to unpack
+ * a loose object header.
+ *
+ * Returns:
+ *
+ * - ULHR_OK on success
+ * - ULHR_BAD on error
+ * - ULHR_TOO_LONG if the header was too long
+ *
+ * It will only parse up to MAX_HEADER_LEN bytes unless an optional
+ * "hdrbuf" argument is non-NULL. This is intended for use with
+ * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
+ * reporting. The full header will be extracted to "hdrbuf" for use
+ * with parse_loose_header(), ULHR_TOO_LONG will still be returned
+ * from this function to indicate that the header was too long.
+ */
+enum unpack_loose_header_result {
+	ULHR_OK,
+	ULHR_BAD,
+	ULHR_TOO_LONG,
+};
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+						    unsigned char *map,
+						    unsigned long mapsize,
+						    void *buffer,
+						    unsigned long bufsiz,
+						    struct strbuf *hdrbuf);
+
+/**
+ * parse_loose_header() parses the starting "<type> <len>\0" of an
+ * object. If it doesn't follow that format -1 is returned. To check
+ * the validity of the <type> populate the "typep" in the "struct
+ * object_info". It will be OBJ_BAD if the object type is unknown. The
+ * parsed <len> can be retrieved via "oi->sizep", and from there
+ * passed to unpack_loose_rest().
+ */
+struct object_info;
+int parse_loose_header(const char *hdr, struct object_info *oi);
+
+/**
+ * With in-core object data in "buf", rehash it to make sure the
+ * object name actually matches "oid" to detect object corruption.
+ *
+ * A negative value indicates an error, usually that the OID is not
+ * what we expected, but it might also indicate another error.
+ */
+int check_object_signature(struct repository *r, const struct object_id *oid,
+			   void *map, unsigned long size,
+			   enum object_type type);
+
+/**
+ * A streaming version of check_object_signature().
+ * Try reading the object named with "oid" using
+ * the streaming interface and rehash it to do the same.
+ */
+int stream_object_signature(struct repository *r, const struct object_id *oid);
+
+int finalize_object_file(const char *tmpfile, const char *filename);
+
+/* Helper to check and "touch" a file */
+int check_and_freshen_file(const char *fn, int freshen);
+
+void *read_object_with_reference(struct repository *r,
+				 const struct object_id *oid,
+				 enum object_type required_type,
+				 unsigned long *size,
+				 struct object_id *oid_ret);
+
+#endif /* OBJECT_FILE_H */
diff --git a/object-name.c b/object-name.c
index 2dd1a0f..523af6f 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1,21 +1,29 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "object-name.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tag.h"
 #include "commit.h"
 #include "tree.h"
-#include "blob.h"
 #include "tree-walk.h"
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
 #include "oid-array.h"
+#include "oidtree.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "pretty.h"
+#include "object-store-ll.h"
+#include "read-cache-ll.h"
 #include "repository.h"
-#include "submodule.h"
+#include "setup.h"
 #include "midx.h"
 #include "commit-reach.h"
 #include "date.h"
+#include "object-file-convert.h"
 
 static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
 
@@ -40,6 +48,7 @@
 
 static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
+	/* 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;
@@ -125,6 +134,8 @@
 {
 	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;
 	num = m->num_objects;
 
 	if (!num)
@@ -140,7 +151,7 @@
 	for (i = first; i < num && !ds->ambiguous; i++) {
 		struct object_id oid;
 		current = nth_midxed_object_oid(&oid, m, i);
-		if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash))
+		if (!match_hash(len, ds->bin_pfx.hash, current->hash))
 			break;
 		update_candidates(ds, current);
 	}
@@ -150,6 +161,8 @@
 			   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;
@@ -168,7 +181,7 @@
 	for (i = first; i < num && !ds->ambiguous; i++) {
 		struct object_id oid;
 		nth_packed_object_id(&oid, p, i);
-		if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash))
+		if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
 			break;
 		update_candidates(ds, &oid);
 	}
@@ -179,6 +192,10 @@
 	struct multi_pack_index *m;
 	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;
+
 	for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
 	     m = m->next)
 		unique_in_midx(m, ds);
@@ -223,7 +240,7 @@
 
 static int disambiguate_commit_only(struct repository *r,
 				    const struct object_id *oid,
-				    void *cb_data_unused)
+				    void *cb_data UNUSED)
 {
 	int kind = oid_object_info(r, oid, NULL);
 	return kind == OBJ_COMMIT;
@@ -231,7 +248,7 @@
 
 static int disambiguate_committish_only(struct repository *r,
 					const struct object_id *oid,
-					void *cb_data_unused)
+					void *cb_data UNUSED)
 {
 	struct object *obj;
 	int kind;
@@ -251,7 +268,7 @@
 
 static int disambiguate_tree_only(struct repository *r,
 				  const struct object_id *oid,
-				  void *cb_data_unused)
+				  void *cb_data UNUSED)
 {
 	int kind = oid_object_info(r, oid, NULL);
 	return kind == OBJ_TREE;
@@ -259,7 +276,7 @@
 
 static int disambiguate_treeish_only(struct repository *r,
 				     const struct object_id *oid,
-				     void *cb_data_unused)
+				     void *cb_data UNUSED)
 {
 	struct object *obj;
 	int kind;
@@ -279,7 +296,7 @@
 
 static int disambiguate_blob_only(struct repository *r,
 				  const struct object_id *oid,
-				  void *cb_data_unused)
+				  void *cb_data UNUSED)
 {
 	int kind = oid_object_info(r, oid, NULL);
 	return kind == OBJ_BLOB;
@@ -317,11 +334,12 @@
 
 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 > the_hash_algo->hexsz)
+	if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
 		return -1;
 
 	memset(ds, 0, sizeof(*ds));
@@ -348,6 +366,7 @@
 	ds->len = len;
 	ds->hex_pfx[len] = '\0';
 	ds->repo = r;
+	ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
 	prepare_alt_odb(r);
 	return 0;
 }
@@ -394,8 +413,10 @@
 		if (commit) {
 			struct pretty_print_context pp = {0};
 			pp.date_mode.type = DATE_SHORT;
-			format_commit_message(commit, "%ad", &date, &pp);
-			format_commit_message(commit, "%s", &msg, &pp);
+			repo_format_commit_message(the_repository, commit,
+						   "%ad", &date, &pp);
+			repo_format_commit_message(the_repository, commit,
+						   "%s", &msg, &pp);
 		}
 
 		/*
@@ -473,16 +494,17 @@
 	return 0;
 }
 
-static int repo_collect_ambiguous(struct repository *r,
+static int repo_collect_ambiguous(struct repository *r UNUSED,
 				  const struct object_id *oid,
 				  void *data)
 {
 	return collect_ambiguous(oid, data);
 }
 
-static int sort_ambiguous(const void *a, const void *b, void *ctx)
+static int sort_ambiguous(const void *va, const void *vb, void *ctx)
 {
 	struct repository *sort_ambiguous_repo = ctx;
+	const struct object_id *a = va, *b = vb;
 	int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
 	int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
 	int a_type_sort;
@@ -492,8 +514,12 @@
 	 * Sorts by hash within the same object type, just as
 	 * oid_array_for_each_unique() would do.
 	 */
-	if (a_type == b_type)
-		return oidcmp(a, b);
+	if (a_type == b_type) {
+		if (a->algo == b->algo)
+			return oidcmp(a, b);
+		else
+			return a->algo > b->algo ? 1 : -1;
+	}
 
 	/*
 	 * Between object types show tags, then commits, and finally
@@ -522,8 +548,12 @@
 	int status;
 	struct disambiguate_state ds;
 	int quietly = !!(flags & GET_OID_QUIETLY);
+	const struct git_hash_algo *algo = r->hash_algo;
 
-	if (init_object_disambiguation(r, name, len, &ds) < 0)
+	if (flags & GET_OID_HASH_ANY)
+		algo = NULL;
+
+	if (init_object_disambiguation(r, name, len, algo, &ds) < 0)
 		return -1;
 
 	if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
@@ -577,7 +607,7 @@
 		if (!ds.ambiguous)
 			ds.fn = NULL;
 
-		repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+		repo_for_each_abbrev(r, ds.hex_pfx, algo, collect_ambiguous, &collect);
 		sort_ambiguous_oid_array(r, &collect);
 
 		if (oid_array_for_each(&collect, show_ambiguous_object, &out))
@@ -599,13 +629,14 @@
 }
 
 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 oid_array collect = OID_ARRAY_INIT;
 	struct disambiguate_state ds;
 	int ret;
 
-	if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
+	if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
 		return -1;
 
 	ds.always_call_fn = 1;
@@ -665,7 +696,7 @@
 	return 0;
 }
 
-static int repo_extend_abbrev_len(struct repository *r,
+static int repo_extend_abbrev_len(struct repository *r UNUSED,
 				  const struct object_id *oid,
 				  void *cb_data)
 {
@@ -758,13 +789,30 @@
 		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)
+{
+	int r;
+	strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
+	r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
+	strbuf_setlen(sb, sb->len + r);
+}
+
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+			      int abbrev_len)
+{
+	strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
+}
+
 int repo_find_unique_abbrev_r(struct repository *r, char *hex,
 			      const struct object_id *oid, int 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 = r->hash_algo->hexsz;
+	const unsigned hexsz = algo->hexsz;
 
 	if (len < 0) {
 		unsigned long count = repo_approximate_object_count(r);
@@ -800,7 +848,7 @@
 
 	find_abbrev_len_packed(&mad);
 
-	if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
+	if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
 		return -1;
 
 	ds.fn = repo_extend_abbrev_len;
@@ -898,6 +946,7 @@
 	char *real_ref = NULL;
 	int refs_found = 0;
 	int at, reflog_len, nth_prior = 0;
+	int fatal = !(flags & GET_OID_QUIETLY);
 
 	if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
 		if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
@@ -952,11 +1001,11 @@
 
 	if (!len && reflog_len)
 		/* allow "@{...}" to mean the current branch reflog */
-		refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, 0);
+		refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, !fatal);
 	else if (reflog_len)
 		refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
 	else
-		refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, 0);
+		refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, !fatal);
 
 	if (!refs_found)
 		return -1;
@@ -1009,6 +1058,15 @@
 						len, str,
 						show_date(co_time, co_tz, DATE_MODE(RFC2822)));
 				}
+			} else if (nth == co_cnt && !is_null_oid(oid)) {
+				/*
+				 * We were asked for the Nth reflog (counting
+				 * from 0), but there were only N entries.
+				 * read_ref_at() will have returned "1" to tell
+				 * us it did not find an entry, but it did
+				 * still fill in the oid with the "old" value,
+				 * which we can use.
+				 */
 			} else {
 				if (flags & GET_OID_QUIETLY) {
 					exit(128);
@@ -1036,7 +1094,7 @@
 	if (ret)
 		return ret;
 	commit = lookup_commit_reference(r, &oid);
-	if (parse_commit(commit))
+	if (repo_parse_commit(r, commit))
 		return MISSING_OBJECT;
 	if (!idx) {
 		oidcpy(result, &commit->object.oid);
@@ -1070,7 +1128,7 @@
 		return MISSING_OBJECT;
 
 	while (generation--) {
-		if (parse_commit(commit) || !commit->parents)
+		if (repo_parse_commit(r, commit) || !commit->parents)
 			return MISSING_OBJECT;
 		commit = commit->parents->item;
 	}
@@ -1361,10 +1419,10 @@
 		commit = pop_most_recent_commit(&list, ONELINE_SEEN);
 		if (!parse_object(r, &commit->object.oid))
 			continue;
-		buf = get_commit_buffer(commit, NULL);
+		buf = repo_get_commit_buffer(r, commit, NULL);
 		p = strstr(buf, "\n\n");
 		matches = negative ^ (p && !regexec(&regex, p + 2, 0, NULL, 0));
-		unuse_commit_buffer(commit, buf);
+		repo_unuse_commit_buffer(r, commit, buf);
 
 		if (matches) {
 			oidcpy(oid, &commit->object.oid);
@@ -1454,7 +1512,7 @@
 		    struct object_id *oid)
 {
 	struct commit *one, *two;
-	struct commit_list *mbs;
+	struct commit_list *mbs = NULL;
 	struct object_id oid_tmp;
 	const char *dots;
 	int st;
@@ -1482,7 +1540,10 @@
 	two = lookup_commit_reference_gently(r, &oid_tmp, 0);
 	if (!two)
 		return -1;
-	mbs = repo_get_merge_bases(r, one, two);
+	if (repo_get_merge_bases(r, one, two, &mbs) < 0) {
+		free_commit_list(mbs);
+		return -1;
+	}
 	if (!mbs || mbs->next)
 		st = -1;
 	else {
@@ -1666,7 +1727,8 @@
 	struct interpret_branch_name_options options = {
 		.allowed = allowed
 	};
-	int used = interpret_branch_name(name, len, sb, &options);
+	int used = repo_interpret_branch_name(the_repository, name, len, sb,
+					      &options);
 
 	if (used < 0)
 		used = 0;
@@ -1719,7 +1781,7 @@
 	strbuf_vaddf(&sb, fmt, ap);
 	va_end(ap);
 
-	ret = get_oid(sb.buf, oid);
+	ret = repo_get_oid(the_repository, sb.buf, oid);
 	strbuf_release(&sb);
 
 	return ret;
diff --git a/object-name.h b/object-name.h
new file mode 100644
index 0000000..064ddc9
--- /dev/null
+++ b/object-name.h
@@ -0,0 +1,131 @@
+#ifndef OBJECT_NAME_H
+#define OBJECT_NAME_H
+
+#include "object.h"
+#include "strbuf.h"
+
+struct object_id;
+struct repository;
+
+struct object_context {
+	unsigned short mode;
+	/*
+	 * symlink_path is only used by get_tree_entry_follow_symlinks,
+	 * and only for symlinks that point outside the repository.
+	 */
+	struct strbuf symlink_path;
+	/*
+	 * If GET_OID_RECORD_PATH is set, this will record path (if any)
+	 * found when resolving the name. The caller is responsible for
+	 * releasing the memory.
+	 */
+	char *path;
+};
+
+/*
+ * Return an abbreviated sha1 unique within this repository's object database.
+ * The result will be at least `len` characters long, and will be NUL
+ * terminated.
+ *
+ * The non-`_r` version returns a static buffer which remains valid until 4
+ * more calls to repo_find_unique_abbrev are made.
+ *
+ * The `_r` variant writes to a buffer supplied by the caller, which must be at
+ * least `GIT_MAX_HEXSZ + 1` bytes. The return value is the number of bytes
+ * written (excluding the NUL terminator).
+ *
+ * Note that while this version avoids the static buffer, it is not fully
+ * reentrant, as it calls into other non-reentrant git code.
+ */
+const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
+int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
+
+/**
+ * Add the abbreviation, as generated by repo_find_unique_abbrev(), of `sha1` to
+ * the strbuf `sb`.
+ */
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+				   const struct object_id *oid, int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+			      int abbrev_len);
+
+int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
+__attribute__((format (printf, 2, 3)))
+int get_oidf(struct object_id *oid, const char *fmt, ...);
+int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid);
+void maybe_die_on_misspelt_object_name(struct repository *repo,
+				       const char *name,
+				       const char *prefix);
+enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
+					 unsigned flags, struct object_id *oid,
+					 struct object_context *oc);
+
+
+typedef int each_abbrev_fn(const struct object_id *oid, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix,
+			 const struct git_hash_algo *algo, each_abbrev_fn, void *);
+
+int set_disambiguate_hint_config(const char *var, const char *value);
+
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ *   places the name of the branch in the given buf and returns the
+ *   number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ *   <branch> is configured to merge with (missing <branch> defaults
+ *   to the current branch), and places the name of the branch in the
+ *   given buf and returns the number of characters parsed if
+ *   successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ */
+#define INTERPRET_BRANCH_LOCAL (1<<0)
+#define INTERPRET_BRANCH_REMOTE (1<<1)
+#define INTERPRET_BRANCH_HEAD (1<<2)
+struct interpret_branch_name_options {
+	/*
+	 * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+	 * expansions: local branches ("refs/heads/"), remote branches
+	 * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+	 * allowed, even ones to refs outside of those namespaces.
+	 */
+	unsigned allowed;
+
+	/*
+	 * If ^{upstream} or ^{push} (or equivalent) is requested, and the
+	 * branch in question does not have such a reference, return -1 instead
+	 * of die()-ing.
+	 */
+	unsigned nonfatal_dangling_mark : 1;
+};
+int repo_interpret_branch_name(struct repository *r,
+			       const char *str, int len,
+			       struct strbuf *buf,
+			       const struct interpret_branch_name_options *options);
+
+struct object *repo_peel_to_type(struct repository *r,
+				 const char *name, int namelen,
+				 struct object *o, enum object_type);
+
+/* Convert to/from hex/sha1 representation */
+#define MINIMUM_ABBREV minimum_abbrev
+#define DEFAULT_ABBREV default_abbrev
+
+/* used when the code does not know or care what the default abbrev is */
+#define FALLBACK_DEFAULT_ABBREV 7
+
+#endif /* OBJECT_NAME_H */
diff --git a/object-store-ll.h b/object-store-ll.h
new file mode 100644
index 0000000..c5f2bb2
--- /dev/null
+++ b/object-store-ll.h
@@ -0,0 +1,536 @@
+#ifndef OBJECT_STORE_LL_H
+#define OBJECT_STORE_LL_H
+
+#include "hashmap.h"
+#include "object.h"
+#include "list.h"
+#include "thread-utils.h"
+#include "oidset.h"
+
+struct oidmap;
+struct oidtree;
+struct strbuf;
+
+struct object_directory {
+	struct object_directory *next;
+
+	/*
+	 * Used to store the results of readdir(3) calls when we are OK
+	 * sacrificing accuracy due to races for speed. That includes
+	 * object existence with OBJECT_INFO_QUICK, as well as
+	 * our search for unique abbreviated hashes. Don't use it for tasks
+	 * requiring greater accuracy!
+	 *
+	 * Be sure to call odb_load_loose_cache() before using.
+	 */
+	uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
+	struct oidtree *loose_objects_cache;
+
+	/* Map between object IDs for loose objects. */
+	struct loose_object_map *loose_map;
+
+	/*
+	 * This is a temporary object store created by the tmp_objdir
+	 * facility. Disable ref updates since the objects in the store
+	 * might be discarded on rollback.
+	 */
+	int disable_ref_updates;
+
+	/*
+	 * This object store is ephemeral, so there is no need to fsync.
+	 */
+	int will_destroy;
+
+	/*
+	 * Path to the alternative object store. If this is a relative path,
+	 * it is relative to the current working directory.
+	 */
+	char *path;
+};
+
+struct input_stream {
+	const void *(*read)(struct input_stream *, unsigned long *len);
+	void *data;
+	int is_finished;
+};
+
+void prepare_alt_odb(struct repository *r);
+int has_alt_odb(struct repository *r);
+char *compute_alternate_path(const char *path, struct strbuf *err);
+struct object_directory *find_odb(struct repository *r, const char *obj_dir);
+typedef int alt_odb_fn(struct object_directory *, void *);
+int foreach_alt_odb(alt_odb_fn, void*);
+typedef void alternate_ref_fn(const struct object_id *oid, void *);
+void for_each_alternate_ref(alternate_ref_fn, void *);
+
+/*
+ * Add the directory to the on-disk alternates file; the new entry will also
+ * take effect in the current process.
+ */
+void add_to_alternates_file(const char *dir);
+
+/*
+ * Add the directory to the in-memory list of alternates (along with any
+ * recursive alternates it points to), but do not modify the on-disk alternates
+ * file.
+ */
+void add_to_alternates_memory(const char *dir);
+
+/*
+ * Replace the current writable object directory with the specified temporary
+ * object directory; returns the former primary object directory.
+ */
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
+
+/*
+ * Restore a previous ODB replaced by set_temporary_main_odb.
+ */
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
+
+/*
+ * Populate and return the loose object cache array corresponding to the
+ * given object ID.
+ */
+struct oidtree *odb_loose_cache(struct object_directory *odb,
+				  const struct object_id *oid);
+
+/* Empty the loose object cache for the specified object directory. */
+void odb_clear_loose_cache(struct object_directory *odb);
+
+/* Clear and free the specified object directory */
+void free_object_directory(struct object_directory *odb);
+
+struct packed_git {
+	struct hashmap_entry packmap_ent;
+	struct packed_git *next;
+	struct list_head mru;
+	struct pack_window *windows;
+	off_t pack_size;
+	const void *index_data;
+	size_t index_size;
+	uint32_t num_objects;
+	size_t crc_offset;
+	struct oidset bad_objects;
+	int index_version;
+	time_t mtime;
+	int pack_fd;
+	int index;              /* for builtin/pack-objects.c */
+	unsigned pack_local:1,
+		 pack_keep:1,
+		 pack_keep_in_core:1,
+		 freshened:1,
+		 do_not_close:1,
+		 pack_promisor:1,
+		 multi_pack_index:1,
+		 is_cruft:1;
+	unsigned char hash[GIT_MAX_RAWSZ];
+	struct revindex_entry *revindex;
+	const uint32_t *revindex_data;
+	const uint32_t *revindex_map;
+	size_t revindex_size;
+	/*
+	 * mtimes_map points at the beginning of the memory mapped region of
+	 * this pack's corresponding .mtimes file, and mtimes_size is the size
+	 * of that .mtimes file
+	 */
+	const uint32_t *mtimes_map;
+	size_t mtimes_size;
+	/* something like ".git/objects/pack/xxxxx.pack" */
+	char pack_name[FLEX_ARRAY]; /* more */
+};
+
+struct multi_pack_index;
+
+static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,
+				     const struct hashmap_entry *entry,
+				     const struct hashmap_entry *entry2,
+				     const void *keydata)
+{
+	const char *key = keydata;
+	const struct packed_git *pg1, *pg2;
+
+	pg1 = container_of(entry, const struct packed_git, packmap_ent);
+	pg2 = container_of(entry2, const struct packed_git, packmap_ent);
+
+	return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
+}
+
+struct raw_object_store {
+	/*
+	 * Set of all object directories; the main directory is first (and
+	 * cannot be NULL after initialization). Subsequent directories are
+	 * alternates.
+	 */
+	struct object_directory *odb;
+	struct object_directory **odb_tail;
+	struct kh_odb_path_map *odb_by_path;
+
+	int loaded_alternates;
+
+	/*
+	 * A list of alternate object directories loaded from the environment;
+	 * this should not generally need to be accessed directly, but will
+	 * populate the "odb" list when prepare_alt_odb() is run.
+	 */
+	char *alternate_db;
+
+	/*
+	 * Objects that should be substituted by other objects
+	 * (see git-replace(1)).
+	 */
+	struct oidmap *replace_map;
+	unsigned replace_map_initialized : 1;
+	pthread_mutex_t replace_mutex; /* protect object replace functions */
+
+	struct commit_graph *commit_graph;
+	unsigned commit_graph_attempted : 1; /* if loading has been attempted */
+
+	/*
+	 * private data
+	 *
+	 * should only be accessed directly by packfile.c and midx.c
+	 */
+	struct multi_pack_index *multi_pack_index;
+
+	/*
+	 * private data
+	 *
+	 * should only be accessed directly by packfile.c
+	 */
+
+	struct packed_git *packed_git;
+	/* A most-recently-used ordered version of the packed_git list. */
+	struct list_head packed_git_mru;
+
+	struct {
+		struct packed_git **packs;
+		unsigned flags;
+	} kept_pack_cache;
+
+	/*
+	 * A map of packfiles to packed_git structs for tracking which
+	 * packs have been loaded already.
+	 */
+	struct hashmap pack_map;
+
+	/*
+	 * A fast, rough count of the number of objects in the repository.
+	 * These two fields are not meant for direct access. Use
+	 * repo_approximate_object_count() instead.
+	 */
+	unsigned long approximate_object_count;
+	unsigned approximate_object_count_valid : 1;
+
+	/*
+	 * Whether packed_git has already been populated with this repository's
+	 * packs.
+	 */
+	unsigned packed_git_initialized : 1;
+};
+
+struct raw_object_store *raw_object_store_new(void);
+void raw_object_store_clear(struct raw_object_store *o);
+
+/*
+ * 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.
+ */
+const char *loose_object_path(struct repository *r, struct strbuf *buf,
+			      const struct object_id *oid);
+
+void *map_loose_object(struct repository *r, const struct object_id *oid,
+		       unsigned long *size);
+
+void *repo_read_object_file(struct repository *r,
+			    const struct object_id *oid,
+			    enum object_type *type,
+			    unsigned long *size);
+
+/* Read and unpack an object file into memory, write memory to an object file */
+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
+
+void hash_object_file(const struct git_hash_algo *algo, const void *buf,
+		      unsigned long len, enum object_type type,
+		      struct object_id *oid);
+
+int write_object_file_flags(const void *buf, unsigned long len,
+			    enum object_type type, struct object_id *oid,
+			    struct object_id *comapt_oid_in, unsigned flags);
+static inline int write_object_file(const void *buf, unsigned long len,
+				    enum object_type type, struct object_id *oid)
+{
+	return write_object_file_flags(buf, len, type, oid, NULL, 0);
+}
+
+int write_object_file_literally(const void *buf, unsigned long len,
+				const char *type, struct object_id *oid,
+				unsigned flags);
+int stream_loose_object(struct input_stream *in_stream, size_t len,
+			struct object_id *oid);
+
+/*
+ * Add an object file to the in-memory object store, without writing it
+ * to disk.
+ *
+ * Callers are responsible for calling write_object_file to record the
+ * object in persistent storage before writing any other new objects
+ * that reference it.
+ */
+int pretend_object_file(void *, unsigned long, enum object_type,
+			struct object_id *oid);
+
+int force_object_loose(const struct object_id *oid, time_t mtime);
+
+struct object_info {
+	/* Request */
+	enum object_type *typep;
+	unsigned long *sizep;
+	off_t *disk_sizep;
+	struct object_id *delta_base_oid;
+	struct strbuf *type_name;
+	void **contentp;
+
+	/* Response */
+	enum {
+		OI_CACHED,
+		OI_LOOSE,
+		OI_PACKED,
+		OI_DBCACHED
+	} whence;
+	union {
+		/*
+		 * struct {
+		 * 	... Nothing to expose in this case
+		 * } cached;
+		 * struct {
+		 * 	... Nothing to expose in this case
+		 * } loose;
+		 */
+		struct {
+			struct packed_git *pack;
+			off_t offset;
+			unsigned int is_delta;
+		} packed;
+	} u;
+};
+
+/*
+ * Initializer for a "struct object_info" that wants no items. You may
+ * also memset() the memory to all-zeroes.
+ */
+#define OBJECT_INFO_INIT { 0 }
+
+/* Invoke lookup_replace_object() on the given hash */
+#define OBJECT_INFO_LOOKUP_REPLACE 1
+/* Allow reading from a loose object file of unknown/bogus type */
+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
+/* Do not retry packed storage after checking packed and loose storage */
+#define OBJECT_INFO_QUICK 8
+/*
+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is
+ * nonzero).
+ */
+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16
+/*
+ * This is meant for bulk prefetching of missing blobs in a partial
+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
+ */
+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
+
+/* Die if object corruption (not just an object being missing) was detected. */
+#define OBJECT_INFO_DIE_IF_CORRUPT 32
+
+int oid_object_info_extended(struct repository *r,
+			     const struct object_id *,
+			     struct object_info *, unsigned flags);
+
+/*
+ * Open the loose object at path, check its hash, and return the contents,
+ * use the "oi" argument to assert things about the object, or e.g. populate its
+ * type, and size. If the object is a blob, then "contents" may return NULL,
+ * to allow streaming of large blobs.
+ *
+ * Returns 0 on success, negative on error (details may be written to stderr).
+ */
+int read_loose_object(const char *path,
+		      const struct object_id *expected_oid,
+		      struct object_id *real_oid,
+		      void **contents,
+		      struct object_info *oi);
+
+/* Retry packed storage after checking packed and loose storage */
+#define HAS_OBJECT_RECHECK_PACKED 1
+
+/*
+ * Returns 1 if the object exists. This function will not lazily fetch objects
+ * in a partial clone.
+ */
+int has_object(struct repository *r, const struct object_id *oid,
+	       unsigned flags);
+
+/*
+ * These macros and functions are deprecated. If checking existence for an
+ * object that is likely to be missing and/or whose absence is relatively
+ * inconsequential (or is consequential but the caller is prepared to handle
+ * it), use has_object(), which has better defaults (no lazy fetch in a partial
+ * clone and no rechecking of packed storage). In the unlikely event that a
+ * caller needs to assert existence of an object that it fully expects to
+ * exist, and wants to trigger a lazy fetch in a partial clone, use
+ * oid_object_info_extended() with a NULL struct object_info.
+ *
+ * These functions can be removed once all callers have migrated to
+ * has_object() and/or oid_object_info_extended().
+ */
+int repo_has_object_file(struct repository *r, const struct object_id *oid);
+int repo_has_object_file_with_flags(struct repository *r,
+				    const struct object_id *oid, int flags);
+
+/*
+ * Return true iff an alternate object database has a loose object
+ * with the specified name.  This function does not respect replace
+ * references.
+ */
+int has_loose_object_nonlocal(const struct object_id *);
+
+int has_loose_object(const struct object_id *);
+
+/**
+ * format_object_header() is a thin wrapper around s xsnprintf() that
+ * writes the initial "<type> <obj-len>" part of the loose object
+ * header. It returns the size that snprintf() returns + 1.
+ */
+int format_object_header(char *str, size_t size, enum object_type type,
+			 size_t objsize);
+
+void assert_oid_type(const struct object_id *oid, enum object_type expect);
+
+/*
+ * Enabling the object read lock allows multiple threads to safely call the
+ * following functions in parallel: repo_read_object_file(),
+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().
+ *
+ * obj_read_lock() and obj_read_unlock() may also be used to protect other
+ * section which cannot execute in parallel with object reading. Since the used
+ * lock is a recursive mutex, these sections can even contain calls to object
+ * reading functions. However, beware that in these cases zlib inflation won't
+ * be performed in parallel, losing performance.
+ *
+ * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If
+ * any of its callees end up calling it, this recursive call won't benefit from
+ * parallel inflation.
+ */
+void enable_obj_read_lock(void);
+void disable_obj_read_lock(void);
+
+extern int obj_read_use_lock;
+extern pthread_mutex_t obj_read_mutex;
+
+static inline void obj_read_lock(void)
+{
+	if(obj_read_use_lock)
+		pthread_mutex_lock(&obj_read_mutex);
+}
+
+static inline void obj_read_unlock(void)
+{
+	if(obj_read_use_lock)
+		pthread_mutex_unlock(&obj_read_mutex);
+}
+
+/*
+ * Iterate over the files in the loose-object parts of the object
+ * directory "path", triggering the following callbacks:
+ *
+ *  - loose_object is called for each loose object we find.
+ *
+ *  - loose_cruft is called for any files that do not appear to be
+ *    loose objects. Note that we only look in the loose object
+ *    directories "objects/[0-9a-f]{2}/", so we will not report
+ *    "objects/foobar" as cruft.
+ *
+ *  - loose_subdir is called for each top-level hashed subdirectory
+ *    of the object directory (e.g., "$OBJDIR/f0"). It is called
+ *    after the objects in the directory are processed.
+ *
+ * Any callback that is NULL will be ignored. Callbacks returning non-zero
+ * will end the iteration.
+ *
+ * In the "buf" variant, "path" is a strbuf which will also be used as a
+ * scratch buffer, but restored to its original contents before
+ * the function returns.
+ */
+typedef int each_loose_object_fn(const struct object_id *oid,
+				 const char *path,
+				 void *data);
+typedef int each_loose_cruft_fn(const char *basename,
+				const char *path,
+				void *data);
+typedef int each_loose_subdir_fn(unsigned int nr,
+				 const char *path,
+				 void *data);
+int for_each_file_in_obj_subdir(unsigned int subdir_nr,
+				struct strbuf *path,
+				each_loose_object_fn obj_cb,
+				each_loose_cruft_fn cruft_cb,
+				each_loose_subdir_fn subdir_cb,
+				void *data);
+int for_each_loose_file_in_objdir(const char *path,
+				  each_loose_object_fn obj_cb,
+				  each_loose_cruft_fn cruft_cb,
+				  each_loose_subdir_fn subdir_cb,
+				  void *data);
+int for_each_loose_file_in_objdir_buf(struct strbuf *path,
+				      each_loose_object_fn obj_cb,
+				      each_loose_cruft_fn cruft_cb,
+				      each_loose_subdir_fn subdir_cb,
+				      void *data);
+
+/* Flags for for_each_*_object() below. */
+enum for_each_object_flags {
+	/* Iterate only over local objects, not alternates. */
+	FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),
+
+	/* Only iterate over packs obtained from the promisor remote. */
+	FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),
+
+	/*
+	 * Visit objects within a pack in packfile order rather than .idx order
+	 */
+	FOR_EACH_OBJECT_PACK_ORDER = (1<<2),
+
+	/* Only iterate over packs that are not marked as kept in-core. */
+	FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),
+
+	/* Only iterate over packs that do not have .keep files. */
+	FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),
+};
+
+/*
+ * Iterate over all accessible loose objects without respect to
+ * reachability. By default, this includes both local and alternate objects.
+ * The order in which objects are visited is unspecified.
+ *
+ * Any flags specific to packs are ignored.
+ */
+int for_each_loose_object(each_loose_object_fn, void *,
+			  enum for_each_object_flags flags);
+
+/*
+ * Iterate over all accessible packed objects without respect to reachability.
+ * By default, this includes both local and alternate packs.
+ *
+ * Note that some objects may appear twice if they are found in multiple packs.
+ * Each pack is visited in an unspecified order. By default, objects within a
+ * pack are visited in pack-idx order (i.e., sorted by oid).
+ */
+typedef int each_packed_object_fn(const struct object_id *oid,
+				  struct packed_git *pack,
+				  uint32_t pos,
+				  void *data);
+int for_each_object_in_pack(struct packed_git *p,
+			    each_packed_object_fn, void *data,
+			    enum for_each_object_flags flags);
+int for_each_packed_object(each_packed_object_fn, void *,
+			   enum for_each_object_flags flags);
+
+#endif /* OBJECT_STORE_LL_H */
diff --git a/object-store.h b/object-store.h
index 1a713d8..1b3e3d7 100644
--- a/object-store.h
+++ b/object-store.h
@@ -1,543 +1,11 @@
 #ifndef OBJECT_STORE_H
 #define OBJECT_STORE_H
 
-#include "cache.h"
-#include "oidmap.h"
-#include "list.h"
-#include "oid-array.h"
-#include "strbuf.h"
-#include "thread-utils.h"
 #include "khash.h"
 #include "dir.h"
-#include "oidtree.h"
-#include "oidset.h"
-
-struct object_directory {
-	struct object_directory *next;
-
-	/*
-	 * Used to store the results of readdir(3) calls when we are OK
-	 * sacrificing accuracy due to races for speed. That includes
-	 * object existence with OBJECT_INFO_QUICK, as well as
-	 * our search for unique abbreviated hashes. Don't use it for tasks
-	 * requiring greater accuracy!
-	 *
-	 * Be sure to call odb_load_loose_cache() before using.
-	 */
-	uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
-	struct oidtree *loose_objects_cache;
-
-	/*
-	 * This is a temporary object store created by the tmp_objdir
-	 * facility. Disable ref updates since the objects in the store
-	 * might be discarded on rollback.
-	 */
-	int disable_ref_updates;
-
-	/*
-	 * This object store is ephemeral, so there is no need to fsync.
-	 */
-	int will_destroy;
-
-	/*
-	 * Path to the alternative object store. If this is a relative path,
-	 * it is relative to the current working directory.
-	 */
-	char *path;
-};
-
-struct input_stream {
-	const void *(*read)(struct input_stream *, unsigned long *len);
-	void *data;
-	int is_finished;
-};
+#include "object-store-ll.h"
 
 KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
 	struct object_directory *, 1, fspathhash, fspatheq)
 
-void prepare_alt_odb(struct repository *r);
-char *compute_alternate_path(const char *path, struct strbuf *err);
-struct object_directory *find_odb(struct repository *r, const char *obj_dir);
-typedef int alt_odb_fn(struct object_directory *, void *);
-int foreach_alt_odb(alt_odb_fn, void*);
-typedef void alternate_ref_fn(const struct object_id *oid, void *);
-void for_each_alternate_ref(alternate_ref_fn, void *);
-
-/*
- * Add the directory to the on-disk alternates file; the new entry will also
- * take effect in the current process.
- */
-void add_to_alternates_file(const char *dir);
-
-/*
- * Add the directory to the in-memory list of alternates (along with any
- * recursive alternates it points to), but do not modify the on-disk alternates
- * file.
- */
-void add_to_alternates_memory(const char *dir);
-
-/*
- * Replace the current writable object directory with the specified temporary
- * object directory; returns the former primary object directory.
- */
-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
-
-/*
- * Restore a previous ODB replaced by set_temporary_main_odb.
- */
-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
-
-/*
- * Populate and return the loose object cache array corresponding to the
- * given object ID.
- */
-struct oidtree *odb_loose_cache(struct object_directory *odb,
-				  const struct object_id *oid);
-
-/* Empty the loose object cache for the specified object directory. */
-void odb_clear_loose_cache(struct object_directory *odb);
-
-/* Clear and free the specified object directory */
-void free_object_directory(struct object_directory *odb);
-
-struct packed_git {
-	struct hashmap_entry packmap_ent;
-	struct packed_git *next;
-	struct list_head mru;
-	struct pack_window *windows;
-	off_t pack_size;
-	const void *index_data;
-	size_t index_size;
-	uint32_t num_objects;
-	uint32_t crc_offset;
-	struct oidset bad_objects;
-	int index_version;
-	time_t mtime;
-	int pack_fd;
-	int index;              /* for builtin/pack-objects.c */
-	unsigned pack_local:1,
-		 pack_keep:1,
-		 pack_keep_in_core:1,
-		 freshened:1,
-		 do_not_close:1,
-		 pack_promisor:1,
-		 multi_pack_index:1,
-		 is_cruft:1;
-	unsigned char hash[GIT_MAX_RAWSZ];
-	struct revindex_entry *revindex;
-	const uint32_t *revindex_data;
-	const uint32_t *revindex_map;
-	size_t revindex_size;
-	/*
-	 * mtimes_map points at the beginning of the memory mapped region of
-	 * this pack's corresponding .mtimes file, and mtimes_size is the size
-	 * of that .mtimes file
-	 */
-	const uint32_t *mtimes_map;
-	size_t mtimes_size;
-	/* something like ".git/objects/pack/xxxxx.pack" */
-	char pack_name[FLEX_ARRAY]; /* more */
-};
-
-struct multi_pack_index;
-
-static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,
-				     const struct hashmap_entry *entry,
-				     const struct hashmap_entry *entry2,
-				     const void *keydata)
-{
-	const char *key = keydata;
-	const struct packed_git *pg1, *pg2;
-
-	pg1 = container_of(entry, const struct packed_git, packmap_ent);
-	pg2 = container_of(entry2, const struct packed_git, packmap_ent);
-
-	return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
-}
-
-struct raw_object_store {
-	/*
-	 * Set of all object directories; the main directory is first (and
-	 * cannot be NULL after initialization). Subsequent directories are
-	 * alternates.
-	 */
-	struct object_directory *odb;
-	struct object_directory **odb_tail;
-	kh_odb_path_map_t *odb_by_path;
-
-	int loaded_alternates;
-
-	/*
-	 * A list of alternate object directories loaded from the environment;
-	 * this should not generally need to be accessed directly, but will
-	 * populate the "odb" list when prepare_alt_odb() is run.
-	 */
-	char *alternate_db;
-
-	/*
-	 * Objects that should be substituted by other objects
-	 * (see git-replace(1)).
-	 */
-	struct oidmap *replace_map;
-	unsigned replace_map_initialized : 1;
-	pthread_mutex_t replace_mutex; /* protect object replace functions */
-
-	struct commit_graph *commit_graph;
-	unsigned commit_graph_attempted : 1; /* if loading has been attempted */
-
-	/*
-	 * private data
-	 *
-	 * should only be accessed directly by packfile.c and midx.c
-	 */
-	struct multi_pack_index *multi_pack_index;
-
-	/*
-	 * private data
-	 *
-	 * should only be accessed directly by packfile.c
-	 */
-
-	struct packed_git *packed_git;
-	/* A most-recently-used ordered version of the packed_git list. */
-	struct list_head packed_git_mru;
-
-	struct {
-		struct packed_git **packs;
-		unsigned flags;
-	} kept_pack_cache;
-
-	/*
-	 * A map of packfiles to packed_git structs for tracking which
-	 * packs have been loaded already.
-	 */
-	struct hashmap pack_map;
-
-	/*
-	 * A fast, rough count of the number of objects in the repository.
-	 * These two fields are not meant for direct access. Use
-	 * approximate_object_count() instead.
-	 */
-	unsigned long approximate_object_count;
-	unsigned approximate_object_count_valid : 1;
-
-	/*
-	 * Whether packed_git has already been populated with this repository's
-	 * packs.
-	 */
-	unsigned packed_git_initialized : 1;
-};
-
-struct raw_object_store *raw_object_store_new(void);
-void raw_object_store_clear(struct raw_object_store *o);
-
-/*
- * 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.
- */
-const char *loose_object_path(struct repository *r, struct strbuf *buf,
-			      const struct object_id *oid);
-
-void *map_loose_object(struct repository *r, const struct object_id *oid,
-		       unsigned long *size);
-
-void *repo_read_object_file(struct repository *r,
-			    const struct object_id *oid,
-			    enum object_type *type,
-			    unsigned long *size);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define read_object_file(oid, type, size) repo_read_object_file(the_repository, oid, type, size)
-#endif
-
-/* Read and unpack an object file into memory, write memory to an object file */
-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
-
-void hash_object_file(const struct git_hash_algo *algo, const void *buf,
-		      unsigned long len, enum object_type type,
-		      struct object_id *oid);
-
-int write_object_file_flags(const void *buf, unsigned long len,
-			    enum object_type type, struct object_id *oid,
-			    unsigned flags);
-static inline int write_object_file(const void *buf, unsigned long len,
-				    enum object_type type, struct object_id *oid)
-{
-	return write_object_file_flags(buf, len, type, oid, 0);
-}
-
-int write_object_file_literally(const void *buf, unsigned long len,
-				const char *type, struct object_id *oid,
-				unsigned flags);
-int stream_loose_object(struct input_stream *in_stream, size_t len,
-			struct object_id *oid);
-
-/*
- * Add an object file to the in-memory object store, without writing it
- * to disk.
- *
- * Callers are responsible for calling write_object_file to record the
- * object in persistent storage before writing any other new objects
- * that reference it.
- */
-int pretend_object_file(void *, unsigned long, enum object_type,
-			struct object_id *oid);
-
-int force_object_loose(const struct object_id *oid, time_t mtime);
-
-/*
- * Open the loose object at path, check its hash, and return the contents,
- * use the "oi" argument to assert things about the object, or e.g. populate its
- * type, and size. If the object is a blob, then "contents" may return NULL,
- * to allow streaming of large blobs.
- *
- * Returns 0 on success, negative on error (details may be written to stderr).
- */
-int read_loose_object(const char *path,
-		      const struct object_id *expected_oid,
-		      struct object_id *real_oid,
-		      void **contents,
-		      struct object_info *oi);
-
-/* Retry packed storage after checking packed and loose storage */
-#define HAS_OBJECT_RECHECK_PACKED 1
-
-/*
- * Returns 1 if the object exists. This function will not lazily fetch objects
- * in a partial clone.
- */
-int has_object(struct repository *r, const struct object_id *oid,
-	       unsigned flags);
-
-/*
- * These macros and functions are deprecated. If checking existence for an
- * object that is likely to be missing and/or whose absence is relatively
- * inconsequential (or is consequential but the caller is prepared to handle
- * it), use has_object(), which has better defaults (no lazy fetch in a partial
- * clone and no rechecking of packed storage). In the unlikely event that a
- * caller needs to assert existence of an object that it fully expects to
- * exist, and wants to trigger a lazy fetch in a partial clone, use
- * oid_object_info_extended() with a NULL struct object_info.
- *
- * These functions can be removed once all callers have migrated to
- * has_object() and/or oid_object_info_extended().
- */
-int repo_has_object_file(struct repository *r, const struct object_id *oid);
-int repo_has_object_file_with_flags(struct repository *r,
-				    const struct object_id *oid, int flags);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define has_object_file(oid) repo_has_object_file(the_repository, oid)
-#define has_object_file_with_flags(oid, flags) repo_has_object_file_with_flags(the_repository, oid, flags)
-#endif
-
-/*
- * Return true iff an alternate object database has a loose object
- * with the specified name.  This function does not respect replace
- * references.
- */
-int has_loose_object_nonlocal(const struct object_id *);
-
-int has_loose_object(const struct object_id *);
-
-/**
- * format_object_header() is a thin wrapper around s xsnprintf() that
- * writes the initial "<type> <obj-len>" part of the loose object
- * header. It returns the size that snprintf() returns + 1.
- */
-int format_object_header(char *str, size_t size, enum object_type type,
-			 size_t objsize);
-
-void assert_oid_type(const struct object_id *oid, enum object_type expect);
-
-/*
- * Enabling the object read lock allows multiple threads to safely call the
- * following functions in parallel: repo_read_object_file(), read_object_file(),
- * read_object_with_reference(), oid_object_info() and oid_object_info_extended().
- *
- * obj_read_lock() and obj_read_unlock() may also be used to protect other
- * section which cannot execute in parallel with object reading. Since the used
- * lock is a recursive mutex, these sections can even contain calls to object
- * reading functions. However, beware that in these cases zlib inflation won't
- * be performed in parallel, losing performance.
- *
- * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If
- * any of its callees end up calling it, this recursive call won't benefit from
- * parallel inflation.
- */
-void enable_obj_read_lock(void);
-void disable_obj_read_lock(void);
-
-extern int obj_read_use_lock;
-extern pthread_mutex_t obj_read_mutex;
-
-static inline void obj_read_lock(void)
-{
-	if(obj_read_use_lock)
-		pthread_mutex_lock(&obj_read_mutex);
-}
-
-static inline void obj_read_unlock(void)
-{
-	if(obj_read_use_lock)
-		pthread_mutex_unlock(&obj_read_mutex);
-}
-
-struct object_info {
-	/* Request */
-	enum object_type *typep;
-	unsigned long *sizep;
-	off_t *disk_sizep;
-	struct object_id *delta_base_oid;
-	struct strbuf *type_name;
-	void **contentp;
-
-	/* Response */
-	enum {
-		OI_CACHED,
-		OI_LOOSE,
-		OI_PACKED,
-		OI_DBCACHED
-	} whence;
-	union {
-		/*
-		 * struct {
-		 * 	... Nothing to expose in this case
-		 * } cached;
-		 * struct {
-		 * 	... Nothing to expose in this case
-		 * } loose;
-		 */
-		struct {
-			struct packed_git *pack;
-			off_t offset;
-			unsigned int is_delta;
-		} packed;
-	} u;
-};
-
-/*
- * Initializer for a "struct object_info" that wants no items. You may
- * also memset() the memory to all-zeroes.
- */
-#define OBJECT_INFO_INIT { 0 }
-
-/* Invoke lookup_replace_object() on the given hash */
-#define OBJECT_INFO_LOOKUP_REPLACE 1
-/* Allow reading from a loose object file of unknown/bogus type */
-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
-/* Do not retry packed storage after checking packed and loose storage */
-#define OBJECT_INFO_QUICK 8
-/*
- * Do not attempt to fetch the object if missing (even if fetch_is_missing is
- * nonzero).
- */
-#define OBJECT_INFO_SKIP_FETCH_OBJECT 16
-/*
- * This is meant for bulk prefetching of missing blobs in a partial
- * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
- */
-#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
-
-/* Die if object corruption (not just an object being missing) was detected. */
-#define OBJECT_INFO_DIE_IF_CORRUPT 32
-
-int oid_object_info_extended(struct repository *r,
-			     const struct object_id *,
-			     struct object_info *, unsigned flags);
-
-/*
- * Iterate over the files in the loose-object parts of the object
- * directory "path", triggering the following callbacks:
- *
- *  - loose_object is called for each loose object we find.
- *
- *  - loose_cruft is called for any files that do not appear to be
- *    loose objects. Note that we only look in the loose object
- *    directories "objects/[0-9a-f]{2}/", so we will not report
- *    "objects/foobar" as cruft.
- *
- *  - loose_subdir is called for each top-level hashed subdirectory
- *    of the object directory (e.g., "$OBJDIR/f0"). It is called
- *    after the objects in the directory are processed.
- *
- * Any callback that is NULL will be ignored. Callbacks returning non-zero
- * will end the iteration.
- *
- * In the "buf" variant, "path" is a strbuf which will also be used as a
- * scratch buffer, but restored to its original contents before
- * the function returns.
- */
-typedef int each_loose_object_fn(const struct object_id *oid,
-				 const char *path,
-				 void *data);
-typedef int each_loose_cruft_fn(const char *basename,
-				const char *path,
-				void *data);
-typedef int each_loose_subdir_fn(unsigned int nr,
-				 const char *path,
-				 void *data);
-int for_each_file_in_obj_subdir(unsigned int subdir_nr,
-				struct strbuf *path,
-				each_loose_object_fn obj_cb,
-				each_loose_cruft_fn cruft_cb,
-				each_loose_subdir_fn subdir_cb,
-				void *data);
-int for_each_loose_file_in_objdir(const char *path,
-				  each_loose_object_fn obj_cb,
-				  each_loose_cruft_fn cruft_cb,
-				  each_loose_subdir_fn subdir_cb,
-				  void *data);
-int for_each_loose_file_in_objdir_buf(struct strbuf *path,
-				      each_loose_object_fn obj_cb,
-				      each_loose_cruft_fn cruft_cb,
-				      each_loose_subdir_fn subdir_cb,
-				      void *data);
-
-/* Flags for for_each_*_object() below. */
-enum for_each_object_flags {
-	/* Iterate only over local objects, not alternates. */
-	FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),
-
-	/* Only iterate over packs obtained from the promisor remote. */
-	FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),
-
-	/*
-	 * Visit objects within a pack in packfile order rather than .idx order
-	 */
-	FOR_EACH_OBJECT_PACK_ORDER = (1<<2),
-
-	/* Only iterate over packs that are not marked as kept in-core. */
-	FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),
-
-	/* Only iterate over packs that do not have .keep files. */
-	FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),
-};
-
-/*
- * Iterate over all accessible loose objects without respect to
- * reachability. By default, this includes both local and alternate objects.
- * The order in which objects are visited is unspecified.
- *
- * Any flags specific to packs are ignored.
- */
-int for_each_loose_object(each_loose_object_fn, void *,
-			  enum for_each_object_flags flags);
-
-/*
- * Iterate over all accessible packed objects without respect to reachability.
- * By default, this includes both local and alternate packs.
- *
- * Note that some objects may appear twice if they are found in multiple packs.
- * Each pack is visited in an unspecified order. By default, objects within a
- * pack are visited in pack-idx order (i.e., sorted by oid).
- */
-typedef int each_packed_object_fn(const struct object_id *oid,
-				  struct packed_git *pack,
-				  uint32_t pos,
-				  void *data);
-int for_each_object_in_pack(struct packed_git *p,
-			    each_packed_object_fn, void *data,
-			    enum for_each_object_flags flags);
-int for_each_packed_object(each_packed_object_fn, void *,
-			   enum for_each_object_flags flags);
-
 #endif /* OBJECT_STORE_H */
diff --git a/object.c b/object.c
index 344087d..51e3848 100644
--- a/object.c
+++ b/object.c
@@ -1,14 +1,19 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "object.h"
 #include "replace-object.h"
+#include "object-file.h"
 #include "object-store.h"
 #include "blob.h"
+#include "statinfo.h"
 #include "tree.h"
 #include "commit.h"
 #include "tag.h"
 #include "alloc.h"
 #include "packfile.h"
 #include "commit-graph.h"
+#include "loose.h"
 
 unsigned int get_max_object_index(void)
 {
@@ -43,8 +48,7 @@
 		len = strlen(str);
 
 	for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
-		if (!strncmp(str, object_type_strings[i], len) &&
-		    object_type_strings[i][len] == '\0')
+		if (!xstrncmpz(object_type_strings[i], str, len))
 			return i;
 
 	if (gentle)
@@ -268,6 +272,7 @@
 				       enum parse_object_flags flags)
 {
 	int skip_hash = !!(flags & PARSE_OBJECT_SKIP_HASH_CHECK);
+	int discard_tree = !!(flags & PARSE_OBJECT_DISCARD_TREE);
 	unsigned long size;
 	enum object_type type;
 	int eaten;
@@ -295,6 +300,17 @@
 		return lookup_object(r, oid);
 	}
 
+	/*
+	 * If the caller does not care about the tree buffer and does not
+	 * care about checking the hash, we can simply verify that we
+	 * have the on-disk object with the correct type.
+	 */
+	if (skip_hash && discard_tree &&
+	    (!obj || obj->type == OBJ_TREE) &&
+	    oid_object_info(r, oid, NULL) == OBJ_TREE) {
+		return &lookup_tree(r, oid)->object;
+	}
+
 	buffer = repo_read_object_file(r, oid, &type, &size);
 	if (buffer) {
 		if (!skip_hash &&
@@ -308,6 +324,8 @@
 					  buffer, &eaten);
 		if (!eaten)
 			free(buffer);
+		if (discard_tree && type == OBJ_TREE)
+			free_tree_buffer((struct tree *)obj);
 		return obj;
 	}
 	return NULL;
@@ -353,6 +371,12 @@
  */
 static char object_array_slopbuf[1];
 
+void object_array_init(struct object_array *array)
+{
+	struct object_array blank = OBJECT_ARRAY_INIT;
+	memcpy(array, &blank, sizeof(*array));
+}
+
 void add_object_array_with_path(struct object *obj, const char *name,
 				struct object_array *array,
 				unsigned mode, const char *path)
@@ -530,6 +554,7 @@
 {
 	free(odb->path);
 	odb_clear_loose_cache(odb);
+	loose_object_map_clear(&odb->loose_map);
 	free(odb);
 }
 
diff --git a/object.h b/object.h
index 31ebe11..9293e70 100644
--- a/object.h
+++ b/object.h
@@ -1,9 +1,10 @@
 #ifndef OBJECT_H
 #define OBJECT_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct buffer_slab;
+struct repository;
 
 struct parsed_object_pool {
 	struct object **obj_hash;
@@ -57,6 +58,8 @@
 
 #define OBJECT_ARRAY_INIT { 0 }
 
+void object_array_init(struct object_array *array);
+
 /*
  * object flag allocation:
  * revision.h:               0---------10         15             23------27
@@ -81,6 +84,70 @@
  */
 #define FLAG_BITS  28
 
+#define TYPE_BITS 3
+
+/*
+ * Values in this enum (except those outside the 3 bit range) are part
+ * of pack file format. See gitformat-pack(5) for more information.
+ */
+enum object_type {
+	OBJ_BAD = -1,
+	OBJ_NONE = 0,
+	OBJ_COMMIT = 1,
+	OBJ_TREE = 2,
+	OBJ_BLOB = 3,
+	OBJ_TAG = 4,
+	/* 5 for future expansion */
+	OBJ_OFS_DELTA = 6,
+	OBJ_REF_DELTA = 7,
+	OBJ_ANY,
+	OBJ_MAX
+};
+
+/* unknown mode (impossible combination S_IFIFO|S_IFCHR) */
+#define S_IFINVALID     0030000
+
+/*
+ * A "directory link" is a link to another git directory.
+ *
+ * The value 0160000 is not normally a valid mode, and
+ * also just happens to be S_IFDIR + S_IFLNK
+ */
+#define S_IFGITLINK	0160000
+#define S_ISGITLINK(m)	(((m) & S_IFMT) == S_IFGITLINK)
+
+#define S_ISSPARSEDIR(m) ((m) == S_IFDIR)
+
+static inline enum object_type object_type(unsigned int mode)
+{
+	return S_ISDIR(mode) ? OBJ_TREE :
+		S_ISGITLINK(mode) ? OBJ_COMMIT :
+		OBJ_BLOB;
+}
+
+#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
+static inline unsigned int create_ce_mode(unsigned int mode)
+{
+	if (S_ISLNK(mode))
+		return S_IFLNK;
+	if (S_ISSPARSEDIR(mode))
+		return S_IFDIR;
+	if (S_ISDIR(mode) || S_ISGITLINK(mode))
+		return S_IFGITLINK;
+	return S_IFREG | ce_permissions(mode);
+}
+
+static inline unsigned int canon_mode(unsigned int mode)
+{
+	if (S_ISREG(mode))
+		return S_IFREG | ce_permissions(mode);
+	if (S_ISLNK(mode))
+		return S_IFLNK;
+	if (S_ISDIR(mode))
+		return S_IFDIR;
+	return S_IFGITLINK;
+}
+
 /*
  * The object type is stored in 3 bits.
  */
@@ -123,6 +190,24 @@
 
 void *object_as_type(struct object *obj, enum object_type type, int quiet);
 
+
+static inline const char *parse_mode(const char *str, uint16_t *modep)
+{
+	unsigned char c;
+	unsigned int mode = 0;
+
+	if (*str == ' ')
+		return NULL;
+
+	while ((c = *str++) != ' ') {
+		if (c < '0' || c > '7')
+			return NULL;
+		mode = (mode << 3) + (c - '0');
+	}
+	*modep = mode;
+	return str;
+}
+
 /*
  * Returns the object, having parsed it to find out what it is.
  *
@@ -130,6 +215,7 @@
  */
 enum parse_object_flags {
 	PARSE_OBJECT_SKIP_HASH_CHECK = 1 << 0,
+	PARSE_OBJECT_DISCARD_TREE = 1 << 1,
 };
 struct object *parse_object(struct repository *r, const struct object_id *oid);
 struct object *parse_object_with_flags(struct repository *r,
diff --git a/oid-array.c b/oid-array.c
index 73ba76e..1f36651 100644
--- a/oid-array.c
+++ b/oid-array.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "oid-array.h"
 #include "hash-lookup.h"
 
@@ -6,12 +6,20 @@
 {
 	ALLOC_GROW(array->oid, array->nr + 1, array->alloc);
 	oidcpy(&array->oid[array->nr++], oid);
+	if (!oid->algo)
+		oid_set_algo(&array->oid[array->nr - 1], the_hash_algo);
 	array->sorted = 0;
 }
 
-static int void_hashcmp(const void *a, const void *b)
+static int void_hashcmp(const void *va, const void *vb)
 {
-	return oidcmp(a, b);
+	const struct object_id *a = va, *b = vb;
+	int ret;
+	if (a->algo == b->algo)
+		ret = oidcmp(a, b);
+	else
+		ret = a->algo > b->algo ? 1 : -1;
+	return ret;
 }
 
 void oid_array_sort(struct oid_array *array)
diff --git a/oidmap.c b/oidmap.c
index 49965fe..8b1bc4d 100644
--- a/oidmap.c
+++ b/oidmap.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash.h"
 #include "oidmap.h"
 
 static int oidmap_neq(const void *hashmap_cmp_fn_data UNUSED,
diff --git a/oidmap.h b/oidmap.h
index c66a83a..05c673e 100644
--- a/oidmap.h
+++ b/oidmap.h
@@ -1,7 +1,7 @@
 #ifndef OIDMAP_H
 #define OIDMAP_H
 
-#include "cache.h"
+#include "hash-ll.h"
 #include "hashmap.h"
 
 /*
diff --git a/oidset.c b/oidset.c
index b36a2ba..91d1385 100644
--- a/oidset.c
+++ b/oidset.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "oidset.h"
+#include "hex.h"
+#include "strbuf.h"
 
 void oidset_init(struct oidset *set, size_t initial_size)
 {
@@ -21,6 +23,16 @@
 	return !added;
 }
 
+void oidset_insert_from_set(struct oidset *dest, struct oidset *src)
+{
+	struct oidset_iter iter;
+	struct object_id *src_oid;
+
+	oidset_iter_init(src, &iter);
+	while ((src_oid = oidset_iter_next(&iter)))
+		oidset_insert(dest, src_oid);
+}
+
 int oidset_remove(struct oidset *set, const struct object_id *oid)
 {
 	khiter_t pos = kh_get_oid_set(&set->set, *oid);
diff --git a/oidset.h b/oidset.h
index ba4a5a2..262f425 100644
--- a/oidset.h
+++ b/oidset.h
@@ -48,6 +48,12 @@
 int oidset_insert(struct oidset *set, const struct object_id *oid);
 
 /**
+ * Insert all the oids that are in set 'src' into set 'dest'; a copy
+ * is made of each oid inserted into set 'dest'.
+ */
+void oidset_insert_from_set(struct oidset *dest, struct oidset *src);
+
+/**
  * Remove the oid from the set.
  *
  * Returns 1 if the oid was present in the set, 0 otherwise.
diff --git a/oidtree.c b/oidtree.c
index 0d39389..daef175 100644
--- a/oidtree.c
+++ b/oidtree.c
@@ -2,8 +2,8 @@
  * A wrapper around cbtree which stores oids
  * May be used to replace oid-array for prefix (abbreviation) matches
  */
+#include "git-compat-util.h"
 #include "oidtree.h"
-#include "alloc.h"
 #include "hash.h"
 
 struct oidtree_iter_data {
diff --git a/oidtree.h b/oidtree.h
index 77898f5..55c8351 100644
--- a/oidtree.h
+++ b/oidtree.h
@@ -2,7 +2,7 @@
 #define OIDTREE_H
 
 #include "cbtree.h"
-#include "hash.h"
+#include "hash-ll.h"
 #include "mem-pool.h"
 
 struct oidtree {
diff --git a/oss-fuzz/.gitignore b/oss-fuzz/.gitignore
index 9acb744..a877c11 100644
--- a/oss-fuzz/.gitignore
+++ b/oss-fuzz/.gitignore
@@ -1,3 +1,5 @@
 fuzz-commit-graph
+fuzz-config
+fuzz-date
 fuzz-pack-headers
 fuzz-pack-idx
diff --git a/oss-fuzz/dummy-cmd-main.c b/oss-fuzz/dummy-cmd-main.c
new file mode 100644
index 0000000..071cb23
--- /dev/null
+++ b/oss-fuzz/dummy-cmd-main.c
@@ -0,0 +1,14 @@
+#include "git-compat-util.h"
+
+/*
+ * When linking the fuzzers, we link against common-main.o to pick up some
+ * symbols. However, even though we ignore common-main:main(), we still need to
+ * provide all the symbols it references. In the fuzzers' case, we need to
+ * provide a dummy cmd_main() for the linker to be happy. It will never be
+ * executed.
+ */
+
+int cmd_main(int argc, const char **argv) {
+	BUG("We should not execute cmd_main() from a fuzz target");
+	return 1;
+}
diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c
index 914026f..2992079 100644
--- a/oss-fuzz/fuzz-commit-graph.c
+++ b/oss-fuzz/fuzz-commit-graph.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "commit-graph.h"
 #include "repository.h"
 
diff --git a/oss-fuzz/fuzz-config.c b/oss-fuzz/fuzz-config.c
new file mode 100644
index 0000000..94027f5
--- /dev/null
+++ b/oss-fuzz/fuzz-config.c
@@ -0,0 +1,33 @@
+#include "git-compat-util.h"
+#include "config.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
+static int config_parser_callback(const char *, const char *,
+					const struct config_context *, void *);
+
+static int config_parser_callback(const char *key, const char *value,
+					const struct config_context *ctx UNUSED,
+					void *data UNUSED)
+{
+	/*
+	 * Visit every byte of memory we are given to make sure the parser
+	 * gave it to us appropriately. We need to unconditionally return 0,
+	 * but we also want to prevent the strlen from being optimized away.
+	 */
+	size_t c = strlen(key);
+
+	if (value)
+		c += strlen(value);
+	return c == SIZE_MAX;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, const size_t size)
+{
+	struct config_options config_opts = { 0 };
+
+	config_opts.error_action = CONFIG_ERROR_SILENT;
+	git_config_from_mem(config_parser_callback, CONFIG_ORIGIN_BLOB,
+				"fuzztest-config", (const char *)data, size, NULL,
+				CONFIG_SCOPE_UNKNOWN, &config_opts);
+	return 0;
+}
diff --git a/oss-fuzz/fuzz-date.c b/oss-fuzz/fuzz-date.c
new file mode 100644
index 0000000..036378b
--- /dev/null
+++ b/oss-fuzz/fuzz-date.c
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "date.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+	int local;
+	int num;
+	char *str;
+	int16_t tz;
+	timestamp_t ts;
+	enum date_mode_type dmtype;
+	struct date_mode *dm;
+
+	if (size <= 4)
+		/*
+		 * we use the first byte to fuzz dmtype and the
+		 * second byte to fuzz local, then the next two
+		 * bytes to fuzz tz offset. The remainder
+		 * (at least one byte) is fed as input to
+		 * approxidate_careful().
+		 */
+		return 0;
+
+	local = !!(*data++ & 0x10);
+	num = *data++ % DATE_UNIX;
+	if (num >= DATE_STRFTIME)
+		num++;
+	dmtype = (enum date_mode_type)num;
+	size -= 2;
+
+	tz = *data++;
+	tz = (tz << 8) | *data++;
+	size -= 2;
+
+	str = xmemdupz(data, size);
+
+	ts = approxidate_careful(str, &num);
+	free(str);
+
+	dm = date_mode_from_type(dmtype);
+	dm->local = local;
+	show_date(ts, (int)tz, dm);
+
+	date_mode_release(dm);
+
+	return 0;
+}
diff --git a/oss-fuzz/fuzz-pack-headers.c b/oss-fuzz/fuzz-pack-headers.c
index 99da1d0..150c0f5 100644
--- a/oss-fuzz/fuzz-pack-headers.c
+++ b/oss-fuzz/fuzz-pack-headers.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "packfile.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c
index 0c3d777..3e19021 100644
--- a/oss-fuzz/fuzz-pack-idx.c
+++ b/oss-fuzz/fuzz-pack-idx.c
@@ -1,4 +1,5 @@
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
 #include "packfile.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cfa67a5..c6c8f94 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -1,18 +1,22 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "commit.h"
-#include "tag.h"
 #include "diff.h"
 #include "revision.h"
-#include "list-objects.h"
 #include "progress.h"
-#include "pack-revindex.h"
 #include "pack.h"
 #include "pack-bitmap.h"
 #include "hash-lookup.h"
 #include "pack-objects.h"
+#include "path.h"
 #include "commit-reach.h"
 #include "prio-queue.h"
+#include "trace2.h"
+#include "tree.h"
+#include "tree-walk.h"
 
 struct bitmapped_commit {
 	struct commit *commit;
@@ -191,6 +195,13 @@
 	unsigned idx; /* within selected array */
 };
 
+static void clear_bb_commit(struct bb_commit *commit)
+{
+	free_commit_list(commit->reverse_edges);
+	bitmap_free(commit->commit_mask);
+	bitmap_free(commit->bitmap);
+}
+
 define_commit_slab(bb_data, struct bb_commit);
 
 struct bitmap_builder {
@@ -332,7 +343,7 @@
 
 static void bitmap_builder_clear(struct bitmap_builder *bb)
 {
-	clear_bb_data(&bb->data);
+	deep_clear_bb_data(&bb->data, clear_bb_commit);
 	free(bb->commits);
 	bb->commits_nr = bb->commits_alloc = 0;
 }
@@ -359,7 +370,7 @@
 	if (parse_tree(tree) < 0)
 		die("unable to load tree object %s",
 		    oid_to_hex(&tree->object.oid));
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
 		switch (object_type(entry.mode)) {
@@ -406,15 +417,19 @@
 
 		if (old_bitmap && mapping) {
 			struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);
+			struct bitmap *remapped = bitmap_new();
 			/*
 			 * If this commit has an old bitmap, then translate that
 			 * bitmap and add its bits to this one. No need to walk
 			 * parents or the tree for this commit.
 			 */
-			if (old && !rebuild_bitmap(mapping, old, ent->bitmap)) {
+			if (old && !rebuild_bitmap(mapping, old, remapped)) {
+				bitmap_or(ent->bitmap, remapped);
+				bitmap_free(remapped);
 				reused_bitmaps_nr++;
 				continue;
 			}
+			bitmap_free(remapped);
 		}
 
 		/*
@@ -425,7 +440,8 @@
 		if (!found)
 			return -1;
 		bitmap_set(ent->bitmap, pos);
-		prio_queue_put(tree_queue, get_commit_tree(c));
+		prio_queue_put(tree_queue,
+			       repo_get_commit_tree(the_repository, c));
 
 		for (p = c->parents; p; p = p->next) {
 			pos = find_object_pos(&p->item->object.oid, &found);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index d2a42ab..2baeaba 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
+#include "gettext.h"
+#include "hex.h"
 #include "strbuf.h"
 #include "tag.h"
 #include "diff.h"
@@ -12,7 +14,9 @@
 #include "pack-objects.h"
 #include "packfile.h"
 #include "repository.h"
-#include "object-store.h"
+#include "trace2.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "list-objects-filter-options.h"
 #include "midx.h"
 #include "config.h"
@@ -47,13 +51,6 @@
 	struct packed_git *pack;
 	struct multi_pack_index *midx;
 
-	/*
-	 * Mark the first `reuse_objects` in the packfile as reused:
-	 * they will be sent as-is without using them for repacking
-	 * calculations
-	 */
-	uint32_t reuse_objects;
-
 	/* mmapped buffer of the whole bitmap index */
 	unsigned char *map;
 	size_t map_size; /* size of the mmaped buffer */
@@ -334,7 +331,7 @@
 	struct stat st;
 	char *bitmap_name = midx_bitmap_filename(midx);
 	int fd = git_open(bitmap_name);
-	uint32_t i;
+	uint32_t i, preferred_pack;
 	struct packed_git *preferred;
 
 	if (fd < 0) {
@@ -376,18 +373,25 @@
 		goto cleanup;
 	}
 
-	if (load_midx_revindex(bitmap_git->midx) < 0) {
+	if (load_midx_revindex(bitmap_git->midx)) {
 		warning(_("multi-pack bitmap is missing required reverse index"));
 		goto cleanup;
 	}
 
 	for (i = 0; i < bitmap_git->midx->num_packs; i++) {
-		if (prepare_midx_pack(the_repository, bitmap_git->midx, i))
-			die(_("could not open pack %s"),
-			    bitmap_git->midx->pack_names[i]);
+		if (prepare_midx_pack(the_repository, bitmap_git->midx, i)) {
+			warning(_("could not open pack %s"),
+				bitmap_git->midx->pack_names[i]);
+			goto cleanup;
+		}
 	}
 
-	preferred = bitmap_git->midx->packs[midx_preferred_pack(bitmap_git)];
+	if (midx_preferred_pack(bitmap_git->midx, &preferred_pack) < 0) {
+		warning(_("could not determine MIDX preferred pack"));
+		goto cleanup;
+	}
+
+	preferred = bitmap_git->midx->packs[preferred_pack];
 	if (!is_pack_valid(preferred)) {
 		warning(_("preferred pack (%s) is invalid"),
 			preferred->pack_name);
@@ -460,7 +464,7 @@
 	return 0;
 }
 
-static int load_reverse_index(struct bitmap_index *bitmap_git)
+static int load_reverse_index(struct repository *r, struct bitmap_index *bitmap_git)
 {
 	if (bitmap_is_midx(bitmap_git)) {
 		uint32_t i;
@@ -474,23 +478,23 @@
 		 * since we will need to make use of them in pack-objects.
 		 */
 		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
-			ret = load_pack_revindex(bitmap_git->midx->packs[i]);
+			ret = load_pack_revindex(r, bitmap_git->midx->packs[i]);
 			if (ret)
 				return ret;
 		}
 		return 0;
 	}
-	return load_pack_revindex(bitmap_git->pack);
+	return load_pack_revindex(r, bitmap_git->pack);
 }
 
-static int load_bitmap(struct bitmap_index *bitmap_git)
+static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git)
 {
 	assert(bitmap_git->map);
 
 	bitmap_git->bitmaps = kh_init_oid_map();
 	bitmap_git->ext_index.positions = kh_init_oid_pos();
 
-	if (load_reverse_index(bitmap_git))
+	if (load_reverse_index(r, bitmap_git))
 		goto failed;
 
 	if (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||
@@ -577,7 +581,7 @@
 {
 	struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
 
-	if (!open_bitmap(r, bitmap_git) && !load_bitmap(bitmap_git))
+	if (!open_bitmap(r, bitmap_git) && !load_bitmap(r, bitmap_git))
 		return bitmap_git;
 
 	free_bitmap_index(bitmap_git);
@@ -586,9 +590,10 @@
 
 struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx)
 {
+	struct repository *r = the_repository;
 	struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
 
-	if (!open_midx_bitmap_1(bitmap_git, midx) && !load_bitmap(bitmap_git))
+	if (!open_midx_bitmap_1(bitmap_git, midx) && !load_bitmap(r, bitmap_git))
 		return bitmap_git;
 
 	free_bitmap_index(bitmap_git);
@@ -951,7 +956,8 @@
 	bitmap_set(data->base, bitmap_pos);
 }
 
-static void show_commit(struct commit *commit, void *data)
+static void show_commit(struct commit *commit UNUSED,
+			void *data UNUSED)
 {
 }
 
@@ -1036,6 +1042,161 @@
 	return 1;
 }
 
+static struct bitmap *fill_in_bitmap(struct bitmap_index *bitmap_git,
+				     struct rev_info *revs,
+				     struct bitmap *base,
+				     struct bitmap *seen)
+{
+	struct include_data incdata;
+	struct bitmap_show_data show_data;
+
+	if (!base)
+		base = bitmap_new();
+
+	incdata.bitmap_git = bitmap_git;
+	incdata.base = base;
+	incdata.seen = seen;
+
+	revs->include_check = should_include;
+	revs->include_check_obj = should_include_obj;
+	revs->include_check_data = &incdata;
+
+	if (prepare_revision_walk(revs))
+		die(_("revision walk setup failed"));
+
+	show_data.bitmap_git = bitmap_git;
+	show_data.base = base;
+
+	traverse_commit_list(revs, show_commit, show_object, &show_data);
+
+	revs->include_check = NULL;
+	revs->include_check_obj = NULL;
+	revs->include_check_data = NULL;
+
+	return base;
+}
+
+struct bitmap_boundary_cb {
+	struct bitmap_index *bitmap_git;
+	struct bitmap *base;
+
+	struct object_array boundary;
+};
+
+static void show_boundary_commit(struct commit *commit, void *_data)
+{
+	struct bitmap_boundary_cb *data = _data;
+
+	if (commit->object.flags & BOUNDARY)
+		add_object_array(&commit->object, "", &data->boundary);
+
+	if (commit->object.flags & UNINTERESTING) {
+		if (bitmap_walk_contains(data->bitmap_git, data->base,
+					 &commit->object.oid))
+			return;
+
+		add_commit_to_bitmap(data->bitmap_git, &data->base, commit);
+	}
+}
+
+static void show_boundary_object(struct object *object UNUSED,
+				 const char *name UNUSED,
+				 void *data UNUSED)
+{
+	BUG("should not be called");
+}
+
+static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
+					    struct rev_info *revs,
+					    struct object_list *roots)
+{
+	struct bitmap_boundary_cb cb;
+	struct object_list *root;
+	unsigned int i;
+	unsigned int tmp_blobs, tmp_trees, tmp_tags;
+	int any_missing = 0;
+
+	cb.bitmap_git = bitmap_git;
+	cb.base = bitmap_new();
+	object_array_init(&cb.boundary);
+
+	revs->ignore_missing_links = 1;
+
+	/*
+	 * OR in any existing reachability bitmaps among `roots` into
+	 * `cb.base`.
+	 */
+	for (root = roots; root; root = root->next) {
+		struct object *object = root->item;
+		if (object->type != OBJ_COMMIT ||
+		    bitmap_walk_contains(bitmap_git, cb.base, &object->oid))
+			continue;
+
+		if (add_commit_to_bitmap(bitmap_git, &cb.base,
+					 (struct commit *)object))
+			continue;
+
+		any_missing = 1;
+	}
+
+	if (!any_missing)
+		goto cleanup;
+
+	tmp_blobs = revs->blob_objects;
+	tmp_trees = revs->tree_objects;
+	tmp_tags = revs->blob_objects;
+	revs->blob_objects = 0;
+	revs->tree_objects = 0;
+	revs->tag_objects = 0;
+
+	/*
+	 * We didn't have complete coverage of the roots. First setup a
+	 * revision walk to (a) OR in any bitmaps that are UNINTERESTING
+	 * between the tips and boundary, and (b) record the boundary.
+	 */
+	trace2_region_enter("pack-bitmap", "boundary-prepare", the_repository);
+	if (prepare_revision_walk(revs))
+		die("revision walk setup failed");
+	trace2_region_leave("pack-bitmap", "boundary-prepare", the_repository);
+
+	trace2_region_enter("pack-bitmap", "boundary-traverse", the_repository);
+	revs->boundary = 1;
+	traverse_commit_list_filtered(revs,
+				      show_boundary_commit,
+				      show_boundary_object,
+				      &cb, NULL);
+	revs->boundary = 0;
+	trace2_region_leave("pack-bitmap", "boundary-traverse", the_repository);
+
+	revs->blob_objects = tmp_blobs;
+	revs->tree_objects = tmp_trees;
+	revs->tag_objects = tmp_tags;
+
+	reset_revision_walk();
+	clear_object_flags(UNINTERESTING);
+
+	/*
+	 * Then add the boundary commit(s) as fill-in traversal tips.
+	 */
+	trace2_region_enter("pack-bitmap", "boundary-fill-in", the_repository);
+	for (i = 0; i < cb.boundary.nr; i++) {
+		struct object *obj = cb.boundary.objects[i].item;
+		if (bitmap_walk_contains(bitmap_git, cb.base, &obj->oid))
+			obj->flags |= SEEN;
+		else
+			add_pending_object(revs, obj, "");
+	}
+	if (revs->pending.nr)
+		cb.base = fill_in_bitmap(bitmap_git, revs, cb.base, NULL);
+	trace2_region_leave("pack-bitmap", "boundary-fill-in", the_repository);
+
+cleanup:
+	object_array_clear(&cb.boundary);
+	revs->ignore_missing_links = 0;
+
+	return cb.base;
+}
+
 static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
 				   struct rev_info *revs,
 				   struct object_list *roots,
@@ -1102,35 +1263,23 @@
 	}
 
 	if (needs_walk) {
-		struct include_data incdata;
-		struct bitmap_show_data show_data;
-
-		if (!base)
-			base = bitmap_new();
-
-		incdata.bitmap_git = bitmap_git;
-		incdata.base = base;
-		incdata.seen = seen;
-
-		revs->include_check = should_include;
-		revs->include_check_obj = should_include_obj;
-		revs->include_check_data = &incdata;
-
-		if (prepare_revision_walk(revs))
-			die(_("revision walk setup failed"));
-
-		show_data.bitmap_git = bitmap_git;
-		show_data.base = base;
-
-		traverse_commit_list(revs,
-				     show_commit, show_object,
-				     &show_data);
-
-		revs->include_check = NULL;
-		revs->include_check_obj = NULL;
-		revs->include_check_data = NULL;
+		/*
+		 * This fill-in traversal may walk over some objects
+		 * again, since we have already traversed in order to
+		 * find the boundary.
+		 *
+		 * But this extra walk should be extremely cheap, since
+		 * all commit objects are loaded into memory, and
+		 * because we skip walking to parents that are
+		 * UNINTERESTING, since it will be marked in the haves
+		 * bitmap already (or it has an on-disk bitmap, since
+		 * OR-ing it in covers all of its ancestors).
+		 */
+		base = fill_in_bitmap(bitmap_git, revs, base, seen);
 	}
 
+	object_list_free(&not_mapped);
+
 	return base;
 }
 
@@ -1145,7 +1294,7 @@
 	for (i = 0; i < eindex->count; ++i) {
 		struct object *obj;
 
-		if (!bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))
+		if (!bitmap_get(objects, st_add(bitmap_num_objects(bitmap_git), i)))
 			continue;
 
 		obj = eindex->objects[i];
@@ -1324,7 +1473,7 @@
 	 * them individually.
 	 */
 	for (i = 0; i < eindex->count; i++) {
-		uint32_t pos = i + bitmap_num_objects(bitmap_git);
+		size_t pos = st_add(i, bitmap_num_objects(bitmap_git));
 		if (eindex->objects[i]->type == type &&
 		    bitmap_get(to_filter, pos) &&
 		    !bitmap_get(tips, pos))
@@ -1415,7 +1564,7 @@
 	}
 
 	for (i = 0; i < eindex->count; i++) {
-		uint32_t pos = i + bitmap_num_objects(bitmap_git);
+		size_t pos = st_add(i, bitmap_num_objects(bitmap_git));
 		if (eindex->objects[i]->type == OBJ_BLOB &&
 		    bitmap_get(to_filter, pos) &&
 		    !bitmap_get(tips, pos) &&
@@ -1517,10 +1666,35 @@
 	return !filter_bitmap(NULL, NULL, NULL, filter);
 }
 
+
+static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git,
+					      struct bitmap *result)
+{
+	struct eindex *eindex = &bitmap_git->ext_index;
+	uint32_t objects_nr;
+	size_t i, pos;
+
+	objects_nr = bitmap_num_objects(bitmap_git);
+	pos = objects_nr / BITS_IN_EWORD;
+
+	if (pos > result->word_alloc)
+		pos = result->word_alloc;
+
+	memset(result->words, 0x00, sizeof(eword_t) * pos);
+	for (i = pos * BITS_IN_EWORD; i < objects_nr; i++)
+		bitmap_unset(result, i);
+
+	for (i = 0; i < eindex->count; ++i) {
+		if (has_object_pack(&eindex->objects[i]->oid))
+			bitmap_unset(result, objects_nr + i);
+	}
+}
+
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
 					 int filter_provided_objects)
 {
 	unsigned int i;
+	int use_boundary_traversal;
 
 	struct object_list *wants = NULL;
 	struct object_list *haves = NULL;
@@ -1571,13 +1745,21 @@
 			object_list_insert(object, &wants);
 	}
 
-	/*
-	 * if we have a HAVES list, but none of those haves is contained
-	 * in the packfile that has a bitmap, we don't have anything to
-	 * optimize here
-	 */
-	if (haves && !in_bitmapped_pack(bitmap_git, haves))
-		goto cleanup;
+	use_boundary_traversal = git_env_bool(GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL, -1);
+	if (use_boundary_traversal < 0) {
+		prepare_repo_settings(revs->repo);
+		use_boundary_traversal = revs->repo->settings.pack_use_bitmap_boundary_traversal;
+	}
+
+	if (!use_boundary_traversal) {
+		/*
+		 * if we have a HAVES list, but none of those haves is contained
+		 * in the packfile that has a bitmap, we don't have anything to
+		 * optimize here
+		 */
+		if (haves && !in_bitmapped_pack(bitmap_git, haves))
+			goto cleanup;
+	}
 
 	/* if we don't want anything, we're done here */
 	if (!wants)
@@ -1588,21 +1770,35 @@
 	 * from disk. this is the point of no return; after this the rev_list
 	 * becomes invalidated and we must perform the revwalk through bitmaps
 	 */
-	if (load_bitmap(bitmap_git) < 0)
+	if (load_bitmap(revs->repo, bitmap_git) < 0)
 		goto cleanup;
 
-	object_array_clear(&revs->pending);
+	if (!use_boundary_traversal)
+		object_array_clear(&revs->pending);
 
 	if (haves) {
-		revs->ignore_missing_links = 1;
-		haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
-		reset_revision_walk();
-		revs->ignore_missing_links = 0;
+		if (use_boundary_traversal) {
+			trace2_region_enter("pack-bitmap", "haves/boundary", the_repository);
+			haves_bitmap = find_boundary_objects(bitmap_git, revs, haves);
+			trace2_region_leave("pack-bitmap", "haves/boundary", the_repository);
+		} else {
+			trace2_region_enter("pack-bitmap", "haves/classic", the_repository);
+			revs->ignore_missing_links = 1;
+			haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
+			reset_revision_walk();
+			revs->ignore_missing_links = 0;
+			trace2_region_leave("pack-bitmap", "haves/classic", the_repository);
+		}
 
 		if (!haves_bitmap)
 			BUG("failed to perform bitmap walk");
 	}
 
+	if (use_boundary_traversal) {
+		object_array_clear(&revs->pending);
+		reset_revision_walk();
+	}
+
 	wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
 
 	if (!wants_bitmap)
@@ -1616,6 +1812,9 @@
 		      wants_bitmap,
 		      &revs->filter);
 
+	if (revs->unpacked)
+		filter_packed_objects_from_bitmap(bitmap_git, wants_bitmap);
+
 	bitmap_git->result = wants_bitmap;
 	bitmap_git->haves = haves_bitmap;
 
@@ -1635,8 +1834,10 @@
  * -1 means "stop trying further objects"; 0 means we may or may not have
  * reused, but you can keep feeding bits.
  */
-static int try_partial_reuse(struct packed_git *pack,
-			     size_t pos,
+static int try_partial_reuse(struct bitmap_index *bitmap_git,
+			     struct bitmapped_pack *pack,
+			     size_t bitmap_pos,
+			     uint32_t pack_pos,
 			     struct bitmap *reuse,
 			     struct pack_window **w_curs)
 {
@@ -1644,40 +1845,18 @@
 	enum object_type type;
 	unsigned long size;
 
-	/*
-	 * try_partial_reuse() is called either on (a) objects in the
-	 * bitmapped pack (in the case of a single-pack bitmap) or (b)
-	 * objects in the preferred pack of a multi-pack bitmap.
-	 * Importantly, the latter can pretend as if only a single pack
-	 * exists because:
-	 *
-	 *   - The first pack->num_objects bits of a MIDX bitmap are
-	 *     reserved for the preferred pack, and
-	 *
-	 *   - Ties due to duplicate objects are always resolved in
-	 *     favor of the preferred pack.
-	 *
-	 * Therefore we do not need to ever ask the MIDX for its copy of
-	 * an object by OID, since it will always select it from the
-	 * preferred pack. Likewise, the selected copy of the base
-	 * object for any deltas will reside in the same pack.
-	 *
-	 * This means that we can reuse pos when looking up the bit in
-	 * the reuse bitmap, too, since bits corresponding to the
-	 * preferred pack precede all bits from other packs.
-	 */
+	if (pack_pos >= pack->p->num_objects)
+		return -1; /* not actually in the pack */
 
-	if (pos >= pack->num_objects)
-		return -1; /* not actually in the pack or MIDX preferred pack */
-
-	offset = delta_obj_offset = pack_pos_to_offset(pack, pos);
-	type = unpack_object_header(pack, w_curs, &offset, &size);
+	offset = delta_obj_offset = pack_pos_to_offset(pack->p, pack_pos);
+	type = unpack_object_header(pack->p, w_curs, &offset, &size);
 	if (type < 0)
 		return -1; /* broken packfile, punt */
 
 	if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) {
 		off_t base_offset;
 		uint32_t base_pos;
+		uint32_t base_bitmap_pos;
 
 		/*
 		 * Find the position of the base object so we can look it up
@@ -1687,24 +1866,48 @@
 		 * and the normal slow path will complain about it in
 		 * more detail.
 		 */
-		base_offset = get_delta_base(pack, w_curs, &offset, type,
+		base_offset = get_delta_base(pack->p, w_curs, &offset, type,
 					     delta_obj_offset);
 		if (!base_offset)
 			return 0;
-		if (offset_to_pack_pos(pack, base_offset, &base_pos) < 0)
-			return 0;
 
-		/*
-		 * We assume delta dependencies always point backwards. This
-		 * lets us do a single pass, and is basically always true
-		 * due to the way OFS_DELTAs work. You would not typically
-		 * find REF_DELTA in a bitmapped pack, since we only bitmap
-		 * packs we write fresh, and OFS_DELTA is the default). But
-		 * let's double check to make sure the pack wasn't written with
-		 * odd parameters.
-		 */
-		if (base_pos >= pos)
-			return 0;
+		offset_to_pack_pos(pack->p, base_offset, &base_pos);
+
+		if (bitmap_is_midx(bitmap_git)) {
+			/*
+			 * Cross-pack deltas are rejected for now, but could
+			 * theoretically be supported in the future.
+			 *
+			 * We would need to ensure that we're sending both
+			 * halves of the delta/base pair, regardless of whether
+			 * or not the two cross a pack boundary. If they do,
+			 * then we must convert the delta to an REF_DELTA to
+			 * refer back to the base in the other pack.
+			 * */
+			if (midx_pair_to_pack_pos(bitmap_git->midx,
+						  pack->pack_int_id,
+						  base_offset,
+						  &base_bitmap_pos) < 0) {
+				return 0;
+			}
+		} else {
+			if (offset_to_pack_pos(pack->p, base_offset,
+					       &base_pos) < 0)
+				return 0;
+			/*
+			 * We assume delta dependencies always point backwards.
+			 * This lets us do a single pass, and is basically
+			 * always true due to the way OFS_DELTAs work. You would
+			 * not typically find REF_DELTA in a bitmapped pack,
+			 * since we only bitmap packs we write fresh, and
+			 * OFS_DELTA is the default). But let's double check to
+			 * make sure the pack wasn't written with odd
+			 * parameters.
+			 */
+			if (base_pos >= pack_pos)
+				return 0;
+			base_bitmap_pos = pack->bitmap_pos + base_pos;
+		}
 
 		/*
 		 * And finally, if we're not sending the base as part of our
@@ -1714,76 +1917,89 @@
 		 * to REF_DELTA on the fly. Better to just let the normal
 		 * object_entry code path handle it.
 		 */
-		if (!bitmap_get(reuse, base_pos))
+		if (!bitmap_get(reuse, base_bitmap_pos))
 			return 0;
 	}
 
 	/*
 	 * If we got here, then the object is OK to reuse. Mark it.
 	 */
-	bitmap_set(reuse, pos);
+	bitmap_set(reuse, bitmap_pos);
 	return 0;
 }
 
-uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
+static void reuse_partial_packfile_from_bitmap_1(struct bitmap_index *bitmap_git,
+						 struct bitmapped_pack *pack,
+						 struct bitmap *reuse)
 {
-	struct multi_pack_index *m = bitmap_git->midx;
-	if (!m)
-		BUG("midx_preferred_pack: requires non-empty MIDX");
-	return nth_midxed_pack_int_id(m, pack_pos_to_midx(bitmap_git->midx, 0));
-}
-
-int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
-				       struct packed_git **packfile_out,
-				       uint32_t *entries,
-				       struct bitmap **reuse_out)
-{
-	struct packed_git *pack;
 	struct bitmap *result = bitmap_git->result;
-	struct bitmap *reuse;
 	struct pack_window *w_curs = NULL;
-	size_t i = 0;
-	uint32_t offset;
-	uint32_t objects_nr;
+	size_t pos = pack->bitmap_pos / BITS_IN_EWORD;
 
-	assert(result);
+	if (!pack->bitmap_pos) {
+		/*
+		 * If we're processing the first (in the case of a MIDX, the
+		 * preferred pack) or the only (in the case of single-pack
+		 * bitmaps) pack, then we can reuse whole words at a time.
+		 *
+		 * This is because we know that any deltas in this range *must*
+		 * have their bases chosen from the same pack, since:
+		 *
+		 * - In the single pack case, there is no other pack to choose
+		 *   them from.
+		 *
+		 * - In the MIDX case, the first pack is the preferred pack, so
+		 *   all ties are broken in favor of that pack (i.e. the one
+		 *   we're currently processing). So any duplicate bases will be
+		 *   resolved in favor of the pack we're processing.
+		 */
+		while (pos < result->word_alloc &&
+		       pos < pack->bitmap_nr / BITS_IN_EWORD &&
+		       result->words[pos] == (eword_t)~0)
+			pos++;
+		memset(reuse->words, 0xFF, pos * sizeof(eword_t));
+	}
 
-	load_reverse_index(bitmap_git);
+	for (; pos < result->word_alloc; pos++) {
+		eword_t word = result->words[pos];
+		size_t offset;
 
-	if (bitmap_is_midx(bitmap_git))
-		pack = bitmap_git->midx->packs[midx_preferred_pack(bitmap_git)];
-	else
-		pack = bitmap_git->pack;
-	objects_nr = pack->num_objects;
+		for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+			size_t bit_pos;
+			uint32_t pack_pos;
 
-	while (i < result->word_alloc && result->words[i] == (eword_t)~0)
-		i++;
-
-	/*
-	 * Don't mark objects not in the packfile or preferred pack. This bitmap
-	 * marks objects eligible for reuse, but the pack-reuse code only
-	 * understands how to reuse a single pack. Since the preferred pack is
-	 * guaranteed to have all bases for its deltas (in a multi-pack bitmap),
-	 * we use it instead of another pack. In single-pack bitmaps, the choice
-	 * is made for us.
-	 */
-	if (i > objects_nr / BITS_IN_EWORD)
-		i = objects_nr / BITS_IN_EWORD;
-
-	reuse = bitmap_word_alloc(i);
-	memset(reuse->words, 0xFF, i * sizeof(eword_t));
-
-	for (; i < result->word_alloc; ++i) {
-		eword_t word = result->words[i];
-		size_t pos = (i * BITS_IN_EWORD);
-
-		for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
-			if ((word >> offset) == 0)
+			if (word >> offset == 0)
 				break;
 
 			offset += ewah_bit_ctz64(word >> offset);
-			if (try_partial_reuse(pack, pos + offset,
-					      reuse, &w_curs) < 0) {
+
+			bit_pos = pos * BITS_IN_EWORD + offset;
+			if (bit_pos < pack->bitmap_pos)
+				continue;
+			if (bit_pos >= pack->bitmap_pos + pack->bitmap_nr)
+				goto done;
+
+			if (bitmap_is_midx(bitmap_git)) {
+				uint32_t midx_pos;
+				off_t ofs;
+
+				midx_pos = pack_pos_to_midx(bitmap_git->midx, bit_pos);
+				ofs = nth_midxed_offset(bitmap_git->midx, midx_pos);
+
+				if (offset_to_pack_pos(pack->p, ofs, &pack_pos) < 0)
+					BUG("could not find object in pack %s "
+					    "at offset %"PRIuMAX" in MIDX",
+					    pack_basename(pack->p), (uintmax_t)ofs);
+			} else {
+				pack_pos = cast_size_t_to_uint32_t(st_sub(bit_pos, pack->bitmap_pos));
+				if (pack_pos >= pack->p->num_objects)
+					BUG("advanced beyond the end of pack %s (%"PRIuMAX" > %"PRIu32")",
+					    pack_basename(pack->p), (uintmax_t)pack_pos,
+					    pack->p->num_objects);
+			}
+
+			if (try_partial_reuse(bitmap_git, pack, bit_pos,
+					      pack_pos, reuse, &w_curs) < 0) {
 				/*
 				 * try_partial_reuse indicated we couldn't reuse
 				 * any bits, so there is no point in trying more
@@ -1800,11 +2016,97 @@
 
 done:
 	unuse_pack(&w_curs);
+}
 
-	*entries = bitmap_popcount(reuse);
-	if (!*entries) {
-		bitmap_free(reuse);
+static int bitmapped_pack_cmp(const void *va, const void *vb)
+{
+	const struct bitmapped_pack *a = va;
+	const struct bitmapped_pack *b = vb;
+
+	if (a->bitmap_pos < b->bitmap_pos)
 		return -1;
+	if (a->bitmap_pos > b->bitmap_pos)
+		return 1;
+	return 0;
+}
+
+void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+					struct bitmapped_pack **packs_out,
+					size_t *packs_nr_out,
+					struct bitmap **reuse_out,
+					int multi_pack_reuse)
+{
+	struct repository *r = the_repository;
+	struct bitmapped_pack *packs = NULL;
+	struct bitmap *result = bitmap_git->result;
+	struct bitmap *reuse;
+	size_t i;
+	size_t packs_nr = 0, packs_alloc = 0;
+	size_t word_alloc;
+	uint32_t objects_nr = 0;
+
+	assert(result);
+
+	load_reverse_index(r, bitmap_git);
+
+	if (bitmap_is_midx(bitmap_git)) {
+		for (i = 0; i < bitmap_git->midx->num_packs; i++) {
+			struct bitmapped_pack pack;
+			if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
+				warning(_("unable to load pack: '%s', disabling pack-reuse"),
+					bitmap_git->midx->pack_names[i]);
+				free(packs);
+				return;
+			}
+
+			if (!pack.bitmap_nr)
+				continue;
+
+			if (!multi_pack_reuse && pack.bitmap_pos) {
+				/*
+				 * If we're only reusing a single pack, skip
+				 * over any packs which are not positioned at
+				 * the beginning of the MIDX bitmap.
+				 *
+				 * This is consistent with the existing
+				 * single-pack reuse behavior, which only reuses
+				 * parts of the MIDX's preferred pack.
+				 */
+				continue;
+			}
+
+			ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+			memcpy(&packs[packs_nr++], &pack, sizeof(pack));
+
+			objects_nr += pack.p->num_objects;
+
+			if (!multi_pack_reuse)
+				break;
+		}
+
+		QSORT(packs, packs_nr, bitmapped_pack_cmp);
+	} else {
+		ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+
+		packs[packs_nr].p = bitmap_git->pack;
+		packs[packs_nr].bitmap_nr = bitmap_git->pack->num_objects;
+		packs[packs_nr].bitmap_pos = 0;
+
+		objects_nr = packs[packs_nr++].bitmap_nr;
+	}
+
+	word_alloc = objects_nr / BITS_IN_EWORD;
+	if (objects_nr % BITS_IN_EWORD)
+		word_alloc++;
+	reuse = bitmap_word_alloc(word_alloc);
+
+	for (i = 0; i < packs_nr; i++)
+		reuse_partial_packfile_from_bitmap_1(bitmap_git, &packs[i], reuse);
+
+	if (bitmap_is_empty(reuse)) {
+		free(packs);
+		bitmap_free(reuse);
+		return;
 	}
 
 	/*
@@ -1812,9 +2114,9 @@
 	 * need to be handled separately.
 	 */
 	bitmap_and_not(result, reuse);
-	*packfile_out = pack;
+	*packs_out = packs;
+	*packs_nr_out = packs_nr;
 	*reuse_out = reuse;
-	return 0;
 }
 
 int bitmap_walk_contains(struct bitmap_index *bitmap_git,
@@ -1865,7 +2167,8 @@
 
 	for (i = 0; i < eindex->count; ++i) {
 		if (eindex->objects[i]->type == type &&
-			bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))
+		    bitmap_get(objects,
+			       st_add(bitmap_num_objects(bitmap_git), i)))
 			count++;
 	}
 
@@ -1940,7 +2243,8 @@
 		    type_name(bitmap_type));
 }
 
-static void test_show_object(struct object *object, const char *name,
+static void test_show_object(struct object *object,
+			     const char *name UNUSED,
 			     void *data)
 {
 	struct bitmap_test_data *tdata = data;
@@ -2127,12 +2431,13 @@
 uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
 				struct packing_data *mapping)
 {
+	struct repository *r = the_repository;
 	uint32_t i, num_objects;
 	uint32_t *reposition;
 
 	if (!bitmap_is_midx(bitmap_git))
-		load_reverse_index(bitmap_git);
-	else if (load_midx_revindex(bitmap_git->midx) < 0)
+		load_reverse_index(r, bitmap_git);
+	else if (load_midx_revindex(bitmap_git->midx))
 		BUG("rebuild_existing_bitmaps: missing required rev-cache "
 		    "extension");
 
@@ -2277,7 +2582,8 @@
 	for (i = 0; i < eindex->count; i++) {
 		struct object *obj = eindex->objects[i];
 
-		if (!bitmap_get(result, bitmap_num_objects(bitmap_git) + i))
+		if (!bitmap_get(result,
+				st_add(bitmap_num_objects(bitmap_git), i)))
 			continue;
 
 		if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
@@ -2314,7 +2620,11 @@
 
 const struct string_list *bitmap_preferred_tips(struct repository *r)
 {
-	return repo_config_get_value_multi(r, "pack.preferbitmaptips");
+	const struct string_list *dest;
+
+	if (!repo_config_get_string_multi(r, "pack.preferbitmaptips", &dest))
+		return dest;
+	return NULL;
 }
 
 int bitmap_is_preferred_refname(struct repository *r, const char *refname)
@@ -2332,3 +2642,48 @@
 
 	return 0;
 }
+
+static int verify_bitmap_file(const char *name)
+{
+	struct stat st;
+	unsigned char *data;
+	int fd = git_open(name);
+	int res = 0;
+
+	/* It is OK to not have the file. */
+	if (fd < 0 || fstat(fd, &st)) {
+		if (fd >= 0)
+			close(fd);
+		return 0;
+	}
+
+	data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	close(fd);
+	if (!hashfile_checksum_valid(data, st.st_size))
+		res = error(_("bitmap file '%s' has invalid checksum"),
+			    name);
+
+	munmap(data, st.st_size);
+	return res;
+}
+
+int verify_bitmap_files(struct repository *r)
+{
+	int res = 0;
+
+	for (struct multi_pack_index *m = get_multi_pack_index(r);
+	     m; m = m->next) {
+		char *midx_bitmap_name = midx_bitmap_filename(m);
+		res |= verify_bitmap_file(midx_bitmap_name);
+		free(midx_bitmap_name);
+	}
+
+	for (struct packed_git *p = get_all_packs(r);
+	     p; p = p->next) {
+		char *pack_bitmap_name = pack_bitmap_filename(p);
+		res |= verify_bitmap_file(pack_bitmap_name);
+		free(pack_bitmap_name);
+	}
+
+	return res;
+}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index f0180b5..c7dea13 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -52,6 +52,15 @@
 
 struct bitmap_index;
 
+struct bitmapped_pack {
+	struct packed_git *p;
+
+	uint32_t bitmap_pos;
+	uint32_t bitmap_nr;
+
+	uint32_t pack_int_id; /* MIDX only */
+};
+
 struct bitmap_index *prepare_bitmap_git(struct repository *r);
 struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx);
 void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
@@ -62,13 +71,17 @@
 void test_bitmap_walk(struct rev_info *revs);
 int test_bitmap_commits(struct repository *r);
 int test_bitmap_hashes(struct repository *r);
+
+#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
+	"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
+
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
 					 int filter_provided_objects);
-uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
-int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
-				       struct packed_git **packfile,
-				       uint32_t *entries,
-				       struct bitmap **reuse_out);
+void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+					struct bitmapped_pack **packs_out,
+					size_t *packs_nr_out,
+					struct bitmap **reuse_out,
+					int multi_pack_reuse);
 int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,
 			     kh_oid_map_t *reused_bitmaps, int show_progress);
 void free_bitmap_index(struct bitmap_index *);
@@ -111,4 +124,6 @@
 const struct string_list *bitmap_preferred_tips(struct repository *r);
 int bitmap_is_preferred_refname(struct repository *r, const char *refname);
 
+int verify_bitmap_files(struct repository *r);
+
 #endif
diff --git a/pack-check.c b/pack-check.c
index bfb593b..25104d5 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,10 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "pack.h"
-#include "pack-revindex.h"
 #include "progress.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 
 struct idx_entry {
 	off_t                offset;
diff --git a/pack-mtimes.c b/pack-mtimes.c
index 0f9785f..cdf30b8 100644
--- a/pack-mtimes.c
+++ b/pack-mtimes.c
@@ -1,7 +1,10 @@
 #include "git-compat-util.h"
+#include "gettext.h"
 #include "pack-mtimes.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "packfile.h"
+#include "strbuf.h"
 
 static char *pack_mtimes_filename(struct packed_git *p)
 {
diff --git a/pack-mtimes.h b/pack-mtimes.h
index cc957b3..107327c 100644
--- a/pack-mtimes.h
+++ b/pack-mtimes.h
@@ -1,8 +1,6 @@
 #ifndef PACK_MTIMES_H
 #define PACK_MTIMES_H
 
-#include "git-compat-util.h"
-
 #define MTIMES_SIGNATURE 0x4d544d45 /* "MTME" */
 #define MTIMES_VERSION 1
 
diff --git a/pack-objects.c b/pack-objects.c
index 272e8d4..a9d9855 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -1,9 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "pack.h"
 #include "pack-objects.h"
 #include "packfile.h"
-#include "config.h"
+#include "parse.h"
 
 static uint32_t locate_object_entry_hash(struct packing_data *pdata,
 					 const struct object_id *oid,
@@ -151,6 +151,21 @@
 	init_recursive_mutex(&pdata->odb_lock);
 }
 
+void clear_packing_data(struct packing_data *pdata)
+{
+	if (!pdata)
+		return;
+
+	free(pdata->cruft_mtime);
+	free(pdata->in_pack);
+	free(pdata->in_pack_by_idx);
+	free(pdata->in_pack_pos);
+	free(pdata->index);
+	free(pdata->layer);
+	free(pdata->objects);
+	free(pdata->tree_depth);
+}
+
 struct object_entry *packlist_alloc(struct packing_data *pdata,
 				    const struct object_id *oid)
 {
diff --git a/pack-objects.h b/pack-objects.h
index 5794766..b9898a4 100644
--- a/pack-objects.h
+++ b/pack-objects.h
@@ -1,7 +1,7 @@
 #ifndef PACK_OBJECTS_H
 #define PACK_OBJECTS_H
 
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "thread-utils.h"
 #include "pack.h"
 
@@ -169,6 +169,7 @@
 };
 
 void prepare_packing_data(struct repository *r, struct packing_data *pdata);
+void clear_packing_data(struct packing_data *pdata);
 
 /* Protect access to object database */
 static inline void packing_data_lock(struct packing_data *pdata)
diff --git a/pack-revindex.c b/pack-revindex.c
index 08dc160..a7624d8 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -1,9 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "pack-revindex.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "packfile.h"
-#include "config.h"
+#include "strbuf.h"
+#include "trace2.h"
+#include "parse.h"
 #include "midx.h"
+#include "csum-file.h"
 
 struct revindex_entry {
 	off_t offset;
@@ -204,10 +209,14 @@
 	size_t revindex_size;
 	struct revindex_header *hdr;
 
+	if (git_env_bool(GIT_TEST_REV_INDEX_DIE_ON_DISK, 0))
+		die("dying as requested by '%s'", GIT_TEST_REV_INDEX_DIE_ON_DISK);
+
 	fd = git_open(revindex_name);
 
 	if (fd < 0) {
-		ret = -1;
+		/* "No file" means return 1. */
+		ret = 1;
 		goto cleanup;
 	}
 	if (fstat(fd, &st)) {
@@ -259,7 +268,7 @@
 	return ret;
 }
 
-static int load_pack_revindex_from_disk(struct packed_git *p)
+int load_pack_revindex_from_disk(struct packed_git *p)
 {
 	char *revindex_name;
 	int ret;
@@ -282,18 +291,69 @@
 	return ret;
 }
 
-int load_pack_revindex(struct packed_git *p)
+int load_pack_revindex(struct repository *r, struct packed_git *p)
 {
 	if (p->revindex || p->revindex_data)
 		return 0;
 
-	if (!load_pack_revindex_from_disk(p))
+	prepare_repo_settings(r);
+
+	if (r->settings.pack_read_reverse_index &&
+	    !load_pack_revindex_from_disk(p))
 		return 0;
 	else if (!create_pack_revindex_in_memory(p))
 		return 0;
 	return -1;
 }
 
+/*
+ * verify_pack_revindex verifies that the on-disk rev-index for the given
+ * pack-file is the same that would be created if written from scratch.
+ *
+ * A negative number is returned on error.
+ */
+int verify_pack_revindex(struct packed_git *p)
+{
+	int res = 0;
+
+	/* Do not bother checking if not initialized. */
+	if (!p->revindex_map || !p->revindex_data)
+		return res;
+
+	if (!hashfile_checksum_valid((const unsigned char *)p->revindex_map, p->revindex_size)) {
+		error(_("invalid checksum"));
+		res = -1;
+	}
+
+	/* This may fail due to a broken .idx. */
+	if (create_pack_revindex_in_memory(p))
+		return res;
+
+	for (size_t i = 0; i < p->num_objects; i++) {
+		uint32_t nr = p->revindex[i].nr;
+		uint32_t rev_val = get_be32(p->revindex_data + i);
+
+		if (nr != rev_val) {
+			error(_("invalid rev-index position at %"PRIu64": %"PRIu32" != %"PRIu32""),
+			      (uint64_t)i, nr, rev_val);
+			res = -1;
+		}
+	}
+
+	return res;
+}
+
+static int can_use_midx_ridx_chunk(struct multi_pack_index *m)
+{
+	if (!m->chunk_revindex)
+		return 0;
+	if (m->chunk_revindex_len != st_mult(sizeof(uint32_t), m->num_objects)) {
+		error(_("multi-pack-index reverse-index chunk is the wrong size"));
+		return 0;
+	}
+	return 1;
+}
+
 int load_midx_revindex(struct multi_pack_index *m)
 {
 	struct strbuf revindex_name = STRBUF_INIT;
@@ -302,7 +362,7 @@
 	if (m->revindex_data)
 		return 0;
 
-	if (m->chunk_revindex) {
+	if (can_use_midx_ridx_chunk(m)) {
 		/*
 		 * If the MIDX `m` has a `RIDX` chunk, then use its contents for
 		 * the reverse index instead of trying to load a separate `.rev`
@@ -355,7 +415,7 @@
 {
 	unsigned lo, hi;
 
-	if (load_pack_revindex(p) < 0)
+	if (load_pack_revindex(the_repository, p) < 0)
 		return -1;
 
 	lo = 0;
@@ -460,19 +520,12 @@
 	return 0;
 }
 
-int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
+static int midx_key_to_pack_pos(struct multi_pack_index *m,
+				struct midx_pack_key *key,
+				uint32_t *pos)
 {
-	struct midx_pack_key key;
 	uint32_t *found;
 
-	if (!m->revindex_data)
-		BUG("midx_to_pack_pos: reverse index not yet loaded");
-	if (m->num_objects <= at)
-		BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
-
-	key.pack = nth_midxed_pack_int_id(m, at);
-	key.offset = nth_midxed_offset(m, at);
-	key.midx = m;
 	/*
 	 * The preferred pack sorts first, so determine its identifier by
 	 * looking at the first object in pseudo-pack order.
@@ -482,14 +535,43 @@
 	 * implicitly is preferred (and includes all its objects, since ties are
 	 * broken first by pack identifier).
 	 */
-	key.preferred_pack = nth_midxed_pack_int_id(m, pack_pos_to_midx(m, 0));
+	if (midx_preferred_pack(key->midx, &key->preferred_pack) < 0)
+		return error(_("could not determine preferred pack"));
 
-	found = bsearch(&key, m->revindex_data, m->num_objects,
-			sizeof(*m->revindex_data), midx_pack_order_cmp);
+	found = bsearch(key, m->revindex_data, m->num_objects,
+			sizeof(*m->revindex_data),
+			midx_pack_order_cmp);
 
 	if (!found)
-		return error("bad offset for revindex");
+		return -1;
 
 	*pos = found - m->revindex_data;
 	return 0;
 }
+
+int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
+{
+	struct midx_pack_key key;
+
+	if (!m->revindex_data)
+		BUG("midx_to_pack_pos: reverse index not yet loaded");
+	if (m->num_objects <= at)
+		BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
+
+	key.pack = nth_midxed_pack_int_id(m, at);
+	key.offset = nth_midxed_offset(m, at);
+	key.midx = m;
+
+	return midx_key_to_pack_pos(m, &key, pos);
+}
+
+int midx_pair_to_pack_pos(struct multi_pack_index *m, uint32_t pack_int_id,
+			  off_t ofs, uint32_t *pos)
+{
+	struct midx_pack_key key = {
+		.pack = pack_int_id,
+		.offset = ofs,
+		.midx = m,
+	};
+	return midx_key_to_pack_pos(m, &key, pos);
+}
diff --git a/pack-revindex.h b/pack-revindex.h
index 4974e75..422c248 100644
--- a/pack-revindex.h
+++ b/pack-revindex.h
@@ -34,11 +34,13 @@
 #define RIDX_SIGNATURE 0x52494458 /* "RIDX" */
 #define RIDX_VERSION 1
 
-#define GIT_TEST_WRITE_REV_INDEX "GIT_TEST_WRITE_REV_INDEX"
+#define GIT_TEST_NO_WRITE_REV_INDEX "GIT_TEST_NO_WRITE_REV_INDEX"
 #define GIT_TEST_REV_INDEX_DIE_IN_MEMORY "GIT_TEST_REV_INDEX_DIE_IN_MEMORY"
+#define GIT_TEST_REV_INDEX_DIE_ON_DISK "GIT_TEST_REV_INDEX_DIE_ON_DISK"
 
 struct packed_git;
 struct multi_pack_index;
+struct repository;
 
 /*
  * load_pack_revindex populates the revindex's internal data-structures for the
@@ -47,7 +49,23 @@
  * If a '.rev' file is present it is mmap'd, and pointers are assigned into it
  * (instead of using the in-memory variant).
  */
-int load_pack_revindex(struct packed_git *p);
+int load_pack_revindex(struct repository *r, struct packed_git *p);
+
+/*
+ * Specifically load a pack revindex from disk.
+ *
+ * Returns 0 on success, 1 on "no .rev file", and -1 when there is an
+ * error parsing the .rev file.
+ */
+int load_pack_revindex_from_disk(struct packed_git *p);
+
+/*
+ * verify_pack_revindex verifies that the on-disk rev-index for the given
+ * pack-file is the same that would be created if written from scratch.
+ *
+ * A negative number is returned on error.
+ */
+int verify_pack_revindex(struct packed_git *p);
 
 /*
  * load_midx_revindex loads the '.rev' file corresponding to the given
@@ -124,4 +142,7 @@
  */
 int midx_to_pack_pos(struct multi_pack_index *midx, uint32_t at, uint32_t *pos);
 
+int midx_pair_to_pack_pos(struct multi_pack_index *midx, uint32_t pack_id,
+			  off_t ofs, uint32_t *pos);
+
 #endif
diff --git a/pack-write.c b/pack-write.c
index 3363729..80ecfa5 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,11 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "pack.h"
 #include "csum-file.h"
 #include "remote.h"
 #include "chunk-format.h"
 #include "pack-mtimes.h"
-#include "oidmap.h"
 #include "pack-objects.h"
+#include "pack-revindex.h"
+#include "path.h"
+#include "strbuf.h"
 
 void reset_pack_idx_option(struct pack_idx_option *opts)
 {
@@ -309,13 +314,13 @@
 	hashwrite(f, hash, the_hash_algo->rawsz);
 }
 
-static const char *write_mtimes_file(struct packing_data *to_pack,
-				     struct pack_idx_entry **objects,
-				     uint32_t nr_objects,
-				     const unsigned char *hash)
+static char *write_mtimes_file(struct packing_data *to_pack,
+			       struct pack_idx_entry **objects,
+			       uint32_t nr_objects,
+			       const unsigned char *hash)
 {
 	struct strbuf tmp_file = STRBUF_INIT;
-	const char *mtimes_name;
+	char *mtimes_name;
 	struct hashfile *f;
 	int fd;
 
@@ -541,7 +546,7 @@
 			 char **idx_tmp_name)
 {
 	const char *rev_tmp_name = NULL;
-	const char *mtimes_tmp_name = NULL;
+	char *mtimes_tmp_name = NULL;
 
 	if (adjust_shared_perm(pack_tmp_name))
 		die_errno("unable to make temporary pack file readable");
@@ -565,6 +570,9 @@
 		rename_tmp_packfile(name_buffer, rev_tmp_name, "rev");
 	if (mtimes_tmp_name)
 		rename_tmp_packfile(name_buffer, mtimes_tmp_name, "mtimes");
+
+	free((char *)rev_tmp_name);
+	free(mtimes_tmp_name);
 }
 
 void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought)
diff --git a/pack.h b/pack.h
index 01d3859..3ab9e3f 100644
--- a/pack.h
+++ b/pack.h
@@ -4,6 +4,8 @@
 #include "object.h"
 #include "csum-file.h"
 
+struct packed_git;
+struct pack_window;
 struct repository;
 
 /*
diff --git a/packfile.c b/packfile.c
index 79e21ab..d4df7fd 100644
--- a/packfile.c
+++ b/packfile.c
@@ -1,4 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "list.h"
 #include "pack.h"
 #include "repository.h"
@@ -6,16 +9,18 @@
 #include "mergesort.h"
 #include "packfile.h"
 #include "delta.h"
-#include "streaming.h"
 #include "hash-lookup.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
+#include "trace.h"
 #include "tree-walk.h"
 #include "tree.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "midx.h"
 #include "commit-graph.h"
+#include "pack-revindex.h"
 #include "promisor-remote.h"
 
 char *odb_pack_name(struct strbuf *buf,
@@ -178,7 +183,7 @@
 		     */
 		    (sizeof(off_t) <= 4))
 			return error("pack too large for current definition of off_t in %s", path);
-		p->crc_offset = 8 + 4 * 256 + nr * hashsz;
+		p->crc_offset = st_add(8 + 4 * 256, st_mult(nr, hashsz));
 	}
 
 	p->index_version = version;
@@ -373,7 +378,7 @@
 
 void unlink_pack_path(const char *pack_name, int force_delete)
 {
-	static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
+	static const char *exts[] = {".idx", ".pack", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
 	int i;
 	struct strbuf buf = STRBUF_INIT;
 	size_t plen;
@@ -745,7 +750,7 @@
 	p->pack_local = local;
 	p->mtime = st.st_mtime;
 	if (path_len < the_hash_algo->hexsz ||
-	    get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash))
+	    get_hash_hex(path + path_len - the_hash_algo->hexsz, p->hash))
 		hashclr(p->hash);
 	return p;
 }
@@ -1008,6 +1013,16 @@
 	struct object_directory *odb;
 
 	obj_read_lock();
+
+	/*
+	 * Reprepare alt odbs, in case the alternates file was modified
+	 * during the course of this process. This only _adds_ odbs to
+	 * the linked list, so existing odbs will continue to exist for
+	 * the lifetime of the process.
+	 */
+	r->objects->loaded_alternates = 0;
+	prepare_alt_odb(r);
+
 	for (odb = r->objects->odb; odb; odb = odb->next)
 		odb_clear_loose_cache(odb);
 
@@ -1902,10 +1917,10 @@
 		return -1;
 	index += 4 * 256;
 	if (p->index_version == 1) {
-		oidread(oid, index + (hashsz + 4) * n + 4);
+		oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4));
 	} else {
 		index += 8;
-		oidread(oid, index + hashsz * n);
+		oidread(oid, index + st_mult(hashsz, n));
 	}
 	return 0;
 }
@@ -1930,14 +1945,15 @@
 	const unsigned int hashsz = the_hash_algo->rawsz;
 	index += 4 * 256;
 	if (p->index_version == 1) {
-		return ntohl(*((uint32_t *)(index + (hashsz + 4) * (size_t)n)));
+		return ntohl(*((uint32_t *)(index + st_mult(hashsz + 4, n))));
 	} else {
 		uint32_t off;
-		index += 8 + (size_t)p->num_objects * (hashsz + 4);
-		off = ntohl(*((uint32_t *)(index + 4 * n)));
+		index += st_add(8, st_mult(p->num_objects, hashsz + 4));
+		off = ntohl(*((uint32_t *)(index + st_mult(4, n))));
 		if (!(off & 0x80000000))
 			return off;
-		index += (size_t)p->num_objects * 4 + (off & 0x7fffffff) * 8;
+		index += st_add(st_mult(p->num_objects, 4),
+				st_mult(off & 0x7fffffff, 8));
 		check_pack_index_ptr(p, index);
 		return get_be64(index);
 	}
@@ -2136,7 +2152,7 @@
 	int r = 0;
 
 	if (flags & FOR_EACH_OBJECT_PACK_ORDER) {
-		if (load_pack_revindex(p))
+		if (load_pack_revindex(the_repository, p))
 			return -1;
 	}
 
@@ -2204,8 +2220,8 @@
 }
 
 static int add_promisor_object(const struct object_id *oid,
-			       struct packed_git *pack,
-			       uint32_t pos,
+			       struct packed_git *pack UNUSED,
+			       uint32_t pos UNUSED,
 			       void *set_)
 {
 	struct oidset *set = set_;
@@ -2233,7 +2249,8 @@
 		struct tree *tree = (struct tree *)obj;
 		struct tree_desc desc;
 		struct name_entry entry;
-		if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+		if (init_tree_desc_gently(&desc, &tree->object.oid,
+					  tree->buffer, tree->size, 0))
 			/*
 			 * Error messages are given when packs are
 			 * verified, so do not print any here.
@@ -2263,7 +2280,7 @@
 	static int promisor_objects_prepared;
 
 	if (!promisor_objects_prepared) {
-		if (has_promisor_remote()) {
+		if (repo_has_promisor_remote(the_repository)) {
 			for_each_packed_object(add_promisor_object,
 					       &promisor_objects,
 					       FOR_EACH_OBJECT_PROMISOR_ONLY |
diff --git a/packfile.h b/packfile.h
index a3f6723..28c8fd3 100644
--- a/packfile.h
+++ b/packfile.h
@@ -1,13 +1,27 @@
 #ifndef PACKFILE_H
 #define PACKFILE_H
 
-#include "cache.h"
+#include "object.h"
 #include "oidset.h"
 
 /* in object-store.h */
 struct packed_git;
 struct object_info;
 
+struct pack_window {
+	struct pack_window *next;
+	unsigned char *base;
+	off_t offset;
+	size_t len;
+	unsigned int last_used;
+	unsigned int inuse_cnt;
+};
+
+struct pack_entry {
+	off_t offset;
+	struct packed_git *p;
+};
+
 /*
  * Generate the filename to be used for a pack file with checksum "sha1" and
  * extension "ext". The result is written into the strbuf "buf", overwriting
@@ -40,7 +54,7 @@
 struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 typedef void each_file_in_pack_dir_fn(const char *full_path, size_t full_path_len,
-				      const char *file_pach, void *data);
+				      const char *file_name, void *data);
 void for_each_file_in_pack_dir(const char *objdir,
 			       each_file_in_pack_dir_fn fn,
 			       void *data);
@@ -65,7 +79,6 @@
  * for speed.
  */
 unsigned long repo_approximate_object_count(struct repository *r);
-#define approximate_object_count() repo_approximate_object_count(the_repository)
 
 struct packed_git *find_sha1_pack(const unsigned char *sha1,
 				  struct packed_git *packs);
diff --git a/pager.c b/pager.c
index b66bbff..b8822a9 100644
--- a/pager.c
+++ b/pager.c
@@ -1,9 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "editor.h"
+#include "pager.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "alias.h"
 
+int pager_use_color = 1;
+
 #ifndef DEFAULT_PAGER
 #define DEFAULT_PAGER "less"
 #endif
@@ -39,6 +43,7 @@
 }
 
 static int core_pager_config(const char *var, const char *value,
+			     const struct config_context *ctx UNUSED,
 			     void *data UNUSED)
 {
 	if (!strcmp(var, "core.pager"))
@@ -224,7 +229,9 @@
 	char *value;
 };
 
-static int pager_command_config(const char *var, const char *value, void *vdata)
+static int pager_command_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *vdata)
 {
 	struct pager_command_config_data *data = vdata;
 	const char *cmd;
diff --git a/pager.h b/pager.h
new file mode 100644
index 0000000..b774330
--- /dev/null
+++ b/pager.h
@@ -0,0 +1,17 @@
+#ifndef PAGER_H
+#define PAGER_H
+
+struct child_process;
+
+const char *git_pager(int stdout_is_tty);
+void setup_pager(void);
+int pager_in_use(void);
+int term_columns(void);
+void term_clear_line(void);
+int decimal_width(uintmax_t);
+int check_pager_config(const char *cmd);
+void prepare_pager_args(struct child_process *, const char *pager);
+
+extern int pager_use_color;
+
+#endif /* PAGER_H */
diff --git a/parallel-checkout.c b/parallel-checkout.c
index 4f6819f..b5a714c 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -1,12 +1,17 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "entry.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "parallel-checkout.h"
 #include "pkt-line.h"
 #include "progress.h"
+#include "read-cache-ll.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "streaming.h"
+#include "symlinks.h"
 #include "thread-utils.h"
 #include "trace2.h"
 
diff --git a/parse-options-cb.c b/parse-options-cb.c
index d346dbe..bdc7fae 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -1,9 +1,12 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
 #include "branch.h"
-#include "cache.h"
 #include "commit.h"
 #include "color.h"
+#include "date.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-name.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "oid-array.h"
@@ -91,7 +94,7 @@
 
 	if (!arg)
 		return -1;
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		return error("malformed object name %s", arg);
 	commit = lookup_commit_reference(the_repository, &oid);
 	if (!commit)
@@ -110,7 +113,7 @@
 
 	if (!arg)
 		return -1;
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		return error("malformed object name %s", arg);
 	commit = lookup_commit_reference(the_repository, &oid);
 	if (!commit)
@@ -129,7 +132,7 @@
 	}
 	if (!arg)
 		return -1;
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		return error(_("malformed object name '%s'"), arg);
 	oid_array_append(opt->value, &oid);
 	return 0;
@@ -146,7 +149,7 @@
 	}
 	if (!arg)
 		return -1;
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		return error(_("malformed object name '%s'"), arg);
 	*target = oid;
 	return 0;
@@ -208,24 +211,27 @@
 	return 0;
 }
 
-int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
+int parse_opt_strvec(const struct option *opt, const char *arg, int unset)
 {
+	struct strvec *v = opt->value;
+
+	if (unset) {
+		strvec_clear(v);
+		return 0;
+	}
+
+	if (!arg)
+		return -1;
+
+	strvec_push(v, arg);
 	return 0;
 }
 
-/**
- * Report that the option is unknown, so that other code can handle
- * it. This can be used as a callback together with
- * OPTION_LOWLEVEL_CALLBACK to allow an option to be documented in the
- * "-h" output even if it's not being handled directly by
- * parse_options().
- */
-enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
-					   const struct option *opt,
-					   const char *arg, int unset)
+int parse_opt_noop_cb(const struct option *opt UNUSED,
+		      const char *arg UNUSED,
+		      int unset UNUSED)
 {
-	BUG_ON_OPT_ARG(arg);
-	return PARSE_OPT_UNKNOWN;
+	return 0;
 }
 
 /**
diff --git a/parse-options.c b/parse-options.c
index fd47432..30b9e68 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1,9 +1,10 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
-#include "cache.h"
-#include "config.h"
-#include "commit.h"
-#include "color.h"
+#include "abspath.h"
+#include "parse.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "string-list.h"
 #include "utf8.h"
 
 static int disallow_abbreviated_options;
@@ -59,50 +60,18 @@
 	return 0;
 }
 
-static void fix_filename(const char *prefix, const char **file)
+static void fix_filename(const char *prefix, char **file)
 {
-	if (!file || !*file || !prefix || is_absolute_path(*file)
-	    || !strcmp("-", *file))
-		return;
-	*file = prefix_filename(prefix, *file);
+	if (!file || !*file)
+		; /* leave as NULL */
+	else
+		*file = prefix_filename_except_for_dash(prefix, *file);
 }
 
-static enum parse_opt_result opt_command_mode_error(
-	const struct option *opt,
-	const struct option *all_opts,
-	enum opt_parsed flags)
-{
-	const struct option *that;
-	struct strbuf that_name = STRBUF_INIT;
-
-	/*
-	 * Find the other option that was used to set the variable
-	 * already, and report that this is not compatible with it.
-	 */
-	for (that = all_opts; that->type != OPTION_END; that++) {
-		if (that == opt ||
-		    !(that->flags & PARSE_OPT_CMDMODE) ||
-		    that->value != opt->value ||
-		    that->defval != *(int *)opt->value)
-			continue;
-
-		if (that->long_name)
-			strbuf_addf(&that_name, "--%s", that->long_name);
-		else
-			strbuf_addf(&that_name, "-%c", that->short_name);
-		error(_("%s is incompatible with %s"),
-		      optname(opt, flags), that_name.buf);
-		strbuf_release(&that_name);
-		return PARSE_OPT_ERROR;
-	}
-	return error(_("%s : incompatible with something else"),
-		     optname(opt, flags));
-}
-
-static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
-				       const struct option *opt,
-				       const struct option *all_opts,
-				       enum opt_parsed flags)
+static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
+					  const struct option *opt,
+					  enum opt_parsed flags,
+					  const char **argp)
 {
 	const char *s, *arg;
 	const int unset = flags & OPT_UNSET;
@@ -115,14 +84,6 @@
 	if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
 		return error(_("%s takes no value"), optname(opt, flags));
 
-	/*
-	 * Giving the same mode option twice, although unnecessary,
-	 * is not a grave error, so let it pass.
-	 */
-	if ((opt->flags & PARSE_OPT_CMDMODE) &&
-	    *(int *)opt->value && *(int *)opt->value != opt->defval)
-		return opt_command_mode_error(opt, all_opts, flags);
-
 	switch (opt->type) {
 	case OPTION_LOWLEVEL_CALLBACK:
 		return opt->ll_callback(p, opt, NULL, unset);
@@ -177,7 +138,7 @@
 			err = get_arg(p, opt, flags, (const char **)opt->value);
 
 		if (!err)
-			fix_filename(p->prefix, (const char **)opt->value);
+			fix_filename(p->prefix, (char **)opt->value);
 		return err;
 
 	case OPTION_CALLBACK:
@@ -197,6 +158,8 @@
 			p_unset = 0;
 			p_arg = arg;
 		}
+		if (opt->flags & PARSE_OPT_CMDMODE)
+			*argp = p_arg;
 		if (opt->callback)
 			return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0;
 		else
@@ -244,16 +207,92 @@
 	}
 }
 
+struct parse_opt_cmdmode_list {
+	int value, *value_ptr;
+	const struct option *opt;
+	const char *arg;
+	enum opt_parsed flags;
+	struct parse_opt_cmdmode_list *next;
+};
+
+static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
+			       const struct option *opts)
+{
+	ctx->cmdmode_list = NULL;
+
+	for (; opts->type != OPTION_END; opts++) {
+		struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list;
+		int *value_ptr = opts->value;
+
+		if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)
+			continue;
+
+		while (elem && elem->value_ptr != value_ptr)
+			elem = elem->next;
+		if (elem)
+			continue;
+
+		CALLOC_ARRAY(elem, 1);
+		elem->value_ptr = value_ptr;
+		elem->value = *value_ptr;
+		elem->next = ctx->cmdmode_list;
+		ctx->cmdmode_list = elem;
+	}
+}
+
+static char *optnamearg(const struct option *opt, const char *arg,
+			enum opt_parsed flags)
+{
+	if (flags & OPT_SHORT)
+		return xstrfmt("-%c%s", opt->short_name, arg ? arg : "");
+	return xstrfmt("--%s%s%s%s", flags & OPT_UNSET ? "no-" : "",
+		       opt->long_name, arg ? "=" : "", arg ? arg : "");
+}
+
+static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
+				       const struct option *opt,
+				       enum opt_parsed flags)
+{
+	const char *arg = NULL;
+	enum parse_opt_result result = do_get_value(p, opt, flags, &arg);
+	struct parse_opt_cmdmode_list *elem = p->cmdmode_list;
+	char *opt_name, *other_opt_name;
+
+	for (; elem; elem = elem->next) {
+		if (*elem->value_ptr == elem->value)
+			continue;
+
+		if (elem->opt &&
+		    (elem->opt->flags | opt->flags) & PARSE_OPT_CMDMODE)
+			break;
+
+		elem->opt = opt;
+		elem->arg = arg;
+		elem->flags = flags;
+		elem->value = *elem->value_ptr;
+	}
+
+	if (result || !elem)
+		return result;
+
+	opt_name = optnamearg(opt, arg, flags);
+	other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
+	error(_("options '%s' and '%s' cannot be used together"),
+	      opt_name, other_opt_name);
+	free(opt_name);
+	free(other_opt_name);
+	return -1;
+}
+
 static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
 					     const struct option *options)
 {
-	const struct option *all_opts = options;
 	const struct option *numopt = NULL;
 
 	for (; options->type != OPTION_END; options++) {
 		if (options->short_name == *p->opt) {
 			p->opt = p->opt[1] ? p->opt + 1 : NULL;
-			return get_value(p, options, all_opts, OPT_SHORT);
+			return get_value(p, options, OPT_SHORT);
 		}
 
 		/*
@@ -311,98 +350,107 @@
 	return 0;
 }
 
+struct parsed_option {
+	const struct option *option;
+	enum opt_parsed flags;
+};
+
+static void register_abbrev(struct parse_opt_ctx_t *p,
+			    const struct option *option, enum opt_parsed flags,
+			    struct parsed_option *abbrev,
+			    struct parsed_option *ambiguous)
+{
+	if (p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)
+		return;
+	if (abbrev->option &&
+	    !(abbrev->flags == flags && is_alias(p, abbrev->option, option))) {
+		/*
+		 * If this is abbreviated, it is
+		 * ambiguous. So when there is no
+		 * exact match later, we need to
+		 * error out.
+		 */
+		ambiguous->option = abbrev->option;
+		ambiguous->flags = abbrev->flags;
+	}
+	abbrev->option = option;
+	abbrev->flags = flags;
+}
+
 static enum parse_opt_result parse_long_opt(
 	struct parse_opt_ctx_t *p, const char *arg,
 	const struct option *options)
 {
-	const struct option *all_opts = options;
 	const char *arg_end = strchrnul(arg, '=');
-	const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
-	enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
+	const char *arg_start = arg;
+	enum opt_parsed flags = OPT_LONG;
+	int arg_starts_with_no_no = 0;
+	struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG };
+	struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG };
+
+	if (skip_prefix(arg_start, "no-", &arg_start)) {
+		if (skip_prefix(arg_start, "no-", &arg_start))
+			arg_starts_with_no_no = 1;
+		else
+			flags |= OPT_UNSET;
+	}
 
 	for (; options->type != OPTION_END; options++) {
 		const char *rest, *long_name = options->long_name;
-		enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG;
+		enum opt_parsed opt_flags = OPT_LONG;
+		int allow_unset = !(options->flags & PARSE_OPT_NONEG);
 
 		if (options->type == OPTION_SUBCOMMAND)
 			continue;
 		if (!long_name)
 			continue;
 
-again:
-		if (!skip_prefix(arg, long_name, &rest))
-			rest = NULL;
-		if (!rest) {
-			/* abbreviated? */
-			if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT) &&
-			    !strncmp(long_name, arg, arg_end - arg)) {
-is_abbreviated:
-				if (abbrev_option &&
-				    !is_alias(p, abbrev_option, options)) {
-					/*
-					 * If this is abbreviated, it is
-					 * ambiguous. So when there is no
-					 * exact match later, we need to
-					 * error out.
-					 */
-					ambiguous_option = abbrev_option;
-					ambiguous_flags = abbrev_flags;
-				}
-				if (!(flags & OPT_UNSET) && *arg_end)
-					p->opt = arg_end + 1;
-				abbrev_option = options;
-				abbrev_flags = flags ^ opt_flags;
+		if (skip_prefix(long_name, "no-", &long_name))
+			opt_flags |= OPT_UNSET;
+		else if (arg_starts_with_no_no)
+			continue;
+
+		if (((flags ^ opt_flags) & OPT_UNSET) && !allow_unset)
+			continue;
+
+		if (skip_prefix(arg_start, long_name, &rest)) {
+			if (*rest == '=')
+				p->opt = rest + 1;
+			else if (*rest)
 				continue;
-			}
-			/* negation allowed? */
-			if (options->flags & PARSE_OPT_NONEG)
-				continue;
-			/* negated and abbreviated very much? */
-			if (starts_with("no-", arg)) {
-				flags |= OPT_UNSET;
-				goto is_abbreviated;
-			}
-			/* negated? */
-			if (!starts_with(arg, "no-")) {
-				if (skip_prefix(long_name, "no-", &long_name)) {
-					opt_flags |= OPT_UNSET;
-					goto again;
-				}
-				continue;
-			}
-			flags |= OPT_UNSET;
-			if (!skip_prefix(arg + 3, long_name, &rest)) {
-				/* abbreviated and negated? */
-				if (starts_with(long_name, arg + 3))
-					goto is_abbreviated;
-				else
-					continue;
-			}
+			return get_value(p, options, flags ^ opt_flags);
 		}
-		if (*rest) {
-			if (*rest != '=')
-				continue;
-			p->opt = rest + 1;
-		}
-		return get_value(p, options, all_opts, flags ^ opt_flags);
+
+		/* abbreviated? */
+		if (!strncmp(long_name, arg_start, arg_end - arg_start))
+			register_abbrev(p, options, flags ^ opt_flags,
+					&abbrev, &ambiguous);
+
+		/* negated and abbreviated very much? */
+		if (allow_unset && starts_with("no-", arg))
+			register_abbrev(p, options, OPT_UNSET ^ opt_flags,
+					&abbrev, &ambiguous);
 	}
 
-	if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+	if (disallow_abbreviated_options && (ambiguous.option || abbrev.option))
 		die("disallowed abbreviated or ambiguous option '%.*s'",
 		    (int)(arg_end - arg), arg);
 
-	if (ambiguous_option) {
+	if (ambiguous.option) {
 		error(_("ambiguous option: %s "
 			"(could be --%s%s or --%s%s)"),
 			arg,
-			(ambiguous_flags & OPT_UNSET) ?  "no-" : "",
-			ambiguous_option->long_name,
-			(abbrev_flags & OPT_UNSET) ?  "no-" : "",
-			abbrev_option->long_name);
+			(ambiguous.flags & OPT_UNSET) ?  "no-" : "",
+			ambiguous.option->long_name,
+			(abbrev.flags & OPT_UNSET) ?  "no-" : "",
+			abbrev.option->long_name);
 		return PARSE_OPT_HELP;
 	}
-	if (abbrev_option)
-		return get_value(p, abbrev_option, all_opts, abbrev_flags);
+	if (abbrev.option) {
+		if (*arg_end)
+			p->opt = arg_end + 1;
+		return get_value(p, abbrev.option, abbrev.flags);
+	}
 	return PARSE_OPT_UNKNOWN;
 }
 
@@ -410,13 +458,11 @@
 					      const char *arg,
 					      const struct option *options)
 {
-	const struct option *all_opts = options;
-
 	for (; options->type != OPTION_END; options++) {
 		if (!(options->flags & PARSE_OPT_NODASH))
 			continue;
 		if (options->short_name == arg[0] && arg[1] == '\0')
-			return get_value(p, options, all_opts, OPT_SHORT);
+			return get_value(p, options, OPT_SHORT);
 	}
 	return PARSE_OPT_ERROR;
 }
@@ -478,6 +524,9 @@
 		     opts->long_name))
 			optbug(opts, "uses feature "
 			       "not supported for dashless options");
+		if (opts->type == OPTION_SET_INT && !opts->defval &&
+		    opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
+			optbug(opts, "OPTION_SET_INT 0 should not be negatable");
 		switch (opts->type) {
 		case OPTION_COUNTUP:
 		case OPTION_BIT:
@@ -568,6 +617,7 @@
 	    (flags & PARSE_OPT_KEEP_ARGV0))
 		BUG("Can't keep argv0 if you don't have it");
 	parse_options_check(options);
+	build_cmdmode_list(ctx, options);
 }
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
@@ -888,13 +938,18 @@
 			continue;
 		}
 
-		if (!arg[2] /* "--" */ ||
-		    !strcmp(arg + 2, "end-of-options")) {
+		if (!arg[2] /* "--" */) {
 			if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
 				ctx->argc--;
 				ctx->argv++;
 			}
 			break;
+		} else if (!strcmp(arg + 2, "end-of-options")) {
+			if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) {
+				ctx->argc--;
+				ctx->argv++;
+			}
+			break;
 		}
 
 		if (internal_help && !strcmp(arg + 2, "help-all"))
@@ -1000,6 +1055,11 @@
 	precompose_argv_prefix(argc, argv, NULL);
 	free_preprocessed_options(real_options);
 	free(ctx.alias_groups);
+	for (struct parse_opt_cmdmode_list *elem = ctx.cmdmode_list; elem;) {
+		struct parse_opt_cmdmode_list *next = elem->next;
+		free(elem);
+		elem = next;
+	}
 	return parse_options_end(&ctx);
 }
 
@@ -1018,14 +1078,37 @@
 	return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
 }
 
-#define USAGE_OPTS_WIDTH 24
-#define USAGE_GAP         2
+static int usage_indent(FILE *outfile)
+{
+	return fprintf(outfile, "    ");
+}
+
+#define USAGE_OPTS_WIDTH 26
+
+static void usage_padding(FILE *outfile, size_t pos)
+{
+	if (pos < USAGE_OPTS_WIDTH)
+		fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, "");
+	else
+		fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, "");
+}
+
+static const struct option *find_option_by_long_name(const struct option *opts,
+						     const char *long_name)
+{
+	for (; opts->type != OPTION_END; opts++) {
+		if (opts->long_name && !strcmp(opts->long_name, long_name))
+			return opts;
+	}
+	return NULL;
+}
 
 static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
 							 const char * const *usagestr,
 							 const struct option *opts,
 							 int full, int err)
 {
+	const struct option *all_opts = opts;
 	FILE *outfile = err ? stderr : stdout;
 	int need_newline;
 
@@ -1106,7 +1189,8 @@
 
 	for (; opts->type != OPTION_END; opts++) {
 		size_t pos;
-		int pad;
+		const char *cp, *np;
+		const char *positive_name = NULL;
 
 		if (opts->type == OPTION_SUBCOMMAND)
 			continue;
@@ -1125,7 +1209,7 @@
 			need_newline = 0;
 		}
 
-		pos = fprintf(outfile, "    ");
+		pos = usage_indent(outfile);
 		if (opts->short_name) {
 			if (opts->flags & PARSE_OPT_NODASH)
 				pos += fprintf(outfile, "%c", opts->short_name);
@@ -1134,8 +1218,15 @@
 		}
 		if (opts->long_name && opts->short_name)
 			pos += fprintf(outfile, ", ");
-		if (opts->long_name)
-			pos += fprintf(outfile, "--%s", opts->long_name);
+		if (opts->long_name) {
+			const char *long_name = opts->long_name;
+			if ((opts->flags & PARSE_OPT_NONEG) ||
+			    skip_prefix(long_name, "no-", &positive_name))
+				pos += fprintf(outfile, "--%s", long_name);
+			else
+				pos += fprintf(outfile, "--[no-]%s", long_name);
+		}
+
 		if (opts->type == OPTION_NUMBER)
 			pos += utf8_fprintf(outfile, _("-NUM"));
 
@@ -1143,19 +1234,32 @@
 		    !(opts->flags & PARSE_OPT_NOARG))
 			pos += usage_argh(opts, outfile);
 
-		if (pos <= USAGE_OPTS_WIDTH)
-			pad = USAGE_OPTS_WIDTH - pos;
-		else {
-			fputc('\n', outfile);
-			pad = USAGE_OPTS_WIDTH;
-		}
 		if (opts->type == OPTION_ALIAS) {
-			fprintf(outfile, "%*s", pad + USAGE_GAP, "");
+			usage_padding(outfile, pos);
 			fprintf_ln(outfile, _("alias of --%s"),
 				   (const char *)opts->value);
 			continue;
 		}
-		fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
+
+		for (cp = opts->help ? _(opts->help) : ""; *cp; cp = np) {
+			np = strchrnul(cp, '\n');
+			if (*np)
+				np++;
+			usage_padding(outfile, pos);
+			fwrite(cp, 1, np - cp, outfile);
+			pos = 0;
+		}
+		fputc('\n', outfile);
+
+		if (positive_name) {
+			if (find_option_by_long_name(all_opts, positive_name))
+				continue;
+			pos = usage_indent(outfile);
+			pos += fprintf(outfile, "--%s", positive_name);
+			usage_padding(outfile, pos);
+			fprintf_ln(outfile, _("opposite of --no-%s"),
+				   positive_name);
+		}
 	}
 	fputc('\n', outfile);
 
diff --git a/parse-options.h b/parse-options.h
index 50d852f..bd62e20 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -1,6 +1,8 @@
 #ifndef PARSE_OPTIONS_H
 #define PARSE_OPTIONS_H
 
+#include "gettext.h"
+
 /**
  * Refer to Documentation/technical/api-parse-options.txt for the API doc.
  */
@@ -158,71 +160,211 @@
 	parse_opt_subcommand_fn *subcommand_fn;
 };
 
-#define OPT_BIT_F(s, l, v, h, b, f) { OPTION_BIT, (s), (l), (v), NULL, (h), \
-				      PARSE_OPT_NOARG|(f), NULL, (b) }
-#define OPT_COUNTUP_F(s, l, v, h, f) { OPTION_COUNTUP, (s), (l), (v), NULL, \
-				       (h), PARSE_OPT_NOARG|(f) }
-#define OPT_SET_INT_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
-					  (h), PARSE_OPT_NOARG | (f), NULL, (i) }
+#define OPT_BIT_F(s, l, v, h, b, f) { \
+	.type = OPTION_BIT, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG|(f), \
+	.callback = NULL, \
+	.defval = (b), \
+}
+#define OPT_COUNTUP_F(s, l, v, h, f) { \
+	.type = OPTION_COUNTUP, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG|(f), \
+}
+#define OPT_SET_INT_F(s, l, v, h, i, f) { \
+	.type = OPTION_SET_INT, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG | (f), \
+	.defval = (i), \
+}
 #define OPT_BOOL_F(s, l, v, h, f)   OPT_SET_INT_F(s, l, v, h, 1, f)
-#define OPT_CALLBACK_F(s, l, v, a, h, f, cb)			\
-	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
-#define OPT_STRING_F(s, l, v, a, h, f)   { OPTION_STRING,  (s), (l), (v), (a), (h), (f) }
-#define OPT_INTEGER_F(s, l, v, h, f)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
+#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.flags = (f), \
+	.callback = (cb), \
+}
+#define OPT_STRING_F(s, l, v, a, h, f) { \
+	.type = OPTION_STRING, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.flags = (f), \
+}
+#define OPT_INTEGER_F(s, l, v, h, f) { \
+	.type = OPTION_INTEGER, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("n"), \
+	.help = (h), \
+	.flags = (f), \
+}
 
-#define OPT_END()                   { OPTION_END }
-#define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
+#define OPT_END() { \
+	.type = OPTION_END, \
+}
+#define OPT_GROUP(h) { \
+	.type = OPTION_GROUP, \
+	.help = (h), \
+}
 #define OPT_BIT(s, l, v, h, b)      OPT_BIT_F(s, l, v, h, b, 0)
-#define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
-					    PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, \
-					    (set), NULL, (clear) }
-#define OPT_NEGBIT(s, l, v, h, b)   { OPTION_NEGBIT, (s), (l), (v), NULL, \
-				      (h), PARSE_OPT_NOARG, NULL, (b) }
+#define OPT_BITOP(s, l, v, h, set, clear) { \
+	.type = OPTION_BITOP, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG|PARSE_OPT_NONEG, \
+	.defval = (set), \
+	.extra = (clear), \
+}
+#define OPT_NEGBIT(s, l, v, h, b) { \
+	.type = OPTION_NEGBIT, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG, \
+	.defval = (b), \
+}
 #define OPT_COUNTUP(s, l, v, h)     OPT_COUNTUP_F(s, l, v, h, 0)
 #define OPT_SET_INT(s, l, v, h, i)  OPT_SET_INT_F(s, l, v, h, i, 0)
 #define OPT_BOOL(s, l, v, h)        OPT_BOOL_F(s, l, v, h, 0)
-#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
-				      (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
-#define OPT_CMDMODE_F(s, l, v, h, i, f)  { OPTION_SET_INT, (s), (l), (v), NULL, \
-				      (h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), NULL, (i) }
+#define OPT_HIDDEN_BOOL(s, l, v, h) { \
+	.type = OPTION_SET_INT, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
+	.defval = 1, \
+}
+#define OPT_CMDMODE_F(s, l, v, h, i, f) { \
+	.type = OPTION_SET_INT, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \
+	.defval = (i), \
+}
 #define OPT_CMDMODE(s, l, v, h, i)  OPT_CMDMODE_F(s, l, v, h, i, 0)
 
 #define OPT_INTEGER(s, l, v, h)     OPT_INTEGER_F(s, l, v, h, 0)
-#define OPT_MAGNITUDE(s, l, v, h)   { OPTION_MAGNITUDE, (s), (l), (v), \
-				      N_("n"), (h), PARSE_OPT_NONEG }
+#define OPT_MAGNITUDE(s, l, v, h) { \
+	.type = OPTION_MAGNITUDE, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("n"), \
+	.help = (h), \
+	.flags = PARSE_OPT_NONEG, \
+}
 #define OPT_STRING(s, l, v, a, h)   OPT_STRING_F(s, l, v, a, h, 0)
-#define OPT_STRING_LIST(s, l, v, a, h) \
-				    { OPTION_CALLBACK, (s), (l), (v), (a), \
-				      (h), 0, &parse_opt_string_list }
-#define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
-				      (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
-#define OPT_EXPIRY_DATE(s, l, v, h) \
-	{ OPTION_CALLBACK, (s), (l), (v), N_("expiry-date"),(h), 0,	\
-	  parse_opt_expiry_date_cb }
-#define OPT_CALLBACK(s, l, v, a, h, f) OPT_CALLBACK_F(s, l, v, a, h, 0, f)
-#define OPT_NUMBER_CALLBACK(v, h, f) \
-	{ OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
-	  PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
-#define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
-				       N_("file"), (h) }
-#define OPT_COLOR_FLAG(s, l, v, h) \
-	{ OPTION_CALLBACK, (s), (l), (v), N_("when"), (h), PARSE_OPT_OPTARG, \
-		parse_opt_color_flag_cb, (intptr_t)"always" }
+#define OPT_STRING_LIST(s, l, v, a, h) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.callback = &parse_opt_string_list, \
+}
+#define OPT_STRVEC(s, l, v, a, h) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.callback = &parse_opt_strvec, \
+}
+#define OPT_UYN(s, l, v, h) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG, \
+	.callback = &parse_opt_tertiary, \
+}
+#define OPT_EXPIRY_DATE(s, l, v, h) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("expiry-date"), \
+	.help = (h), \
+	.callback = parse_opt_expiry_date_cb, \
+}
+#define OPT_CALLBACK(s, l, v, a, h, cb) OPT_CALLBACK_F(s, l, v, a, h, 0, cb)
+#define OPT_NUMBER_CALLBACK(v, h, cb) { \
+	.type = OPTION_NUMBER, \
+	.value = (v), \
+	.help = (h), \
+	.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, \
+	.callback = (cb), \
+}
+#define OPT_FILENAME(s, l, v, h) { \
+	.type = OPTION_FILENAME, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("file"), \
+	.help = (h), \
+}
+#define OPT_COLOR_FLAG(s, l, v, h) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("when"), \
+	.help = (h), \
+	.flags = PARSE_OPT_OPTARG, \
+	.callback = parse_opt_color_flag_cb, \
+	.defval = (intptr_t)"always", \
+}
 
-#define OPT_NOOP_NOARG(s, l) \
-	{ OPTION_CALLBACK, (s), (l), NULL, NULL, \
-	  N_("no-op (backward compatibility)"),		\
-	  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
+#define OPT_NOOP_NOARG(s, l) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.help = N_("no-op (backward compatibility)"), \
+	.flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, \
+	.callback = parse_opt_noop_cb, \
+}
 
-#define OPT_ALIAS(s, l, source_long_name) \
-	{ OPTION_ALIAS, (s), (l), (source_long_name) }
+#define OPT_ALIAS(s, l, source_long_name) { \
+	.type = OPTION_ALIAS, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (source_long_name), \
+}
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
 	.type = OPTION_SUBCOMMAND, \
 	.long_name = (l), \
 	.value = (v), \
 	.flags = (f), \
-	.subcommand_fn = (fn) }
+	.subcommand_fn = (fn), \
+}
 #define OPT_SUBCOMMAND(l, v, fn)    OPT_SUBCOMMAND_F((l), (v), (fn), 0)
 
 /*
@@ -303,6 +445,8 @@
 
 /*----- incremental advanced APIs -----*/
 
+struct parse_opt_cmdmode_list;
+
 /*
  * It's okay for the caller to consume argv/argc in the usual way.
  * Other fields of that structure are private to parse-options and should not
@@ -317,7 +461,7 @@
 	unsigned has_subcommands;
 	const char *prefix;
 	const char **alias_groups; /* must be in groups of 3 elements! */
-	struct option *updated_options;
+	struct parse_opt_cmdmode_list *cmdmode_list;
 };
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
@@ -347,10 +491,8 @@
 int parse_opt_commit(const struct option *, const char *, int);
 int parse_opt_tertiary(const struct option *, const char *, int);
 int parse_opt_string_list(const struct option *, const char *, int);
+int parse_opt_strvec(const struct option *, const char *, int);
 int parse_opt_noop_cb(const struct option *, const char *, int);
-enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
-					   const struct option *,
-					   const char *, int);
 int parse_opt_passthru(const struct option *, const char *, int);
 int parse_opt_passthru_argv(const struct option *, const char *, int);
 /* value is enum branch_track* */
@@ -358,34 +500,80 @@
 
 #define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', "verbose", (var), (h))
 #define OPT__QUIET(var, h)    OPT_COUNTUP('q', "quiet",   (var), (h))
-#define OPT__VERBOSITY(var) \
-	{ OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
-	  PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
-	{ OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
-	  PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
+#define OPT__VERBOSITY(var) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = 'v', \
+	.long_name = "verbose", \
+	.value = (var), \
+	.help = N_("be more verbose"), \
+	.flags = PARSE_OPT_NOARG, \
+	.callback = &parse_opt_verbosity_cb, \
+}, { \
+	.type = OPTION_CALLBACK, \
+	.short_name = 'q', \
+	.long_name = "quiet", \
+	.value = (var), \
+	.help = N_("be more quiet"), \
+	.flags = PARSE_OPT_NOARG, \
+	.callback = &parse_opt_verbosity_cb, \
+}
 #define OPT__DRY_RUN(var, h)  OPT_BOOL('n', "dry-run", (var), (h))
 #define OPT__FORCE(var, h, f) OPT_COUNTUP_F('f', "force",   (var), (h), (f))
-#define OPT__ABBREV(var)  \
-	{ OPTION_CALLBACK, 0, "abbrev", (var), N_("n"),	\
-	  N_("use <n> digits to display object names"),	\
-	  PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__ABBREV(var) { \
+	.type = OPTION_CALLBACK, \
+	.long_name = "abbrev", \
+	.value = (var), \
+	.argh = N_("n"), \
+	.help = N_("use <n> digits to display object names"), \
+	.flags = PARSE_OPT_OPTARG, \
+	.callback = &parse_opt_abbrev_cb, \
+}
 #define OPT__SUPER_PREFIX(var) \
 	OPT_STRING_F(0, "super-prefix", (var), N_("prefix"), \
 		N_("prefixed path to initial superproject"), PARSE_OPT_HIDDEN)
 
 #define OPT__COLOR(var, h) \
 	OPT_COLOR_FLAG(0, "color", (var), (h))
-#define OPT_COLUMN(s, l, v, h) \
-	{ OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }
-#define OPT_PASSTHRU(s, l, v, a, h, f) \
-	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }
-#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \
-	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv }
-#define _OPT_CONTAINS_OR_WITH(name, variable, help, flag) \
-	{ OPTION_CALLBACK, 0, name, (variable), N_("commit"), (help), \
-	  PARSE_OPT_LASTARG_DEFAULT | flag, \
-	  parse_opt_commits, (intptr_t) "HEAD" \
-	}
+#define OPT_COLUMN(s, l, v, h) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("style"), \
+	.help = (h), \
+	.flags = PARSE_OPT_OPTARG, \
+	.callback = parseopt_column_callback, \
+}
+#define OPT_PASSTHRU(s, l, v, a, h, f) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.flags = (f), \
+	.callback = parse_opt_passthru, \
+}
+#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) { \
+	.type = OPTION_CALLBACK, \
+	.short_name = (s), \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = (a), \
+	.help = (h), \
+	.flags = (f), \
+	.callback = parse_opt_passthru_argv, \
+}
+#define _OPT_CONTAINS_OR_WITH(l, v, h, f) { \
+	.type = OPTION_CALLBACK, \
+	.long_name = (l), \
+	.value = (v), \
+	.argh = N_("commit"), \
+	.help = (h), \
+	.flags = PARSE_OPT_LASTARG_DEFAULT | (f), \
+	.callback = parse_opt_commits, \
+	.defval = (intptr_t) "HEAD", \
+}
 #define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
 #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
@@ -395,4 +583,10 @@
 #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
 #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
 
+#define OPT_IPVERSION(v) \
+	OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \
+		TRANSPORT_FAMILY_IPV4, PARSE_OPT_NONEG), \
+	OPT_SET_INT_F('6', "ipv6", (v), N_("use IPv6 addresses only"), \
+		TRANSPORT_FAMILY_IPV6, PARSE_OPT_NONEG)
+
 #endif
diff --git a/parse.c b/parse.c
new file mode 100644
index 0000000..42d691a
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,182 @@
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "parse.h"
+
+static uintmax_t get_unit_factor(const char *end)
+{
+	if (!*end)
+		return 1;
+	else if (!strcasecmp(end, "k"))
+		return 1024;
+	else if (!strcasecmp(end, "m"))
+		return 1024 * 1024;
+	else if (!strcasecmp(end, "g"))
+		return 1024 * 1024 * 1024;
+	return 0;
+}
+
+int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
+{
+	if (value && *value) {
+		char *end;
+		intmax_t val;
+		intmax_t factor;
+
+		if (max < 0)
+			BUG("max must be a positive integer");
+
+		errno = 0;
+		val = strtoimax(value, &end, 0);
+		if (errno == ERANGE)
+			return 0;
+		if (end == value) {
+			errno = EINVAL;
+			return 0;
+		}
+		factor = get_unit_factor(end);
+		if (!factor) {
+			errno = EINVAL;
+			return 0;
+		}
+		if ((val < 0 && -max / factor > val) ||
+		    (val > 0 && max / factor < val)) {
+			errno = ERANGE;
+			return 0;
+		}
+		val *= factor;
+		*ret = val;
+		return 1;
+	}
+	errno = EINVAL;
+	return 0;
+}
+
+static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
+{
+	if (value && *value) {
+		char *end;
+		uintmax_t val;
+		uintmax_t factor;
+
+		/* negative values would be accepted by strtoumax */
+		if (strchr(value, '-')) {
+			errno = EINVAL;
+			return 0;
+		}
+		errno = 0;
+		val = strtoumax(value, &end, 0);
+		if (errno == ERANGE)
+			return 0;
+		if (end == value) {
+			errno = EINVAL;
+			return 0;
+		}
+		factor = get_unit_factor(end);
+		if (!factor) {
+			errno = EINVAL;
+			return 0;
+		}
+		if (unsigned_mult_overflows(factor, val) ||
+		    factor * val > max) {
+			errno = ERANGE;
+			return 0;
+		}
+		val *= factor;
+		*ret = val;
+		return 1;
+	}
+	errno = EINVAL;
+	return 0;
+}
+
+int git_parse_int(const char *value, int *ret)
+{
+	intmax_t tmp;
+	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+int git_parse_int64(const char *value, int64_t *ret)
+{
+	intmax_t tmp;
+	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+	uintmax_t tmp;
+	if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+int git_parse_ssize_t(const char *value, ssize_t *ret)
+{
+	intmax_t tmp;
+	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+int git_parse_maybe_bool_text(const char *value)
+{
+	if (!value)
+		return 1;
+	if (!*value)
+		return 0;
+	if (!strcasecmp(value, "true")
+	    || !strcasecmp(value, "yes")
+	    || !strcasecmp(value, "on"))
+		return 1;
+	if (!strcasecmp(value, "false")
+	    || !strcasecmp(value, "no")
+	    || !strcasecmp(value, "off"))
+		return 0;
+	return -1;
+}
+
+int git_parse_maybe_bool(const char *value)
+{
+	int v = git_parse_maybe_bool_text(value);
+	if (0 <= v)
+		return v;
+	if (git_parse_int(value, &v))
+		return !!v;
+	return -1;
+}
+
+/*
+ * Parse environment variable 'k' as a boolean (in various
+ * possible spellings); if missing, use the default value 'def'.
+ */
+int git_env_bool(const char *k, int def)
+{
+	const char *v = getenv(k);
+	int val;
+	if (!v)
+		return def;
+	val = git_parse_maybe_bool(v);
+	if (val < 0)
+		die(_("bad boolean environment value '%s' for '%s'"),
+		    v, k);
+	return val;
+}
+
+/*
+ * Parse environment variable 'k' as ulong with possibly a unit
+ * suffix; if missing, use the default value 'val'.
+ */
+unsigned long git_env_ulong(const char *k, unsigned long val)
+{
+	const char *v = getenv(k);
+	if (v && !git_parse_ulong(v, &val))
+		die(_("failed to parse %s"), k);
+	return val;
+}
diff --git a/parse.h b/parse.h
new file mode 100644
index 0000000..07d2193
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,20 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+int git_parse_signed(const char *value, intmax_t *ret, intmax_t max);
+int git_parse_ssize_t(const char *, ssize_t *);
+int git_parse_ulong(const char *, unsigned long *);
+int git_parse_int(const char *value, int *ret);
+int git_parse_int64(const char *value, int64_t *ret);
+
+/**
+ * Same as `git_config_bool`, except that it returns -1 on error rather
+ * than dying.
+ */
+int git_parse_maybe_bool(const char *);
+int git_parse_maybe_bool_text(const char *value);
+
+int git_env_bool(const char *, int);
+unsigned long git_env_ulong(const char *, unsigned long);
+
+#endif /* PARSE_H */
diff --git a/patch-ids.c b/patch-ids.c
index 3153446..a5683b4 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "commit.h"
-#include "hash-lookup.h"
+#include "hash.h"
+#include "hex.h"
 #include "patch-ids.h"
 
 static int patch_id_defined(struct commit *commit)
diff --git a/path.c b/path.c
index 492e17a..67229ed 100644
--- a/path.c
+++ b/path.c
@@ -1,16 +1,21 @@
 /*
  * Utilities for paths and pathnames
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
 #include "worktree.h"
+#include "setup.h"
 #include "submodule-config.h"
 #include "path.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "lockfile.h"
 #include "exec-cmd.h"
 
@@ -23,8 +28,6 @@
 	return 0;
 }
 
-static char bad_path[] = "/bad-path/";
-
 static struct strbuf *get_pathname(void)
 {
 	static struct strbuf pathname_array[4] = {
@@ -54,21 +57,6 @@
 		strbuf_remove(sb, 0, path - sb->buf);
 }
 
-char *mksnpath(char *buf, size_t n, const char *fmt, ...)
-{
-	va_list args;
-	unsigned len;
-
-	va_start(args, fmt);
-	len = vsnprintf(buf, n, fmt, args);
-	va_end(args);
-	if (len >= n) {
-		strlcpy(buf, bad_path, n);
-		return buf;
-	}
-	return (char *)cleanup_path(buf);
-}
-
 static int dir_prefix(const char *buf, const char *dir)
 {
 	int len = strlen(dir);
@@ -347,7 +335,8 @@
  * Helper function for update_common_dir: returns 1 if the dir
  * prefix is common.
  */
-static int check_common(const char *unmatched, void *value, void *baton)
+static int check_common(const char *unmatched, void *value,
+			void *baton UNUSED)
 {
 	struct common_dir *dir = value;
 
@@ -865,7 +854,7 @@
 	return NULL;
 }
 
-static int calc_shared_perm(int mode)
+int calc_shared_perm(int mode)
 {
 	int tweak;
 
@@ -1206,6 +1195,26 @@
 	return normalize_path_copy_len(dst, src, NULL);
 }
 
+int strbuf_normalize_path(struct strbuf *src)
+{
+	struct strbuf dst = STRBUF_INIT;
+
+	strbuf_grow(&dst, src->len);
+	if (normalize_path_copy(dst.buf, src->buf) < 0) {
+		strbuf_release(&dst);
+		return -1;
+	}
+
+	/*
+	 * normalize_path does not tell us the new length, so we have to
+	 * compute it by looking for the new NUL it placed
+	 */
+	strbuf_setlen(&dst, strlen(dst.buf));
+	strbuf_swap(src, &dst);
+	strbuf_release(&dst);
+	return 0;
+}
+
 /*
  * path = Canonical absolute path
  * prefixes = string_list containing normalized, absolute paths without
@@ -1562,7 +1571,5 @@
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
 REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
 REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
-REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
-REPO_GIT_PATH_FUNC(auto_merge, "AUTO_MERGE")
 REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
 REPO_GIT_PATH_FUNC(shallow, "shallow")
diff --git a/path.h b/path.h
index 0a59c85..ea96487 100644
--- a/path.h
+++ b/path.h
@@ -3,6 +3,7 @@
 
 struct repository;
 struct strbuf;
+struct string_list;
 
 /*
  * The result to all functions which return statically allocated memory may be
@@ -23,12 +24,6 @@
 	__attribute__((format (printf, 1, 2)));
 
 /*
- * Construct a path and place the result in the provided buffer `buf`.
- */
-char *mksnpath(char *buf, size_t n, const char *fmt, ...)
-	__attribute__((format (printf, 3, 4)));
-
-/*
  * The `git_common_path` family of functions will construct a path into a
  * repository's common git directory, which is shared by all worktrees.
  */
@@ -174,12 +169,75 @@
 const char *git_path_merge_rr(struct repository *r);
 const char *git_path_merge_mode(struct repository *r);
 const char *git_path_merge_head(struct repository *r);
-const char *git_path_merge_autostash(struct repository *r);
-const char *git_path_auto_merge(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
-
 int ends_with_path_components(const char *path, const char *components);
+int validate_headref(const char *ref);
+
+int calc_shared_perm(int mode);
+int adjust_shared_perm(const char *path);
+
+char *interpolate_path(const char *path, int real_home);
+const char *enter_repo(const char *path, int strict);
+const char *remove_leading_path(const char *in, const char *prefix);
+const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
+int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
+int normalize_path_copy(char *dst, const char *src);
+/**
+ * Normalize in-place the path contained in the strbuf. If an error occurs,
+ * the contents of "sb" are left untouched, and -1 is returned.
+ */
+int strbuf_normalize_path(struct strbuf *src);
+int longest_ancestor_length(const char *path, struct string_list *prefixes);
+char *strip_path_suffix(const char *path, const char *suffix);
+int daemon_avoid_alias(const char *path);
+
+/*
+ * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
+ * details.
+ */
+int is_ntfs_dotgit(const char *name);
+int is_ntfs_dotgitmodules(const char *name);
+int is_ntfs_dotgitignore(const char *name);
+int is_ntfs_dotgitattributes(const char *name);
+int is_ntfs_dotmailmap(const char *name);
+
+/*
+ * Returns true iff "str" could be confused as a command-line option when
+ * passed to a sub-program like "ssh". Note that this has nothing to do with
+ * shell-quoting, which should be handled separately; we're assuming here that
+ * the string makes it verbatim to the sub-program.
+ */
+int looks_like_command_line_option(const char *str);
+
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CONFIG_HOME/$subdir/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
+ * "$HOME/.config/$subdir/$filename". Return NULL upon error.
+ */
+char *xdg_config_home_for(const char *subdir, const char *filename);
+
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
+ * "$HOME/.config/git/$filename". Return NULL upon error.
+ */
+char *xdg_config_home(const char *filename);
+
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
+ * "$HOME/.cache/git/$filename". Return NULL upon error.
+ */
+char *xdg_cache_home(const char *filename);
+
+/*
+ * Create a directory and (if share is nonzero) adjust its permissions
+ * according to the shared_repository setting. Only use this for
+ * directories under $GIT_DIR.  Don't use it for working tree
+ * directories.
+ */
+void safe_create_dir(const char *dir, int share);
 
 #endif /* PATH_H */
diff --git a/pathspec.c b/pathspec.c
index ab70fcb..2133b9f 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,10 +1,18 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "parse.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
 #include "pathspec.h"
 #include "attr.h"
+#include "read-cache.h"
+#include "repository.h"
+#include "setup.h"
 #include "strvec.h"
+#include "symlinks.h"
 #include "quote.h"
+#include "wildmatch.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -101,16 +109,37 @@
 	{ PATHSPEC_ATTR,    '\0', "attr" },
 };
 
-static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
+static void prefix_magic(struct strbuf *sb, int prefixlen,
+			 unsigned magic, const char *element)
 {
-	int i;
-	strbuf_addstr(sb, ":(");
-	for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-		if (magic & pathspec_magic[i].bit) {
-			if (sb->buf[sb->len - 1] != '(')
-				strbuf_addch(sb, ',');
-			strbuf_addstr(sb, pathspec_magic[i].name);
+	/* No magic was found in element, just add prefix magic */
+	if (!magic) {
+		strbuf_addf(sb, ":(prefix:%d)", prefixlen);
+		return;
+	}
+
+	/*
+	 * At this point, we know that parse_element_magic() was able
+	 * to extract some pathspec magic from element. So we know
+	 * element is correctly formatted in either shorthand or
+	 * longhand form
+	 */
+	if (element[1] != '(') {
+		/* Process an element in shorthand form (e.g. ":!/<match>") */
+		strbuf_addstr(sb, ":(");
+		for (int i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+			if ((magic & pathspec_magic[i].bit) &&
+			    pathspec_magic[i].mnemonic) {
+				if (sb->buf[sb->len - 1] != '(')
+					strbuf_addch(sb, ',');
+				strbuf_addstr(sb, pathspec_magic[i].name);
+			}
 		}
+	} else {
+		/* For the longhand form, we copy everything up to the final ')' */
+		size_t len = strchr(element, ')') - element;
+		strbuf_add(sb, element, len);
+	}
 	strbuf_addf(sb, ",prefix:%d)", prefixlen);
 }
 
@@ -459,7 +488,12 @@
 		match = prefix_path_gently(prefix, prefixlen,
 					   &prefixlen, copyfrom);
 		if (!match) {
-			const char *hint_path = get_git_work_tree();
+			const char *hint_path;
+
+			if (!have_git_dir())
+				die(_("'%s' is outside the directory tree"),
+				    copyfrom);
+			hint_path = get_git_work_tree();
 			if (!hint_path)
 				hint_path = get_git_dir();
 			die(_("%s: '%s' is outside repository at '%s'"), elt,
@@ -480,7 +514,7 @@
 		struct strbuf sb = STRBUF_INIT;
 
 		/* Preserve the actual prefix length of each pattern */
-		prefix_magic(&sb, prefixlen, element_magic);
+		prefix_magic(&sb, prefixlen, element_magic, elt);
 
 		strbuf_addstr(&sb, match);
 		item->original = strbuf_detach(&sb, NULL);
@@ -525,24 +559,29 @@
 	return strcmp(a->match, b->match);
 }
 
-static void NORETURN unsupported_magic(const char *pattern,
-				       unsigned magic)
+void pathspec_magic_names(unsigned magic, struct strbuf *out)
 {
-	struct strbuf sb = STRBUF_INIT;
 	int i;
 	for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
 		const struct pathspec_magic *m = pathspec_magic + i;
 		if (!(magic & m->bit))
 			continue;
-		if (sb.len)
-			strbuf_addstr(&sb, ", ");
+		if (out->len)
+			strbuf_addstr(out, ", ");
 
 		if (m->mnemonic)
-			strbuf_addf(&sb, _("'%s' (mnemonic: '%c')"),
+			strbuf_addf(out, _("'%s' (mnemonic: '%c')"),
 				    m->name, m->mnemonic);
 		else
-			strbuf_addf(&sb, "'%s'", m->name);
+			strbuf_addf(out, "'%s'", m->name);
 	}
+}
+
+static void NORETURN unsupported_magic(const char *pattern,
+				       unsigned magic)
+{
+	struct strbuf sb = STRBUF_INIT;
+	pathspec_magic_names(magic, &sb);
 	/*
 	 * We may want to substitute "this command" with a command
 	 * name. E.g. when "git add -p" or "git add -i" dies when running
@@ -730,7 +769,7 @@
 	if (name[namelen])
 		name = to_free = xmemdupz(name, namelen);
 
-	git_check_attr(istate, NULL, name, item->attr_check);
+	git_check_attr(istate, name, item->attr_check);
 
 	free(to_free);
 
diff --git a/pathspec.h b/pathspec.h
index 41f6adf..fec4399 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -130,6 +130,14 @@
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
 void clear_pathspec(struct pathspec *);
 
+/*
+ * Add a human-readable string to "out" representing the PATHSPEC_* flags set
+ * in "magic". The result is suitable for error messages, but not for
+ * parsing as pathspec magic itself (you get 'icase' with quotes, not
+ * :(icase)).
+ */
+void pathspec_magic_names(unsigned magic, struct strbuf *out);
+
 static inline int ps_strncmp(const struct pathspec_item *item,
 			     const char *s1, const char *s2, size_t n)
 {
@@ -171,6 +179,11 @@
 			 const char *name, int namelen,
 			 const struct pathspec_item *item);
 
+int match_pathspec(struct index_state *istate,
+		   const struct pathspec *pathspec,
+		   const char *name, int namelen,
+		   int prefix, char *seen, int is_dir);
+
 /*
  * Determine whether a pathspec will match only entire index entries (non-sparse
  * files and/or entire sparse directories). If the pathspec has the potential to
diff --git a/perl/FromCPAN/Error.pm b/perl/FromCPAN/Error.pm
index d82b713..5b97e03 100644
--- a/perl/FromCPAN/Error.pm
+++ b/perl/FromCPAN/Error.pm
@@ -1025,7 +1025,7 @@
 
 =head1 MAINTAINER
 
-Shlomi Fish, L<http://www.shlomifish.org/> .
+Shlomi Fish, L<https://www.shlomifish.org/> .
 
 =head1 PAST MAINTAINERS
 
diff --git a/perl/Git.pm b/perl/Git.pm
index 117765d..03bf570 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -7,7 +7,7 @@
 
 package Git;
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm
index 895e759..5454c3a 100644
--- a/perl/Git/I18N.pm
+++ b/perl/Git/I18N.pm
@@ -1,5 +1,5 @@
 package Git::I18N;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
diff --git a/perl/Git/LoadCPAN.pm b/perl/Git/LoadCPAN.pm
index 0c360bc..8c7fa80 100644
--- a/perl/Git/LoadCPAN.pm
+++ b/perl/Git/LoadCPAN.pm
@@ -1,5 +1,5 @@
 package Git::LoadCPAN;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
diff --git a/perl/Git/LoadCPAN/Error.pm b/perl/Git/LoadCPAN/Error.pm
index 5d84c20..5cecb0f 100644
--- a/perl/Git/LoadCPAN/Error.pm
+++ b/perl/Git/LoadCPAN/Error.pm
@@ -1,5 +1,5 @@
 package Git::LoadCPAN::Error;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
diff --git a/perl/Git/LoadCPAN/Mail/Address.pm b/perl/Git/LoadCPAN/Mail/Address.pm
index 340e88a..9f80809 100644
--- a/perl/Git/LoadCPAN/Mail/Address.pm
+++ b/perl/Git/LoadCPAN/Mail/Address.pm
@@ -1,5 +1,5 @@
 package Git::LoadCPAN::Mail::Address;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
diff --git a/perl/Git/Packet.pm b/perl/Git/Packet.pm
index d144f51..d896e69 100644
--- a/perl/Git/Packet.pm
+++ b/perl/Git/Packet.pm
@@ -1,5 +1,5 @@
 package Git::Packet;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 6ce2e28..7721708 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -1752,7 +1752,7 @@
 END {
 	# Force cache writeout explicitly instead of waiting for
 	# global destruction to avoid segfault in Storable:
-	# http://rt.cpan.org/Public/Bug/Display.html?id=36087
+	# https://rt.cpan.org/Public/Bug/Display.html?id=36087
 	unmemoize_svn_mergeinfo_functions();
 }
 
diff --git a/pkt-line.c b/pkt-line.c
index ce4e73b..24479ea 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -1,6 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "copy.h"
 #include "pkt-line.h"
+#include "gettext.h"
+#include "hex.h"
 #include "run-command.h"
+#include "sideband.h"
+#include "trace.h"
+#include "write-or-die.h"
 
 char packet_buffer[LARGE_PACKET_MAX];
 static const char *packet_trace_prefix = "git";
@@ -367,10 +373,14 @@
 	return ret;
 }
 
-int packet_length(const char lenbuf_hex[4])
+int packet_length(const char lenbuf_hex[4], size_t size)
 {
-	int val = hex2chr(lenbuf_hex);
-	return (val < 0) ? val : (val << 8) | hex2chr(lenbuf_hex + 2);
+	if (size < 4)
+		BUG("buffer too small");
+	return	hexval(lenbuf_hex[0]) << 12 |
+		hexval(lenbuf_hex[1]) <<  8 |
+		hexval(lenbuf_hex[2]) <<  4 |
+		hexval(lenbuf_hex[3]);
 }
 
 static char *find_packfile_uri_path(const char *buffer)
@@ -413,7 +423,7 @@
 		return PACKET_READ_EOF;
 	}
 
-	len = packet_length(linelen);
+	len = packet_length(linelen, sizeof(linelen));
 
 	if (len < 0) {
 		if (options & PACKET_READ_GENTLE_ON_READ_ERROR)
@@ -453,8 +463,32 @@
 	}
 
 	if ((options & PACKET_READ_CHOMP_NEWLINE) &&
-	    len && buffer[len-1] == '\n')
-		len--;
+	    len && buffer[len-1] == '\n') {
+		if (options & PACKET_READ_USE_SIDEBAND) {
+			int band = *buffer & 0xff;
+			switch (band) {
+			case 1:
+				/* Chomp newline for payload */
+				len--;
+				break;
+			case 2:
+			case 3:
+				/*
+				 * Do not chomp newline for progress and error
+				 * message.
+				 */
+				break;
+			default:
+				/*
+				 * Bad sideband, let's leave it to
+				 * demultiplex_sideband() to catch this error.
+				 */
+				break;
+			}
+		} else {
+			len--;
+		}
+	}
 
 	buffer[len] = 0;
 	if (options & PACKET_READ_REDACT_URI_PATH &&
@@ -583,17 +617,19 @@
 	reader->options = options;
 	reader->me = "git";
 	reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
+	strbuf_init(&reader->scratch, 0);
 }
 
 enum packet_read_status packet_reader_read(struct packet_reader *reader)
 {
-	struct strbuf scratch = STRBUF_INIT;
-
 	if (reader->line_peeked) {
 		reader->line_peeked = 0;
 		return reader->status;
 	}
 
+	if (reader->use_sideband)
+		reader->options |= PACKET_READ_USE_SIDEBAND;
+
 	/*
 	 * Consume all progress packets until a primary payload packet is
 	 * received
@@ -611,7 +647,7 @@
 			break;
 		if (demultiplex_sideband(reader->me, reader->status,
 					 reader->buffer, reader->pktlen, 1,
-					 &scratch, &sideband_type))
+					 &reader->scratch, &sideband_type))
 			break;
 	}
 
diff --git a/pkt-line.h b/pkt-line.h
index 79c538b..3b33cc6 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -1,9 +1,7 @@
 #ifndef PKTLINE_H
 #define PKTLINE_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
-#include "sideband.h"
 
 /*
  * Write a packetized stream, where each line is preceded by
@@ -86,6 +84,7 @@
 #define PACKET_READ_DIE_ON_ERR_PACKET    (1u<<2)
 #define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3)
 #define PACKET_READ_REDACT_URI_PATH      (1u<<4)
+#define PACKET_READ_USE_SIDEBAND         (1u<<5)
 int packet_read(int fd, char *buffer, unsigned size, int options);
 
 /*
@@ -95,7 +94,7 @@
  * If lenbuf_hex contains non-hex characters, return -1. Otherwise, return the
  * numeric value of the length header.
  */
-int packet_length(const char lenbuf_hex[4]);
+int packet_length(const char lenbuf_hex[4], size_t size);
 
 /*
  * Read a packetized line into a buffer like the 'packet_read()' function but
@@ -195,6 +194,9 @@
 
 	/* hash algorithm in use */
 	const struct git_hash_algo *hash_algo;
+
+	/* hold temporary sideband message */
+	struct strbuf scratch;
 };
 
 /*
@@ -247,4 +249,6 @@
 void packet_writer_delim(struct packet_writer *writer);
 void packet_writer_flush(struct packet_writer *writer);
 
+void packet_trace_identity(const char *prog);
+
 #endif
diff --git a/po/README.md b/po/README.md
index 3e4f897..ec08aa2 100644
--- a/po/README.md
+++ b/po/README.md
@@ -412,7 +412,7 @@
 - Do not use non-ASCII characters in the subject of a commit.
 
 - The length of commit subject (first line of the commit log) should
-  be less than 50 characters, and the length of other lines of the
+  be no more than 50 characters, and the length of other lines of the
   commit log should be no more than 72 characters.
 
 - Add "Signed-off-by" trailer to your commit log, like other commits
diff --git a/po/TEAMS b/po/TEAMS
index 5a63397..87df7c6 100644
--- a/po/TEAMS
+++ b/po/TEAMS
@@ -51,7 +51,7 @@
 Leader:		Arusekk <arek_koz@o2.pl>
 
 Language:	pt_PT (Portuguese - Portugal)
-Repository:	https://codeberg.org/git-pt/Git-PO-pt_PT/
+Repository:	https://gitlab.com/alexandre1985/git-pt/
 Leader:		Daniel Santos <dacs.git@brilhante.top>
 
 Language:	ru (Russian)
@@ -67,16 +67,22 @@
 Repository:	https://github.com/bitigchi/git-po/
 Leader:		Emir SARI <bitigchi@me.com>
 
+Language:	uk (Ukrainian)
+Repository:	https://github.com/arkid15r/git-ukrainian-l10n/
+Leader:		Arkadii Yakovets <ark@cho.red>
+Members:	Kateryna Golovanova <kate@kgthreads.com>
+
 Language:	vi (Vietnamese)
 Repository:	https://github.com/vnwildman/git/
 Leader:		Trần Ngọc Quân <vnwildman AT gmail.com>
 Members:	Nguyễn Thái Ngọc Duy <pclouds AT gmail.com>
 
 Language:	zh_CN (Simplified Chinese)
-Repository:	https://github.com/fangyi-zhou/git-po/
-Leader:		Fangyi Zhou <me AT fangyi.io>
+Repository:	https://github.com/dyrone/git/
+Leader:		Teng Long <dyroneteng AT gmail.com>
 Members:	Ray Chen <oldsharp AT gmail.com>
 		依云 <lilydjwg AT gmail.com>
+		Fangyi Zhou <me AT fangyi.io>
 		Jiang Xin <worldhello.net AT gmail.com>
 
 Language:	zh_TW (Traditional Chinese)
diff --git a/po/bg.po b/po/bg.po
index daec132..6b95add 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,7 +1,7 @@
 # Bulgarian translation of git po-file.
-# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Alexander Shopov <ash@kambanaria.org>.
+# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Alexander Shopov <ash@kambanaria.org>.
 # This file is distributed under the same license as the git package.
-# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023.
+# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024.
 # ========================
 # DICTIONARY TO MERGE IN GIT GUI
 # ------------------------
@@ -136,11 +136,13 @@
 # island marks граници на групите
 # reflog журнал на указателите
 # hash контролна сума, изчисляване на контролна сума
-# fanout откъс (разперване???)
+# fanout откъс за разпределяне
 # idx - index of pack file, 1 index per 1 packfile
 # midx, multi-pack index - файл с индекса за множество пакети
 # overlay mode - припокриващ режим (при изтеглянe)
 # incremental file нарастващ файл
+# commit-graph граф с подавания
+# commit-graph chain верига на гра̀фа с подавания
 # split (commit-graphr) раздробен (граф с подавания)
 # clobber (a tag) презаписвам (етикет)
 # blame извеждане на авторство
@@ -187,6 +189,9 @@
 # timestamp времево клеймо
 # bare repository голо хранилище
 # resolve-undo отмяна на разрешените подавания
+# resolve conflict коригирам конфликт
+# resolve reference установяване на обекта, сочен от указателя, проследяване на указателя
+# cannot resolve reference  не може да сее открие към какво сочи указателят
 # maintenance задачи по поддръжка
 # GLE последна грешка в нишката - от GetLatError: https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
 # lookup table таблица със съответствия
@@ -199,14 +204,37 @@
 # gitattributes file файл с атрибути на git
 # advertised обявен за наличен
 # superproject свръхпроект
+# rev-index обратен индекс (reverse index)
+# dererging branches раздалечили се клони
+# master/main branch основен клон
+# unborn/orphan branch неродѐн клон (а не несъздаден) - клон без никакви подавания, включително и началното
+# parse анализ, анализирам
+# reinitialize repository зануляване на хранилището и инициализиране
+# replay изпълняване/прилагане наново
+# BTMP chunk откъс за побитова маска
+# OID fanout chunk откъс за разпределянето
+# OID lookup chunk  откъс за търсенето
+# autostash автоматично скатано
+# symref файл с указател (regular file that stores a string that begins with ref: refs/)
+#
+#
 #
 # ------------------------
 # „$var“ - може да не сработва за shell има gettext и eval_gettext - проверка - намират се лесно по „$
 # ------------------------
+# табулация в началото на реда се заменя с четири интервала
+# по подобен начин отстъпът на примерна команда е четири интервала
+# ------------------------
+#
 # FIXME
-# HEAD as a reference vs head of a branch
-# git update-index -h извежда само един ред, а не цялата помощ за опциите
 # git fetch --al работи подобно на --all
+#
+# ----
+#
+# TODO
+# Причастно-страдателни форми (бъде отворен) -> Възвратно-страдателни форми (се отвори)
+# <ТЕРМИН> -> ТЕРМИН
+#
 # ------------------------
 # export PO_FILE=bg.po
 # msgattrib --only-fuzzy  $PO_FILE > todo1.po
@@ -216,10 +244,10 @@
 # for i in `sort -u FILES`; do cnt=`grep $i FILES | wc -l`; echo $cnt $i ;done | sort -n
 msgid ""
 msgstr ""
-"Project-Id-Version: git 2.40\n"
+"Project-Id-Version: git 2.44\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-01 01:20+0000\n"
-"PO-Revision-Date: 2023-03-02 08:54+0200\n"
+"POT-Creation-Date: 2024-02-16 09:33+0100\n"
+"PO-Revision-Date: 2024-02-16 09:38+0100\n"
 "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
 "Language-Team: Bulgarian <dict@fsa-bg.org>\n"
 "Language: bg\n"
@@ -233,7 +261,7 @@
 msgstr "Неуспешен анализ — „%s“."
 
 msgid "could not read index"
-msgstr "индексът не може да бъде прочетен"
+msgstr "индексът не може да се прочете"
 
 msgid "binary"
 msgstr "двоично"
@@ -252,7 +280,7 @@
 msgstr "неуспешно добавяне в индекса на „%s“"
 
 msgid "could not write index"
-msgstr "индексът не може да бъде записан"
+msgstr "индексът не може да се запише"
 
 #, c-format
 msgid "updated %d path\n"
@@ -272,7 +300,7 @@
 msgstr "Отмяна"
 
 msgid "Could not parse HEAD^{tree}"
-msgstr "Указателят „HEAD^{tree}“ не може да бъде анализиран"
+msgstr "Указателят „HEAD^{tree}“ не може да се анализира"
 
 #, c-format
 msgid "reverted %d path\n"
@@ -375,7 +403,7 @@
 msgstr "път"
 
 msgid "could not refresh index"
-msgstr "индексът не може да бъде обновен"
+msgstr "индексът не може да се обнови"
 
 #, c-format
 msgid "Bye.\n"
@@ -401,8 +429,8 @@
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "staging."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"добавено към индекса."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"добави към индекса."
 
 msgid ""
 "y - stage this hunk\n"
@@ -437,8 +465,8 @@
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "stashing."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"скътано."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"скатае."
 
 msgid ""
 "y - stash this hunk\n"
@@ -473,8 +501,8 @@
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "unstaging."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"извадено от индекса."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"извади от индекса."
 
 msgid ""
 "y - unstage this hunk\n"
@@ -510,8 +538,8 @@
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "applying."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"набелязано за прилагане."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"набележи за прилагане."
 
 msgid ""
 "y - apply this hunk to index\n"
@@ -548,8 +576,8 @@
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
 "discarding."
 msgstr ""
-"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
-"набелязано за зануляване."
+"Ако кръпката може да се приложи чисто, редактираното парче незабавно ще се "
+"набележи за зануляване."
 
 msgid ""
 "y - discard this hunk from worktree\n"
@@ -727,7 +755,7 @@
 "За да пропуснете редовете, започващи с „%c“: заменете знака с „ “ (стават "
 "контекст)\n"
 "За да пропуснете редовете, започващи с „%c“: изтрийте ги.\n"
-"Редовете, които започват с „%c“ ще бъдат пропуснати.\n"
+"Редовете, които започват с „%c“ ще се пропуснат.\n"
 
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
@@ -735,7 +763,7 @@
 "aborted and the hunk is left unchanged.\n"
 msgstr ""
 "Ако е невъзможно чисто прилагане на кода, ще може пак да редактирате.  Ако\n"
-"изтриете всички редове от парчето код, то ще бъде оставено непроменено, а\n"
+"изтриете всички редове от парчето код, то ще се остави непроменено, а\n"
 "редактирането — отказано.\n"
 
 msgid "could not parse hunk header"
@@ -826,14 +854,14 @@
 msgstr "Никое парче не напасва на регулярния израз"
 
 msgid "Sorry, cannot split this hunk"
-msgstr "Това парче не може да бъде разделено"
+msgstr "Това парче не може да се раздели"
 
 #, c-format
 msgid "Split into %d hunks."
 msgstr "Разделяне на %d парчета."
 
 msgid "Sorry, cannot edit this hunk"
-msgstr "Това парче не може да бъде редактирано"
+msgstr "Това парче не може да се редактира"
 
 msgid "'git apply' failed"
 msgstr "неуспешно изпълнение на „git apply“"
@@ -867,9 +895,8 @@
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Отмяната е блокирана от неслети файлове."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "Действието „%s“ е блокирано от неслети файлове."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Пребазирането е блокирано от неслети файлове."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -890,6 +917,23 @@
 msgid "Exiting because of unfinished merge."
 msgstr "Изход от програмата заради незавършено сливане."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Раздалечили се клони не може да се превъртят.  Ползвайте:\n"
+"\n"
+"    git merge --no-ff\n"
+"\n"
+"или:\n"
+"\n"
+"    git rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Не може да се извърши превъртане, преустановяване на действието."
 
@@ -935,9 +979,8 @@
 "Бележка: преминаване към „%s“.\n"
 "\n"
 "Указателят „HEAD“ не е свързан.  Може да разглеждате, да правите произволни\n"
-"промѐни и да ги подавате.  Ако изтеглите нещо друго, всички промѐни ще "
-"бъдат\n"
-"забравени и никой клон няма да се промѐни.\n"
+"промѐни и да ги подавате.  Ако изтеглите нещо друго, всички промѐни ще се\n"
+"забравят и никой клон няма да се промѐни.\n"
 "\n"
 "Ако искате да създадете нов клон, за да запазите подаванията си, може да\n"
 "направите това като зададете име на клон към опцията „-c“ на командата\n"
@@ -1001,9 +1044,15 @@
 msgid "'%s' outside a repository"
 msgstr "„%s“ извън хранилище"
 
+msgid "failed to read patch"
+msgstr "кръпката не може да се прочете"
+
+msgid "patch too large"
+msgstr "твърде голяма кръпка"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
-msgstr "Регулярният израз за времевото клеймо „%s“ не може за бъде компилиран"
+msgstr "Регулярният израз за времевото клеймо „%s“ не може да се компилира "
 
 #, c-format
 msgid "regexec returned %d for input: %s"
@@ -1108,11 +1157,11 @@
 
 #, c-format
 msgid "unable to read symlink %s"
-msgstr "символната връзка „%s“ не може да бъде прочетена"
+msgstr "символната връзка „%s“ не може да се прочете"
 
 #, c-format
 msgid "unable to open or read %s"
-msgstr "файлът „%s“ не може да бъде отворен или прочетен"
+msgstr "файлът „%s“ не може да се отвори или прочете"
 
 #, c-format
 msgid "invalid start of line: '%c'"
@@ -1165,12 +1214,11 @@
 #, c-format
 msgid "the necessary postimage %s for '%s' cannot be read"
 msgstr ""
-"необходимият резултат след операцията  — „%s“ за „%s“ не може да бъде "
-"прочетен"
+"необходимият резултат след операцията  — „%s“ за „%s“ не може да се прочете"
 
 #, c-format
 msgid "binary patch does not apply to '%s'"
-msgstr "двоичната кръпка не може да бъде приложена върху „%s“"
+msgstr "двоичната кръпка не може да се приложи върху „%s“"
 
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
@@ -1188,7 +1236,7 @@
 
 #, c-format
 msgid "failed to read %s"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
 
 #, c-format
 msgid "reading from '%s' beyond a symbolic link"
@@ -1215,7 +1263,7 @@
 
 #, c-format
 msgid "cannot read the current contents of '%s'"
-msgstr "текущото съдържание на „%s“ не може да бъде прочетено"
+msgstr "текущото съдържание на „%s“ не може да се прочете"
 
 #, c-format
 msgid "Failed to perform three-way merge...\n"
@@ -1271,7 +1319,7 @@
 
 #, c-format
 msgid "%s: patch does not apply"
-msgstr "Кръпката „%s“ не може да бъде приложена"
+msgstr "Кръпката „%s“ не може да се приложи"
 
 #, c-format
 msgid "Checking patch %s..."
@@ -1351,7 +1399,11 @@
 
 #, c-format
 msgid "cannot open %s"
-msgstr "„%s“ не може да бъде отворен"
+msgstr "„%s“ не може да се отвори"
+
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "„%s“ не може да се изтрие"
 
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1370,11 +1422,11 @@
 "На входа няма непразни кръпки (те се приемат при опция „--allow-empty“)"
 
 msgid "unable to read index file"
-msgstr "индексът не може да бъде записан"
+msgstr "индексът не може да се запише"
 
 #, c-format
 msgid "can't open patch '%s': %s"
-msgstr "кръпката „%s“ не може да бъде отворена: %s"
+msgstr "кръпката „%s“ не може да се отвори: %s"
 
 #, c-format
 msgid "squelched %d whitespace error"
@@ -1397,7 +1449,7 @@
 "Добавени са %d реда след корекцията на грешките в знаците за интервали."
 
 msgid "Unable to write new index file"
-msgstr "Новият индекс не може да бъде записан"
+msgstr "Новият индекс не може да се запише"
 
 msgid "don't apply changes matching the given path"
 msgstr "без прилагане на промѐните напасващи на дадения път"
@@ -1427,7 +1479,7 @@
 msgstr "проверка дали кръпката може да се приложи, без действително прилагане"
 
 msgid "make sure the patch is applicable to the current index"
-msgstr "проверка дали кръпката може да бъде приложена към текущия индекс"
+msgstr "проверка дали кръпката може да се приложи към текущия индекс"
 
 msgid "mark new files with `git add --intent-to-add`"
 msgstr "отбелязване на новите файлове с „git add --intent-to-add“"
@@ -1440,8 +1492,7 @@
 
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr ""
-"кръпката да бъде приложена.  Опцията се комбинира с „--check“/„--stat“/„--"
-"summary“"
+"кръпката се приложи.  Опцията се комбинира с „--check“/„--stat“/„--summary“"
 
 msgid "attempt three-way merge, fall back on normal patch if that fails"
 msgstr ""
@@ -1497,7 +1548,7 @@
 
 #, c-format
 msgid "cannot stream blob %s"
-msgstr "обектът-BLOB „%s“ не може да бъде обработен"
+msgstr "обектът-BLOB „%s“ не може да се обработи"
 
 #, c-format
 msgid "unsupported file mode: 0%o (SHA1: %s)"
@@ -1509,10 +1560,10 @@
 
 #, c-format
 msgid "unable to start '%s' filter"
-msgstr "филтърът „%s“ не може да бъде стартиран"
+msgstr "филтърът „%s“ не може да се стартира"
 
 msgid "unable to redirect descriptor"
-msgstr "дескрипторът не може да бъде пренасочен"
+msgstr "дескрипторът не може да се пренасочи"
 
 #, c-format
 msgid "'%s' filter reported error"
@@ -1544,7 +1595,11 @@
 
 #, c-format
 msgid "cannot read '%s'"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
+
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "пътят „%s“ съвпада с файлове извън текущата директория"
 
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1562,9 +1617,6 @@
 msgid "not a tree object: %s"
 msgstr "не е обект-дърво: %s"
 
-msgid "current working directory is untracked"
-msgstr "текущата работна директория не е следена"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Файлът „%s“ липсва"
@@ -1650,6 +1702,10 @@
 msgstr "Неочаквана опция „--output“"
 
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "излишна опция или стойност на командния ред: „%s“"
+
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "Непознат формат на архив: „%s“"
 
@@ -1691,6 +1747,19 @@
 msgid "ignoring overly large gitattributes blob '%s'"
 msgstr "прескачане на прекалено големия обект-BLOB за атрибути на git: „%s“"
 
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr ""
+"неправилна стойност за опцията „--attr-source“ или променливата "
+"„GIT_ATTR_SOURCE“"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "„stat“ не може да се изпълни върху „%s“"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "обектът „%s“ не може да бъде прочетен"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Неправилно цитирано съдържание във файла „%s“: %s"
@@ -1743,7 +1812,7 @@
 "So we cannot be sure the first %s commit is between %s and %s.\n"
 "We continue anyway."
 msgstr ""
-"базата за сливане между „%s“ и [%s] трябва да бъде прескочена.\n"
+"базата за сливане между „%s“ и [%s] трябва да се прескочи.\n"
 "Не може да сме сигурни, че първото %s подаване е между „%s“ и „%s“.\n"
 "Двоичното търсене продължава."
 
@@ -1757,11 +1826,11 @@
 
 #, c-format
 msgid "could not create file '%s'"
-msgstr "файлът „%s“ не може да бъде създаден"
+msgstr "файлът „%s“ не може да се създаде"
 
 #, c-format
 msgid "could not read file '%s'"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
 
 msgid "reading bisect refs failed"
 msgstr "неуспешно прочитане на указателите за двоично търсене"
@@ -1796,9 +1865,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "опциите „--contents“ и „--reverse“ са несъвместими"
 
-msgid "cannot use --contents with final commit object name"
-msgstr "опцията „--contents“ е несъвместима с име на обект от крайно подаване"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "Едновременното задаване на опциите „--reverse“ и „--first-parent“ изисква "
@@ -1819,7 +1885,7 @@
 
 #, c-format
 msgid "cannot read blob %s for path %s"
-msgstr "обектът-BLOB „%s“ в пътя %s не може да бъде прочетен"
+msgstr "обектът-BLOB „%s“ в пътя %s не може да се прочете"
 
 msgid ""
 "cannot inherit upstream tracking configuration of multiple refs when "
@@ -1846,7 +1912,7 @@
 msgstr "клонът „%s“ ще следи:"
 
 msgid "unable to write upstream branch configuration"
-msgstr "настройките за следения клон не може да бъдат записани"
+msgstr "настройките за следения клон не може да се запишат"
 
 msgid ""
 "\n"
@@ -1919,11 +1985,11 @@
 msgid "a branch named '%s' already exists"
 msgstr "вече съществува клон с име „%s“."
 
-# FIXME
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"не може принудително да обновите клона „%s“, който е изтеглен в пътя „%s“"
+"не може принудително да обновите клона „%s“, който се ползва от работното "
+"дърво в „%s“"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1982,12 +2048,8 @@
 msgstr "подмодул „%s“: клонът „%s“ не може да се създаде"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "„%s“ вече е изтеглен в „%s“"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "Указателят „HEAD“ на работното дърво „%s“ не е обновен"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "„%s“ вече се ползва от работното дърво в „%s“"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [ОПЦИЯ…] [--] ПЪТ…"
@@ -1996,17 +2058,6 @@
 msgid "cannot chmod %cx '%s'"
 msgstr "права̀та на „%2$s“ не може да се зададат да са %1$cx"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "неочакван изходен код при генериране на разлика: %c"
-
-msgid "updating files failed"
-msgstr "неуспешно обновяване на файловете"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "изтриване на „%s“\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Промѐни, които и след обновяването на индекса не са добавени към него:"
 
@@ -2017,29 +2068,26 @@
 "Настройката „add.interactive.useBuiltin“ е премахната!\n"
 "За подробности я потърсете в изхода от „git help config“."
 
-msgid "Could not read the index"
-msgstr "Индексът не може да бъде прочетен"
-
-msgid "Could not write patch"
-msgstr "Кръпката не може да бъде записана"
+msgid "could not read the index"
+msgstr "индексът не може да се прочете"
 
 msgid "editing patch failed"
 msgstr "неуспешно редактиране на кръпка"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Не може да се получи информация чрез „stat“ за файла „%s“"
+msgid "could not stat '%s'"
+msgstr "неуспешно изпълнение на „stat“ върху „%s“"
 
-msgid "Empty patch. Aborted."
-msgstr "Празна кръпка, преустановяване на действието."
+msgid "empty patch. aborted"
+msgstr "празна кръпка, преустановяване на действието"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Кръпката „%s“ не може да бъде приложена"
+msgid "could not apply '%s'"
+msgstr "кръпката „%s“ не може да се приложи"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
-"Следните пътища ще бъдат игнорирани според някой от файловете „.gitignore“:\n"
+"Следните пътища ще се игнорират според някой от файловете „.gitignore“:\n"
 
 msgid "dry run"
 msgstr "пробно изпълнение"
@@ -2066,7 +2114,7 @@
 msgstr "уеднаквяване на знаците за край на файл (включва опцията „-u“)"
 
 msgid "record only the fact that the path will be added later"
-msgstr "отбелязване само на факта, че пътят ще бъде добавен по-късно"
+msgstr "отбелязване само на факта, че пътят ще се добави по-късно"
 
 msgid "add changes from all tracked and untracked files"
 msgstr "добавяне на всички промѐни в следените и неследените файлове"
@@ -2080,7 +2128,7 @@
 msgstr "без добавяне на нови файлове, само обновяване на индекса"
 
 msgid "just skip files which cannot be added because of errors"
-msgstr "прескачане на файловете, които не може да бъдат добавени поради грешки"
+msgstr "прескачане на файловете, които не може да се добавят поради грешки"
 
 msgid "check if - even missing - files are ignored in dry run"
 msgstr ""
@@ -2169,6 +2217,9 @@
 msgid "index file corrupt"
 msgstr "файлът с индекса е повреден"
 
+msgid "unable to write new index file"
+msgstr "неуспешно записване на новия индекс"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "неправилно действие „%s“ за „%s“"
@@ -2179,7 +2230,7 @@
 
 #, c-format
 msgid "could not read '%s'"
-msgstr "файлът „%s“ не може да бъде прочетен"
+msgstr "файлът „%s“ не може да се прочете"
 
 msgid "could not parse author script"
 msgstr "скриптът за автор не може да се анализира"
@@ -2205,11 +2256,11 @@
 
 #, c-format
 msgid "could not open '%s' for reading"
-msgstr "файлът не може да бъде прочетен: „%s“"
+msgstr "файлът не може да се прочете: „%s“"
 
 #, c-format
 msgid "could not open '%s' for writing"
-msgstr "„%s“ не може да бъде отворен за запис"
+msgstr "„%s“ не може да се отвори за запис"
 
 #, c-format
 msgid "could not parse patch '%s'"
@@ -2217,7 +2268,7 @@
 
 msgid "Only one StGIT patch series can be applied at once"
 msgstr ""
-"Само една поредица от кръпки от „StGIT“ може да бъде прилагана в даден момент"
+"Само една поредица от кръпки от „StGIT“ може да се приложи в даден момент"
 
 msgid "invalid timestamp"
 msgstr "неправилна стойност за времево клеймо"
@@ -2229,14 +2280,14 @@
 msgstr "неправилно отместване на часовия пояс"
 
 msgid "Patch format detection failed."
-msgstr "Форматът на кръпката не може да бъде определен."
+msgstr "Форматът на кръпката не може да се определи."
 
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr "директорията „%s“ не може да бъде създадена"
+msgstr "директорията „%s“ не може да се създаде"
 
 msgid "Failed to split patches."
-msgstr "Кръпките не може да бъдат разделени."
+msgstr "Кръпките не може да се разделят."
 
 #, c-format
 msgid "When you have resolved this problem, run \"%s --continue\"."
@@ -2281,7 +2332,7 @@
 
 #, c-format
 msgid "unable to parse commit %s"
-msgstr "подаването не може да бъде анализирано: %s"
+msgstr "подаването не може да се анализира: %s"
 
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
 msgstr ""
@@ -2311,7 +2362,7 @@
 msgstr "прилагане върху празна история"
 
 msgid "failed to write commit object"
-msgstr "обектът за подаването не може да бъде записан"
+msgstr "обектът за подаването не може да се запише"
 
 #, c-format
 msgid "cannot resume: %s does not exist."
@@ -2331,12 +2382,11 @@
 "на всичко:"
 
 msgid "unable to write index file"
-msgstr "индексът не може да бъде записан"
+msgstr "индексът не може да се запише"
 
 #, c-format
 msgid "Dirty index: cannot apply patches (dirty: %s)"
-msgstr ""
-"Индексът не е чист: кръпките не може да бъдат приложени (замърсени са: %s)"
+msgstr "Индексът не е чист: кръпките не може да се приложат (замърсени са: %s)"
 
 #, c-format
 msgid "Skipping: %.*s"
@@ -2388,15 +2438,12 @@
 "След корекция на конфликтите изпълнете „git add“ върху поправените файлове.\n"
 "За да приемете „изтрити от тях“, изпълнете „git rm“ върху изтритите файлове."
 
-msgid "unable to write new index file"
-msgstr "неуспешно записване на новия индекс"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "„%s“ не е разпознат като обект."
 
 msgid "failed to clean index"
-msgstr "индексът не може да бъде изчистен"
+msgstr "индексът не може да се изчисти"
 
 msgid ""
 "You seem to have moved HEAD since the last 'am' failure.\n"
@@ -2410,17 +2457,13 @@
 
 #, c-format
 msgid "failed to read '%s'"
-msgstr "„%s“ не може да бъде прочетен"
-
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "опциите „%s=%s“ и „%s=%s“ са несъвместими"
+msgstr "„%s“ не може да се прочете"
 
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
-msgstr "git am [ОПЦИЯ…] [(ФАЙЛ_С_ПОЩА | ДИРЕКТОРИЯ_С_ПОЩА)…]"
+msgstr "git am [ОПЦИЯ…] [(ФАЙЛ_С_ПОЩА|ДИРЕКТОРИЯ_С_ПОЩА)…]"
 
 msgid "git am [<options>] (--continue | --skip | --abort)"
-msgstr "git am [ОПЦИЯ…] (--continue | --skip | --abort)"
+msgstr "git am [ОПЦИЯ…] (--continue|--skip|--abort)"
 
 msgid "run interactively"
 msgstr "интерактивна работа"
@@ -2458,11 +2501,6 @@
 msgstr ""
 "подаване на опцията „--keep-cr“ на командата „git-mailsplit“ за формат „mbox“"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"без подаване на опцията „--keep-cr“ на командата „git-mailsplit“ независимо "
-"от „am.keepcr“"
-
 msgid "strip everything before a scissors line"
 msgstr "пропускане на всичко преди реда за отрязване"
 
@@ -2560,7 +2598,7 @@
 msgstr "git apply [ОПЦИЯ…] [КРЪПКА…]"
 
 msgid "could not redirect output"
-msgstr "изходът не може да бъде пренасочен"
+msgstr "изходът не може да се пренасочи"
 
 msgid "git archive: Remote with no URL"
 msgstr "git archive: Липсва адрес за отдалеченото хранилище"
@@ -2580,12 +2618,12 @@
 msgstr "git archive: очакваше се изчистване на буферите чрез „flush“"
 
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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-{old,good}"
-"=УПРАВЛЯВАЩА_ДУМА] [--no-checkout] [--first-parent] [ЛОШО [ДОБРО…]] [--] "
-"[ПЪТ…]"
+"git bisect start [--term-(new,bad)=УПРАВЛЯВАЩА_ДУМА --term-(old,"
+"good)=УПРАВЛЯВАЩА_ДУМА] [--no-checkout] [--first-parent] [ЛОШО [ДОБРО…]] "
+"[--] [ПЪТ…]"
 
 msgid "git bisect (good|bad) [<rev>...]"
 msgstr "git bisect (good|bad) [ВЕРСИЯ…]"
@@ -2599,8 +2637,8 @@
 msgid "git bisect replay <logfile>"
 msgstr "git bisect replay ИМЕ_НА_ФАЙЛ"
 
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run КОМАНДА…"
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run КОМАНДА… [АРГУМЕНТ…]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2612,7 +2650,7 @@
 
 #, c-format
 msgid "cannot open file '%s' for reading"
-msgstr "файлът „%s“ не може да бъде отворен за четене"
+msgstr "файлът „%s“ не може да се отвори за четене"
 
 #, c-format
 msgid "'%s' is not a valid term"
@@ -2641,8 +2679,8 @@
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr ""
-"първоначално указаното „%s“ в указателя „HEAD“ не може да бъде\n"
-"изтеглено.  Пробвайте да изпълните командата „git bisect reset ПОДАВАНЕ“."
+"първоначално указаното „%s“ в указателя „HEAD“ не може да се\n"
+"изтегли.  Пробвайте да изпълните командата „git bisect reset ПОДАВАНЕ“."
 
 #, c-format
 msgid "Bad bisect_write argument: %s"
@@ -3013,7 +3051,7 @@
 msgstr "Редове с авторство"
 
 msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
-msgstr "git branch [ОПЦИЯ…] [-r | -a] [--merged] [--no-merged]"
+msgstr "git branch [ОПЦИЯ…] [-r|-a] [--merged] [--no-merged]"
 
 msgid ""
 "git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
@@ -3024,65 +3062,77 @@
 msgstr "git branch [ОПЦИЯ…] [-l] [ШАБЛОН…]"
 
 msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
-msgstr "git branch [ОПЦИЯ…] [-r] (-d | -D) ИМЕ_НА_КЛОН…"
+msgstr "git branch [ОПЦИЯ…] [-r] (-d|-D) ИМЕ_НА_КЛОН…"
 
 msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
-msgstr "git branch [ОПЦИЯ…] (-m | -M) [СТАР_КЛОН] НОВ_КЛОН"
+msgstr "git branch [ОПЦИЯ…] (-m|-M) [СТАР_КЛОН] НОВ_КЛОН"
 
 msgid "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
-msgstr "git branch [ОПЦИЯ…] (-c | -C) [СТАР_КЛОН] НОВ_КЛОН"
+msgstr "git branch [ОПЦИЯ…] (-c|-C) [СТАР_КЛОН] НОВ_КЛОН"
 
 msgid "git branch [<options>] [-r | -a] [--points-at]"
-msgstr "git branch [ОПЦИЯ…] [-r | -a] [--points-at]"
+msgstr "git branch [ОПЦИЯ…] [-r|-a] [--points-at]"
 
 msgid "git branch [<options>] [-r | -a] [--format]"
-msgstr "git branch [ОПЦИЯ…] [-r | -a] [--format]"
+msgstr "git branch [ОПЦИЯ…] [-r|-a] [--format]"
 
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "изтриване на клона „%s“, който е слят към „%s“,\n"
-"    но още не е слят към върха „HEAD“."
+"    но още не е слят към върха „HEAD“"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "отказване на изтриване на клона „%s“, който не е слят към\n"
-"    „%s“, но е слят към върха „HEAD“."
+"    „%s“, но е слят към върха „HEAD“"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Обектът-подаване за „%s“ не може да бъде открит"
+msgid "couldn't look up commit object for '%s'"
+msgstr "обектът-подаване за „%s“ не може да се открие"
 
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+msgid "the branch '%s' is not fully merged"
+msgstr "клонът „%s“ не е слят напълно"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"Клонът „%s“ не е слят напълно.  Ако сте сигурни, че искате\n"
-"да го изтриете, изпълнете „git branch -D %s“."
+"Ако сте сигурни, че искате да го изтриете, изпълнете:\n"
+"\n"
+"    git branch -D %s"
 
-msgid "Update of config-file failed"
-msgstr "Неуспешно обновяване на конфигурационния файл"
+msgid "update of config-file failed"
+msgstr "неуспешно обновяване на конфигурационния файл"
 
 msgid "cannot use -a with -d"
 msgstr "опциите „-a“ и „-d“ са несъвместими"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Не може да изтриете клона „%s“, който е изтеглен в пътя „%s“"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"не може да изтриете клона „%s“, който се ползва от работното дърво в „%s“"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "следящият клон „%s“ не може да бъде открит."
+msgid "remote-tracking branch '%s' not found"
+msgstr "следящият клон „%s“ липсва"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "клонът „%s“ не може да бъде открит."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"клонът „%s“ не може да бъде открит.\n"
+"Пробвахте ли опцията „--remote“?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "клонът „%s“ липсва"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -3100,51 +3150,55 @@
 
 #, c-format
 msgid "HEAD (%s) points outside of refs/heads/"
-msgstr "„HEAD“ (%s) сочи извън директорията „refs/heads“"
+msgstr "„HEAD“ (%s) сочи извън директорията „refs/heads/“"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Клонът „%s“ се пребазира върху „%s“"
+msgid "branch %s is being rebased at %s"
+msgstr "клонът „%s“ се пребазира върху „%s“"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Търси се двоично в клона „%s“ при „%s“"
+msgid "branch %s is being bisected at %s"
+msgstr "търси се двоично в клона „%s“ при „%s“"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Неправилно име на клон: „%s“"
+msgid "HEAD of working tree %s is not updated"
+msgstr "Указателят „HEAD“ на работното дърво „%s“ не е обновен"
 
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "В клона „%s“ все още няма подавания."
+msgid "invalid branch name: '%s'"
+msgstr "неправилно име на клон: „%s“"
 
 #, c-format
-msgid "No branch named '%s'."
-msgstr "Липсва клон на име „%s“."
-
-msgid "Branch rename failed"
-msgstr "Неуспешно преименуване на клон"
-
-msgid "Branch copy failed"
-msgstr "Неуспешно копиране на клон"
+msgid "no commit on branch '%s' yet"
+msgstr "в клона „%s“ все още няма подавания"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Клонът с неправилно име „%s“ е копиран"
+msgid "no branch named '%s'"
+msgstr "липсва клон на име „%s“"
+
+msgid "branch rename failed"
+msgstr "неуспешно преименуване на клон"
+
+msgid "branch copy failed"
+msgstr "неуспешно копиране на клон"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Клонът с неправилно име „%s“ е преименуван"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "клонът с неправилно име „%s“ е копиран"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Клонът е преименуван на „%s“, но указателят „HEAD“ не е обновен"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "клонът с неправилно име „%s“ е преименуван"
 
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Клонът е преименуван, но конфигурационният файл не е обновен"
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "клонът е преименуван на „%s“, но указателят „HEAD“ не е обновен"
 
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Клонът е копиран, но конфигурационният файл не е обновен"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "клонът е преименуван, но конфигурационният файл не е обновен"
+
+msgid "branch is copied, but update of config-file failed"
+msgstr "клонът е копиран, но конфигурационният файл не е обновен"
 
 #, c-format
 msgid ""
@@ -3213,6 +3267,9 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "преместване/преименуване на клон, дори ако има вече клон с такова име"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "без извеждане на нов ред след празен форматиран указател"
+
 msgid "copy a branch and its reflog"
 msgstr "копиране на клон и принадлежащия му журнал на указателите"
 
@@ -3258,8 +3315,8 @@
 msgid "format to use for the output"
 msgstr "ФОРМАТ за изхода"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Не може да се открие към какво сочи указателят „HEAD“"
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "указателят „HEAD“ не сочи към обект"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "В директорията „refs/heads“ липсва файл „HEAD“"
@@ -3278,16 +3335,16 @@
 msgid "branch name required"
 msgstr "Необходимо е име на клон"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Не може да зададете описание на несвързан „HEAD“"
+msgid "cannot give description to detached HEAD"
+msgstr "не може да зададете описание на несвързан „HEAD“"
 
 msgid "cannot edit description of more than one branch"
-msgstr "Не може да редактирате описанието на повече от един клон едновременно"
+msgstr "не може да редактирате описанието на повече от един клон едновременно"
 
-msgid "cannot copy the current branch while not on any."
+msgid "cannot copy the current branch while not on any"
 msgstr "не може да копирате текущия клон, защото сте извън който и да е клон"
 
-msgid "cannot rename the current branch while not on any."
+msgid "cannot rename the current branch while not on any"
 msgstr ""
 "не може да преименувате текущия клон, защото сте извън който и да е клон"
 
@@ -3302,32 +3359,31 @@
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"Следеното от „HEAD“ не може да се зададе да е „%s“, защото то не сочи към "
-"никой клон."
+"следеното от „HEAD“ не може да се зададе да е „%s“, защото то не сочи към "
+"никой клон"
 
 #, c-format
 msgid "no such branch '%s'"
-msgstr "Няма клон на име „%s“."
+msgstr "няма клон на име „%s“."
 
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "Не съществува клон на име „%s“."
+msgstr "не съществува клон на име „%s“."
 
 msgid "too many arguments to unset upstream"
 msgstr "прекалено много аргументи към командата за спиране на следене"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr ""
-"Следеното от „HEAD“ не може да махне, защото то не сочи към никой клон."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "следеното от „HEAD“ не може да махне, защото то не сочи към никой клон"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Няма информация клонът „%s“ да следи някой друг"
+msgid "branch '%s' has no upstream information"
+msgstr "няма информация клонът „%s“ да следи някой друг"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "опциите „-a“ и „-r“ на „git branch“ са несъвместими с име на клон.\n"
@@ -3335,7 +3391,7 @@
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "опцията „--set-upstream“ вече не се поддържа.  Използвайте „--track“ или „--"
 "set-upstream-to“"
@@ -3360,7 +3416,7 @@
 "git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
 "              [--diagnose[=<mode>]]"
 msgstr ""
-"git bugreport [(-o | --output-directory) ПЪТ] [(-s | --suffix) ФОРМАТ]\n"
+"git bugreport [(-o|--output-directory) ПЪТ] [(-s|--suffix) ФОРМАТ]\n"
 "              [--diagnose[=РЕЖИМ]]"
 
 msgid ""
@@ -3414,6 +3470,10 @@
 msgstr "укажете суфикса на файловете във формат за „strftime“"
 
 #, c-format
+msgid "unknown argument `%s'"
+msgstr "непозната опция „%s“"
+
+#, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "родителските директории на „%s“ не може да бъдат създадени"
 
@@ -3436,16 +3496,14 @@
 msgstr "Новият доклад е създаден в „%s“.\n"
 
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q|--quiet|--progress ]\n"
 "                  [--version=ВЕРСИЯ] ФАЙЛ ОПЦИЯ_ЗА_git-rev-list…"
 
 msgid "git bundle verify [-q | --quiet] <file>"
-msgstr "git bundle verify [-q | --quiet] ФАЙЛ"
+msgstr "git bundle verify [-q|--quiet] ФАЙЛ"
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr "git bundle list-heads ФАЙЛ [ИМЕ_НА_УКАЗАТЕЛ…]"
@@ -3462,12 +3520,11 @@
 msgid "show progress meter"
 msgstr "извеждане на напредъка"
 
-msgid "show progress meter during object writing phase"
-msgstr "извеждане на напредъка във фазата на запазване на обектите"
+msgid "historical; same as --progress"
+msgstr "изоставена опция, същото като „--progress“"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr ""
-"същото действие като опцията „--all-progress“ при извеждането на напредъка"
+msgid "historical; does nothing"
+msgstr "изоставена опция, нищо не прави"
 
 msgid "specify bundle format version"
 msgstr "версия на пратката"
@@ -3517,28 +3574,27 @@
 msgstr "git cat-file ВИД ОБЕКТ"
 
 msgid "git cat-file (-e | -p) <object>"
-msgstr "git cat-file (-e | -p) ОБЕКТ"
+msgstr "git cat-file (-e|-p) ОБЕКТ"
 
 msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
-msgstr "git cat-file (-t | -s) [--allow-unknown-type] ОБЕКТ"
-
-msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
+msgstr "git cat-file (-t|-s) [--allow-unknown-type] ОБЕКТ"
 
 msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [ВЕРСИЯ:ПЪТ|ДЪРВО | --path=ПЪТ|ДЪРВО ВЕРСИЯ]"
+"git cat-file (--textconv|--filters)\n"
+"             [ВЕРСИЯ:ПЪТ|ДЪРВО|--path=ПЪТ|ДЪРВО ВЕРСИЯ]"
+
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch|--batch-check|--batch-command) [--batch-all-objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv|--filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Проверка за съществуването на обекта или извеждане на съдържанието му"
@@ -3582,6 +3638,9 @@
 msgid "stdin is NUL-terminated"
 msgstr "стандартният вход да ползва нулевия знак „NUL“ за разделител"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "стандартните вход и изход да ползват нулевия знак „NUL“ за разделител"
+
 msgid "read commands from stdin"
 msgstr "изчитане на командите от стандартния вход"
 
@@ -3656,14 +3715,12 @@
 "git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
 "<pathname>..."
 msgstr ""
-"git check-attr [--source УКАЗАТЕЛ_КЪМ_ДЪРВО] [-a | --all | АТРИБУТ…] [--] "
-"ПЪТ…"
+"git check-attr [--source УКАЗАТЕЛ_КЪМ_ДЪРВО] [-a|--all|АТРИБУТ…] [--] ПЪТ…"
 
 msgid ""
 "git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
 msgstr ""
-"git check-attr --stdin [-z] [--source УКАЗАТЕЛ_КЪМ_ДЪРВО] [-a | --all | "
-"АТРИБУТ…]"
+"git check-attr --stdin [-z] [--source УКАЗАТЕЛ_КЪМ_ДЪРВО] [-a|--all|АТРИБУТ…]"
 
 msgid "report all attributes set on file"
 msgstr "извеждане на всички атрибути, зададени върху файл"
@@ -3843,6 +3900,10 @@
 msgstr "опцията „%3$s“ е несъвместима както с „%1$s“, така и с „%2$s“"
 
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "„%s“, „%s“ и „%s“ са несъвместими с изтеглянето на дърво"
+
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "пътят „%s“ не е слят"
 
@@ -4101,8 +4162,8 @@
 msgid "new-branch"
 msgstr "НОВ_КЛОН"
 
-msgid "new unparented branch"
-msgstr "нов клон без родител"
+msgid "new unborn branch"
+msgstr "нов неродѐн клон"
 
 msgid "update ignored files (default)"
 msgstr "обновяване на игнорираните файлове (стандартно)"
@@ -4132,7 +4193,7 @@
 
 #, c-format
 msgid "could not resolve %s"
-msgstr "„%s“ не може да бъде открит"
+msgstr "„%s“ не може да бъде проследен"
 
 msgid "invalid path specification"
 msgstr "указан е неправилен път"
@@ -4207,7 +4268,7 @@
 msgid ""
 "git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
 "[<pathspec>...]"
-msgstr "git clean [-d] [-f] [-i] [-n] [-q] [-e ШАБЛОН] [-x | -X] [--] [ПЪТ…]"
+msgstr "git clean [-d] [-f] [-i] [-n] [-q] [-e ШАБЛОН] [-x|-X] [--] [ПЪТ…]"
 
 #, c-format
 msgid "Removing %s\n"
@@ -4355,9 +4416,6 @@
 "което изисква някоя от опциите „-i“, „-n“ или „-f“.  Няма да се извърши "
 "изчистване"
 
-msgid "-x and -X cannot be used together"
-msgstr "опциите „-x“ и „-X“ са несъвместими"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]"
 
@@ -4449,6 +4507,9 @@
 msgid "separate git dir from working tree"
 msgstr "отделна СЛУЖЕБНА_ДИРЕКТОРИЯ за git извън работното дърво"
 
+msgid "specify the reference format to use"
+msgstr "указване на форма̀та за указател"
+
 msgid "key=value"
 msgstr "КЛЮЧ=СТОЙНОСТ"
 
@@ -4461,12 +4522,6 @@
 msgid "option to transmit"
 msgstr "опция за пренос"
 
-msgid "use IPv4 addresses only"
-msgstr "само адреси IPv4"
-
-msgid "use IPv6 addresses only"
-msgstr "само адреси IPv6"
-
 msgid "apply partial clone filters to submodules"
 msgstr "прилагане на филтрите за непълно хранилище към подмодулите"
 
@@ -4499,6 +4554,10 @@
 msgstr "„%s“ съществува и не е директория"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "„%s“ е символна връзка, не може да се клонира с опцията „--local“"
+
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "неуспешно итериране по „%s“"
 
@@ -4578,12 +4637,9 @@
 msgid "You must specify a repository to clone."
 msgstr "Трябва да укажете кое хранилище искате да клонирате."
 
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"опцията „--bundle-uri“ е несъвместима с „--depth“, „--shallow-since“ и „--"
-"shallow-exclude“"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "непознат формат на съхранение: „%s“"
 
 #, c-format
 msgid "repository '%s' does not exist"
@@ -4721,11 +4777,11 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir ДИРЕКТОРИЯ] [--append]\n"
-"                       [--split[=СТРАТЕГИЯ]] [--reachable | --stdin-packs | "
-"--stdin-commits]\n"
+"                       [--split[=СТРАТЕГИЯ]] [--reachable|--stdin-packs|--"
+"stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters БРОЙ] [--"
 "[no-]progress]\n"
 "                       ОПЦИИ_ЗА_РАЗДЕЛЯНЕ"
@@ -4738,13 +4794,17 @@
 
 msgid "if the commit-graph is split, only verify the tip file"
 msgstr ""
-"ако гра̀фа с подаванията е раздробен, да се проверява само файлът на върха"
+"ако графът с подаванията е раздробен, да се проверява само файлът на върха"
 
 #, c-format
 msgid "Could not open commit-graph '%s'"
 msgstr "Графът с подаванията не може да се отвори: „%s“"
 
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "веригата на гра̀фа с подаванията не може да се отвори: „%s“"
+
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "непознат аргумент към „--split“: %s"
 
@@ -4863,13 +4923,13 @@
 "           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
 "           [--] [<pathspec>...]"
 msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u РЕЖИМ] [--amend]\n"
-"           [--dry-run] [(-c | -C | --squash) ПОДАВАНЕ | --fixup [(amend|"
+"git commit [-a|--interactive|--patch] [-s] [-v] [-u РЕЖИМ] [--amend]\n"
+"           [--dry-run] [(-c|-C|--squash) ПОДАВАНЕ |--fixup [(amend|"
 "reword):]ПОДАВАНЕ)]\n"
-"           [-F ФАЙЛ | -m СЪОБЩЕНИЕ] [--reset-author] [--allow-empty]\n"
+"           [-F ФАЙЛ|-m СЪОБЩЕНИЕ] [--reset-author] [--allow-empty]\n"
 "           [--allow-empty-message] [--no-verify] [-e] [--author=АВТОР]\n"
 "           [--date=ДАТА] [--cleanup=РЕЖИМ] [--[no-]status]\n"
-"           [-i | -o] [--pathspec-from-file=ФАЙЛ> [--pathspec-file-nul]]\n"
+"           [-i|-o] [--pathspec-from-file=ФАЙЛ> [--pathspec-file-nul]]\n"
 "           [(--trailer ЛЕКСЕМА[(=|:)СТОЙНОСТ])…] [-"
 "S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]]\n"
 "           [--] [ПЪТ…]"
@@ -4935,6 +4995,9 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "неуспешно обновяване на файловете"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "върховото дърво (HEAD tree object) не може да бъде извадено от пакет"
 
@@ -4953,9 +5016,6 @@
 msgid "Failed to update main cache tree"
 msgstr "Кешът за обектите-дървета не може да бъде обновен"
 
-msgid "unable to write new_index file"
-msgstr "новият индекс (new_index) не може да бъде записан"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "по време на сливане не може да се извърши частично подаване."
 
@@ -4994,7 +5054,7 @@
 "използвани всички подобни знаци"
 
 #, c-format
-msgid "could not lookup commit %s"
+msgid "could not lookup commit '%s'"
 msgstr "следното подаване не може да бъде открито: %s"
 
 #, c-format
@@ -5367,12 +5427,12 @@
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"хранилището е обновено, но новият файл за индекс „new_index“\n"
-"не е записан.  Проверете дали дискът не е препълнен или не сте\n"
-"превишили дисковата си квота.  За възстановяване изпълнете:\n"
+"хранилището е обновено, но новият файл за индекс не е записан.\n"
+"Проверете дали дискът не е препълнен или не сте превишили\n"
+"дисковата си квота.  За възстановяване изпълнете:\n"
 "\n"
 "    git restore --staged :/"
 
@@ -5820,7 +5880,7 @@
 "git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
 "             [--mode=<mode>]"
 msgstr ""
-"git diagnose [(-o | --output-directory) ПЪТ] [(-s | --suffix) ФОРМАТ]\n"
+"git diagnose [(-o|--output-directory) ПЪТ] [(-s|--suffix) ФОРМАТ]\n"
 "             [--diagnose[=РЕЖИМ]]"
 
 msgid "specify a destination for the diagnostics archive"
@@ -6063,7 +6123,7 @@
 msgstr "git fetch [ОПЦИЯ…] ГРУПА"
 
 msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
-msgstr "git fetch --multiple [ОПЦИЯ…] [(ХРАНИЛИЩЕ | ГРУПА)…]"
+msgstr "git fetch --multiple [ОПЦИЯ…] [(ХРАНИЛИЩЕ|ГРУПА)…]"
 
 msgid "git fetch --all [<options>]"
 msgstr "git fetch --all [ОПЦИЯ…]"
@@ -6071,6 +6131,181 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "опцията „fetch.parallel“ трябва да е неотрицателна"
 
+msgid "couldn't find remote ref HEAD"
+msgstr "указателят „HEAD“ в отдалеченото хранилище не може да бъде открит"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "От %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "обектът „%s“ липсва"
+
+msgid "[up to date]"
+msgstr "[актуален]"
+
+msgid "[rejected]"
+msgstr "[отхвърлен]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "в текущия клон не може да се доставя"
+
+msgid "[tag update]"
+msgstr "[обновяване на етикетите]"
+
+msgid "unable to update local ref"
+msgstr "локален указател не може да бъде обновен"
+
+msgid "would clobber existing tag"
+msgstr "съществуващ етикет ще бъде презаписан"
+
+msgid "[new tag]"
+msgstr "[нов етикет]"
+
+msgid "[new branch]"
+msgstr "[нов клон]"
+
+msgid "[new ref]"
+msgstr "[нов указател]"
+
+msgid "forced update"
+msgstr "принудително обновяване"
+
+msgid "non-fast-forward"
+msgstr "същинско сливане"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "„%s“ не може да бъде отворен"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"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"
+"„--show-forced-updates“, а за да я включите за постоянно, изпълнете:\n"
+"\n"
+"    git config fetch.showForcedUpdates true"
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"Проверката за принудителни изтласквания отне %.2f сек.  Може да я прескочите "
+"еднократно с опцията „--no-show-forced-updates“, а за да я изключите за "
+"постоянно, изпълнете:\n"
+"\n"
+"    git config fetch.showForcedUpdates false\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "хранилището „%s“ не изпрати всички необходими обекти\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr ""
+"отхвърляне на „%s“, защото плитките върхове не може да бъдат обновявани"
+
+#, c-format
+msgid ""
+"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"
+"предизвикват конфликта"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (обектът „%s“ ще се окаже извън клон)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (обектът „%s“ вече е извън клон)"
+
+msgid "[deleted]"
+msgstr "[изтрит]"
+
+msgid "(none)"
+msgstr "(нищо)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "не може да доставите в клона „%s“, който е изтеглен в пътя „%s“"
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "стойността „%2$s“ за опцията „%1$s“ не е съвместима с „%3$s“"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "опцията „%s“ се прескача при „%s“\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "„%s“ е неправилен обект"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "обектът „%s“ не съществува"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr ""
+"засечени са множество клони, това е несъвместимо с опцията „--set-upstream“"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"следеното от „HEAD“ не може да се смени от „%2$s“ на „%1$s“, защото второто "
+"не сочи към никой клон."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "не може да указвате клон за следене на отдалечен етикет"
+
+msgid "not setting upstream for a remote tag"
+msgstr "не може да указвате клон за следене на отдалечен етикет"
+
+msgid "unknown branch type"
+msgstr "непознат вид клон"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"не е открит клон за следене;\n"
+"трябва изрично да зададете точно един клон с опцията „--set-upstream“"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Доставяне на „%s“\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "„%s“ не може да се достави"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "„%s“ не може да се достави (изходният код е: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"не сте указали отдалечено хранилище; задайте или адрес, или име\n"
+"на отдалечено хранилище, откъдето да се доставят новите версии."
+
+msgid "you need to specify a tag name"
+msgstr "трябва да укажете име на етикет"
+
 msgid "fetch from all remotes"
 msgstr "доставяне от всички отдалечени хранилища"
 
@@ -6180,181 +6415,6 @@
 msgid "accept refspecs from stdin"
 msgstr "четене на указателите от стандартния вход"
 
-msgid "couldn't find remote ref HEAD"
-msgstr "указателят „HEAD“ в отдалеченото хранилище не може да бъде открит"
-
-#, c-format
-msgid "object %s not found"
-msgstr "обектът „%s“ липсва"
-
-msgid "[up to date]"
-msgstr "[актуален]"
-
-msgid "[rejected]"
-msgstr "[отхвърлен]"
-
-msgid "can't fetch into checked-out branch"
-msgstr "в текущия клон не може да се доставя"
-
-msgid "[tag update]"
-msgstr "[обновяване на етикетите]"
-
-msgid "unable to update local ref"
-msgstr "локален указател не може да бъде обновен"
-
-msgid "would clobber existing tag"
-msgstr "съществуващ етикет ще бъде презаписан"
-
-msgid "[new tag]"
-msgstr "[нов етикет]"
-
-msgid "[new branch]"
-msgstr "[нов клон]"
-
-msgid "[new ref]"
-msgstr "[нов указател]"
-
-msgid "forced update"
-msgstr "принудително обновяване"
-
-msgid "non-fast-forward"
-msgstr "същинско сливане"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "„%s“ не може да бъде отворен"
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"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"
-"„--show-forced-updates“, а за да я включите за постоянно, изпълнете:\n"
-"\n"
-"    git config fetch.showForcedUpdates true"
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"Проверката за принудителни изтласквания отне %.2f сек.  Може да я прескочите "
-"еднократно с опцията „--no-show-forced-updates“, а за да я изключите за "
-"постоянно, изпълнете:\n"
-"\n"
-"    git config fetch.showForcedUpdates false\n"
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "хранилището „%s“ не изпрати всички необходими обекти\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr ""
-"отхвърляне на „%s“, защото плитките върхове не може да бъдат обновявани"
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "От %.*s\n"
-
-#, c-format
-msgid ""
-"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"
-"предизвикват конфликта"
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (обектът „%s“ ще се окаже извън клон)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (обектът „%s“ вече е извън клон)"
-
-msgid "[deleted]"
-msgstr "[изтрит]"
-
-msgid "(none)"
-msgstr "(нищо)"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "не може да доставите в клона „%s“, който е изтеглен в пътя „%s“"
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "стойността „%2$s“ за опцията „%1$s“ не е съвместима с „%3$s“"
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "опцията „%s“ се прескача при „%s“\n"
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr "„%s“ е неправилен обект"
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr "обектът „%s“ не съществува"
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr ""
-"засечени са множество клони, това е несъвместимо с опцията „--set-upstream“"
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"следеното от „HEAD“ не може да се смени от „%2$s“ на „%1$s“, защото второто "
-"не сочи към никой клон."
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "не може да указвате клон за следене на отдалечен етикет"
-
-msgid "not setting upstream for a remote tag"
-msgstr "не може да указвате клон за следене на отдалечен етикет"
-
-msgid "unknown branch type"
-msgstr "непознат вид клон"
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"не е открит клон за следене;\n"
-"трябва изрично да зададете точно един клон с опцията „--set-upstream“"
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "Доставяне на „%s“\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr "„%s“ не може да се достави"
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "„%s“ не може да се достави (изходният код е: %d)\n"
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"не сте указали отдалечено хранилище; задайте или адрес, или име\n"
-"на отдалечено хранилище, откъдето да се доставят новите версии."
-
-msgid "you need to specify a tag name"
-msgstr "трябва да укажете име на етикет"
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr ""
 "Опцията „--negotiate-only“ изисква една или повече опции „--negotiation-"
@@ -6405,8 +6465,7 @@
 
 msgid ""
 "git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
-msgstr ""
-"git fmt-merge-msg [-m СЪОБЩЕНИЕ] [--log[=БРОЙ] | --no-log] [--file ФАЙЛ]"
+msgstr "git fmt-merge-msg [-m СЪОБЩЕНИЕ] [--log[=БРОЙ]|--no-log] [--file ФАЙЛ]"
 
 msgid "populate log with at most <n> entries from shortlog"
 msgstr ""
@@ -6473,6 +6532,12 @@
 msgid "print only refs which don't contain the commit"
 msgstr "извеждане само на указателите, които не съдържат това ПОДАВАНЕ"
 
+msgid "read reference patterns from stdin"
+msgstr "изчитане на шаблоните за указатели от стандартния вход"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "непознат аргумент към опцията „--stdin“"
+
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=НАСТРОЙКА [--] АРГУМЕНТ…"
 
@@ -6485,6 +6550,10 @@
 msgid "missing --config=<config>"
 msgstr "липсва --config=НАСТРОЙКА"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "получена е неправилена настройка „--config=%s“"
+
 msgid "unknown"
 msgstr "непознат"
 
@@ -6629,21 +6698,30 @@
 
 #, c-format
 msgid "notice: %s points to an unborn branch (%s)"
-msgstr "предупреждение: „%s“ сочи към все още несъществуващ клон (%s)"
-
-msgid "Checking cache tree"
-msgstr "Проверка на кеша на обектите-дървета"
+msgstr "предупреждение: „%s“ сочи към неродѐн клон (%s)"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "„%s“: неправилен указател за SHA1 в кеша на обектите-дървета"
+msgid "Checking cache tree of %s"
+msgstr "Проверка на кеша на обектите-дървета на „%s“"
+
+#, c-format
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "„%s“: неправилен указател за SHA1 в кеша на обектите-дървета на „%s“"
 
 msgid "non-tree in cache-tree"
 msgstr "в кеша на обектите-дървета има нещо, което не е дърво"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "„%s“: неправилен указател за отмяна на разрешените подавания"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "„%s“: неправилен указател за отмяна на разрешените подавания на „%s“"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "обратният индекс на пакета „%s“ не може да бъде зареден"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "неправилен обратен индекс за пакета „%s“"
 
 msgid ""
 "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
@@ -6832,6 +6910,9 @@
 msgid "pack unreferenced objects separately"
 msgstr "пакетиране на обектите, към които нищо не сочи, отделно"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "с „--cruft“ размерът на новите ненужни пакети се ограничава"
+
 msgid "be more thorough (increased runtime)"
 msgstr "изчерпателно търсене на боклука (за сметка на повече време работа)"
 
@@ -7014,12 +7095,6 @@
 msgid "'crontab' died"
 msgstr "процесът на „crontab“ умря"
 
-msgid "failed to start systemctl"
-msgstr "неуспешно стартиране на „systemctl“"
-
-msgid "failed to run systemctl"
-msgstr "неуспешно изпълнение на „systemctl“"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "неуспешно изтриване на „%s“"
@@ -7028,6 +7103,12 @@
 msgid "failed to flush '%s'"
 msgstr "грешка при изчистването на буферите при записването на „%s“"
 
+msgid "failed to start systemctl"
+msgstr "неуспешно стартиране на „systemctl“"
+
+msgid "failed to run systemctl"
+msgstr "неуспешно изпълнение на „systemctl“"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "непознат аргумент към „--scheduler“: %s"
@@ -7051,6 +7132,9 @@
 msgid "scheduler to trigger git maintenance run"
 msgstr "ПЛАНИРАЩият_МОДУЛ, който да изпълнява задачите"
 
+msgid "failed to set up maintenance schedule"
+msgstr "неуспешно насрочване на задачите по поддръжка"
+
 msgid "failed to add repo to global config"
 msgstr "неуспешно добавяне на хранилище към файла с глобални настройки"
 
@@ -7082,6 +7166,10 @@
 msgstr "дървото не може да бъде прочетено (%s)"
 
 #, c-format
+msgid "unable to read tree %s"
+msgstr "дървото не може да бъде прочетено: %s"
+
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "не може да се изпълни „grep“ от обект от вида %s"
 
@@ -7126,7 +7214,7 @@
 msgid "search in subdirectories (default)"
 msgstr "търсене в поддиректориите (стандартно)"
 
-msgid "descend at most <depth> levels"
+msgid "descend at most <n> levels"
 msgstr "навлизане максимално на тази ДЪЛБОЧИНА в дървото"
 
 msgid "use extended POSIX regular expressions"
@@ -7279,7 +7367,7 @@
 "git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
 "                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t ВИД] [-w] [--path=ФАЙЛ | --no-filters]\n"
+"git hash-object [-t ВИД] [-w] [--path=ФАЙЛ|--no-filters]\n"
 "                [--stdin [--literally]] [--] ФАЙЛ…"
 
 msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
@@ -7503,10 +7591,6 @@
 "СЪВПАДЕНИЕ НА СТОЙНОСТИТЕ ЗА СУМИТЕ ЗА SHA1: „%s“ НА ДВА РАЗЛИЧНИ ОБЕКТА!"
 
 #, c-format
-msgid "unable to read %s"
-msgstr "обектът „%s“ не може да бъде прочетен"
-
-#, c-format
 msgid "cannot read existing object info %s"
 msgstr "съществуващият обект в „%s“ не може да бъде прочетен"
 
@@ -7645,89 +7729,16 @@
 msgid "fsck error in pack objects"
 msgstr "грешка при проверка с „fsck“ на пакетните обекти"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "не може да се получи информация чрез „stat“ за шаблона „%s“"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "директорията „%s“ не може да бъде отворена"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "връзката „%s“ не може да бъде прочетена"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "не може да се създаде символна връзка „%s“ в „%s“"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "„%s“ не може да се копира в „%s“"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "игнориране на шаблона „%s“"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "няма шаблони в „%s“"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "шаблоните няма да бъдат копирани от „%s“: „%s“"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "неправилно име на първоначалния клон: „%s“"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "файлове от вид %d не се поддържат"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "„%s“ не може да се премести в „%s“"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr ""
-"опит за повторно задаване на първото подаване в хранилището с различна "
-"контролна сума"
-
-#, c-format
-msgid "%s already exists"
-msgstr "Директорията „%s“ вече съществува"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: „--initial-branch=%s“ се пропуска"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr ""
-"Инициализиране наново на съществуващо, споделено хранилище на Git в „%s%s“\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Инициализиране наново на съществуващо хранилище на Git в „%s%s“\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Инициализиране на празно, споделено хранилище на Git в „%s%s“\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Инициализиране на празно хранилище на Git в „%s%s“\n"
-
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=ДИРЕКТОРИЯ_С_ШАБЛОНИ]\n"
+"git init [-q|--quiet] [--bare] [--template=ДИРЕКТОРИЯ_С_ШАБЛОНИ]\n"
 "         [--separate-git-dir ДИРЕКТОРИЯ_НА_GIT] [--object-format=ФОРМАТ]\n"
-"         [-b КЛОН | --initial-branch=КЛОН]\n"
+"         [-b КЛОН|--initial-branch=КЛОН]\n"
 "         [--shared[=ПРАВА̀]] [ДИРЕКТОРИЯ]"
 
 msgid "permissions"
@@ -7772,11 +7783,11 @@
 
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer ЛЕКСЕМА[(=|:)СТОЙНОСТ])…]\n"
+"                       [(--trailer (КЛЮЧ|СИНОНИМ)[(=|:)СТОЙНОСТ])…]\n"
 "                       [--parse] [ФАЙЛ…]"
 
 msgid "edit files in place"
@@ -7785,6 +7796,9 @@
 msgid "trim empty trailers"
 msgstr "изчистване на празните епилози"
 
+msgid "placement"
+msgstr "местоположение"
+
 msgid "where to place the new trailer"
 msgstr "къде да се постави новият епилог"
 
@@ -7797,17 +7811,19 @@
 msgid "output only the trailers"
 msgstr "извеждане само на епилозите"
 
-msgid "do not apply config rules"
-msgstr "без прилагане на правилата за настройките"
+msgid "do not apply trailer.* configuration variables"
+msgstr "без прилагане на конфигуриращи променливи, завършващи с епилог.*"
 
-msgid "join whitespace-continued values"
-msgstr "сливане на стойностите последване от знаци за интервали"
+msgid "reformat multiline trailer values as single-line values"
+msgstr ""
+"форматиране на епилози, които заемат повече от един ред, в стойности на един "
+"ред"
 
-msgid "set parsing options"
-msgstr "опции при анализ"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "псевдоним на комбинацията „--only-trailers --only-input --unfold“"
 
-msgid "do not treat --- specially"
-msgstr "„---“ няма специално значение"
+msgid "do not treat \"---\" as the end of input"
+msgstr "„---“ не отбелязва края на входа"
 
 msgid "trailer(s) to add"
 msgstr "епилози за добавяне"
@@ -7896,6 +7912,10 @@
 msgid "not a range"
 msgstr "не е диапазон"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "файлът с описанието на клона „%s“ не може да бъде прочетен"
+
 msgid "cover letter needs email format"
 msgstr "придружаващото писмо трябва да е форматирано като е-писмо"
 
@@ -7907,7 +7927,7 @@
 msgstr "неправилен формат на заглавната част за отговор „in-reply-to“: %s"
 
 msgid "git format-patch [<options>] [<since> | <revision-range>]"
-msgstr "git format-patch [ОПЦИЯ…] [ОТ | ДИАПАЗОН_НА_ВЕРСИИТЕ]"
+msgstr "git format-patch [ОПЦИЯ…] [ОТ|ДИАПАЗОН_НА_ВЕРСИИТЕ]"
 
 msgid "two output directories?"
 msgstr "може да укажете максимум една директория за изход"
@@ -7998,6 +8018,9 @@
 "генериране на частите на придружаващото писмо на базата на описанието на "
 "клона"
 
+msgid "use branch description from file"
+msgstr "ползване на описание на клон от файл"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "използване на този „[ПРЕФИКС]“ вместо „[PATCH]“"
 
@@ -8170,6 +8193,10 @@
 "ОТДАЛЕЧЕН_КЛОН.\n"
 
 #, c-format
+msgid "could not get object info about '%s'"
+msgstr "не може да се получи информация за обекта „%s“"
+
+#, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "неправилен формат за „ls-tree“: елементът „%s“ не започва с „(“"
 
@@ -8281,7 +8308,7 @@
 "              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=КОМАНДА]\n"
-"              [-q | --quiet] [--exit-code] [--get-url] [--sort=КЛЮЧ]\n"
+"              [-q|--quiet] [--exit-code] [--get-url] [--sort=КЛЮЧ]\n"
 "              [--symref] [ХРАНИЛИЩЕ [ШАБЛОН]]"
 
 msgid "do not print remote URL"
@@ -8315,10 +8342,6 @@
 msgstr "git ls-tree [ОПЦИЯ…] УКАЗАТЕЛ_КЪМ_ДЪРВО [ПЪТ…]"
 
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "не може да се получи информация за обекта „%s“"
-
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "неправилен формат за „ls-tree“: елементът „%s“ не започва с „(“"
 
@@ -8411,10 +8434,10 @@
 msgstr "празна пощенска кутия mbox: „%s“"
 
 msgid "git merge-base [-a | --all] <commit> <commit>..."
-msgstr "git merge-base [-a | --all] ПОДАВАНЕ ПОДАВАНЕ…"
+msgstr "git merge-base [-a|--all] ПОДАВАНЕ ПОДАВАНЕ…"
 
 msgid "git merge-base [-a | --all] --octopus <commit>..."
-msgstr "git merge-base [-a | --all] --octopus ПОДАВАНЕ…"
+msgstr "git merge-base [-a|--all] --octopus ПОДАВАНЕ…"
 
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor ПОДАВАНЕ_1 ПОДАВАНЕ_2"
@@ -8448,9 +8471,20 @@
 "git merge-file [ОПЦИЯ…] [-L ИМЕ_1 [-L ОРИГИНАЛ [-L ИМЕ_2]]] ФАЙЛ_1 ОРИГ_ФАЙЛ "
 "ФАЙЛ_2"
 
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"опцията приема следните варианти за алгоритъм за разлики: „myers“ (по "
+"Майерс), „minimal“ (минимизиране на разликите), „patience“ (пасианс) и "
+"„histogram“ (хистограмен)"
+
 msgid "send results to standard output"
 msgstr "извеждане на резултатите на стандартния изход"
 
+msgid "use object IDs instead of filenames"
+msgstr "ползване на идентификатори на обекти вместо имена на файлове"
+
 msgid "use a diff3 based merge"
 msgstr "сливане на базата на „diff3“"
 
@@ -8466,6 +8500,12 @@
 msgid "for conflicts, use a union version"
 msgstr "при конфликти да се ползва обединена версия"
 
+msgid "<algorithm>"
+msgstr "АЛГОРИТЪМ"
+
+msgid "choose a diff algorithm"
+msgstr "избор на АЛГОРИТЪМа за разлики"
+
 msgid "for conflicts, use this marker size"
 msgstr "при конфликти да се ползва маркер с такъв БРОЙ знаци"
 
@@ -8476,6 +8516,13 @@
 msgstr "задаване на етикети за ФАЙЛ_1/ОРИГИНАЛ/ФАЙЛ_2"
 
 #, c-format
+msgid "object '%s' does not exist"
+msgstr "обектът „%s“ не съществува"
+
+msgid "Could not write object file"
+msgstr "Файлът с обекти не може да се запише"
+
+#, c-format
 msgid "unknown option %s"
 msgstr "непозната опция: „%s“"
 
@@ -8494,7 +8541,7 @@
 
 #, c-format
 msgid "could not resolve ref '%s'"
-msgstr "указателят „%s“ не може да бъде изтрит"
+msgstr "указателят „%s“ не може да бъде проследен"
 
 #, c-format
 msgid "Merging %s with %s\n"
@@ -8537,11 +8584,18 @@
 msgid "specify a merge-base for the merge"
 msgstr "база за сливането"
 
+msgid "option=value"
+msgstr "ОПЦИЯ=СТОЙНОСТ"
+
+msgid "option for selected merge strategy"
+msgstr "ОПЦИЯ за избраната стратегия за сливане"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "„--trivial-merge“ е несъвместима с другите опции"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "опциите „--merge-base“ и „--stdin“ са несъвместими"
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "непозната опция за стратегия: -X%s"
 
 #, c-format
 msgid "malformed input line: '%s'."
@@ -8585,7 +8639,7 @@
 msgid "add (at most <n>) entries from shortlog to merge commit message"
 msgstr ""
 "добавяне (на максимум такъв БРОЙ) записи от съкратения журнал в съобщението "
-"за подаване"
+"за подаване със сливане"
 
 msgid "create a single commit instead of doing a merge"
 msgstr "създаване на едно подаване вместо извършване на сливане"
@@ -8611,12 +8665,6 @@
 msgid "merge strategy to use"
 msgstr "СТРАТЕГИЯ за сливане, която да се ползва"
 
-msgid "option=value"
-msgstr "ОПЦИЯ=СТОЙНОСТ"
-
-msgid "option for selected merge strategy"
-msgstr "ОПЦИЯ за избраната стратегия за сливане"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "СЪОБЩЕНИЕ при подаването със сливане (при същински сливания)"
 
@@ -8680,10 +8728,6 @@
 msgstr "Поддържа се само сливане на точно две истории."
 
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "непозната опция за стратегия: -X%s"
-
-#, c-format
 msgid "unable to write %s"
 msgstr "„%s“ не може да бъде записан"
 
@@ -8988,8 +9032,8 @@
 msgid "can not move directory into itself"
 msgstr "директория не може да се премести в себе си"
 
-msgid "cannot move directory over file"
-msgstr "директория не може да се премести върху файл"
+msgid "destination already exists"
+msgstr "целта съществува"
 
 msgid "source directory is empty"
 msgstr "първоначалната директория е празна"
@@ -9070,22 +9114,26 @@
 msgstr "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] [list [ОБЕКТ]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] add [-f] [--allow-empty] [-m СЪОБЩЕНИЕ "
-"| -F ФАЙЛ | (-c | -C) ОБЕКТ] [ОБЕКТ]"
+"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<paragraph-break>] [--[no-]stripspace] [-m "
+"СЪОБЩЕНИЕ|-F ФАЙЛ|(-c|-C) ОБЕКТ] [ОБЕКТ]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] copy [-f] ОБЕКТ_ИЗТОЧНИК ОБЕКТ_ЦЕЛ"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] append [--allow-empty] [-m СЪОБЩЕНИЕ | "
-"-F ФАЙЛ | (-c | -C) ОБЕКТ] [ОБЕКТ]"
+"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] append [--allow-empty] [--"
+"[no-]separator|--separator=КРАЙ_НА_АБЗАЦ] [--[no-]stripspace] [-m СЪОБЩЕНИЕ "
+"| -F ФАЙЛ|(-c|-C) ОБЕКТ] [ОБЕКТ]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] edit [--allow-empty] [ОБЕКТ]"
@@ -9096,7 +9144,7 @@
 msgid ""
 "git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"
 msgstr ""
-"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] merge [-v | -q] [-s СТРАТЕГИЯ] "
+"git notes [--ref УКАЗАТЕЛ_ЗА_БЕЛЕЖКА] merge [-v|-q] [-s СТРАТЕГИЯ] "
 "УКАЗАТЕЛ_ЗА_БЕЛЕЖКА"
 
 msgid "git notes [--ref <notes-ref>] remove [<object>...]"
@@ -9220,6 +9268,15 @@
 msgid "replace existing notes"
 msgstr "замяна на съществуващите бележки"
 
+msgid "<paragraph-break>"
+msgstr "КРАЙ_НА_АБЗАЦ"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "вмъкване на такъв КРАЙ_НА_АБЗАЦ между абзаците"
+
+msgid "remove unnecessary whitespace"
+msgstr "премахване на излишните знаци за интервали"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9384,12 +9441,12 @@
 
 msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [ОПЦИЯ…] [< СПИСЪК_С_УКАЗАТЕЛИ | < СПИСЪК_С_ОБЕКТИ]"
+"git pack-objects --stdout [ОПЦИЯ…] [< СПИСЪК_С_УКАЗАТЕЛИ|< СПИСЪК_С_ОБЕКТИ]"
 
 msgid ""
 "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects [ОПЦИЯ…] ПРЕФИКС_НА_ИМЕТО [< СПИСЪК_С_УКАЗАТЕЛИ | < "
+"git pack-objects [ОПЦИЯ…] ПРЕФИКС_НА_ИМЕТО [< СПИСЪК_С_УКАЗАТЕЛИ|< "
 "СПИСЪК_С_ОБЕКТИ]"
 
 #, c-format
@@ -9499,6 +9556,11 @@
 msgstr "неправилен брой разлики"
 
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr ""
+"неправилна стойност за преизползването на пакети „pack.allowPackReuse“: „%s“"
+
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -9526,13 +9588,13 @@
 msgstr "пакетният файл „%s“ не може да бъде достъпен"
 
 msgid "Enumerating cruft objects"
-msgstr "Изброяване на излишните обекти"
+msgstr "Изброяване на ненужните обекти"
 
 msgid "unable to add cruft objects"
-msgstr "неуспешно добавяне на излишни обекти"
+msgstr "неуспешно добавяне на ненужни обекти"
 
 msgid "Traversing cruft objects"
-msgstr "Обхождане на излишните обекти"
+msgstr "Обхождане на ненужните обекти"
 
 #, c-format
 msgid ""
@@ -9552,7 +9614,7 @@
 
 msgid "could not load cruft pack .mtimes"
 msgstr ""
-"времената на промяна (.mtimes) на пакета с излишни файлове не може да се "
+"времената на промяна (.mtimes) на пакета с ненужни файлове не може да се "
 "заредят"
 
 msgid "cannot open pack index"
@@ -9584,6 +9646,13 @@
 msgid "bad index version '%s'"
 msgstr "неправилна версия на индекса „%s“"
 
+msgid "show progress meter during object writing phase"
+msgstr "извеждане на напредъка във фазата на запазване на обектите"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr ""
+"същото действие като опцията „--all-progress“ при извеждането на напредъка"
+
 msgid "<version>[,<offset>]"
 msgstr "ВЕРСИЯ[,ОТМЕСТВАНЕ]"
 
@@ -9664,10 +9733,10 @@
 msgstr "разпакетиране на недостижимите обекти, които са по-нови от това ВРЕМЕ"
 
 msgid "create a cruft pack"
-msgstr "създаване на пакет с излишните обекти"
+msgstr "създаване на пакет с ненужните обекти"
 
 msgid "expire cruft objects older than <time>"
-msgstr "обявяване на излишните обекти по-стари от това ВРЕМЕ за остарели"
+msgstr "обявяване на ненужните обекти по-стари от това ВРЕМЕ за остарели"
 
 msgid "use the sparse reachability algorithm"
 msgstr "използване на алгоритъм за частична достижимост"
@@ -9745,9 +9814,6 @@
 "опцията „--thin“не може да се използва за създаване на пакетни файлове с "
 "индекс"
 
-msgid "cannot use --filter without --stdout"
-msgstr "опцията „--filter“ изисква „--stdout“"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "опциите „--filter“ и „--stdin-packs“ са несъвместими"
 
@@ -9761,19 +9827,16 @@
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "опциите „--stdin-packs“ и „--cruft“ са несъвместими"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "опциите „--max-pack-size“ и „--cruft“ са несъвместими"
-
 msgid "Enumerating objects"
 msgstr "Изброяване на обектите"
 
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Общо: %<PRIu32> (разлики: %<PRIu32>), преизползвани: %<PRIu32> (разлики: "
-"%<PRIu32>), преизползвани при пакетиране: %<PRIu32>"
+"%<PRIu32>), преизползвани при пакетиране: %<PRIu32> (от %<PRIuMAX>)"
 
 msgid ""
 "'git pack-redundant' is nominated for removal.\n"
@@ -9788,8 +9851,14 @@
 "ни известите с е-писмо до пощенския списък:\n"
 "<git@vger.kernel.org>.\n"
 
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "трябва да добавите и опцията „--i-still-use-this“"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include ШАБЛОН] [--exclude ШАБЛОН]"
 
 msgid "pack everything"
 msgstr "пакетиране на всичко"
@@ -9797,14 +9866,20 @@
 msgid "prune loose refs (default)"
 msgstr "окастряне на недостижимите указатели (стандартно)"
 
+msgid "references to include"
+msgstr "кои указатели да се включат"
+
+msgid "references to exclude"
+msgstr "кои указатели да се прескочат"
+
 msgid "git patch-id [--stable | --unstable | --verbatim]"
-msgstr "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable|--unstable|--verbatim]"
 
 msgid "use the unstable patch-id algorithm"
-msgstr "използване на нестабилен алгоритъм за идентифициране на кръпка"
+msgstr "използване на нестабилния алгоритъм за идентифициране на кръпка"
 
 msgid "use the stable patch-id algorithm"
-msgstr "използване на стабилен алгоритъм за идентифициране на кръпка"
+msgstr "използване на стабилния алгоритъм за идентифициране на кръпка"
 
 msgid "don't strip whitespace from the patch"
 msgstr "без махане на празните знаци в кръпката"
@@ -9856,6 +9931,12 @@
 msgid "number of submodules pulled in parallel"
 msgstr "брой подмодули издърпани паралелно"
 
+msgid "use IPv4 addresses only"
+msgstr "само адреси IPv4"
+
+msgid "use IPv6 addresses only"
+msgstr "само адреси IPv6"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9957,13 +10038,13 @@
 "приоритет пред настройките.\n"
 
 msgid "Updating an unborn branch with changes added to the index."
-msgstr "Обновяване на все още несъздаден клон с промѐните от индекса"
+msgstr "Обновяване на неродѐн клон с промѐните от индекса"
 
 msgid "pull with rebase"
 msgstr "издърпване с пребазиране"
 
-msgid "please commit or stash them."
-msgstr "трябва да подадете или скатаете промѐните."
+msgid "Please commit or stash them."
+msgstr "Промѐните трябва или да се подадат, или да се скатаят."
 
 #, c-format
 msgid ""
@@ -10001,7 +10082,7 @@
 
 msgid "Need to specify how to reconcile divergent branches."
 msgstr ""
-"Трябва да укажете как да се решават разликите при разминаване на клоните."
+"Трябва да укажете как да се решават разликите при раздалечаване на клоните."
 
 msgid "cannot rebase with locally recorded submodule modifications"
 msgstr ""
@@ -10121,35 +10202,37 @@
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Обновяването е отхвърлено, защото върхът на текущия ви клон е преди върха "
-"на\n"
+"Обновяването е отхвърлено, защото върхът на текущия ви клон следва върха на\n"
 "отдалечения клон.  Внесете отдалечените промѐни (напр. с командата „git "
 "pull…“),\n"
-"преди отново да изтласкате промѐните.  За повече информация вижте раздела\n"
-"„Note about fast-forwards“ в страницата от ръководството „git push --help“."
-
-msgid ""
-"Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
-"See the 'Note about fast-forwards' in 'git push --help' for details."
-msgstr ""
-"Обновяването е отхвърлено, защото върхът на изтласквания клон е преди върха\n"
-"на отдалечения клон.  Преминете към клона и внесете отдалечените промѐни "
-"(напр.\n"
-"с командата „git pull…“), преди отново да изтласкате промѐните.  За повече\n"
-"информация погледнете раздела „Note about fast-forwards“ в страницата от\n"
+"преди отново да изтласкате промѐните.\n"
+"За повече информация вижте раздела „Note about fast-forwards“ в страницата "
+"от\n"
 "ръководството „git push --help“."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Обновяването е отхвърлено, защото върхът на изтласквания клон следва върха\n"
+"на отдалечения клон.  Преминете към клона и внесете отдалечените промѐни "
+"(напр.\n"
+"с командата „git pull…“), преди отново да изтласкате промѐните.\n"
+"За повече информация вижте раздела „Note about fast-forwards“ в страницата "
+"от\n"
+"ръководството „git push --help“."
+
+msgid ""
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Обновяването е отхвърлено, защото хранилището, към което изтласквате, "
@@ -10179,14 +10262,17 @@
 "да го променѝте да сочи към подобен обект.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Обновяването е отхвърлено, защото върхът на следящия клон е обновяван след\n"
-"последното изтегляне.  Внесете отдалечените промѐни (напр. с командата\n"
-"„git pull…“), преди отново принудително да изтласкате промѐните.\n"
+"Обновяването е отхвърлено, защото върхът на отдалечения клон съдържа "
+"промени\n"
+"след последното изтегляне.  Внесете отдалечените промѐни (напр. с командата\n"
+"„git pull…“), преди отново да изтласкате промѐните.\n"
+"За повече информация вижте раздела „Note about fast-forwards“ в страницата\n"
+"от ръководството „git push --help“."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -10210,8 +10296,8 @@
 msgid "repository"
 msgstr "хранилище"
 
-msgid "push all refs"
-msgstr "изтласкване на всички указатели"
+msgid "push all branches"
+msgstr "изтласкване на всички клони"
 
 msgid "mirror all refs"
 msgstr "огледално копие на всички указатели"
@@ -10219,8 +10305,10 @@
 msgid "delete refs"
 msgstr "изтриване на указателите"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "изтласкване на етикетите (несъвместимо с опциите „--all“ и „--mirror“)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"изтласкване на етикетите (несъвместимо с опциите „--all“, „--branches“ и „--"
+"mirror“)"
 
 msgid "force updates"
 msgstr "принудително обновяване"
@@ -10346,9 +10434,9 @@
 "              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
 "              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=ПРЕФИКС)\n"
-"              [-u | -i]] [--index-output=ФАЙЛ] [--no-sparse-checkout]\n"
-"              (--empty | УКАЗАТЕЛ_КЪМ_ДЪРВО_1 [УКАЗАТЕЛ_КЪМ_ДЪРВО_2 "
+"git read-tree [(-m [--trivial] [--aggressive]|--reset|--prefix=ПРЕФИКС)\n"
+"              [-u|-i]] [--index-output=ФАЙЛ] [--no-sparse-checkout]\n"
+"              (--empty|УКАЗАТЕЛ_КЪМ_ДЪРВО_1 [УКАЗАТЕЛ_КЪМ_ДЪРВО_2 "
 "[УКАЗАТЕЛ_КЪМ_ДЪРВО_3]])"
 
 msgid "write resulting index to <file>"
@@ -10409,7 +10497,7 @@
 "git rebase [-i] [options] [--exec <cmd>] [--onto <newbase> | --keep-base] "
 "[<upstream> [<branch>]]"
 msgstr ""
-"git rebase [-i] [ОПЦИЯ…] [--exec КОМАНДА] [--onto НОВА_БАЗА | --keep-base] "
+"git rebase [-i] [ОПЦИЯ…] [--exec КОМАНДА] [--onto НОВА_БАЗА|--keep-base] "
 "[КЛОН_ИЗТОЧНИК [КЛОН]]"
 
 msgid ""
@@ -10485,6 +10573,10 @@
 "В резултат те не може да се пребазират."
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Неправилен режим за „--rebase-merges“: %s"
+
+#, c-format
 msgid "could not switch to %s"
 msgstr "не може да се премине към „%s“"
 
@@ -10499,6 +10591,15 @@
 "неправилна стойност „%s“: вариантите са „drop“ (прескачане), "
 "„keep“ (запазване) и „ask“ (питане)"
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"ползването на „--rebase-merges“ с празен низ за аргумент е остаряло и в "
+"бъдеще ще спре да работи. Ползвайте опцията „--rebase-merges“ без аргумент, "
+"за да постигнете същия резултат."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10728,20 +10829,12 @@
 msgid "switch `C' expects a numerical value"
 msgstr "опцията „C“ очаква число за аргумент"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Неизвестна стратегия: „%s“"
-
-msgid "--strategy requires --merge or --interactive"
-msgstr ""
-"опцията „--strategy“ изисква някоя от опциите „--merge“ или „--interactive“"
-
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"опциите за прилагане са несъвместими с „rebase.autosquash“.  Пробвайте да "
-"добавите опцията „--no-autosquash“"
+"опциите за прилагане са несъвместими с „rebase.rebaseMerges“.  Пробвайте да "
+"добавите опцията „--no-rebase-merges“"
 
 msgid ""
 "apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
@@ -10789,9 +10882,6 @@
 msgid "Does not point to a valid commit '%s'"
 msgstr "Указателят „%s“ не сочи към подаване"
 
-msgid "Please commit or stash them."
-msgstr "Промѐните трябва или да се подадат, или да се скатаят."
-
 msgid "HEAD is up to date."
 msgstr "Указателят „HEAD“ е напълно актуален."
 
@@ -10902,7 +10992,7 @@
 msgstr ""
 "git reflog expire [--expire=ВРЕМЕ] [--expire-unreachable=ВРЕМЕ]\n"
 "                  [--rewrite] [--updateref] [--stale-fix]\n"
-"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"                  [--dry-run|-n] [--verbose] [--all [--single-worktree]|"
 "УКАЗАТЕЛ…]"
 
 msgid ""
@@ -10910,7 +11000,7 @@
 "                  [--dry-run | -n] [--verbose] <ref>@{<specifier>}..."
 msgstr ""
 "git reflog delete [--rewrite] [--updateref]\n"
-"                  [--dry-run | -n] [--verbose] УКАЗАТЕЛ@{УТОЧНЕНИЕ}…"
+"                  [--dry-run|-n] [--verbose] УКАЗАТЕЛ@{УТОЧНЕНИЕ}…"
 
 msgid "git reflog exists <ref>"
 msgstr "git reflog exists УКАЗАТЕЛ"
@@ -10978,7 +11068,7 @@
 "git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--"
 "mirror=<fetch|push>] <name> <url>"
 msgstr ""
-"git remote add [-t КЛОН] [-m ОСНОВЕН_КЛОН] [-f] [--tags | --no-tags] [--"
+"git remote add [-t КЛОН] [-m ОСНОВЕН_КЛОН] [-f] [--tags|--no-tags] [--"
 "mirror=<fetch|push>] ИМЕ АДРЕС"
 
 msgid "git remote rename [--[no-]progress] <old> <new>"
@@ -10988,19 +11078,18 @@
 msgstr "git remote remove ИМЕ"
 
 msgid "git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"
-msgstr "git remote set-head ИМЕ (-a | --auto | -d | --delete | КЛОН)"
+msgstr "git remote set-head ИМЕ (-a|--auto|-d|--delete|КЛОН)"
 
 msgid "git remote [-v | --verbose] show [-n] <name>"
-msgstr "git remote [-v | --verbose] show [-n] ИМЕ"
+msgstr "git remote [-v|--verbose] show [-n] ИМЕ"
 
 msgid "git remote prune [-n | --dry-run] <name>"
-msgstr "git remote prune [-n | --dry-run] ИМЕ"
+msgstr "git remote prune [-n|--dry-run] ИМЕ"
 
 msgid ""
 "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
 msgstr ""
-"git remote [-v | --verbose] update [-p | --prune] [(ГРУПА | "
-"ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ)…]"
+"git remote [-v|--verbose] update [-p|--prune] [(ГРУПА|ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ)…]"
 
 msgid "git remote set-branches [--add] <name> <branch>..."
 msgstr "git remote set-branches [--add] ИМЕ КЛОН…"
@@ -11033,7 +11122,7 @@
 msgstr "git remote prune [ОПЦИЯ…] ИМЕ"
 
 msgid "git remote update [<options>] [<group> | <remote>]..."
-msgstr "git remote update [ОПЦИЯ…] [ГРУПА | ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ…]"
+msgstr "git remote update [ОПЦИЯ…] [ГРУПА|ОТДАЛЕЧЕНО_ХРАНИЛИЩЕ…]"
 
 #, c-format
 msgid "Updating %s"
@@ -11057,11 +11146,12 @@
 msgid "fetch the remote branches"
 msgstr "отдалечените клони не може да бъдат доставени"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "внасяне на всички етикети и принадлежащите им обекти при доставяне"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "може и да не се доставят никакви етикети (чрез опцията „--no-tags“)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"внасяне на всички етикети и принадлежащите им обекти при доставяне\n"
+"или да не се доставят никакви етикети (чрез опцията „--no-tags“)"
 
 msgid "branch(es) to track"
 msgstr "клон/и за следене"
@@ -11460,6 +11550,10 @@
 msgid "could not remove stale bitmap: %s"
 msgstr "изтриването на остарялата битова маска „%s“ е невъзможно"
 
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "името на пакетния файл „%s“ не започва с директорията за обекти с „%s“"
+
 msgid "pack everything in a single pack"
 msgstr "пакетиране на всичко в пакет"
 
@@ -11470,15 +11564,15 @@
 
 msgid "same as -a, pack unreachable cruft objects separately"
 msgstr ""
-"същото като опцията „-a“.  Недостижимите излишни обекти да се пакетират "
+"същото като опцията „-a“.  Недостижимите ненужни обекти да се пакетират "
 "отделно"
 
 msgid "approxidate"
 msgstr "евристична дата"
 
-msgid "with -C, expire objects older than this"
+msgid "with --cruft, expire objects older than this"
 msgstr ""
-"с опцията „-C“: обявяване на обектите по-стари от това ВРЕМЕ за остарели"
+"с опцията „--cruft“: обявяване на обектите по-стари от това ВРЕМЕ за остарели"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr ""
@@ -11545,19 +11639,22 @@
 msgstr "запазване на многопакетен индекс за създадените пакети"
 
 msgid "pack prefix to store a pack containing pruned objects"
-msgstr "префикс на името на пакетния за пакети за окастрени обекти"
+msgstr "префикс на имената на пакетите за окастрени обекти"
+
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "префикс на имената на пакетите за филтрирани обекти"
 
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "пакетите в хранилище с важни обекти не може да се трият"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "опцията „%s“ изисква „%s“"
+
 msgid "Nothing new to pack."
 msgstr "Нищо ново за пакетиране"
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "името на пакетния файл „%s“ не започва с директорията за обекти с „%s“"
-
-#, c-format
 msgid "renaming pack to '%s' failed"
 msgstr "неуспешно преименуване на пакетния файл на „%s“"
 
@@ -11757,9 +11854,79 @@
 msgid "only one pattern can be given with -l"
 msgstr "опцията „-l“ приема точно един шаблон"
 
+msgid "need some commits to replay"
+msgstr "необходимо е да има подавания за прилагане отново"
+
+msgid "--onto and --advance are incompatible"
+msgstr "опциите „--onto“ и „--advance“ са несъвместими"
+
+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 ""
+"цели с множество източници не може да се придвижат напред, защото подредбата "
+"не е добре дефинирана"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"не може да се определи дали това действие е за „--advance“ или „--onto“"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"цели с множество клони-източници не може да се придвижат напред, защото "
+"подредбата не е добре дефинирана"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "правилната база за „--onto“ не може да се определи"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(ЕКСПЕРИМЕНТАЛНО!) git replay ([--contained] --onto НОВА_БАЗА | --advance "
+"КЛОН) ДИАПАЗОН_ПОДАВАНИЯ…"
+
+msgid "make replay advance given branch"
+msgstr "прилагането наново придвижва дадения КЛОН напред"
+
+msgid "replay onto given commit"
+msgstr "прилагането наново върху даденото ПОДАВАНЕ"
+
+msgid "advance all branches contained in revision-range"
+msgstr "придвижване на всички КЛОНи в ДИАПАЗОНа_ПОДАВАНИЯ"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "изисква се някоя от опциите „--onto“ или „--advance“"
+
+#, 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“ има превес"
+
+msgid "error preparing revisions"
+msgstr "грешка при подготовката на версии"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "не се поддържа прилагане наново и на началното подаване!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "не се поддържа прилагане наново и на подавания със сливане!"
+
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
-msgstr "git rerere [clear | forget ПЪТ… | diff | status | remaining | gc]"
+msgstr "git rerere [clear|forget ПЪТ…|diff|status|remaining|gc]"
 
 msgid "register clean resolutions in index"
 msgstr "регистриране на чисти корекции на конфликти в индекса"
@@ -11773,8 +11940,7 @@
 
 msgid ""
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
-msgstr ""
-"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [ПОДАВАНЕ]"
+msgstr "git reset [--mixed|--soft|--hard|--merge|--keep] [-q] [ПОДАВАНЕ]"
 
 msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..."
 msgstr "git reset [-q] [УКАЗАТЕЛ_КЪМ_ДЪРВО] [--] ПЪТИЩА…"
@@ -11965,18 +12131,12 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "непознат режим за „--abbrev-ref“: „%s“"
 
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "опциите „--exclude-hidden“ и „--branches“ са несъвместими"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "опциите „--exclude-hidden“ и „--tags“ са несъвместими"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "опциите „--exclude-hidden“ и „--remotes“ са несъвместими"
-
 msgid "this operation must be run in a work tree"
 msgstr "тази команда трябва да се изпълни в работно дърво"
 
+msgid "Could not read the index"
+msgstr "Индексът не може да бъде прочетен"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "непознат режим за „--show-object-format“: „%s“"
@@ -11989,7 +12149,7 @@
 "           [-S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]] ПОДАВАНЕ…"
 
 msgid "git revert (--continue | --skip | --abort | --quit)"
-msgstr "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue|--skip|--abort|--quit)"
 
 msgid ""
 "git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
@@ -11999,7 +12159,7 @@
 "                [-S[ИДЕНТИФИКАТОР_НА_КЛЮЧ]] ПОДАВАНЕ…"
 
 msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
-msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue|--skip|--abort|--quit)"
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -12065,7 +12225,7 @@
 "       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 "       [--] [<pathspec>...]"
 msgstr ""
-"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"git rm [-f|--force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
 "       [--quiet] [--pathspec-from-file=ФАЙЛ [--pathspec-file-nul]]\n"
 "       [--] [ПЪТ…]"
 
@@ -12152,12 +12312,15 @@
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=ПАКЕТ]\n"
 "              [--verbose] [--thin] [--atomic]\n"
-"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
-"              [ХОСТ:]ДИРЕКТОРИЯ (--all | УКАЗАТЕЛ…)"
+"              [--[no-]signed|--signed=(true|false|if-asked)]\n"
+"              [ХОСТ:]ДИРЕКТОРИЯ (--all|УКАЗАТЕЛ…)"
 
 msgid "remote name"
 msgstr "име на отдалечено хранилище"
 
+msgid "push all refs"
+msgstr "изтласкване на всички указатели"
+
 msgid "use stateless RPC protocol"
 msgstr "използване на протокол без запазване на състоянието за RPC"
 
@@ -12218,14 +12381,14 @@
 "                [--no-name | --sha1-name] [--topics]\n"
 "                [(<rev> | <glob>)...]"
 msgstr ""
-"git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
-"                [--current] [--color[=КОГА] | --no-color] [--sparse]\n"
-"                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics]\n"
-"                [(РЕВИЗИЯ | УКАЗАТЕЛ)…]"
+"git show-branch [-a|--all] [-r|--remotes] [--topo-order|--date-order]\n"
+"                [--current] [--color[=КОГА]|--no-color] [--sparse]\n"
+"                [--more=<n>|--list|--independent|--merge-base]\n"
+"                [--no-name|--sha1-name] [--topics]\n"
+"                [(РЕВИЗИЯ|УКАЗАТЕЛ)…]"
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
-msgstr "git show-branch (-g | --reflog)[=БРОЙ[,БАЗА]] [--list] [УКАЗАТЕЛ]"
+msgstr "git show-branch (-g|--reflog)[=БРОЙ[,БАЗА]] [--list] [УКАЗАТЕЛ]"
 
 #, c-format
 msgid "ignoring %s; cannot handle more than %d ref"
@@ -12324,25 +12487,46 @@
 msgstr "Непознат алгоритъм за контролни суми"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
-"             [-s | --hash[=БРОЙ]] [--abbrev[=БРОЙ]] [--tags]\n"
+"git show-ref [--head] [-d|--dereference]\n"
+"             [-s|--hash[=БРОЙ]] [--abbrev[=БРОЙ]] [--tags]\n"
 "             [--heads] [--] [ШАБЛОН…]"
 
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q|--quiet] [-d|--dereference]\n"
+"             [-s|--hash[=БРОЙ]] [--abbrev[=БРОЙ]]\n"
+"             [--] [ШАБЛОН…]"
+
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=ШАБЛОН]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists УКАЗАТЕЛ"
+
+msgid "reference does not exist"
+msgstr "указателят не съществува"
+
+msgid "failed to look up reference"
+msgstr "соченото от указателя липсва"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "извеждане на етикетите (може да се комбинира с върховете)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "извеждане на върховете (може да се комбинира с етикетите)"
 
+msgid "check for reference existence without resolving"
+msgstr "проверка за съществуване на указател без проследяването му"
+
 msgid "stricter reference checking, requires exact ref path"
-msgstr "строга проверка на указателите, изисква се указател с пълен път"
+msgstr "по-строга проверка за указатели, изисква точен път на указател"
 
 msgid "show the HEAD reference, even if it would be filtered out"
 msgstr "задължително извеждане и на указателя HEAD"
@@ -12364,9 +12548,10 @@
 "локалното хранилище"
 
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) ОПЦИЯ…"
+"git sparse-checkout (init|list|set|add|reapply|disable|check-rules) ОПЦИЯ…"
 
 msgid "this worktree is not sparse"
 msgstr "това работно дърво не е частично"
@@ -12463,7 +12648,7 @@
 "ръководството на командата „git-sparse-checkout“)."
 
 msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
-msgstr "git sparse-checkout add [--skip-checks] (--stdin | ШАБЛОН…)"
+msgstr "git sparse-checkout add [--skip-checks] (--stdin|ШАБЛОН…)"
 
 msgid ""
 "skip some sanity checks on the given paths that might give false positives"
@@ -12482,7 +12667,7 @@
 "(--stdin | <patterns>)"
 msgstr ""
 "git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] "
-"(--stdin | ШАБЛОН…)"
+"(--stdin|ШАБЛОН…)"
 
 msgid "must be in a sparse-checkout to reapply sparsity patterns"
 msgstr ""
@@ -12492,6 +12677,24 @@
 msgid "error while refreshing working directory"
 msgstr "грешка при обновяване на работната директория"
 
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks] [--[no-]cone] [--rules-"
+"file ФАЙЛ]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "разделяне на входните и изходните файлове с нулевия знак „NUL“"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"когато е придружено с „--rules-file“ шаблоните се третират като шаблони в "
+"пътеводен режим"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "ползване на шаблоните във ФАЙЛа вместо текущите."
+
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [ОПЦИЯ_ЗА_ЖУРНАЛ…]"
 
@@ -12499,23 +12702,23 @@
 "git stash show [-u | --include-untracked | --only-untracked] [<diff-"
 "options>] [<stash>]"
 msgstr ""
-"git stash show [-u | --include-untracked | --only-untracked] "
-"[ОПЦИЯ_ЗА_РАЗЛИКА…] [СКАТАНО]"
+"git stash show [-u|--include-untracked|--only-untracked] [ОПЦИЯ_ЗА_РАЗЛИКА…] "
+"[СКАТАНО]"
 
 msgid "git stash drop [-q | --quiet] [<stash>]"
-msgstr "git stash drop [-q | --quiet] [СКАТАНО]"
+msgstr "git stash drop [-q|--quiet] [СКАТАНО]"
 
 msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q | --quiet] [СКАТАНО]"
+msgstr "git stash pop [--index] [-q|--quiet] [СКАТАНО]"
 
 msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q | --quiet] [СКАТАНО]"
+msgstr "git stash apply [--index] [-q|--quiet] [СКАТАНО]"
 
 msgid "git stash branch <branchname> [<stash>]"
 msgstr "git stash branch КЛОН [СКАТАНО]"
 
 msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
-msgstr "git stash store [-m | --message СЪОБЩЕНИЕ] [-q | --quiet] ПОДАВАНЕ"
+msgstr "git stash store [-m|--message СЪОБЩЕНИЕ] [-q|--quiet] ПОДАВАНЕ"
 
 msgid ""
 "git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q "
@@ -12525,10 +12728,9 @@
 "          [--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"
+"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"
 "          [--] [ПЪТ…]]"
 
@@ -12537,9 +12739,9 @@
 "--quiet]\n"
 "          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
-"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
-"--quiet]\n"
-"          [-u | --include-untracked] [-a | --all] [СЪОБЩЕНИЕ]"
+"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
+"quiet]\n"
+"          [-u|--include-untracked] [-a|--all] [СЪОБЩЕНИЕ]"
 
 msgid "git stash create [<message>]"
 msgstr "git stash create [СЪОБЩЕНИЕ]"
@@ -12918,7 +13120,7 @@
 
 msgid ""
 "git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"
-msgstr "git submodule deinit [--quiet] [-f | --force] [--all | [--] [ПЪТ…]]"
+msgstr "git submodule deinit [--quiet] [-f|--force] [--all|[--] [ПЪТ…]]"
 
 msgid "Use '--all' if you really want to deinitialize all submodules"
 msgstr "Използвайте „--all“, за да премахнете всички подмодули"
@@ -13020,6 +13222,10 @@
 msgstr "Прескачане на подмодула „%s“"
 
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "не може да се клонира подмодул „%s“ без адрес"
+
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Неуспешен опит за клониране на „%s“.  Насрочен е втори опит"
 
@@ -13153,6 +13359,9 @@
 "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
 "[--] [ПЪТ…]"
 
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Не може да се открие към какво сочи указателят „HEAD“"
+
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [ОПЦИЯ…] [ПЪТ…]"
 
@@ -13325,9 +13534,8 @@
 "git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
 "        <tagname> [<commit> | <object>]"
 msgstr ""
-"git tag [-a | -s | -u ИДЕНТИФИКАТОР_НА_КЛЮЧ] [-f] [-m СЪОБЩЕНИЕ | -F ФАЙЛ] [-"
-"e]\n"
-"        ЕТИКЕТ [ПОДАВАНЕ | ОБЕКТ]"
+"git tag [-a|-s|-u ИДЕНТИФИКАТОР_НА_КЛЮЧ] [-f] [-m СЪОБЩЕНИЕ|-F ФАЙЛ] [-e]\n"
+"        ЕТИКЕТ [ПОДАВАНЕ|ОБЕКТ]"
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d ЕТИКЕТ…"
@@ -13339,7 +13547,7 @@
 "        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
 "git tag [-n[БРОЙ]] -l [--contains ПОДАВАНЕ] [--no-contains ПОДАВАНЕ]\n"
-"        [--points-at ОБЕКТ] [--column[=ОПЦИЯ…] | --no-column]\n"
+"        [--points-at ОБЕКТ] [--column[=ОПЦИЯ…]|--no-column]\n"
 "        [--create-reflog] [--sort=<key>] [--format=ФОРМАТ]\n"
 "        [--merged ПОДАВАНЕ] [--no-merged ПОДАВАНЕ] [ШАБЛОН…]"
 
@@ -13640,6 +13848,9 @@
 msgid "write index in this format"
 msgstr "записване на индекса в този формат"
 
+msgid "report on-disk index format version"
+msgstr "извеждане на версията на форма̀та на индекса на диска"
+
 msgid "enable or disable split index"
 msgstr "включване или изключване на разделянето на индекса"
 
@@ -13665,6 +13876,14 @@
 msgid "clear fsmonitor valid bit"
 msgstr "изчистване на флага за следенето чрез файловата система"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "версия на индекс: бе %d, променена на %d"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13764,7 +13983,7 @@
 msgstr "трансферът да се преустанови след този БРОЙ секунди"
 
 msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
-msgstr "git verify-commit [-v | --verbose] [--raw] ПОДАВАНЕ…"
+msgstr "git verify-commit [-v|--verbose] [--raw] ПОДАВАНЕ…"
 
 msgid "print commit contents"
 msgstr "извеждане на съдържанието на подаването"
@@ -13773,7 +13992,7 @@
 msgstr "извеждане на необработения изход от състоянието на „gpg“"
 
 msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] [--] ПАКЕТ.idx…"
+msgstr "git verify-pack [-v|--verbose] [-s|--stat-only] [--] ПАКЕТ.idx…"
 
 msgid "verbose"
 msgstr "извеждане на подробна информация"
@@ -13782,20 +14001,20 @@
 msgstr "извеждане само на статистиката"
 
 msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=ФОРМАТ] [--raw] ЕТИКЕТ…"
+msgstr "git verify-tag [-v|--verbose] [--format=ФОРМАТ] [--raw] ЕТИКЕТ…"
 
 msgid "print tag contents"
 msgstr "извеждане на съдържанието на ЕТИКЕТи"
 
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason НИЗ]]\n"
-"                 [-b НОВ_КЛОН] ПЪТ [УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ]"
+"                 [--orphan] [(-b|-B) НОВ_КЛОН] ПЪТ [УКАЗАТЕЛ_КЪМ_ПОДАВАНЕ]"
 
 msgid "git worktree list [-v | --porcelain [-z]]"
-msgstr "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v|--porcelain [-z]]"
 
 msgid "git worktree lock [--reason <string>] <worktree>"
 msgstr "git worktree lock [--reason ПРИЧИНА] ФОРМАТ"
@@ -13815,6 +14034,37 @@
 msgid "git worktree unlock <worktree>"
 msgstr "git worktree unlock ДЪРВО"
 
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Липсва клон-източник, затова се приема „--orphan“"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"За да създадете работно дърво за това хранилище\n"
+"с нов неродѐн клон — който няма дори и начално подаване,\n"
+"ползвайте опцията „--orphan“:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"За да създадете работно дърво за това хранилище\n"
+"с нов неродѐн клон — който няма дори и начално подаване,\n"
+"ползвайте опцията „--orphan“:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "Изтриване на „%s/%s“: %s"
@@ -13873,6 +14123,10 @@
 msgstr "инициализация"
 
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "създаденото в „%s“ работно дърво липсва"
+
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "Приготвяне на работното дърво (нов клон „%s“)"
 
@@ -13887,9 +14141,33 @@
 msgstr "Приготвяне на работното дърво (изтегляне на „%s“)"
 
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "недостижим обект: неправилен указател: %s"
+
+#, c-format
 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 ""
+"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"
+"Ползвайте комбинацията „add -f“ за принудително действие или първо "
+"доставете\n"
+"обектите от отдалеченото хранилище"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "Изтегляне КЛОНа, дори и да е изтеглен в друго работно дърво"
 
@@ -13899,6 +14177,9 @@
 msgid "create or reset a branch"
 msgstr "създаване или зануляване на клони"
 
+msgid "create unborn branch"
+msgstr "създаване на неродѐн клон"
+
 msgid "populate the new working tree"
 msgstr "подготвяне на новото работно дърво"
 
@@ -13918,6 +14199,10 @@
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "опциите „%s“, „%s“ и „%s“ са несъвместими"
 
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "опциите „%s“ и указателите към подавания са несъвместими"
+
 msgid "added with --lock"
 msgstr "добавена с „--lock“"
 
@@ -14154,6 +14439,14 @@
 msgstr[0] "Пратката изисква следния указател:"
 msgstr[1] "Пратката изисква следните %<PRIuMAX> указатели:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Пратката ползва следния алгоритъм за контролни суми „%s“"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Пратката изисква следния филтър: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "неуспешно дублиране на дескриптора на пратката с „dup“"
 
@@ -14190,6 +14483,10 @@
 msgstr "идентификаторът за краен откъс се явява по-рано от очакваното"
 
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "откъсът с идентификатор %<PRIx32> не е подравнен по %d-байта"
+
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "неправилно отместване на откъс/и %<PRIx64> и %<PRIx64>"
 
@@ -14243,9 +14540,9 @@
 msgid "Move objects and refs by archive"
 msgstr "Местене на обекти и указатели по архиви"
 
-msgid "Provide content or type and size information for repository objects"
+msgid "Provide contents or details of repository objects"
 msgstr ""
-"Предоставяне на съдържанието или вида и размерите на обекти от хранилище"
+"Предоставяне на съдържанието или друга информация на обекти от хранилище"
 
 msgid "Display gitattributes information"
 msgstr "Извеждане на информацията за атрибутите на git (gitattributes)"
@@ -14392,10 +14689,9 @@
 msgid "A portable graphical interface to Git"
 msgstr "Графичен интерфейс към Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
+msgid "Compute object ID and optionally create an object from a file"
 msgstr ""
-"Изчисляване на идентификатор на обект и евентуално създаване на обект-BLOB "
-"от файл"
+"Изчисляване на идентификатор на обект и евентуално създаване на обект от файл"
 
 msgid "Display help information about Git"
 msgstr "Извеждане на помощта за Git"
@@ -14542,6 +14838,11 @@
 msgid "Create, list, delete refs to replace objects"
 msgstr "Създаване, извеждане, изтриване на указатели за замяна на обекти"
 
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"ЕКСПЕРИМЕНТАЛНО: прилагане на подавания върху нова база, работи и с голи "
+"хранилища"
+
 msgid "Generates a summary of pending changes"
 msgstr "Обобщение на предстоящите промѐни"
 
@@ -14663,7 +14964,7 @@
 msgid "Display version information about Git"
 msgstr "Извеждане на версията на Git"
 
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "Извеждане на журнал с разликите, въведени с всяко подаване"
 
 msgid "Manage multiple working trees"
@@ -14780,6 +15081,36 @@
 msgid "commit-graph file is too small"
 msgstr "файлът за гра̀фа с подаванията е твърде малък"
 
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "откъсът за разпределянето в гра̀фа с подаванията е прекалено малък"
+
+msgid "commit-graph fanout values out of order"
+msgstr ""
+"стойностите за откъс за разпределяне в гра̀фа с подаванията не са подредени"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "откъсът за търсенето в гра̀фа с подаванията е прекалено малък"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr ""
+"откъсът за данните за подаванията в гра̀фа с подаванията е с неправилен размер"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "откъсът за поколенията в гра̀фа с подаванията е с неправилен размер"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr ""
+"откъсът за индекса с промѐни в пътищата в гра̀фа с подаванията е прекалено "
+"малък"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"прескачане на прекалено малък откъс за индекса с промѐни (%<PRIuMAX> < "
+"%<PRIuMAX>) в пътищата в гра̀фа с подаванията"
+
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr "отпечатъкът на гра̀фа с подаванията %X не съвпада с %X"
@@ -14796,13 +15127,36 @@
 msgid "commit-graph file is too small to hold %u chunks"
 msgstr "файлът с гра̀фа на подаванията е твърде малък, за да съдържа %u откъси"
 
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"откъсът за разпределянето необходимо на гра̀фа с подаванията липсва или е "
+"повреден"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"откъсът за търсенето необходимо на гра̀фа с подаванията липсва или е повреден"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"откъсът за данните необходими на гра̀фа с подаванията липсва или е повреден"
+
 msgid "commit-graph has no base graphs chunk"
 msgstr "базовият откъс липсва в гра̀фа с подаванията"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "базовият откъс в гра̀фа с подаванията е прекалено малък"
+
 msgid "commit-graph chain does not match"
 msgstr "веригата на гра̀фа с подаванията не съвпада"
 
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "броят подавания в основния граф е прекалено голям: %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "веригата на гра̀фа с подаванията е твърде малка"
+
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr ""
 "грешка във веригата на гра̀фа с подаванията: ред „%s“ не е контролна сума"
@@ -14820,8 +15174,15 @@
 
 msgid "commit-graph requires overflow generation data but has none"
 msgstr ""
-"графът с подаванията изисква генериране на данни за отместването, но такива "
-"липсват"
+"графът с подаванията изисква данни за прелелите поколения, но такива липсват"
+
+msgid "commit-graph overflow generation data is too small"
+msgstr "прекалено малко данни за прелелите поколения в гра̀фа с подаванията"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr ""
+"указателят за допълнителните ребра в гра̀фа с подаванията е извън позволения "
+"диапазон"
 
 msgid "Loading known commits in commit graph"
 msgstr "Зареждане на познатите подавания в гра̀фа с подаванията"
@@ -14865,7 +15226,8 @@
 msgstr "Откриване на още върхове в гра̀фа с подаванията"
 
 msgid "failed to write correct number of base graph ids"
-msgstr "правилният брой на базовите идентификатори не може да се запише"
+msgstr ""
+"правилният брой на идентификаторите в основния граф не може да се запише"
 
 msgid "unable to create temporary graph layer"
 msgstr "не може да бъде създаден временен слой за гра̀фа с подаванията"
@@ -14889,6 +15251,15 @@
 msgid "failed to rename temporary commit-graph file"
 msgstr "временният файл на гра̀фа с подаванията не може да бъде преименуван"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"не може да се слеят графове с %<PRIuMAX> и %<PRIuMAX> подавания (съответно)"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "графът „%s“ не може да се слее, прекалено много подавания: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Търсене на подаванията със сливания"
 
@@ -14915,16 +15286,13 @@
 #, c-format
 msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
 msgstr ""
-"неправилна стойност за откъс в гра̀фа с подаванията: fanout[%d] = %u, а "
-"трябва да е %u"
+"неправилна стойност за откъс за разпределяне в гра̀фа с подаванията: "
+"fanout[%d] = %u, а трябва да е %u"
 
 #, c-format
 msgid "failed to parse commit %s from commit-graph"
 msgstr "подаване „%s“ в гра̀фа с подаванията не може да се анализира"
 
-msgid "Verifying commits in commit graph"
-msgstr "Проверка на подаванията в гра̀фа"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14950,20 +15318,6 @@
 msgstr "списъкът с родители на „%s“ в гра̀фа с подаванията е прекалено къс"
 
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"номерът на поколението на подаване „%s“ в гра̀фа с подаванията е 0, а другаде "
-"не е"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"номерът на поколението на подаване „%s“ в гра̀фа с подаванията не е 0, а "
-"другаде е"
-
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
 "номерът на поколението на подаване „%s“ в гра̀фа с подаванията е %<PRIuMAX> < "
@@ -14976,6 +15330,17 @@
 "%<PRIuMAX>"
 
 #, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"гра̀фа с подаванията съдържа както нулеви, така и ненулеви поколения (напр. "
+"подаванията „%s“ и „%s“)"
+
+msgid "Verifying commits in commit graph"
+msgstr "Проверка на подаванията в гра̀фа"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s не е подаване!"
 
@@ -15002,6 +15367,12 @@
 "    git config advice.graftFileDeprecated false"
 
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"подаването „%s“ присъства в гра̀фа с подаванията, но липсва в базата от данни "
+"за обектите"
+
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr ""
 "Подаването „%s“ е с недоверен подпис от GPG, който твърди, че е на „%s“."
@@ -15421,8 +15792,8 @@
 msgid "bad zlib compression level %d"
 msgstr "неправилно ниво на компресиране: %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "настройката „core.commentChar“ трябва да е само един знак"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "настройката „core.commentChar“ трябва да е само един знак от ASCII"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -15463,10 +15834,6 @@
 msgid "unable to resolve config blob '%s'"
 msgstr "обектът-BLOB „%s“ с конфигурации не може да бъде открит"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "„%s“ не може да бъде анализиран"
-
 msgid "unable to parse command-line config"
 msgstr "неправилни настройки от командния ред"
 
@@ -15540,6 +15907,10 @@
 msgstr "неправилно име на раздел: %s"
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "ред %2$<PRIuMAX> в „%1$s“ е прекалено дълъг"
+
+#, c-format
 msgid "missing value for '%s'"
 msgstr "липсва стойност за „%s“"
 
@@ -15947,9 +16318,6 @@
 msgid "--merge-base does not work with ranges"
 msgstr "опцията „--merge-base“ не работи с диапазони"
 
-msgid "--merge-base only works with commits"
-msgstr "опцията „--merge-base“ работи само с подавания"
-
 msgid "unable to get HEAD"
 msgstr "Указателят „HEAD“ не може да бъде получен"
 
@@ -15959,6 +16327,12 @@
 msgid "multiple merge bases found"
 msgstr "много бази за сливане"
 
+msgid "cannot compare stdin to a directory"
+msgstr "стандартният вход не може да се сравни с директория"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "именован канал не може да се сравни с директория"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [ОПЦИЯ…] ПЪТ ПЪТ"
 
@@ -16011,6 +16385,10 @@
 msgstr "Непозната стойност „%s“ за настройката „diff.submodule“"
 
 #, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "непозната стойност за настройката „%s“: „%s“"
+
+#, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
@@ -16023,6 +16401,13 @@
 msgstr ""
 "външната програма за разлики завърши неуспешно.  Спиране на работата при „%s“"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "опцията „--follow“ изисква точно един път"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "магическите пътища не се поддържат от „--follow“: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr "опциите „%s“, „%s“, „%s“ и „%s“ са несъвместими"
@@ -16036,9 +16421,6 @@
 "options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
 msgstr "опциите „%s“ и „%s“ са несъвместими, използвайте „%s“ с „%s“ и „%s“"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "опцията „--follow“ изисква точно един път"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "неправилна стойност за „--stat“: %s"
@@ -16085,14 +16467,6 @@
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "неправилен режим „%s“ за „ --color-moved-ws“"
 
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"опцията приема следните варианти за алгоритъм за разлики: „myers“ (по "
-"Майерс), „minimal“ (минимизиране на разликите), „patience“ (пасианс) и "
-"„histogram“ (хистограмен)"
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "неправилен аргумент към „%s“"
@@ -16136,8 +16510,8 @@
 msgid "output only the last line of --stat"
 msgstr "извеждане само на последния ред на „--stat“"
 
-msgid "<param1,param2>..."
-msgstr "ПАРАМЕТЪР_1, ПАРАМЕТЪР_2, …"
+msgid "<param1>,<param2>..."
+msgstr "ПАРАМЕТЪР_1,ПАРАМЕТЪР_2,…"
 
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
@@ -16146,8 +16520,8 @@
 msgid "synonym for --dirstat=cumulative"
 msgstr "псевдоним на „--dirstat=cumulative“"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "псевдоним на „--dirstat=ФАЙЛ…,ПАРАМЕТЪР_1,ПАРАМЕТЪР_2,…“"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "псевдоним на „--dirstat=files,ПАРАМЕТЪР_1,ПАРАМЕТЪР_2,…“"
 
 msgid "warn if changes introduce conflict markers or whitespace errors"
 msgstr ""
@@ -16235,6 +16609,9 @@
 msgid "do not show any source or destination prefix"
 msgstr "без префикс за източника и целта"
 
+msgid "use default prefixes a/ and b/"
+msgstr "ползване на стандартните префикси „a/“ и „b/“"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "извеждане на контекст между последователните парчета с разлики от указания "
@@ -16328,12 +16705,6 @@
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "разлика по хистограмния алгоритъм"
 
-msgid "<algorithm>"
-msgstr "АЛГОРИТЪМ"
-
-msgid "choose a diff algorithm"
-msgstr "избор на АЛГОРИТЪМа за разлики"
-
 msgid "<text>"
 msgstr "ТЕКСТ"
 
@@ -16541,6 +16912,14 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "Подсказка: чака се редакторът ви да затвори файла …%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "в „%s“ не може да се пише"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "„%s“ не може да се редактира"
+
 msgid "Filtering content"
 msgstr "Филтриране на съдържанието"
 
@@ -16799,16 +17178,9 @@
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
 "           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
-"git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
-"           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
-"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
-"bare]\n"
-"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--config-env=<name>=<envvar>] <command> [<args>]\n"
-"git [-v | --version] [-h | --help] [-C ПЪТ] [-c ИМЕ=СТОЙНОСТ]\n"
+"git [-v|--version] [-h|--help] [-C ПЪТ] [-c ИМЕ=СТОЙНОСТ]\n"
 "           [--exec-path[=ПЪТ]] [--html-path] [--man-path] [--info-path]\n"
-"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
-"bare]\n"
+"           [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]\n"
 "           [--git-dir=ПЪТ] [--work-tree=ПЪТ] [--namespace=ИМЕ]\n"
 "           [--config-env=ИМЕ=ПРОМЕНЛИВА_НА_СРЕДАТА] КОМАНДА [АРГ…]"
 
@@ -16846,6 +17218,10 @@
 msgstr "опцията „--config-env“ изисква ключ\n"
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "опцията „--attr-source“ изисква източник на атрибути\n"
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "непозната опция: „%s“\n"
 
@@ -17390,12 +17766,12 @@
 "Неуспешно сливане на подмодула „%s“, но са открити множество решения:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "Неуспешно вътрешно сливане"
+msgid "failed to execute internal merge"
+msgstr "неуспешно вътрешно сливане"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "„%s“ не може да се добави в базата с данни"
+msgid "unable to add %s to database"
+msgstr "„%s“ не може да се добави в базата от данни"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17844,7 +18220,22 @@
 msgstr "кешът не може да бъде прочетен"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "неправилен размер на откъс (OID fanout) на индекса за множество пакети"
+msgstr ""
+"неправилен размер на откъса за разпределянето в индекса за множество пакети"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"неправилна подредба на откъси (OID fanout): fanout[%d] = %<PRIx32> > "
+"%<PRIx32> = fanout[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "неправилен размер на откъса за търсенето в индекса за множество пакети"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"неправилен размер на откъса за отместванията в индекса за множество пакети"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17864,17 +18255,25 @@
 "версията на контролната сума на индекса за множество пакети %u не съвпада с "
 "%u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "липсва откъс (pack-name) от индекс за множество пакети"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"откъсът за имена на пакети в индекса за множество пакети липсва или е "
+"повреден"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "липсва откъс (OID fanout) от индекс за множество пакети"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"откъсът за разпределянето в индекса за множество пакети липсва или е повреден"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "липсва откъс (OID lookup) от индекс за множество пакети"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "откъсът за търсене в индекс за множество пакети липсва или е повреден"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "липсва откъс за отместванията на обекти от индекс за множество пакети"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"откъсът за отмествания в индекс за множество пакети липсва или е повреден"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr ""
+"откъсът за име на пакет в индекс за множество пакети липсва или е повреден"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17887,11 +18286,23 @@
 msgstr ""
 "неправилен идентификатор на пакет (pack-int-id): %u (от общо %u пакети)"
 
+msgid "MIDX does not contain the BTMP chunk"
+msgstr ""
+"липсва откъс за побитова маска във файла за индекса за множество пакети"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "пакетът за битови маски %<PRIu32> не може да се отвори"
+
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr ""
 "индексът за множество пакети съдържа 64-битови отмествания, но размерът на "
 "„off_t“ е недостатъчен"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr ""
+"стойността на отместването в индекса за множество пакети е извън диапазона"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "пакетният файл „%s“ не може да бъде добавен"
@@ -17965,7 +18376,9 @@
 msgstr "индексът за множество пакети не може да бъде изчистен при „%s“"
 
 msgid "multi-pack-index file exists, but failed to parse"
-msgstr "файлът с индекса за множество пакети, но не може да бъде анализиран"
+msgstr ""
+"файлът с индекса за множество пакети съществува, но не може да бъде "
+"анализиран"
 
 msgid "incorrect checksum"
 msgstr "неправилна сума за проверка"
@@ -17973,13 +18386,6 @@
 msgid "Looking for referenced packfiles"
 msgstr "Търсене на указаните пакетни файлове"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"неправилна подредба на откъси (OID fanout): fanout[%d] = %<PRIx32> > "
-"%<PRIx32> = fanout[%d]"
-
 msgid "the midx contains no oid"
 msgstr "във файла с индекса за множество пакети няма идентификатори на обекти"
 
@@ -18524,6 +18930,11 @@
 msgid "could not open pack %s"
 msgstr "пакетът „%s“ не може да се отвори"
 
+msgid "could not determine MIDX preferred pack"
+msgstr ""
+"предпочитаният пакет за файла с индекса за множество пакети не може да се "
+"определи"
+
 #, c-format
 msgid "preferred pack (%s) is invalid"
 msgstr "предпочитаният пакет „%s“ е неправилен"
@@ -18551,6 +18962,11 @@
 "маска на подаване „%s“"
 
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"пакетът не може да се зареди: „%s“, преизползването на пакети се изключва"
+
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "обектът „%s“ липсва в битовата маска на видовете"
 
@@ -18588,6 +19004,10 @@
 msgstr "използваното място за съхранение на „%s“ не може да бъде получено"
 
 #, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "неправилна сума за проверка за файла с битови маски „%s“"
+
+#, c-format
 msgid "mtimes file %s is too small"
 msgstr "файлът с времето на промяна (mtime) „%s“ е твърде малък"
 
@@ -18633,6 +19053,21 @@
 "идентификатор на контролна сума %2$<PRIu32> на файла с обратен индекс „%1$s“ "
 "не се поддържа"
 
+msgid "invalid checksum"
+msgstr "неправилна сума за проверка"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+"неправилна позиция в обратния индекс при %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr ""
+"неправилен размер на откъс за обратен индекс в индекса за множество пакети"
+
+msgid "could not determine preferred pack"
+msgstr "предпочитаният пакет не може да се определи"
+
 msgid "cannot both write and verify reverse index"
 msgstr "обратният индекс не може едновременно да се записва и да се проверява"
 
@@ -18691,14 +19126,6 @@
 msgstr "опцията „%s“ изисква аргумент"
 
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "опциите „%s“ и „%s“ са несъвместими"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "опцията „%s“ е несъвместима с нещо"
-
-#, c-format
 msgid "%s takes no value"
 msgstr "опцията „%s“ не приема аргументи"
 
@@ -18782,6 +19209,10 @@
 msgid "-NUM"
 msgstr "-ЧИСЛО"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "обратното на „--no-%s“"
+
 msgid "expiry-date"
 msgstr "период на валидност/запазване"
 
@@ -18813,6 +19244,14 @@
 "знак „NUL“"
 
 #, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "неправилна булева стойност „%s“ за „%s“"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "„%s“ не може да бъде анализиран"
+
+#, c-format
 msgid "Could not make %s writable by group"
 msgstr "Не може да се дадат права̀ за запис в директорията „%s“ на групата"
 
@@ -18860,6 +19299,10 @@
 msgstr "%s: опциите „literal“ и „glob“ са несъвместими"
 
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "„%s“ е извън дървото на директориите"
+
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: „%s“ е извън хранилището при „%s“"
 
@@ -18991,6 +19434,13 @@
 msgstr "журналът с подаванията на „%s“ не може да бъде анализиран"
 
 #, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "неправилен ненужен връх: „%s“"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "допълнителните скорошни обекти не може да се изброят"
+
+#, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr ""
 "няма да бъде добавен псевдоним за файл „%s“ („%s“ вече съществува в индекса)"
@@ -19013,10 +19463,6 @@
 msgstr "„%s“ не може да се добави в индекса"
 
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "„stat“ не може да се изпълни върху „%s“"
-
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "„%s“ съществува и като файл, и като директория"
 
@@ -19134,10 +19580,6 @@
 msgstr "индексът не може да бъде превърнат в частичен"
 
 #, c-format
-msgid "could not stat '%s'"
-msgstr "неуспешно изпълнение на „stat“ върху „%s“"
-
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "не може да се отвори директорията на git: %s"
 
@@ -19153,6 +19595,14 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: не може да се премине към етап №0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "неочакван изходен код при генериране на разлика: %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "изтриване на „%s“\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -19202,7 +19652,7 @@
 " e, edit ПОДАВАНЕ   — прилагане на подаването и спиране при него за още "
 "промѐни\n"
 " s, squash ПОДАВАНЕ — вкарване на подаването в предходното му\n"
-" f, fixup [-C | -c] ПОДАВАНЕ\n"
+" f, fixup [-C|-c] ПОДАВАНЕ\n"
 "                    — вкарване на подаването в предходното му, без смяна на\n"
 "                      съобщението.  С „-C“ се използва само съобщението на\n"
 "                      настоящото, а с „-c“ освен това се отваря редакторът\n"
@@ -19214,7 +19664,7 @@
 " d, drop ПОДАВАНЕ   — прескачане на подаването\n"
 " l, label ЕТИКЕТ    — задаване на етикет на указаното от HEAD\n"
 " t, reset ЕТИКЕТ    — зануляване на HEAD към ЕТИКЕТа\n"
-" m, merge [-C ПОДАВАНЕ | -c ПОДАВАНЕ] ЕТИКЕТ [# ЕДИН_РЕД]\n"
+" m, merge [-C ПОДАВАНЕ|-c ПОДАВАНЕ] ЕТИКЕТ [# ЕДИН_РЕД]\n"
 "                    — създаване на подаване със сливане със съобщението от\n"
 "                      първоначалното подаване (или съобщението от ЕДИН_РЕД,\n"
 "                      ако не е зададено подаване със сливане.  С опцията\n"
@@ -19355,6 +19805,22 @@
 msgstr "очаква се положителна стойност за „contents:lines=%s“"
 
 #, c-format
+msgid "argument expected for %s"
+msgstr "„%s“ изисква аргумент"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "очаква се положителна стойност за „%s=%s“"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "„%s=%s“ не може да се анализира докрай"
+
+#, c-format
+msgid "value expected %s="
+msgstr "очаква се стойност за „%s=“"
+
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "очаква се положителна стойност за „%s“ в %%(%s)"
 
@@ -19379,6 +19845,10 @@
 msgstr "очаква се положителна широчина с лексемата „%%(align)“"
 
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "очакван формат: %%(ahead-behind:ПОДАВАНЕ)"
+
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "неправилно име на обект: „%.*s“"
 
@@ -19424,6 +19894,9 @@
 msgstr ""
 "опцията „--format=%.*s“ е несъвместима с „--python“, „--shell“, „--tcl“"
 
+msgid "failed to run 'describe'"
+msgstr "неуспешно изпълнение на „describe“"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(извън клон, пребазиране на „%s“)"
@@ -19485,6 +19958,9 @@
 msgid "field name to sort on"
 msgstr "име на полето, по което да е подредбата"
 
+msgid "exclude refs which match pattern"
+msgstr "прескачана на указателите напасващи на ШАБЛОНа"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "„%s“ не е журнал с подавания"
@@ -19531,7 +20007,7 @@
 
 #, c-format
 msgid "ignoring dangling symref %s"
-msgstr "игнориране на указател на обект извън клон „%s“"
+msgstr "игнориране на файл с указател на обект извън клон „%s“"
 
 #, c-format
 msgid "log for ref %s has gap after %s"
@@ -19572,10 +20048,6 @@
 msgstr "невъзможно е едновременно да се обработват „%s“ и „%s“"
 
 #, c-format
-msgid "could not remove reference %s"
-msgstr "Указателят „%s“ не може да бъде изтрит"
-
-#, c-format
 msgid "could not delete reference %s: %s"
 msgstr "Указателят „%s“ не може да бъде изтрит: %s"
 
@@ -19929,13 +20401,14 @@
 "Your branch and '%s' have diverged,\n"
 "and have %d and %d different commits each, respectively.\n"
 msgstr[0] ""
-"Текущият клон се е отделил от „%s“,\n"
+"Текущият клон се е раздалечил от „%s“,\n"
 "двата имат съответно по %d и %d несъвпадащи подавания.\n"
 msgstr[1] ""
-"Текущият клон се е отделил от „%s“,\n"
+"Текущият клон се е раздалечил от „%s“,\n"
 "двата имат съответно по %d и %d несъвпадащи подавания.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
 msgstr "  (слейте отдалечения клон в локалния чрез „git pull“)\n"
 
 #, c-format
@@ -20012,10 +20485,6 @@
 msgstr "липсва запазена корекция на конфликт при „%s“"
 
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "„%s“ не може да се изтрие"
-
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Предварителният вариант на „%s“ е обновен"
 
@@ -20058,6 +20527,10 @@
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "опцията „--unpacked=ПАКЕТЕН_ФАЙЛ“ вече не се поддържа"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "опциите „%s“ и „--stdin“ са несъвместими"
+
 msgid "your current branch appears to be broken"
 msgstr "Текущият клон е повреден"
 
@@ -20146,8 +20619,15 @@
 msgid "only download metadata for the branch that will be checked out"
 msgstr "да се свалят метаданните само за изтегляния клон"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]"
+msgid "create repository within 'src' directory"
+msgstr "създаване на хранилище в директория „src“"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch ОСНОВЕН_КЛОН] [--full-clone]\n"
+"    [--[no-]src] АДРЕС [ЗАЧИСЛЕНА_ДИРЕКТОРИЯ]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -20188,7 +20668,7 @@
 msgstr "пренастройване на всички зачислени директории"
 
 msgid "scalar reconfigure [--all | <enlistment>]"
-msgstr "scalar reconfigure [--all | ЗАЧИСЛЕНА_ДИРЕКТОРИЯ]"
+msgstr "scalar reconfigure [--all|ЗАЧИСЛЕНА_ДИРЕКТОРИЯ]"
 
 msgid "--all or <enlistment>, but not both"
 msgstr "опцията „--all“ и указването на зачислена директория не са съвместими"
@@ -20198,12 +20678,29 @@
 msgstr "остарялото скаларно хранилище (scalar.repo) „%s“ не може да се изтрие"
 
 #, c-format
-msgid "removing stale scalar.repo '%s'"
+msgid "removed stale scalar.repo '%s'"
 msgstr "изтриване на остарялото скаларно хранилище (scalar.repo) „%s“"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "вече няма хранилище на git в „%s“"
+msgid "repository at '%s' has different owner"
+msgstr "хранилището „%s“ се притежава от друг"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "хранилището в „%s“ е с неправилен формат"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "в „%s“ липсва хранилище на git"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"за да преустановите регистрацията на хранилището в Scalar, изпълнете\n"
+"\n"
+"    git config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -20232,7 +20729,7 @@
 msgstr "включване и на опциите за компилиране на git"
 
 msgid "scalar verbose [-v | --verbose] [--build-options]"
-msgstr "scalar verbose [-v | --verbose] [--build-options]"
+msgstr "scalar verbose [-v|--verbose] [--build-options]"
 
 msgid "-C requires a <directory>"
 msgstr "„-C“ изисква ДИРЕКТОРИЯ"
@@ -20384,10 +20881,6 @@
 msgstr "„%s“ не може да се заключи"
 
 #, c-format
-msgid "could not write to '%s'"
-msgstr "в „%s“ не може да се пише"
-
-#, c-format
 msgid "could not write eol to '%s'"
 msgstr "краят на ред не може да се запише в „%s“"
 
@@ -20644,10 +21137,6 @@
 msgstr "%s: неразпозната стойност за родителското подаване „%s“"
 
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "„%s“ не може да се преименува на „%s“"
-
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "подаването „%s“… не може да бъде отменено: „%s“"
 
@@ -20742,14 +21231,14 @@
 
 #, c-format
 msgid "try \"git revert (--continue | %s--abort | --quit)\""
-msgstr "използвайте „git revert (--continue | %s--abort | --quit)“"
+msgstr "използвайте „git revert (--continue|%s--abort|--quit)“"
 
 msgid "cherry-pick is already in progress"
 msgstr "в момента вече се извършва отбиране на подавания"
 
 #, c-format
 msgid "try \"git cherry-pick (--continue | %s--abort | --quit)\""
-msgstr "използвайте „git cherry-pick (--continue | %s--abort | --quit)“"
+msgstr "използвайте „git cherry-pick (--continue|%s--abort|--quit)“"
 
 #, c-format
 msgid "could not create sequencer directory '%s'"
@@ -20757,9 +21246,6 @@
 "директорията за определянето на последователността „%s“ не може да бъде "
 "създадена"
 
-msgid "could not lock HEAD"
-msgstr "указателят „HEAD“ не може да се заключи"
-
 msgid "no cherry-pick or revert in progress"
 msgstr ""
 "в момента не се извършва отбиране на подавания или пребазиране на клона"
@@ -20862,13 +21348,13 @@
 "    git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "и променѝ индекса и/или работното дърво\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "и променѝ индекса и/или работното дърво.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
@@ -20986,6 +21472,9 @@
 msgid "Autostash exists; creating a new stash entry."
 msgstr "Вече има запис за автоматично скатано, затова се създава нов запис."
 
+msgid "autostash reference is a symref"
+msgstr "указателят за автоматично скатано e файл с указател"
+
 msgid "could not detach HEAD"
 msgstr "указателят „HEAD“ не може да се отдели"
 
@@ -21020,14 +21509,14 @@
 "    git rebase --continue\n"
 
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Пребазиране (%d/%d)%s"
-
-#, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Спиране при „%s“…  %.*s\n"
 
 #, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Пребазиране (%d/%d)%s"
+
+#, c-format
 msgid "unknown command %d"
 msgstr "непозната команда %d"
 
@@ -21274,6 +21763,85 @@
 msgstr "неуспешно изпълнение на „setsid“"
 
 #, c-format
+msgid "cannot stat template '%s'"
+msgstr "не може да се получи информация чрез „stat“ за шаблона „%s“"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "директорията „%s“ не може да бъде отворена"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "връзката „%s“ не може да бъде прочетена"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "не може да се създаде символна връзка „%s“ в „%s“"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "„%s“ не може да се копира в „%s“"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "игнориране на шаблона „%s“"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "няма шаблони в „%s“"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "шаблоните няма да бъдат копирани от „%s“: „%s“"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "неправилно име на първоначалния клон: „%s“"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: „--initial-branch=%s“ се пропуска"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "файлове от вид %d не се поддържат"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "„%s“ не може да се премести в „%s“"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr ""
+"опит за зануляване на хранилището и инициализиране с различна контролна сума"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"опит за зануляване на хранилището и инициализиране с различен формат на "
+"съхраняване"
+
+#, c-format
+msgid "%s already exists"
+msgstr "Директорията „%s“ вече съществува"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr ""
+"Инициализиране наново на съществуващо, споделено хранилище на Git в „%s%s“\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Инициализиране наново на съществуващо хранилище на Git в „%s%s“\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Инициализиране на празно, споделено хранилище на Git в „%s%s“\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Инициализиране на празно хранилище на Git в „%s%s“\n"
+
+#, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
 msgstr "обектът в индекса е директория, но не частично изтеглена (%08x)"
 
@@ -21325,10 +21893,6 @@
 msgstr[1] "%u байта/сек."
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr "„%s“ не може да се редактира"
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "игнориране на подозрително име на подмодул: „%s“"
 
@@ -21531,12 +22095,6 @@
 "какъв брой записи в кеша на обектите-дървета да се отбележат като невалидни "
 "(стандартно е 0)"
 
-msgid "unhandled options"
-msgstr "неподдържани опции"
-
-msgid "error preparing revisions"
-msgstr "грешка при подготовката на версии"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "подаването „%s“ не е отбелязано като достижимо"
@@ -21693,9 +22251,6 @@
 msgid "invalid remote service path"
 msgstr "неправилен път на отдалечената услуга"
 
-msgid "operation not supported by protocol"
-msgstr "опцията не се поддържа от протокола"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "неуспешно свързване към подуслугата „%s“"
@@ -21831,10 +22386,6 @@
 msgstr "протокол версия 2 все още не се поддържа"
 
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "непозната стойност за настройката „%s“: „%s“"
-
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "преносът по „%s“ не е позволен"
 
@@ -21887,6 +22438,9 @@
 "спъсъкът с адреси на пратки обявени за налични от сървъра не може да се "
 "получи "
 
+msgid "operation not supported by protocol"
+msgstr "опцията не се поддържа от протокола"
+
 msgid "too-short tree object"
 msgstr "прекалено кратък обект-дърво"
 
@@ -22275,6 +22829,9 @@
 msgid "unable to get current working directory"
 msgstr "текущата работна директория е недостъпна"
 
+msgid "unable to get random bytes"
+msgstr "не може да се получат случайни байтове"
+
 msgid "Unmerged paths:"
 msgstr "Неслети пътища:"
 
@@ -22727,6 +23284,10 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "не може да извършите „%s“, защото в индекса има неподадени промѐни."
 
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "непознат стил „%s“ за „%s“"
+
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
 "merge"
@@ -22920,13 +23481,13 @@
 "Изтрийте всичко, ако не искате да изпратите обобщаващо писмо.\n"
 
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "„%s“ не може да се отвори: %s"
-
-#, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "„%s.final“ не може да се отвори: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "„%s“ не може да се отвори: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Обобщаващото писмо е празно и се прескача\n"
 
@@ -23085,13 +23646,17 @@
 msgstr "(%s) Не може да бъде се изпълни „%s“"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Добавяне на „%s: %s“ от: „%s“\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Неправилен изход от: „%s“."
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) програмният канал не може да се затвори за изпълнението на „%s“"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Добавяне на „%s: %s“ от: „%s“\n"
+
 msgid "cannot send message as 7bit"
 msgstr "съобщението не може да се изпрати чрез 7 битови знаци"
 
diff --git a/po/ca.po b/po/ca.po
index c6d2dd6..bcb4da8 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -1,17 +1,20 @@
 # Catalan translations for Git.
 # This file is distributed under the same license as the Git package.
 # Alex Henrie <alexhenrie24@gmail.com>, 2014-2016.
-# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2016-2023
+# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2016-2024
 #
-# Terminologia i criteris utilitzats
+# Terminologia
 #
 #   Anglès           |  Català
 #   -----------------+---------------------------------
 #   ahead            |  davant per
 #   amend            |  esmenar
+#   branch           |  branca
 #   broken           |  malmès
 #   bundle           |  farcell
+#   check out        |  agafar
 #   chunk            |  fragment
+#   commit           |  comissió
 #   cover letter     |  carta de presentació
 #   cruft            |  superflu
 #   delta            |  diferència
@@ -26,6 +29,7 @@
 #   hint             |  consell
 #   hook             |  lligam
 #   hunk             |  tros
+#   multi-pack-index |  índex multipaquet
 #   not supported    |  no està admès
 #   pull             |  baixar
 #   push             |  pujar
@@ -52,6 +56,7 @@
 #   Anglès           |  Català
 #   -----------------+---------------------------------
 #   blame            |  «blame»
+#   fanout           |  «fanout»
 #   HEAD             |  HEAD (f, la branca actual) - (no s'apostrofa)
 #   cherry pick      |  «cherry pick»
 #   promisor         |  «promisor»
@@ -64,12 +69,16 @@
 #
 # Vegeu també https://git.github.io/htmldocs/gitglossary.html
 #
+# Criteris
+#   - Mantingueu en anglès les referències a seccions de la documentació, ja que no està traduïda.
+#   - Usem la convenció valenciana per a «per / per a», que inclou l'ús de «per a» davant d'infinitiu
+#
 msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-01 01:20+0000\n"
-"PO-Revision-Date: 2023-03-01 19:00-0600\n"
+"POT-Creation-Date: 2024-02-16 07:14+0100\n"
+"PO-Revision-Date: 2024-02-16 07:16+0100\n"
 "Last-Translator: Jordi Mas i Hernàndez <jmas@softcatala.org>\n"
 "Language-Team: Catalan\n"
 "Language: ca\n"
@@ -699,9 +708,8 @@
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Revertir no és possible perquè teniu fitxers sense fusionar."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "No és possible %s perquè teniu fitxers sense fusionar."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Fer «rebase» no és possible perquè teniu fitxers sense fusionar."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -723,6 +731,23 @@
 msgid "Exiting because of unfinished merge."
 msgstr "S'està sortint a causa d'una fusió no terminada."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Les branques divergents no es poden avançar ràpidament, cal que feu:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"o:\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "No és possible avançar ràpidament, s'està avortant."
 
@@ -829,6 +854,12 @@
 msgid "'%s' outside a repository"
 msgstr "«%s» fora d'un repositori"
 
+msgid "failed to read patch"
+msgstr "s'ha produït un error en llegir el pedaç"
+
+msgid "patch too large"
+msgstr "el pedaç és massa gran"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "No es pot preparar l'expressió regular de marca de temps %s"
@@ -1174,6 +1205,10 @@
 msgstr "no es pot obrir %s"
 
 #, c-format
+msgid "cannot unlink '%s'"
+msgstr "no es pot fer «unlink» de «%s»"
+
+#, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "El tros #%d s'ha aplicat netament."
 
@@ -1365,6 +1400,11 @@
 msgstr "no es pot llegir «%s»"
 
 #, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+"l'especificació de camí «%s» coincideix amb fitxers fora del directori actual"
+
+#, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "l'especificació de camí «%s» no ha coincidit amb cap fitxer"
 
@@ -1380,9 +1420,6 @@
 msgid "not a tree object: %s"
 msgstr "no és un objecte d'arbre: %s"
 
-msgid "current working directory is untracked"
-msgstr "no se segueix el directori de treball actual"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Fitxer no trobat: %s"
@@ -1468,6 +1505,10 @@
 msgstr "Opció inesperada --output"
 
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "paràmetre extra de la línia d'ordres «%s»"
+
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "Format d'arxiu desconegut «%s»"
 
@@ -1503,11 +1544,22 @@
 
 #, c-format
 msgid "ignoring overly large gitattributes file '%s'"
-msgstr "s'ignorarà el fitxer «%s» gitattributes per massa gran"
+msgstr "s'ignorarà el fitxer «%s» gitattributes per a ser massa gran"
 
 #, c-format
 msgid "ignoring overly large gitattributes blob '%s'"
-msgstr "s'ignorarà la blob «%s» gitattributes per massa gran"
+msgstr "s'ignorarà el blob «%s» gitattributes per a ser massa gran"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "--attr-source incorrecte o GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "no s'ha pogut fer «stat» a «%s»"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "no s'ha pogut llegir %s"
 
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
@@ -1615,9 +1667,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents i --reverse no funcionen bé juntes."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "no es pot usar --contents amb el nom d'objecte de la comissió final"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse i --first-parent junts requereixen una última comissió especificada"
@@ -1737,8 +1786,10 @@
 msgstr "ja existeix una branca amb nom «%s»"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "no es pot forçar l'actualització de la branca «%s», agafada a «%s»"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"no es pot forçar l'actualització de la branca «%s» utilitzada per l'arbre de "
+"treball a «%s»"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1799,12 +1850,8 @@
 msgstr "submòdul «%s»: no es pot crear la branca: «%s»"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "«%s» ja s'ha agafat a «%s»"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD de l'arbre de treball %s no està actualitzat"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "«%s» ja s'utilitza en l'arbre de treball a «%s»"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<opcions>] [--] <especificació-de-camí>..."
@@ -1813,17 +1860,6 @@
 msgid "cannot chmod %cx '%s'"
 msgstr "no es pot fer chmod %cx «%s»"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "estat de diff inesperat %c"
-
-msgid "updating files failed"
-msgstr "s'ha produït un error en actualitzar els fitxers"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "elimina «%s»\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Canvis «unstaged» després d'actualitzar l'índex:"
 
@@ -1834,25 +1870,22 @@
 "s'ha eliminat la configuració add.interactive.useBuiltin\n"
 "Per a més detalls, vegeu la seva entrada a «git help config»."
 
-msgid "Could not read the index"
-msgstr "No s'ha pogut llegir l'índex"
-
-msgid "Could not write patch"
-msgstr "No s'ha pogut escriure el pedaç"
+msgid "could not read the index"
+msgstr "no s'ha pogut llegir l'índex"
 
 msgid "editing patch failed"
 msgstr "l'edició del pedaç ha fallat"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "No s'ha pogut fer stat a «%s»"
+msgid "could not stat '%s'"
+msgstr "no s'ha pogut fer stat a «%s»"
 
-msgid "Empty patch. Aborted."
-msgstr "El pedaç és buit. S'ha avortat."
+msgid "empty patch. aborted"
+msgstr "pedaç buit. interromput"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "No s'ha pogut aplicar «%s»"
+msgid "could not apply '%s'"
+msgstr "no s'ha pogut aplicar «%s»"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
@@ -1981,6 +2014,9 @@
 msgid "index file corrupt"
 msgstr "fitxer d'índex malmès"
 
+msgid "unable to write new index file"
+msgstr "no s'ha pogut escriure un fitxer d'índex nou"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "acció «%s» incorrecta per a «%s»"
@@ -2189,9 +2225,6 @@
 "Podeu executar «git rm» en un fitxer per a acceptar «suprimit per ells» pel "
 "fitxer."
 
-msgid "unable to write new index file"
-msgstr "no s'ha pogut escriure un fitxer d'índex nou"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "No s'ha pogut analitzar l'objecte «%s»."
@@ -2210,10 +2243,6 @@
 msgid "failed to read '%s'"
 msgstr "s'ha produït un error en llegir «%s»"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "les opcions «%s=%s» i «%s=%s» no es poden usar juntes"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<opcions>] [(<bústia> | <directori-de-correu>)...]"
 
@@ -2253,10 +2282,6 @@
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "passa l'indicador --keep-cr a git-mailsplit per al format mbox"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"no passis l'indicador --keep-cr a git-mailsplit independentment d'am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "elimina tot abans d'una línia de tisores"
 
@@ -2370,10 +2395,10 @@
 msgstr "git archive: s'esperava una neteja"
 
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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-"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
 "checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 
 msgid "git bisect (good|bad) [<rev>...]"
@@ -2388,8 +2413,8 @@
 msgid "git bisect replay <logfile>"
 msgstr "git bisect replay <logfile>"
 
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <ordre>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <cmd> [<arg>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2709,7 +2734,7 @@
 msgstr "mostra la marca de temps en cru (per defecte: desactivat)"
 
 msgid "show long commit SHA1 (Default: off)"
-msgstr "mostra l'SHA1 de comissió llarg (per defecte: desactivat)"
+msgstr "mostra l'SHA1 de la comissió en format llarg (per defecte: desactivat)"
 
 msgid "suppress author name and timestamp (Default: off)"
 msgstr "omet el nom d'autor i la marca de temps (per defecte: desactivat)"
@@ -2817,50 +2842,57 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"s'està suprimint la branca «%s» que s'ha\n"
-"         fusionat a «%s», però encara no\n"
-"         s'ha fusionat a HEAD."
+"s'està suprimint la branca «%s» que s'ha fusionat a\n"
+"         «%s», però encara no s'ha fusionat a HEAD"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"no s'està suprimint la branca «%s» que encara no\n"
-"         s'ha fusionat a «%s», encara que està\n"
-"         fusionada a HEAD."
+"no s'està suprimint la branca «%s» que encara no s'ha fusionat a\n"
+"         «%s», encara que s'hagi fusionat a HEAD"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "No s'ha pogut trobar l'objecte de comissió de «%s»"
+msgid "couldn't look up commit object for '%s'"
+msgstr "no s'ha pogut cercar l'objecte de comissió per a «%s»"
 
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"La branca «%s» no està totalment fusionada.\n"
-"Si esteu segur que la voleu suprimir, executeu «git branch -D %s»."
+msgid "the branch '%s' is not fully merged"
+msgstr "la branca «%s» no està completament fusionada"
 
-msgid "Update of config-file failed"
-msgstr "L'actualització del fitxer de configuració ha fallat"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Si esteu segur que voleu suprimir-la, executeu «git branch -D %s»"
+
+msgid "update of config-file failed"
+msgstr "ha fallat l'actualització del fitxer de configuració"
 
 msgid "cannot use -a with -d"
 msgstr "no es pot usar -a amb -d"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "No es pot suprimir la branca «%s» agafada a «%s»"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"no es pot suprimir la branca «%s» utilitzada per l'arbre de treball a «%s»"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "no s'ha trobat la branca amb seguiment remot «%s»."
+msgid "remote-tracking branch '%s' not found"
+msgstr "no s'ha trobat la branca de seguiment remot «%s»"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "no s'ha trobat la branca «%s»."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"no s'ha trobat la branca «%s».\n"
+"Us heu oblidat de --remote?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "no s'ha trobat la branca «%s»"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2881,52 +2913,56 @@
 msgstr "HEAD (%s) apunta fora de refs/heads/"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "S'està fent «rebase» en la branca %s a %s"
+msgid "branch %s is being rebased at %s"
+msgstr "a la branca %s se li està fent a «rebase» a %s"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "La branca %s s'està bisecant a %s"
+msgid "branch %s is being bisected at %s"
+msgstr "la branca %s s'està bisecant a %s"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Nom de branca no vàlid: «%s»"
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD de l'arbre de treball %s no està actualitzat"
 
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Encara no hi ha cap comissió en la branca «%s»."
+msgid "invalid branch name: '%s'"
+msgstr "el nom de la branca no és vàlid: «%s»"
 
 #, c-format
-msgid "No branch named '%s'."
-msgstr "No hi ha cap branca amb nom «%s»."
-
-msgid "Branch rename failed"
-msgstr "El canvi de nom de branca ha fallat"
-
-msgid "Branch copy failed"
-msgstr "La còpia de la branca ha fallat"
+msgid "no commit on branch '%s' yet"
+msgstr "encara no hi ha cap comissió a la branca «%s»"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "S'ha creat una còpia d'una branca mal anomenada «%s»"
+msgid "no branch named '%s'"
+msgstr "no hi ha cap branca anomenada «%s»"
+
+msgid "branch rename failed"
+msgstr "ha fallat el canvi de nom de la branca"
+
+msgid "branch copy failed"
+msgstr "ha fallat la còpia de la branca"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "S'ha canviat el nom de la branca mal anomenada «%s»"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "s'ha creat una còpia d'una branca mal anomenada «%s»"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "S'ha canviat el nom de la branca a %s, però HEAD no està actualitzat!"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "s'ha canviat el nom d'una branca «%s» mal anomenada"
 
-msgid "Branch is renamed, but update of config-file failed"
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "s'ha canviat el nom de la branca a %s, però HEAD no s'ha actualitzat"
+
+msgid "branch is renamed, but update of config-file failed"
 msgstr ""
-"La branca està canviada de nom, però l'actualització del fitxer de "
-"configuració ha fallat"
+"s'ha canviat el nom de la branca, però ha fallat l'actualització del fitxer "
+"de configuració"
 
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr ""
-"La branca està copiada, però l'actualització del fitxer de configuració ha "
-"fallat"
+"s'ha copiat la branca, però ha fallat l'actualització del fitxer de "
+"configuració"
 
 #, c-format
 msgid ""
@@ -2992,6 +3028,9 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "mou/canvia de nom una branca, encara que el destí existeixi"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "no emetis cap línia nova després de refs amb format buit"
+
 msgid "copy a branch and its reflog"
 msgstr "copia una branca i el seu registre de referència"
 
@@ -3037,8 +3076,8 @@
 msgid "format to use for the output"
 msgstr "format a usar en la sortida"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "S'ha produït un error en resoldre HEAD com a referència vàlida."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "no s'ha pogut resoldre HEAD com a referència vàlida"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD no trobat sota refs/heads!"
@@ -3056,17 +3095,18 @@
 msgid "branch name required"
 msgstr "cal el nom de branca"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "No es pot donar descripció a una HEAD separada"
+msgid "cannot give description to detached HEAD"
+msgstr "no s'ha pogut donar la descripció al HEAD separat"
 
 msgid "cannot edit description of more than one branch"
 msgstr "no es pot editar la descripció de més d'una branca"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "no es pot copiar branca actual mentre no s'és a cap."
+msgid "cannot copy the current branch while not on any"
+msgstr "no es pot copiar la branca actual mentre no pertanyi a cap"
 
-msgid "cannot rename the current branch while not on any."
-msgstr "no es pot canviar el nom de la branca actual mentre no s'és a cap."
+msgid "cannot rename the current branch while not on any"
+msgstr ""
+"no s'ha pogut canviar el nom de la branca actual mentre no pertanyi a cap"
 
 msgid "too many branches for a copy operation"
 msgstr "hi ha massa branques per a una operació de còpia"
@@ -3079,9 +3119,9 @@
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"no s'ha pogut establir la font de HEAD com a %s quan no assenyala cap branca."
+"no s'ha pogut configurar la font de HEAD a %s quan no apunta a cap branca"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3094,27 +3134,26 @@
 msgid "too many arguments to unset upstream"
 msgstr "hi ha massa arguments per a desassignar la font"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr ""
-"no s'ha pogut desassignar la font de HEAD perquè no assenyala cap branca."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "no s'ha pogut desassignar la font del HEAD quan no apunta a cap branca"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "La branca «%s» no té informació de font"
+msgid "branch '%s' has no upstream information"
+msgstr "la branca «%s» no té informació de la font"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Les opcions -a i -r a «git branch» no prenen un nom de branca.\n"
-"Volíeu usar -a|-r --list <patró>?"
+"les opcions -a, i -r, a «git branch» no prenen un nom de branca.\n"
+"Volíeu utilitzar: -a|-r --list <patró>?"
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
-"l'opció --set-upstream ja no s'admet. En lloc seu, useu «--track» o «--set-"
-"upstream-to»."
+"l'opció «--set-upstream» ja no és admesa. Utilitzeu en comptes «--track» o "
+"«--set-upstream-to»"
 
 msgid "git version:\n"
 msgstr "versió de git:\n"
@@ -3188,6 +3227,10 @@
 msgstr "especifiqueu un sufix en format strftime per al nom de fitxer"
 
 #, c-format
+msgid "unknown argument `%s'"
+msgstr "argument desconegut «%s»"
+
+#, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "no s'han pogut crear els directoris principals de «%s»"
 
@@ -3210,12 +3253,10 @@
 msgstr "S'ha creat un nou informe a «%s».\n"
 
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<versió>] <fitxer> <git-rev-list-args>"
 
 msgid "git bundle verify [-q | --quiet] <file>"
@@ -3236,11 +3277,11 @@
 msgid "show progress meter"
 msgstr "mostra l'indicador de progrés"
 
-msgid "show progress meter during object writing phase"
-msgstr "mostra l'indicador de progrés durant la fase d'escriptura d'objectes"
+msgid "historical; same as --progress"
+msgstr "històric; el mateix que --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "similar a --all-progress quan l'indicador de progrés es mostra"
+msgid "historical; does nothing"
+msgstr "històric; no fa res"
 
 msgid "specify bundle format version"
 msgstr "especifica la versió del format del farcell"
@@ -3296,22 +3337,22 @@
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objecte>"
 
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 
 msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
 
 msgid "Check object existence or emit object contents"
 msgstr "Comprova l'existència de l'objecte o emet el contingut de l'objecte"
@@ -3351,6 +3392,9 @@
 msgid "stdin is NUL-terminated"
 msgstr "l'entrada és acabada amb NUL"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin i stdout estan terminats amb NUL"
+
 msgid "read commands from stdin"
 msgstr "llegeix les ordres de stdin"
 
@@ -3607,6 +3651,10 @@
 msgstr "«%s» o «%s» no poden utilitzar-se amb %s"
 
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "«%s», «%s» o «%s» no es poden utilitzar en agafar un arbre"
+
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "el camí «%s» està sense fusionar"
 
@@ -3858,8 +3906,8 @@
 msgid "new-branch"
 msgstr "branca-nova"
 
-msgid "new unparented branch"
-msgstr "branca òrfena nova"
+msgid "new unborn branch"
+msgstr "branca no nascuda nova"
 
 msgid "update ignored files (default)"
 msgstr "actualitza els fitxers ignorats (per defecte)"
@@ -4111,9 +4159,6 @@
 "clean.requireForce és per defecte cert i ni -i, -n ni -f s'han indicat; "
 "refusant netejar"
 
-msgid "-x and -X cannot be used together"
-msgstr "-x i -X no es poden usar junts"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<opcions>] [--] <repositori> [<directori>]"
 
@@ -4202,6 +4247,9 @@
 msgid "separate git dir from working tree"
 msgstr "separa el directori de git de l'arbre de treball"
 
+msgid "specify the reference format to use"
+msgstr "especifiqueu el format de referència a usar"
+
 msgid "key=value"
 msgstr "clau=valor"
 
@@ -4214,12 +4262,6 @@
 msgid "option to transmit"
 msgstr "opció a transmetre"
 
-msgid "use IPv4 addresses only"
-msgstr "usa només adreces IPv4"
-
-msgid "use IPv6 addresses only"
-msgstr "usa només adreces IPv6"
-
 msgid "apply partial clone filters to submodules"
 msgstr "aplica els filtres de clonatge parcial als submòduls"
 
@@ -4250,12 +4292,16 @@
 msgstr "%s existeix i no és directori"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "«%s» és un enllaç simbòlic, es rebutja clonar amb --local"
+
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "no s'ha pogut iniciar l'iterador sobre «%s»"
 
 #, c-format
 msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "l'enllaç simbòlic «%s» existeix, es nega a clonar amb --local"
+msgstr "l'enllaç simbòlic «%s» existeix, es rebutja a clonar amb --local"
 
 #, c-format
 msgid "failed to unlink '%s'"
@@ -4323,11 +4369,9 @@
 msgid "You must specify a repository to clone."
 msgstr "Heu d'especificar un repositori per a clonar."
 
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri és incompatible amb --depth, --shallow-since i --shallow-exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "el format d'emmagatzematge de referència «%s» és desconegut"
 
 #, c-format
 msgid "repository '%s' does not exist"
@@ -4457,14 +4501,14 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <dir>] [--append]\n"
 "                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 
 msgid "dir"
 msgstr "directori"
@@ -4481,6 +4525,10 @@
 msgstr "No s'ha pogut obrir el graf de comissions «%s»"
 
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "no s'ha pogut obrir la cadena «%s» del graf de comissions"
+
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "argument --split no reconegut, %s"
 
@@ -4657,6 +4705,9 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "s'ha produït un error en actualitzar els fitxers"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "s'ha produït un error en desempaquetar l'objecte d'arbre HEAD"
 
@@ -4675,9 +4726,6 @@
 msgid "Failed to update main cache tree"
 msgstr "S'ha produït un error en actualitzar l'arbre principal de memòria cau"
 
-msgid "unable to write new_index file"
-msgstr "no s'ha pogut escriure el fitxer new_index"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "no es pot fer una comissió parcial durant una fusió."
 
@@ -4716,8 +4764,8 @@
 "no sigui usat en el missatge de comissió actual"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "no s'ha pogut cercar la comissió %s"
+msgid "could not lookup commit '%s'"
+msgstr "no s'ha pogut cercar la comissió «%s»"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5080,13 +5128,13 @@
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
 "s'ha actualitzat el repositori, però no s'ha pogut escriure\n"
-" el fitxer «new_index». Comproveu que el disc no està ple i\n"
-"que la quota no s'ha excedit, i després, feu\n"
-"«git restore --staged :/» per a recuperar-lo."
+"el fitxer d'índex nou. Comproveu que el disc no està ple i\n"
+"la quota no s'ha excedit, i després feu «git restore --staged :/n»\n"
+"per a recuperar-ho."
 
 msgid "git config [<options>]"
 msgstr "git config [<opcions>]"
@@ -5157,7 +5205,7 @@
 msgstr "elimina una secció: nom"
 
 msgid "list all"
-msgstr "llista tots"
+msgstr "llista'ls tots"
 
 msgid "use string equality when comparing values to 'value-pattern'"
 msgstr ""
@@ -5263,7 +5311,7 @@
 msgstr "no s'admet escriure a stdin"
 
 msgid "writing config blobs is not supported"
-msgstr "no s'ha admet l'escriptura de blobs de configuració"
+msgstr "no s'admet l'escriptura de blobs de configuració"
 
 #, c-format
 msgid ""
@@ -5436,7 +5484,7 @@
 "However, there were unannotated tags: try --tags."
 msgstr ""
 "Cap etiqueta anotada pot descriure «%s».\n"
-"No obstant, hi havia etiquetes no anotades: proveu --tags."
+"No obstant això, hi havia etiquetes no anotades: proveu --tags."
 
 #, c-format
 msgid ""
@@ -5517,7 +5565,7 @@
 
 #, c-format
 msgid "option '%s' and commit-ishes cannot be used together"
-msgstr "les opcions «%s» i de comissió no es poden usar juntes"
+msgstr "opció «%s» i les de comissió no es poden usar juntes"
 
 msgid ""
 "git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
@@ -5682,7 +5730,7 @@
 
 msgid "select handling of commit messages in an alternate encoding"
 msgstr ""
-"selecciona la gestió dels missatges de publicació en una codificació "
+"selecciona la gestió dels missatges de comissió en una codificació "
 "alternativa"
 
 msgid "dump marks to this file"
@@ -5778,6 +5826,178 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel no pot ser negatiu"
 
+msgid "couldn't find remote ref HEAD"
+msgstr "no s'ha pogut trobar la referència HEAD remota"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "De %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "objecte %s no trobat"
+
+msgid "[up to date]"
+msgstr "[al dia]"
+
+msgid "[rejected]"
+msgstr "[rebutjat]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "no es pot obtenir en la branca actual"
+
+msgid "[tag update]"
+msgstr "[actualització d'etiqueta]"
+
+msgid "unable to update local ref"
+msgstr "no s'ha pogut actualitzar la referència local"
+
+msgid "would clobber existing tag"
+msgstr "s'adjuntaria l'etiqueta existent"
+
+msgid "[new tag]"
+msgstr "[etiqueta nova]"
+
+msgid "[new branch]"
+msgstr "[branca nova]"
+
+msgid "[new ref]"
+msgstr "[referència nova]"
+
+msgid "forced update"
+msgstr "actualització forçada"
+
+msgid "non-fast-forward"
+msgstr "sense avanç ràpid"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "no es pot obrir «%s»"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"en obtenir normalment indica quines branques tenien una actualització "
+"forçada,\n"
+"però aquesta comprovació s'ha desactivat. Per a tornar a habilitar-la, "
+"utilitzeu\n"
+"«--show-forced-updates» o executeu «git config fetch.showForcedUpdates true»"
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"s'ha trigat %.2f segons a comprovar les actualitzacions forçades. Podeu\n"
+"utilitzar «--no-show-forced-updates» o executar «git config \n"
+"fetch.showForcedUpdates false» per a evitar aquesta comprovació.\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s no ha enviat tots els objectes necessaris\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr ""
+"s'ha rebutjat %s perquè no es permeten actualitzar les arrels superficials"
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"algunes referències locals no s'han pogut actualitzar;\n"
+" intenteu executar «git remote prune %s» per a eliminar\n"
+" qualsevol branca antiga o conflictiva"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s es tornarà penjant)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s s'ha tornat penjant)"
+
+msgid "[deleted]"
+msgstr "[suprimit]"
+
+msgid "(none)"
+msgstr "(cap)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "s'ha rebutjat l'obtenció en la branca «%s» agafada a «%s»"
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "l'opció «%s» amb valor «%s» no és vàlida per a %s"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "s'ignora l'opció «%s» per a %s\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s no és un objecte vàlid"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "l'objecte %s no existeix"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "s'han detectat múltiples branques, incompatible amb --set-upstream"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"no s'ha pogut establir la font de HEAD a «%s» des de «%s» quan no assenyala "
+"cap branca."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr ""
+"no s'està configurant la font per a una branca remota amb seguiment remot"
+
+msgid "not setting upstream for a remote tag"
+msgstr "no s'està configurant la font d'una etiqueta remota"
+
+msgid "unknown branch type"
+msgstr "tipus de branca desconegut"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"no s'ha trobat cap branca d'origen.\n"
+"heu d'especificar exactament una branca amb l'opció --set-upstream"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "S'està obtenint %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "no s'ha pogut obtenir %s"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "no s'ha pogut obtenir «%s» (codi de sortida: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"no s'ha especificat cap repositori remot. Especifiqueu un URL o\n"
+"un nom remot del qual s'han d'obtenir les revisions noves"
+
+msgid "you need to specify a tag name"
+msgstr "necessiteu especificar un nom d'etiqueta"
+
 msgid "fetch from all remotes"
 msgstr "obtén de tots els remots"
 
@@ -5888,178 +6108,6 @@
 msgid "accept refspecs from stdin"
 msgstr "llegeix les especificacions de referència des de stdin"
 
-msgid "couldn't find remote ref HEAD"
-msgstr "no s'ha pogut trobar la referència HEAD remota"
-
-#, c-format
-msgid "object %s not found"
-msgstr "objecte %s no trobat"
-
-msgid "[up to date]"
-msgstr "[al dia]"
-
-msgid "[rejected]"
-msgstr "[rebutjat]"
-
-msgid "can't fetch into checked-out branch"
-msgstr "no es pot obtenir en la branca actual"
-
-msgid "[tag update]"
-msgstr "[actualització d'etiqueta]"
-
-msgid "unable to update local ref"
-msgstr "no s'ha pogut actualitzar la referència local"
-
-msgid "would clobber existing tag"
-msgstr "s'adjuntaria l'etiqueta existent"
-
-msgid "[new tag]"
-msgstr "[etiqueta nova]"
-
-msgid "[new branch]"
-msgstr "[branca nova]"
-
-msgid "[new ref]"
-msgstr "[referència nova]"
-
-msgid "forced update"
-msgstr "actualització forçada"
-
-msgid "non-fast-forward"
-msgstr "sense avanç ràpid"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "no es pot obrir «%s»"
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"en obtenir normalment indica quines branques tenien una actualització "
-"forçada,\n"
-"però aquesta comprovació s'ha desactivat. Per a tornar a habilitar-la, "
-"utilitzeu\n"
-"«--show-forced-updates» o executeu «git config fetch.showForcedUpdates true»"
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"s'ha trigat %.2f segons a comprovar les actualitzacions forçades. Podeu\n"
-"utilitzar «--no-show-forced-updates» o executar «git config \n"
-"fetch.showForcedUpdates false» per a evitar aquesta comprovació.\n"
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s no ha enviat tots els objectes necessaris\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr ""
-"s'ha rebutjat %s perquè no es permeten actualitzar les arrels superficials"
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "De %.*s\n"
-
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"algunes referències locals no s'han pogut actualitzar;\n"
-" intenteu executar «git remote prune %s» per a eliminar\n"
-" qualsevol branca antiga o conflictiva"
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s es tornarà penjant)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s s'ha tornat penjant)"
-
-msgid "[deleted]"
-msgstr "[suprimit]"
-
-msgid "(none)"
-msgstr "(cap)"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "s'ha rebutjat l'obtenció en la branca «%s» agafada a «%s»"
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "l'opció «%s» amb valor «%s» no és vàlida per a %s"
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "s'ignora l'opció «%s» per a %s\n"
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s no és un objecte vàlid"
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr "l'objecte %s no existeix"
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "s'han detectat múltiples branques, incompatible amb --set-upstream"
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"no s'ha pogut establir la font de HEAD a «%s» des de «%s» quan no assenyala "
-"cap branca."
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr ""
-"no s'està configurant la font per a una branca remota amb seguiment remot"
-
-msgid "not setting upstream for a remote tag"
-msgstr "no s'està configurant la font d'una etiqueta remota"
-
-msgid "unknown branch type"
-msgstr "tipus de branca desconegut"
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"no s'ha trobat cap branca d'origen.\n"
-"heu d'especificar exactament una branca amb l'opció --set-upstream"
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "S'està obtenint %s\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr "no s'ha pogut obtenir %s"
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "no s'ha pogut obtenir «%s» (codi de sortida: %d)\n"
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"no s'ha especificat cap repositori remot. Especifiqueu un URL o\n"
-"un nom remot del qual s'han d'obtenir les revisions noves"
-
-msgid "you need to specify a tag name"
-msgstr "necessiteu especificar un nom d'etiqueta"
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only necessita un o més --negotiation-tip=*"
 
@@ -6176,6 +6224,12 @@
 msgid "print only refs which don't contain the commit"
 msgstr "imprimeix només les referències que no continguin la comissió"
 
+msgid "read reference patterns from stdin"
+msgstr "llegeix els patrons de referència de l'entrada estàndard"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "s'han proporcionat arguments desconeguts amb --stdin"
+
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<config> [--] <arguments>"
 
@@ -6188,6 +6242,10 @@
 msgid "missing --config=<config>"
 msgstr "falta --config=<config>"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "s'ha obtingut una configuració incorrecta --config=%s"
+
 msgid "unknown"
 msgstr "desconegut"
 
@@ -6334,19 +6392,28 @@
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "avís: %s apunta a una branca no nascuda (%s)"
 
-msgid "Checking cache tree"
-msgstr "S'està comprovant l'arbre de la memòria cau"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "S'està comprovant l'arbre de la memòria cau %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: apuntador sha1 no vàlid a l'arbre de la memòria cau"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: punter sha1 no vàlid a l'arbre de la memòria cau %s"
 
 msgid "non-tree in cache-tree"
 msgstr "un no arbre en l'arbre de la memòria cau"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: el punter sha1 no és vàlid a «resolve-undo»"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: punter sha1 no vàlid a «resolve-undo» de %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "no s'ha pogut carregar l'índex de reversió per al paquet «%s»"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "rev-index no vàlid per al paquet «%s»"
 
 msgid ""
 "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
@@ -6532,6 +6599,9 @@
 msgid "pack unreferenced objects separately"
 msgstr "empaqueta els objectes no referenciats de forma separada"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "amb --cruft, limiteu la mida dels paquets cruft nous"
+
 msgid "be more thorough (increased runtime)"
 msgstr "sigues més exhaustiu (el temps d'execució augmenta)"
 
@@ -6710,12 +6780,6 @@
 msgid "'crontab' died"
 msgstr "«crontab» ha mort"
 
-msgid "failed to start systemctl"
-msgstr "s'ha produït un error en iniciar systemctl"
-
-msgid "failed to run systemctl"
-msgstr "s'ha produït un error en executar systemctl"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "s'ha produït un error en suprimir «%s»"
@@ -6724,6 +6788,12 @@
 msgid "failed to flush '%s'"
 msgstr "no s'ha pogut buidar «%s»"
 
+msgid "failed to start systemctl"
+msgstr "s'ha produït un error en iniciar systemctl"
+
+msgid "failed to run systemctl"
+msgstr "s'ha produït un error en executar systemctl"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "l'argument --scheduler no reconegut «%s»"
@@ -6747,6 +6817,9 @@
 msgid "scheduler to trigger git maintenance run"
 msgstr "planificador per a activar l'execució de manteniment del git"
 
+msgid "failed to set up maintenance schedule"
+msgstr "no s'ha pogut configurar la planificació del manteniment"
+
 msgid "failed to add repo to global config"
 msgstr "no s'ha pogut afegir un repositori a la configuració global"
 
@@ -6778,6 +6851,10 @@
 msgstr "no s'ha pogut llegir l'arbre (%s)"
 
 #, c-format
+msgid "unable to read tree %s"
+msgstr "no s'ha pogut llegir l'arbre %s"
+
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "no es pot fer grep des d'un objecte de tipus %s"
 
@@ -6821,8 +6898,8 @@
 msgid "search in subdirectories (default)"
 msgstr "cerca als subdirectoris (per defecte)"
 
-msgid "descend at most <depth> levels"
-msgstr "descendeix com a màxim <profunditat> nivells"
+msgid "descend at most <n> levels"
+msgstr "descendeix com a màxim <n> nivells"
 
 msgid "use extended POSIX regular expressions"
 msgstr "usa les expressions regulars POSIX ampliades"
@@ -7196,10 +7273,6 @@
 msgstr "S'HA TROBAT UNA COL·LISIÓ SHA1 AMB %s !"
 
 #, c-format
-msgid "unable to read %s"
-msgstr "no s'ha pogut llegir %s"
-
-#, c-format
 msgid "cannot read existing object info %s"
 msgstr "no es pot llegir la informació d'objecte existent %s"
 
@@ -7336,85 +7409,16 @@
 msgid "fsck error in pack objects"
 msgstr "error fsck als objectes del paquet"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "no es pot fer stat en la plantilla «%s»"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "no es pot fer opendir en el directori «%s»"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "no es pot fer readlink en «%s»"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "no es pot fer symlink en «%s» «%s»"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "no es pot copiar «%s» a «%s»"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "s'està ignorant la plantilla %s"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "plantilles no trobades a %s"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "no s'estan copiant plantilles de «%s»: %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "nom de branca inicial no vàlid: «%s»"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "no s'ha pogut gestionar el tipus de fitxer %d"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "no s'ha pogut moure %s a %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "s'ha intentat reinicialitzar el repositori amb un resum diferent"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s ja existeix"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "reinicialització: s'ha ignorat --initial-branch=%s"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "S'ha reinicialitzat el repositori compartit existent del Git en %s%s\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "S'ha reinicialitzat el repositori existent del Git en %s%s\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "S'ha inicialitzat un repositori compartit buit del Git en %s%s\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "S'ha inicialitzat un repositori buit del Git en %s%s\n"
-
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 
@@ -7459,12 +7463,12 @@
 
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
-"                       [--parse] [<fitxer>...]"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 
 msgid "edit files in place"
 msgstr "edita els fitxers in situ"
@@ -7472,6 +7476,9 @@
 msgid "trim empty trailers"
 msgstr "escurça els remolcs buits"
 
+msgid "placement"
+msgstr "posicionament"
+
 msgid "where to place the new trailer"
 msgstr "on ubicar el «trailer» nou"
 
@@ -7484,17 +7491,18 @@
 msgid "output only the trailers"
 msgstr "mostra només els «trailer»"
 
-msgid "do not apply config rules"
-msgstr "no apliquis les regles de configuració"
+msgid "do not apply trailer.* configuration variables"
+msgstr "no apliquis les variables de configuració trailer.*"
 
-msgid "join whitespace-continued values"
-msgstr "uneix els valors continus amb espais en blanc"
+msgid "reformat multiline trailer values as single-line values"
+msgstr ""
+"reformata els valors del tràiler multilínia com a valors de línia única"
 
-msgid "set parsing options"
-msgstr "estableix les opcions d'anàlisi"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "àlies per a --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "no tractis --- especialment"
+msgid "do not treat \"---\" as the end of input"
+msgstr "no tractis «---» com el final de l'entrada"
 
 msgid "trailer(s) to add"
 msgstr "remolcs a afegir"
@@ -7583,6 +7591,10 @@
 msgid "not a range"
 msgstr "no és un interval"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "no es pot llegir el fitxer de descripció de la branca «%s»"
+
 msgid "cover letter needs email format"
 msgstr "la carta de presentació necessita un format de correu electrònic"
 
@@ -7684,6 +7696,9 @@
 "genera parts d'una carta de presentació basant-se en la descripció d'una "
 "branca"
 
+msgid "use branch description from file"
+msgstr "utilitza la descripció de la branca des del fitxer"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "useu [<prefix>] en comptes de [PATCH]"
 
@@ -7847,6 +7862,10 @@
 "manualment.\n"
 
 #, c-format
+msgid "could not get object info about '%s'"
+msgstr "no s'ha pogut obtenir la informació sobre l'objecte «%s»"
+
+#, 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 «(»"
 
@@ -7991,10 +8010,6 @@
 msgstr "git ls-tree [<opcions>] <arbre> [<camí>...]"
 
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "no s'ha pogut obtenir la informació sobre l'objecte «%s»"
-
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "format incorrecte del ls-tree: l'element '%s' no comença amb «(»"
 
@@ -8119,9 +8134,18 @@
 "git merge-file [<opcions>] [-L <nom1> [-L <original> [-L <nom2>]]] <fitxer1> "
 "<fitxer-original> <fitxer2>"
 
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"l'opció diff-algorithm accepta «myers», «minimal», «patience» i «histogram»"
+
 msgid "send results to standard output"
 msgstr "envia els resultats a la sortida estàndard"
 
+msgid "use object IDs instead of filenames"
+msgstr "utilitza els ID dels objectes en comptes dels noms de fitxer"
+
 msgid "use a diff3 based merge"
 msgstr "usa una fusió basada en diff3"
 
@@ -8137,6 +8161,12 @@
 msgid "for conflicts, use a union version"
 msgstr "en conflictes, usa una versió d'unió"
 
+msgid "<algorithm>"
+msgstr "<algorisme>"
+
+msgid "choose a diff algorithm"
+msgstr "trieu un algorisme per al diff"
+
 msgid "for conflicts, use this marker size"
 msgstr "en conflictes, usa aquesta mida de marcador"
 
@@ -8147,6 +8177,13 @@
 msgstr "estableix les etiquetes per a fitxer1/fitxer-original/fitxer2"
 
 #, c-format
+msgid "object '%s' does not exist"
+msgstr "l'objecte «%s» no existeix"
+
+msgid "Could not write object file"
+msgstr "No s'ha pogut escriure el fitxer de l'objecte"
+
+#, c-format
 msgid "unknown option %s"
 msgstr "opció desconeguda %s"
 
@@ -8207,11 +8244,18 @@
 msgid "specify a merge-base for the merge"
 msgstr "cal especificar una referència base per a la fusió"
 
+msgid "option=value"
+msgstr "opció=valor"
+
+msgid "option for selected merge strategy"
+msgstr "opció per a l'estratègia de fusió seleccionada"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge és incompatible amb totes les altres opcions"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base és incompatible amb --stdin"
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "opció d'estratègia desconeguda: -X%s"
 
 #, c-format
 msgid "malformed input line: '%s'."
@@ -8281,12 +8325,6 @@
 msgid "merge strategy to use"
 msgstr "estratègia de fusió a usar"
 
-msgid "option=value"
-msgstr "opció=valor"
-
-msgid "option for selected merge strategy"
-msgstr "opció per a l'estratègia de fusió seleccionada"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "missatge de comissió de fusió (per a una fusió no d'avanç ràpid)"
 
@@ -8347,10 +8385,6 @@
 msgstr "No s'està gestionant res a part de la fusió de dos caps."
 
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "opció d'estratègia desconeguda: -X%s"
-
-#, c-format
 msgid "unable to write %s"
 msgstr "no s'ha pogut escriure %s"
 
@@ -8649,8 +8683,8 @@
 msgid "can not move directory into itself"
 msgstr "no es pot moure un directori a dins d'ell mateix"
 
-msgid "cannot move directory over file"
-msgstr "no es pot moure un directori sobre un fitxer"
+msgid "destination already exists"
+msgstr "la destinació ja existeix"
 
 msgid "source directory is empty"
 msgstr "el directori d'origen està buit"
@@ -8729,22 +8763,26 @@
 msgstr "git notes [--ref <referència-de-notes>] [llista [<objecte>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referència-de-notes>] add [-f] [--allow-empty] [-m "
-"<missatge> | -F <fitxer> | (-c | -C) <objecte>] [<objecte>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <referència-de-notes>] copy [-f] <d'objecte> <a-objecte>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referència-de-notes>] append [--allow-empty] [-m "
-"<missatge> | -F <fitxer> | (-c | -C) <objecte>] [<objecte>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr ""
@@ -8878,6 +8916,15 @@
 msgid "replace existing notes"
 msgstr "reemplaça les notes existents"
 
+msgid "<paragraph-break>"
+msgstr "<paragraph-break>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "insereix <paragraph-break> entre paràgrafs"
+
+msgid "remove unnecessary whitespace"
+msgstr "elimina l'espai en blanc innecessari"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9153,6 +9200,10 @@
 msgstr "inconsistència amb el comptador de diferències"
 
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "valor pack.allowPackReuse value no vàlid: «%s»"
+
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -9236,6 +9287,12 @@
 msgid "bad index version '%s'"
 msgstr "versió d'índex incorrecta «%s»"
 
+msgid "show progress meter during object writing phase"
+msgstr "mostra l'indicador de progrés durant la fase d'escriptura d'objectes"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "similar a --all-progress quan l'indicador de progrés es mostra"
+
 msgid "<version>[,<offset>]"
 msgstr "<versió>[,<desplaçament>]"
 
@@ -9393,9 +9450,6 @@
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin no es pot utilitzar per a construir un paquet indexable"
 
-msgid "cannot use --filter without --stdout"
-msgstr "no es pot utilitzar --filter sense --stdout"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "no es pot utilitzar --filter sense --stdin-packs"
 
@@ -9408,19 +9462,16 @@
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "no es pot --stdin-packs amb --cruft"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "no es pot usar --max-pack-size amb --cruft"
-
 msgid "Enumerating objects"
 msgstr "S'estan enumerant els objectes"
 
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Total %<PRIu32> (%<PRIu32> diferències), reusats %<PRIu32> (%<PRIu32> "
-"diferències), paquets reusats %<PRIu32>"
+"diferències), paquets reusats %<PRIu32> (de %<PRIuMAX>)"
 
 msgid ""
 "'git pack-redundant' is nominated for removal.\n"
@@ -9435,8 +9486,14 @@
 "i feu-nos saber que encara l'useu enviant un correu electrònic\n"
 "a <git@vger.kernel.org>.  Gràcies.\n"
 
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "es rebutja a executar sense --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <patró>] [--exclude <patró>]"
 
 msgid "pack everything"
 msgstr "empaqueta-ho tot"
@@ -9444,6 +9501,12 @@
 msgid "prune loose refs (default)"
 msgstr "poda les referències soltes (per defecte)"
 
+msgid "references to include"
+msgstr "referències a incloure"
+
+msgid "references to exclude"
+msgstr "referències a excloure"
+
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
 
@@ -9502,6 +9565,12 @@
 msgid "number of submodules pulled in parallel"
 msgstr "nombre de submòduls baixats en paral·lel"
 
+msgid "use IPv4 addresses only"
+msgstr "usa només adreces IPv4"
+
+msgid "use IPv6 addresses only"
+msgstr "usa només adreces IPv6"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9609,8 +9678,8 @@
 msgid "pull with rebase"
 msgstr "baixar fent «rebase»"
 
-msgid "please commit or stash them."
-msgstr "cometeu-los o emmagatzemeu-los."
+msgid "Please commit or stash them."
+msgstr "Cometeu-los o emmagatzemeu-los."
 
 #, c-format
 msgid ""
@@ -9769,43 +9838,39 @@
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè el punt de la vostra branca\n"
-"actual està darrere de la seva branca remota corresponent. Integreu\n"
-"els canvis remots (per exemple, «git pull ...») abans de pujar de nou.\n"
-"Vegeu la «Nota sobre avanços ràpids» a «git push --help» per a més "
-"informació."
+"S'han rebutjat les actualitzacions perquè la punta de la branca actual\n"
+"està darrere de la seva contrapart remota. Si voleu integrar els canvis,\n"
+"remots useu «git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè un punt de branca pujada\n"
-"està darrere de la seva branca remota corresponent. Agafeu aquesta\n"
-"branca i integreu els canvis remots (per exemple, «git pull ...»)\n"
-"abans de pujar de nou.\n"
-"Vegeu la «Nota sobre avanços ràpids» a «git push --help» per a més "
-"informació."
+"Les actualitzacions s'han rebutjat perquè la branca pujada està darrere\n"
+"de la seva contrapart remota. Si voleu integrar els canvis remots,\n"
+"utilitzeu «git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè el remot conté canvis\n"
-"que no teniu localment. Això acostuma a ser causat per un altre repositori\n"
-"que ha pujat a la mateixa referència. Pot ser que primer vulgueu\n"
-"integrar els canvis remots (per exemple, «git pull ...») abans de\n"
-"pujar de nou.\n"
-"Vegeu la «Nota sobre avanços ràpids» a «git push --help» per a més "
-"informació."
+"Les actualitzacions s'han rebutjat perquè el remot conté canvis que no "
+"teniu\n"
+"localment. Això sol ser causat per un altre repositori que a pujat a\n"
+"la mateixa referència. Si voleu integrar els canvis remots, utilitzeu\n"
+"«git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
@@ -9822,15 +9887,15 @@
 "«--force».\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"S'han rebutjat les actualitzacions perquè el punt actual de la vostra\n"
-"branca està darrere de la seva branca remota corresponent. Integreu\n"
-"els canvis localment (per exemple, «git pull ...») abans de forçar\n"
-"una pujada.\n"
+"Les actualitzacions s'han rebutjat perquè la branca de seguiment remot\n"
+"s'ha actualitzat des de l'última baixada. Si voleu integrar els\n"
+"canvis remots, utilitzeu «git pull» abans de tornar a pujar.\n"
+"Vegeu «Note about fast-forwards» a «git push --help» per a més detalls."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9854,7 +9919,7 @@
 msgid "repository"
 msgstr "repositori"
 
-msgid "push all refs"
+msgid "push all branches"
 msgstr "puja totes les referències"
 
 msgid "mirror all refs"
@@ -9863,8 +9928,8 @@
 msgid "delete refs"
 msgstr "suprimeix les referències"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "puja les etiquetes (no es pot usar amb --all o --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "puja les etiquetes (no es pot usar amb --all, --branches o --mirror)"
 
 msgid "force updates"
 msgstr "força les actualitzacions"
@@ -10130,6 +10195,10 @@
 "Com a resultat, git no pot fer un «rebase» d'elles."
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Mode de fusió de rebase desconegut: %s"
+
+#, c-format
 msgid "could not switch to %s"
 msgstr "no s'ha pogut commutar a %s"
 
@@ -10143,6 +10212,15 @@
 msgstr ""
 "tipus buit no reconegut «%s»; els valors vàlids són «drop», «keep» i «ask»."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges amb un argument de cadena buit està obsolet i deixarà de "
+"funcionar en una versió futura del Git. Utilitzeu --rebase-merges sense un "
+"argument, que fa el mateix."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10357,19 +10435,12 @@
 msgid "switch `C' expects a numerical value"
 msgstr "«switch» «c» espera un valor numèric"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Mode desconegut: %s"
-
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy requereix --merge o --interactive"
-
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"les opcions «apply» són incompatibles amb rebase.autosquash. Considereu "
-"afegir-hi --no-autosquash"
+"les opcions «apply» són incompatibles amb rebase.rebaseMerges. Considereu "
+"afegir-hi --no-rebase-merges"
 
 msgid ""
 "apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
@@ -10415,9 +10486,6 @@
 msgid "Does not point to a valid commit '%s'"
 msgstr "No apunta a una comissió vàlida «%s»"
 
-msgid "Please commit or stash them."
-msgstr "Cometeu-los o emmagatzemeu-los."
-
 msgid "HEAD is up to date."
 msgstr "HEAD està al dia."
 
@@ -10677,11 +10745,12 @@
 msgid "fetch the remote branches"
 msgstr "obtén les branques remotes"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "en obtenir, importa totes les etiquetes i tots els objectes"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "o no obtinguis cap etiqueta (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"importa totes les etiquetes i objectes associats en obtenir\n"
+"o no obtingueu cap etiqueta (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "branques a seguir"
@@ -11077,6 +11146,10 @@
 msgid "could not remove stale bitmap: %s"
 msgstr "no s'ha pogut eliminar el mapa de bits estancat: %s"
 
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "el prefix de paquet %s no comença amb objdir %s"
+
 msgid "pack everything in a single pack"
 msgstr "empaqueta-ho tot en un únic paquet"
 
@@ -11091,8 +11164,8 @@
 msgid "approxidate"
 msgstr "data aproximada"
 
-msgid "with -C, expire objects older than this"
-msgstr "amb -C, venç els objectes més antics que aquest"
+msgid "with --cruft, expire objects older than this"
+msgstr "amb --cruft, vencen els objectes més antics que aquest"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "elimina els paquets redundants, i executeu git-prune-packed"
@@ -11157,17 +11230,22 @@
 msgstr ""
 "prefix del paquet per a emmagatzemar un paquet que contingui objectes podats"
 
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr ""
+"prefix del paquet per a emmagatzemar un paquet que contingui objectes "
+"filtrats"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "no es poden suprimir paquets en un repositori d'objectes preciosos"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "l'opció «%s» només es pot utilitzar juntament amb «%s»"
+
 msgid "Nothing new to pack."
 msgstr "Res nou a empaquetar."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "el prefix de paquet %s no comença amb objdir %s"
-
-#, c-format
 msgid "renaming pack to '%s' failed"
 msgstr "el canvi del nom a «%s» ha fallat"
 
@@ -11368,6 +11446,77 @@
 msgid "only one pattern can be given with -l"
 msgstr "només es pot especificar un patró amb -l"
 
+msgid "need some commits to replay"
+msgstr "calen algunes comissions per tornar a reproduir"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto i --advance són incompatibles"
+
+msgid "all positive revisions given must be references"
+msgstr "totes les revisions positives que s'han donat han de ser referències"
+
+msgid "argument to --advance must be a reference"
+msgstr "l'argument per a --advance ha de ser una referència"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"no es pot avançar l'objectiu amb múltiples fonts perquè l'ordenació no "
+"estaria definida correctament"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"no es pot determinar implícitament si aquesta és una operació --advance o --"
+"onto"
+
+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"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "no es pot determinar implícitament la base correcta per a --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+
+msgid "make replay advance given branch"
+msgstr "fes avançar la repetició de la branca donada"
+
+msgid "replay onto given commit"
+msgstr "torna a reproduir a la comissió donada"
+
+msgid "advance all branches contained in revision-range"
+msgstr "avança totes les branques contingudes a l'interval de revisions"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "l'opció --onto o --advance és obligatòria"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"algunes opcions de referència se sobreescriuran de forma forçada com a «%s» "
+"bits a «struct rev_info»"
+
+msgid "error preparing revisions"
+msgstr "s'ha produït un error en preparar les revisions"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "encara no s'admet la reproducció cap avall en una comissió arrel"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "encara no s'admet la repetició de les comissió de fusió"
+
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
@@ -11576,18 +11725,12 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "mode desconegut per a --abbrev-ref: %s"
 
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden no es pot utilitzar juntament amb --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden no es pot utilitzar juntament amb --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden no es pot utilitzar juntament amb --remotes"
-
 msgid "this operation must be run in a work tree"
 msgstr "aquesta operació s'ha d'executar en un arbre de treball"
 
+msgid "Could not read the index"
+msgstr "No s'ha pogut llegir l'índex"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "mode desconegut per a --show-object-format: %s"
@@ -11764,6 +11907,9 @@
 msgid "remote name"
 msgstr "nom del remot"
 
+msgid "push all refs"
+msgstr "puja totes les referències"
+
 msgid "use stateless RPC protocol"
 msgstr "usa el protocol RPC sense estat"
 
@@ -11930,23 +12076,44 @@
 msgstr "Algorisme de resum desconegut"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
-"             [--heads] [--] [<patró>...]"
+"             [--heads] [--] [<pattern>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<patró>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <ref>"
+
+msgid "reference does not exist"
+msgstr "la referència no existeix"
+
+msgid "failed to look up reference"
+msgstr "s'ha produït en cercar la referència"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "mostra només les etiquetes (es pot combinar amb heads)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "mostra només els caps (es pot combinar amb tags)"
 
+msgid "check for reference existence without resolving"
+msgstr "comprova l'existència de referència sense resoldre"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr ""
 "comprovació de referència més estricta, requereix el camí de referència "
@@ -11968,9 +12135,11 @@
 msgstr "mostra les referències de stdin que no siguin en el repositori local"
 
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<opcions>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<opcions>]"
 
 msgid "this worktree is not sparse"
 msgstr "aquest arbre de treball no és dispers"
@@ -12094,6 +12263,24 @@
 msgid "error while refreshing working directory"
 msgstr "s'ha produït un error en actualitzar el directori de treball"
 
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <fitxer>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "acaba els fitxers d'entrada i de sortida amb un caràcter NUL"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"quan s'utilitza amb --rules-file, interpreta els patrons com a patrons del "
+"mode con"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "utilitza patrons en <file> en lloc dels actuals."
+
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<log-options>]"
 
@@ -12620,6 +12807,10 @@
 msgstr "S'està ometent el submòdul «%s»"
 
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "no es pot clonar el submòdul «%s» sense un URL"
+
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "S'ha produït un error en clonar «%s». S'ha programat un reintent"
 
@@ -12752,6 +12943,9 @@
 "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
 "[--] [<camí>...]"
 
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "S'ha produït un error en resoldre HEAD com a referència vàlida."
+
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<opcions>] [<camí>...]"
 
@@ -13226,6 +13420,9 @@
 msgid "write index in this format"
 msgstr "escriu l'índex en aquest format"
 
+msgid "report on-disk index format version"
+msgstr "informa sobre la versió del format de l'índex del disc"
+
 msgid "enable or disable split index"
 msgstr "habilita o inhabilita l'índex dividit"
 
@@ -13250,6 +13447,14 @@
 msgid "clear fsmonitor valid bit"
 msgstr "esborra el bit de validesa del fsmonitor"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: era %d, s'ha establert a %d"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13373,10 +13578,10 @@
 
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
-"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <camí> [<commit-ish>]"
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <cadena>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <camí> [<commit-ish>]"
 
 msgid "git worktree list [-v | --porcelain [-z]]"
 msgstr "git worktree list [-v | --porcelain [-z]]"
@@ -13399,6 +13604,37 @@
 msgid "git worktree unlock <worktree>"
 msgstr "git worktree unlock <worktree>"
 
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "No hi ha cap branca d'origen possible, inferint «--orphan»"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Si voleu crear un arbre de treball que contingui una branca no nascuda\n"
+"nova (branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"utilitzant l'argument --orphan:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Si voleu crear un arbre de treball que contingui una branca no nascuda\n"
+"nova (branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"utilitzant l'argument --orphan:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "S'està eliminant %s/%s: %s"
@@ -13457,6 +13693,10 @@
 msgstr "s'està inicialitzant"
 
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "no s'ha pogut trobar l'arbre de treball creat «%s»"
+
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "S'està preparant l'arbre de treball (branca nova «%s»)"
 
@@ -13471,9 +13711,30 @@
 msgstr "S'està preparant l'arbre de treball (s'està agafant «%s»)"
 
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "no accessible: referència no vàlida: %s"
+
+#, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "S'està preparant l'arbre de treball (HEAD %s separat)"
 
+#, 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 ""
+"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 ""
+"No hi ha referències locals o remotes malgrat hi existeix almenys un\n"
+"remot, aturada; useu «add -f» per a anul·lar o obtenir primer un remot"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "agafa <branca> encara que sigui agafada en altre arbre de treball"
 
@@ -13483,6 +13744,9 @@
 msgid "create or reset a branch"
 msgstr "crea o restableix una branca"
 
+msgid "create unborn branch"
+msgstr "crea una branca no nascuda"
+
 msgid "populate the new working tree"
 msgstr "emplena l'arbre de treball nou"
 
@@ -13504,6 +13768,10 @@
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "les opcions «%s», «%s», i «%s» no es poden usar juntes"
 
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "opció «%s» i les de comissió no es poden usar juntes"
+
 msgid "added with --lock"
 msgstr "afegit amb --lock"
 
@@ -13735,6 +14003,14 @@
 msgstr[0] "El farcell requereix aquesta referència:"
 msgstr[1] "El farcell requereix aquestes %<PRIuMAX> referències:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "El farcell utilitza aquest algoritme de resum: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "El farcell utilitza aquest filtre: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "no s'ha pogut duplicar el descriptor del farcell"
 
@@ -13771,6 +14047,10 @@
 "l'identificador de fragment de finalització apareix abans del que s'esperava"
 
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "ID del fragment %<PRIx32> no alineat %d-byte"
+
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "desplaçament incorrecte del fragment %<PRIx64> i %<PRIx64>"
 
@@ -13824,10 +14104,8 @@
 msgid "Move objects and refs by archive"
 msgstr "Mou els objectes i les referències per arxiu"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Proveeix contingut o informació del tipus i mida per als objectes del "
-"repositori"
+msgid "Provide contents or details of repository objects"
+msgstr "Proporcioneu el contingut o els detalls dels objectes del repositori"
 
 msgid "Display gitattributes information"
 msgstr "Mostra la informació de .gitattributes"
@@ -13968,8 +14246,9 @@
 msgid "A portable graphical interface to Git"
 msgstr "Una interfície gràfica portable per al Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Calcula l'ID de l'objecte i opcionalment crea un blob des del fitxer"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr ""
+"Calcula l'ID de l'objecte i opcionalment crea un objecte des del fitxer"
 
 msgid "Display help information about Git"
 msgstr "Mostra informació d'ajuda del Git"
@@ -14054,7 +14333,7 @@
 msgstr "Construeix un objecte en arbre a partir de text formatat amb ls-tree"
 
 msgid "Write and verify multi-pack-indexes"
-msgstr "Escriu i verifica els índexs dels paquets multipaquet"
+msgstr "Escriu i verifica els índexs multipaquet"
 
 msgid "Move or rename a file, a directory, or a symlink"
 msgstr "Mou o canvia de nom a un fitxer, directori o enllaç simbòlic"
@@ -14121,6 +14400,11 @@
 msgid "Create, list, delete refs to replace objects"
 msgstr "Crea, llista i esborra referències per a substituir objectes"
 
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPERIMENTAL: torna a reproduir comissions sobre una nova base, també "
+"funciona amb repositoris nus"
+
 msgid "Generates a summary of pending changes"
 msgstr "Genera un resum dels canvis pendents"
 
@@ -14245,8 +14529,8 @@
 msgid "Display version information about Git"
 msgstr "Mostra informació de la versió del Git"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Mostra registres amb la diferència introduïda per cada comissió"
+msgid "Show logs with differences each commit introduces"
+msgstr "Mostra els registres amb les diferències que introdueix cada comissió"
 
 msgid "Manage multiple working trees"
 msgstr "Gestiona múltiples arbres de treball"
@@ -14362,6 +14646,35 @@
 msgid "commit-graph file is too small"
 msgstr "el fitxer del graf de comissions és massa petit"
 
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr ""
+"el fragment de «fanout» de l'oid del graf de comissions és de mida incorrecta"
+
+msgid "commit-graph fanout values out of order"
+msgstr "valors de graf de comissions de «fanout» estan fora d'ordre"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "el fragment de cerca OID és de mida incorrecta"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "el fragment de dades del graf de comissions és de mida incorrecta"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr ""
+"el fragment de les generacions del graf de comissions és de mida incorrecta"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr ""
+"el fragment d'índex del canvi del camí del graf de comissions és massa petit"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"s'ignorarà un fragment massa petit de camí canviat (%<PRIuMAX> < %<PRIuMAX>) "
+"al fitxer del graf de comissions"
+
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr ""
@@ -14381,13 +14694,37 @@
 msgstr ""
 "el fitxer del graf de comissions és massa petit per a guardar %u fragments"
 
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment del «fanout» OID requerit al graf de "
+"comissions"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment de cerca d'OID requerit al graf de comissions"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"manca o està corromput el fragment de dades de publicació requerit al graf "
+"de comissions"
+
 msgid "commit-graph has no base graphs chunk"
 msgstr "el fragment del graf de comissions no té grafs de base"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "el fragment de grafs base de la gràfica de comissió és massa petit"
+
 msgid "commit-graph chain does not match"
 msgstr "la cadena del graf de comissions no coincideix"
 
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "el nombre de comissions en el graf base és massa alt: %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "el fitxer de cadena del graf de comissions és massa petit"
+
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr ""
 "la cadena del graf de comissions no és vàlida: la línia «%s» no és un hash"
@@ -14409,6 +14746,14 @@
 "el graf de comissions requereix dades de generació de desbordaments però no "
 "en té cap"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr ""
+"les dades de generació de desbordament del graf de comissions són massa "
+"petites"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "punter de vores extra del graf de comissió està fora dels límits"
+
 msgid "Loading known commits in commit graph"
 msgstr "S'estan carregant comissions conegudes al graf de comissions"
 
@@ -14482,6 +14827,14 @@
 msgstr ""
 "no s'ha pogut canviar el nom del fitxer temporal del graf de comissions"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "no es poden fusionar els gràfics amb %<PRIuMAX>, %<PRIuMAX>entregues"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "no es pot fusionar el graf %s, hi ha massa comissions: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "S'estan escanejant les comissions fusionades"
 
@@ -14516,9 +14869,6 @@
 msgstr ""
 "s'ha produït un error en analitzar la comissió %s del graf de comissions"
 
-msgid "Verifying commits in commit graph"
-msgstr "S'estan verificant les comissions al graf de comissions"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14545,20 +14895,6 @@
 msgstr "la llista pare del graf de comissions per %s acaba aviat"
 
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"el graf de comissions té nombre de generació zero per a la comissió %s, però "
-"té no zero en altres llocs"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"el graf de comissions té un nombre de generació diferent de zero per a "
-"comissió %s però té zero en altres llocs"
-
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
 "generació del graf de comissions per a la comissió %s és %<PRIuMAX> < "
@@ -14571,6 +14907,17 @@
 "%<PRIuMAX> != %<PRIuMAX>"
 
 #, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"El graf de comissió té tant generacions zero com no nul·les (p. ex., "
+"comissions «%s» i «%s»)"
+
+msgid "Verifying commits in commit graph"
+msgstr "S'estan verificant les comissions al graf de comissions"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s no és una comissió!"
 
@@ -14594,6 +14941,12 @@
 "«git config advice.graftFileDeprecated false»"
 
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"la comissió %s existeix al graf de comissions però no a la base de dades "
+"d'objectes"
+
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "La comissió %s té una signatura GPG no fiable, suposadament de %s."
 
@@ -14748,7 +15101,7 @@
 msgstr "Referència anterior no vàlida"
 
 msgid "Unmatched [ or [^"
-msgstr "[ or [^ no emparellat"
+msgstr "[ o [^ no emparellat"
 
 msgid "Unmatched ( or \\("
 msgstr "( o \\( no emparellat"
@@ -14775,7 +15128,7 @@
 msgstr "Expressió regular és massa gran"
 
 msgid "Unmatched ) or \\)"
-msgstr ") o \\) no no emparellat"
+msgstr ") o \\) no emparellat"
 
 msgid "No previous regular expression"
 msgstr "No hi ha expressió regular anterior"
@@ -14994,8 +15347,8 @@
 msgid "bad zlib compression level %d"
 msgstr "nivell de compressió de zlib incorrecte %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar només hauria de ser un caràcter"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar només hauria de ser un caràcter ASCII"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -15033,10 +15386,6 @@
 msgid "unable to resolve config blob '%s'"
 msgstr "no s'ha pogut resoldre el blob de configuració: «%s»"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "s'ha produït un error en analitzar %s"
-
 msgid "unable to parse command-line config"
 msgstr "no s'ha pogut analitzar la configuració de la línia d'ordres"
 
@@ -15108,6 +15457,11 @@
 msgstr "nom de secció no vàlida: %s"
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"es rebutja treballar amb una línia massa llarga a «%s» a la línia %<PRIuMAX>"
+
+#, c-format
 msgid "missing value for '%s'"
 msgstr "falta el valor per «%s»"
 
@@ -15509,9 +15863,6 @@
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base no funciona amb intervals"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base només funciona amb comissions"
-
 msgid "unable to get HEAD"
 msgstr "no s'ha pogut obtenir HEAD"
 
@@ -15521,6 +15872,12 @@
 msgid "multiple merge bases found"
 msgstr "s'han trobat múltiples bases de fusió"
 
+msgid "cannot compare stdin to a directory"
+msgstr "no es pot comparar stdin amb un directori"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "no es pot comparar una canonada amb nom amb un directori"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<opcions>] <camí> <camí>"
 
@@ -15570,6 +15927,10 @@
 "Valor desconegut de la variable de configuració de «diff.submodule»: «%s»"
 
 #, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "valor desconegut per al config «%s»': %s"
+
+#, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
@@ -15581,6 +15942,13 @@
 msgid "external diff died, stopping at %s"
 msgstr "el diff external s'ha mort, s'està aturant a %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow requereix exactament una especificació de camí"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "el «pathspec» màgic no està suportat per --follow: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr "les opcions «%s», «%s», «%s», i «%s» no es poden usar juntes"
@@ -15595,9 +15963,6 @@
 msgstr ""
 "les opcions «%s» i «%s» no es poden usar juntes, useu «%s» amb «%s» i «%s»"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow requereix exactament una especificació de camí"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "valor --stat no vàlid: %s"
@@ -15642,12 +16007,6 @@
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "mode «%s» no vàlid en --color-moved-ws"
 
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"l'opció diff-algorithm accepta «myers», «minimal», «patience» i «histogram»"
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "argument no vàlid a %s"
@@ -15692,8 +16051,8 @@
 msgid "output only the last line of --stat"
 msgstr "mostra només l'última línia de --stat"
 
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
 
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
@@ -15704,8 +16063,8 @@
 msgid "synonym for --dirstat=cumulative"
 msgstr "sinònim de --dirstat=cumulative"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "sinònim de --dirstat=files,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "sinònim de --dirstat=files,<param1>,<param2>..."
 
 msgid "warn if changes introduce conflict markers or whitespace errors"
 msgstr ""
@@ -15790,6 +16149,9 @@
 msgid "do not show any source or destination prefix"
 msgstr "no mostris cap prefix d'origen o destí"
 
+msgid "use default prefixes a/ and b/"
+msgstr "utilitza els prefixos per defecte a/ i b/"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "mostra el context entre trossos de diferència fins al nombre especificat de "
@@ -15885,12 +16247,6 @@
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "genera diff usant l'algorisme «histogram diff»"
 
-msgid "<algorithm>"
-msgstr "<algorisme>"
-
-msgid "choose a diff algorithm"
-msgstr "trieu un algorisme per al diff"
-
 msgid "<text>"
 msgstr "<text>"
 
@@ -16101,6 +16457,14 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "consell: s'està esperant que el vostre editor tanqui el fitxer...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "no s'ha pogut escriure a «%s»"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "no s'ha pogut editar «%s»"
+
 msgid "Filtering content"
 msgstr "S'està filtrant el contingut"
 
@@ -16399,6 +16763,10 @@
 msgstr "no s'ha indicat cap clau de configuració per a --config-env\n"
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "no s'ha donat d'atribut font per a --attr-source\n"
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "opció desconeguda: %s\n"
 
@@ -16866,7 +17234,7 @@
 msgstr ""
 "No s'ha pogut crear «%s.lock»: %s.\n"
 "\n"
-"Sembla que un altre procés de git s'està executant en aquest\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"
@@ -16929,12 +17297,12 @@
 "solucions possibles:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "S'ha produït un error en executar la fusió interna"
+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"
+msgid "unable to add %s to database"
+msgstr "no s'ha pogut afegir %s a la base de dades"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17383,7 +17751,21 @@
 msgstr "s'ha produït un error en llegir la memòria cau"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "l'OID de l'índex multipaquet és d'una mida incorrecta"
+msgstr "l'OID «fanout» de l'índex multipaquet és d'una mida incorrecta"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"oid «fanout» desordenat: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "El fragment de cerca OID índex multipaquet és de mida incorrecta"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"el fragment de desplaçament de l'objecte índex multipaquet és d'una mida "
+"incorrecta"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17404,17 +17786,28 @@
 msgstr ""
 "la versió del resum índex multipaquet %u no coincideix amb la versió %u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "falta un fragment de nom de paquet necessari al multi-index"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment del nom de paquet requerit de l'índex "
+"multipaquet"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "falta un fragment «fanout» OID necessari al multi-pack-index"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment del «fanout» OID requerit a l'índex "
+"multipaquet"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "falta un fragment de cerca «fanout» OID necessari al multi-pack-index"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment de cerca d'OID necessari a l'índex "
+"multipaquet"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "falta un fragment necessari dels desplaçaments al multi-pack-index"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment de l'índex multipaquet dels objectes "
+"requerits"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "el fragment de nom de l'índex multipaquet és massa curt"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17426,11 +17819,21 @@
 msgid "bad pack-int-id: %u (%u total packs)"
 msgstr "pack-int-id: %u incorrecte (%u paquets en total)"
 
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX no conté el fragment BTMP"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "no s'ha pogut carregar el paquet amb bits %<PRIu32>"
+
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr ""
 "l'índex multipaquet emmagatzema un desplaçament de 64 bits, però off_t és "
 "massa petit"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr "desplaçament gran de l'índex multipaquet està fora dels límits"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "no s'ha pogut afegir el fitxer de paquet «%s»"
@@ -17511,11 +17914,6 @@
 msgid "Looking for referenced packfiles"
 msgstr "S'estan cercant fitxers empaquetats referenciats"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "oid fanout desordenat: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
 msgid "the midx contains no oid"
 msgstr "el midx no conté cap oid"
 
@@ -17768,7 +18166,7 @@
 msgstr "l'objecte ha fallat fsck: %s"
 
 msgid "refusing to create malformed object"
-msgstr "es nega a crear un objecte mal format"
+msgstr "es rebutja crear un objecte mal format"
 
 #, c-format
 msgid "read error while indexing %s"
@@ -17990,7 +18388,7 @@
 msgstr "el resum no coincideix %s"
 
 msgid "trying to write commit not in index"
-msgstr "s'està intentant no escriure la publicació a l'índex"
+msgstr "s'està intentant no escriure la comissió a l'índex"
 
 msgid "failed to load bitmap index (corrupted?)"
 msgstr ""
@@ -18047,6 +18445,9 @@
 msgid "could not open pack %s"
 msgstr "no s'ha pogut obrir el paquet %s"
 
+msgid "could not determine MIDX preferred pack"
+msgstr "no s'ha pogut determinar el paquet preferit MIDX"
+
 #, c-format
 msgid "preferred pack (%s) is invalid"
 msgstr "el paquet preferit (%s) no és vàlid"
@@ -18073,6 +18474,12 @@
 "comissió «%s»"
 
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"no s'ha pogut carregar el paquet: «%s», s'està inhabilitant lareutilització "
+"de paquets"
+
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "no s'ha trobat l'objecte «%s» als tipus de mapes de bits"
 
@@ -18110,6 +18517,10 @@
 msgstr "no s'ha pogut obtenir l'ús del disc de «%s»"
 
 #, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "el fitxer de mapa de bits «%s» té una suma de verificació no vàlida"
+
+#, c-format
 msgid "mtimes file %s is too small"
 msgstr "el fitxer mtimes %s és massa petit"
 
@@ -18149,6 +18560,21 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "el fitxer d'índex invers %s té un ID de resum %<PRIu32> no admès"
 
+msgid "invalid checksum"
+msgstr "suma de verificació no vàlida"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+"posició no vàlida de l'índex de reversió a %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr ""
+"el fragment de l'index invers de l'índex multipaquet és de mida incorrecta"
+
+msgid "could not determine preferred pack"
+msgstr "no s'ha pogut determinar el paquet preferit"
+
 msgid "cannot both write and verify reverse index"
 msgstr "no es pot escriure i verificar l'índex invers"
 
@@ -18202,14 +18628,6 @@
 msgstr "%s requereix un valor"
 
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s és incompatible amb %s"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: és incompatible amb alguna altra cosa"
-
-#, c-format
 msgid "%s takes no value"
 msgstr "%s no accepta cap valor"
 
@@ -18292,6 +18710,10 @@
 msgid "-NUM"
 msgstr "-NUM"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "oposat a --no-%s"
+
 msgid "expiry-date"
 msgstr "data-de-caducitat"
 
@@ -18323,6 +18745,14 @@
 "separats amb el caràcter NUL"
 
 #, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "el valor «%s» booleà de l'entorn és incorrecte per a «%s»"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "s'ha produït un error en analitzar %s"
+
+#, c-format
 msgid "Could not make %s writable by group"
 msgstr "No s'ha pogut fer %s escrivible pel grup"
 
@@ -18373,6 +18803,10 @@
 msgstr "%s: «literal» i «glob» són incompatibles"
 
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "«%s» és fora de l'arbre de directoris"
+
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: «%s» està fora del repositori en «%s»"
 
@@ -18501,6 +18935,13 @@
 msgstr "no s'ha pogut llegir el fitxer de registre per a «%s»"
 
 #, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "punta extra extra no vàlida: «%s»"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "no s'han pogut enumerar els objectes recents addicionals"
+
+#, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr "no s'afegirà l'àlies «%s»: («%s» ja existeix en l'índex)"
 
@@ -18521,10 +18962,6 @@
 msgstr "no s'ha pogut afegir «%s» a l'índex"
 
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "no s'ha pogut fer «stat» a «%s»"
-
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "«%s» apareix com a fitxer i com a directori"
 
@@ -18632,10 +19069,6 @@
 msgstr "s'ha produït un error en convertir a un índex dispers"
 
 #, c-format
-msgid "could not stat '%s'"
-msgstr "no s'ha pogut fer stat a «%s»"
-
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "no s'ha pogut obrir el directori git: %s"
 
@@ -18651,6 +19084,14 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: no es pot baixar fins al «stage» #0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "estat de diff inesperat %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "elimina «%s»\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18767,7 +19208,7 @@
 "\n"
 msgstr ""
 "\n"
-"No obstant, si elimineu tot, s'avortarà el «rebase».\n"
+"No obstant això, si elimineu tot, s'avortarà el «rebase».\n"
 "\n"
 
 #, c-format
@@ -18855,6 +19296,22 @@
 msgstr "valor positiu esperat conté:lines=%s"
 
 #, c-format
+msgid "argument expected for %s"
+msgstr "s'esperava un argument per a %s"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "valor positiu esperat %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "no es pot analitzar completament %s=%s"
+
+#, c-format
+msgid "value expected %s="
+msgstr "s'esperava un valor %s="
+
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "valor positiu esperat «%s» a %%(%s)"
 
@@ -18879,6 +19336,10 @@
 msgstr "amplada positiva esperada amb l'àtom %%(align)"
 
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "format esperat: %%(ahead-behind:<committish>)"
+
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "nom de camp mal format: %.*s"
 
@@ -18925,6 +19386,9 @@
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "no es pot usar --format=%.*s amb --python, --shell, --tcl"
 
+msgid "failed to run 'describe'"
+msgstr "no s'ha pogut executar «describe»"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(sense branca, s'està fent «rebase» %s)"
@@ -18986,6 +19450,9 @@
 msgid "field name to sort on"
 msgstr "nom del camp en el qual ordenar"
 
+msgid "exclude refs which match pattern"
+msgstr "exclou refs que coincideixin amb el patró"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "no és un registre de referència: %s"
@@ -19076,10 +19543,6 @@
 msgstr "no es poden processar «%s» i «%s» a la vegada"
 
 #, c-format
-msgid "could not remove reference %s"
-msgstr "no s'ha pogut eliminar la referència %s"
-
-#, c-format
 msgid "could not delete reference %s: %s"
 msgstr "no s'ha pogut suprimir la referència %s: %s"
 
@@ -19303,7 +19766,7 @@
 "'%s:refs/tags/%s'?"
 msgstr ""
 "La part <src> de l'especificació de la referència és un objecte d'etiqueta.\n"
-"Voleu crear una etiqueta pujant-la a «%srefs/tags/%s»?"
+"Voleu crear una etiqueta pujant-la a «%s:refs/tags/%s»?"
 
 #, c-format
 msgid ""
@@ -19312,7 +19775,7 @@
 "'%s:refs/tags/%s'?"
 msgstr ""
 "La part <src> de l'especificació de la referència és un objecte d'arbre.\n"
-"Voleu crear una etiqueta pujant-la a «%srefs/tags/%s»?"
+"Voleu crear una etiqueta pujant-la a «%s:refs/tags/%s»?"
 
 #, c-format
 msgid ""
@@ -19439,8 +19902,10 @@
 "La vostra branca i «%s» han divergit,\n"
 "i tenen %d i %d comissions distintes cada una, respectivament.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (useu «git pull» per a fusionar la branca remota amb la vostra)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (utilitzeu «git pull» si voleu integrar la branca remota amb la vostra)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19513,10 +19978,6 @@
 msgstr "no hi ha cap resolució recordada per a «%s»"
 
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "no es pot fer «unlink» de «%s»"
-
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Imatge prèvia actualitzada per a «%s»"
 
@@ -19556,6 +20017,10 @@
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<packfile> ja no s'admet"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "opció no vàlida: «%s» en mode --stdin"
+
 msgid "your current branch appears to be broken"
 msgstr "la vostra branca actual sembla malmesa"
 
@@ -19642,8 +20107,15 @@
 msgid "only download metadata for the branch that will be checked out"
 msgstr "només baixa les metadades per a la branca que s'agafarà"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<opcions>] [--] <repositori> [<dir>]"
+msgid "create repository within 'src' directory"
+msgstr "crea un repositori dins del directori «src»"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19694,12 +20166,28 @@
 msgstr "no s'ha pogut suprimir el scalar.repo «%s» estancat"
 
 #, c-format
-msgid "removing stale scalar.repo '%s'"
-msgstr "s'està eliminant el scalar.repo «%s» estancat"
+msgid "removed stale scalar.repo '%s'"
+msgstr "s'ha eliminat l'scalar.repo estancat «%s»"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "no existeix un repositori de git a: «%s»"
+msgid "repository at '%s' has different owner"
+msgstr "el dipòsit a «%s» té un propietari diferent"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "el dipòsit a «%s» té un problema de format"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "no s'ha trobat el dipòsit a «%s»"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"per a desregistrar aquest dipòsit de l'escalar, executeu\n"
+"\tgit config --global --unset --fixed-value scalar.repo «%s»"
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19855,10 +20343,6 @@
 msgstr "no s'ha pogut bloquejar «%s»"
 
 #, c-format
-msgid "could not write to '%s'"
-msgstr "no s'ha pogut escriure a «%s»"
-
-#, c-format
 msgid "could not write eol to '%s'"
 msgstr "no s'ha pogut escriure el terminador de línia a «%s»"
 
@@ -20111,10 +20595,6 @@
 msgstr "%s: no es pot analitzar la comissió pare %s"
 
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "no s'ha pogut canviar el nom «%s» a «%s»"
-
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "no s'ha pogut revertir %s... %s"
 
@@ -20221,9 +20701,6 @@
 msgid "could not create sequencer directory '%s'"
 msgstr "no s'ha pogut crear el directori de seqüenciador «%s»"
 
-msgid "could not lock HEAD"
-msgstr "no s'ha pogut bloquejar HEAD"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "ni hi ha cap «cherry pick» ni cap reversió en curs"
 
@@ -20318,13 +20795,13 @@
 " git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "i ha fet canvis a l'índex i/o l'arbre de treball\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "i ha fet canvis a l'índex i/o a l'arbre de treball.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
@@ -20334,7 +20811,8 @@
 "però ha deixat canvis a l'índex i/o l'arbre de treball\n"
 "Cometeu o feu «stash» dels vostres canvis, i llavors executeu\n"
 "\n"
-" git rebase --continue\n"
+"  git rebase --continue\n"
+"\n"
 
 #, c-format
 msgid "illegal label name: '%.*s'"
@@ -20441,6 +20919,9 @@
 msgstr ""
 "El «stash» automàtic ja existeix; s'està creant una entrada «stash» nova."
 
+msgid "autostash reference is a symref"
+msgstr "la referència d'autostash és un symref"
+
 msgid "could not detach HEAD"
 msgstr "no s'ha pogut separar HEAD"
 
@@ -20473,14 +20954,14 @@
 "    git rebase --continue\n"
 
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "S'està fent «rebase» (%d/%d)%s"
-
-#, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Aturat a %s...  %.*s\n"
 
 #, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "S'està fent «rebase» (%d/%d)%s"
+
+#, c-format
 msgid "unknown command %d"
 msgstr "ordre %d desconeguda"
 
@@ -20684,7 +21165,8 @@
 "not a git repository (or any parent up to mount point %s)\n"
 "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
 msgstr ""
-"no és un repositori de git (ni cap pare fins al punt de muntatge %s)\n"
+"no és un repositori de git (ni existeix cap pare fins al punt de muntatge "
+"%s)\n"
 "S'atura a la frontera de sistema de fitxers (GIT_DISCOVERY_ACROSS_FILESYSTEM "
 "no està establert)."
 
@@ -20721,6 +21203,83 @@
 msgstr "«setsid» ha fallat"
 
 #, c-format
+msgid "cannot stat template '%s'"
+msgstr "no es pot fer stat en la plantilla «%s»"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "no es pot fer opendir en el directori «%s»"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "no es pot fer readlink en «%s»"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "no es pot fer symlink en «%s» «%s»"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "no es pot copiar «%s» a «%s»"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "s'està ignorant la plantilla %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "plantilles no trobades a %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "no s'estan copiant plantilles de «%s»: %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "nom de branca inicial no vàlid: «%s»"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "reinicialització: s'ha ignorat --initial-branch=%s"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "no s'ha pogut gestionar el tipus de fitxer %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "no s'ha pogut moure %s a %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "s'ha intentat reinicialitzar el repositori amb un resum diferent"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"s'ha intentat reactivar el repositori amb un format d'emmagatzematge de "
+"referència diferent"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s ja existeix"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "S'ha reinicialitzat el repositori compartit existent del Git en %s%s\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "S'ha reinicialitzat el repositori existent del Git en %s%s\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "S'ha inicialitzat un repositori compartit buit del Git en %s%s\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "S'ha inicialitzat un repositori buit del Git en %s%s\n"
+
+#, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
 msgstr "l'entrada d'índex és un directori, però no dispers (%08x)"
 
@@ -20772,10 +21331,6 @@
 msgstr[1] "%u bytes/s"
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr "no s'ha pogut editar «%s»"
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "s'està ignorant el nom de submòdul sospitós %s"
 
@@ -20977,12 +21532,6 @@
 msgstr ""
 "nombre d'entrades a l'arbre de la memòria cau a invalidar (per defecte 0)"
 
-msgid "unhandled options"
-msgstr "opcions no gestionades"
-
-msgid "error preparing revisions"
-msgstr "s'ha produït un error en preparar les revisions"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "la comissió %s no està marcada com abastable"
@@ -21135,9 +21684,6 @@
 msgid "invalid remote service path"
 msgstr "el camí del servei remot no és vàlid"
 
-msgid "operation not supported by protocol"
-msgstr "opció no admesa pel protocol"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "no es pot connectar al subservei %s"
@@ -21270,10 +21816,6 @@
 "encara no s'ha implementat la compatibilitat amb la versió v2 del protocol"
 
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "valor desconegut per al config «%s»': %s"
-
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "no es permet el transport «%s»"
 
@@ -21326,6 +21868,9 @@
 msgstr ""
 "no s'ha pogut recuperar la llista de paquets d'URI anunciats pel servidor"
 
+msgid "operation not supported by protocol"
+msgstr "opció no admesa pel protocol"
+
 msgid "too-short tree object"
 msgstr "objecte d'arbre massa curt"
 
@@ -21723,6 +22268,9 @@
 msgid "unable to get current working directory"
 msgstr "no s'ha pogut obtenir el directori de treball actual"
 
+msgid "unable to get random bytes"
+msgstr "no s'han pogut obtenir bytes aleatoris"
+
 msgid "Unmerged paths:"
 msgstr "Camins sense fusionar:"
 
@@ -22164,6 +22712,10 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "no es pot %s: El vostre índex conté canvis sense cometre."
 
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "estil desconegut «%s» donat per a «%s»"
+
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
 "merge"
@@ -22350,13 +22902,13 @@
 "Esborreu el contingut del cos si no voleu enviar cap resum.\n"
 
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "S'ha produït un error en obrir %s: %s"
-
-#, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "S'ha produït un error en obrir %s.final: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "S'ha produït un error en obrir %s: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "El correu electrònic de resum està buit, s'omet\n"
 
@@ -22512,13 +23064,17 @@
 msgstr "(%s) no s'ha pogut executar «%s»"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) S'està afegint %s: %s des de: «%s»\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Sortida mal formada de «%s»"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) s'ha produït un error en tancar el conducte «%s»"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) S'està afegint %s: %s des de: «%s»\n"
+
 msgid "cannot send message as 7bit"
 msgstr "no es pot enviar el missatge en 7 bits"
 
@@ -22555,367 +23111,3 @@
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Esteu segur que voleu enviar %s? [y|N]: "
-
-#~ msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-#~ msgstr "git bisect--helper --bisect-state (bad|new) [<rev>]"
-
-#~ msgid "won't bisect on cg-seek'ed tree"
-#~ msgstr "no es bisecarà en un arbre en el qual s'ha fet cg-seek"
-
-#~ msgid "--bisect-terms requires 0 or 1 argument"
-#~ msgstr "--bisect-terms requereix 0 o 1 argument"
-
-#~ msgid "--bisect-next requires 0 arguments"
-#~ msgstr "--bisect-next no requereix cap argument"
-
-#~ msgid "--bisect-log requires 0 arguments"
-#~ msgstr "--bisect-log no requereix cap argument"
-
-#~ msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-#~ msgstr "git env--helper --type=[bool|ulong] <opcions> <env-var>"
-
-#~ msgid "default for git_env_*(...) to fall back on"
-#~ msgstr "valor per defecte per a git_env_*(...) en cas d'absència"
-
-#~ msgid "be quiet only use git_env_*() value as exit code"
-#~ msgstr "silenciós només utilitza el valor git_env_*() com a codi de sortida"
-
-#, c-format
-#~ msgid ""
-#~ "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-#~ msgstr ""
-#~ "l'opció «--default» espera un valor booleà amb «--type=bool», no «%s»"
-
-#, c-format
-#~ msgid ""
-#~ "option `--default' expects an unsigned long value with `--type=ulong`, "
-#~ "not `%s`"
-#~ msgstr ""
-#~ "l'opció «--default» espera un valor llarg sense signe amb «--type=ulong», "
-#~ "no «%s»"
-
-#, c-format
-#~ msgid "%s doesn't support --super-prefix"
-#~ msgstr "%s no admet --super-prefix"
-
-#, c-format
-#~ msgid "no prefix given for --super-prefix\n"
-#~ msgstr "no s'ha especificat cap prefix per a --super-prefix\n"
-
-#, c-format
-#~ msgid "failed to read object %s"
-#~ msgstr "s'ha produït un error en llegir l'objecte %s"
-
-#~ msgid "file write error"
-#~ msgstr "s'ha produït un error en escriure al fitxer"
-
-#~ msgid "corrupt commit"
-#~ msgstr "comissió corrupta"
-
-#~ msgid "corrupt tag"
-#~ msgstr "etiqueta corrupta"
-
-#, c-format
-#~ msgid "%%(objecttype) does not take arguments"
-#~ msgstr "%%(objecttype) no accepta arguments"
-
-#, c-format
-#~ msgid "%%(deltabase) does not take arguments"
-#~ msgstr "%%(deltabase) no accepta arguments"
-
-#, c-format
-#~ msgid "%%(body) does not take arguments"
-#~ msgstr "%%(body) no accepta arguments"
-
-#, c-format
-#~ msgid "unrecognized email option: %s"
-#~ msgstr "opció del correu electrònic no reconeguda: «%s»"
-
-#, c-format
-#~ msgid ""
-#~ "It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-#~ "may speed it up, but you have to be careful not to forget to add\n"
-#~ "new files yourself (see 'git help status')."
-#~ msgstr ""
-#~ "S'ha trigat %.2f segons enumerar els fitxers no seguits.\n"
-#~ "«status -uno» pot accelerar-ho, però heu d'anar amb compte de no\n"
-#~ "oblidar-vos d'afegir fitxers nous vosaltres mateixos (vegeu\n"
-#~ "«git help status»)."
-
-#, perl-format
-#~ msgid "%12s %12s %s"
-#~ msgstr "%12s %12s %s"
-
-#, perl-format
-#~ msgid "touched %d path\n"
-#~ msgid_plural "touched %d paths\n"
-#~ msgstr[0] "modificat %d camí\n"
-#~ msgstr[1] "modificat %d camins\n"
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for staging."
-#~ msgstr ""
-#~ "Si el pedaç s'aplica correctament, el tros editat es marcarà "
-#~ "immediatament\n"
-#~ "per «staging»."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for stashing."
-#~ msgstr ""
-#~ "Si el pedaç s'aplica correctament, el tros editat es marcarà "
-#~ "immediatament\n"
-#~ "per «stashing»."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for unstaging."
-#~ msgstr ""
-#~ "Si el pedaç s'aplica correctament, el tros editat es marcarà "
-#~ "immediatament\n"
-#~ "per «unstaging»."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for applying."
-#~ msgstr ""
-#~ "Si el pedaç s'aplica correctament, el tros editat es marcarà "
-#~ "immediatament\n"
-#~ "per a aplicar-se."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for discarding."
-#~ msgstr ""
-#~ "Si el pedaç s'aplica correctament, el tros editat es marcarà "
-#~ "immediatament\n"
-#~ "per a descartar-se."
-
-#, perl-format
-#~ msgid "failed to open hunk edit file for writing: %s"
-#~ msgstr "s'ha produït un error en escriure al fitxer d'edició del tros: %s"
-
-#, perl-format
-#~ msgid ""
-#~ "---\n"
-#~ "To remove '%s' lines, make them ' ' lines (context).\n"
-#~ "To remove '%s' lines, delete them.\n"
-#~ "Lines starting with %s will be removed.\n"
-#~ msgstr ""
-#~ "---\n"
-#~ "Per a eliminar les línies «%s», convertiu-les en línies ' ' (context).\n"
-#~ "Per a eliminar les línies «%s», suprimiu-les.\n"
-#~ "Les línies que comencin per %s s'eliminaran.\n"
-
-#, perl-format
-#~ msgid "failed to open hunk edit file for reading: %s"
-#~ msgstr "s'ha produït un error en llegir al fitxer d'edició del tros: %s"
-
-#~ msgid ""
-#~ "y - stage this hunk\n"
-#~ "n - do not stage this hunk\n"
-#~ "q - quit; do not stage this hunk or any of the remaining ones\n"
-#~ "a - stage this hunk and all later hunks in the file\n"
-#~ "d - do not stage this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - fes «stage» d'aquest tros\n"
-#~ "n - no facis «stage» d'aquest tros\n"
-#~ "q - surt; no facis «stage» d'aquest tros o de cap altre restant\n"
-#~ "a - fes «stage» d'aquest tros i tota la resta de trossos del fitxer\n"
-#~ "d - no facis «stage» d'aquest tros o de cap altre restant del fitxer"
-
-#~ msgid ""
-#~ "y - stash this hunk\n"
-#~ "n - do not stash this hunk\n"
-#~ "q - quit; do not stash this hunk or any of the remaining ones\n"
-#~ "a - stash this hunk and all later hunks in the file\n"
-#~ "d - do not stash this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - fes «stash» d'aquest tros\n"
-#~ "n - no facis «stash» d'aquest tros\n"
-#~ "q - surt; no facis «stash» d'aquest tros o de cap altre restant\n"
-#~ "a - fes «stash» d'aquest tros i tota la resta de trossos del fitxer\n"
-#~ "d - no facis «stash» d'aquest tros o de cap altre restant del fitxer"
-
-#~ msgid ""
-#~ "y - unstage this hunk\n"
-#~ "n - do not unstage this hunk\n"
-#~ "q - quit; do not unstage this hunk or any of the remaining ones\n"
-#~ "a - unstage this hunk and all later hunks in the file\n"
-#~ "d - do not unstage this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - fes «unstage» d'aquest tros\n"
-#~ "n - no facis «unstage» d'aquest tros\n"
-#~ "q - surt; no facis «unstage» d'aquest tros o de cap altre restant\n"
-#~ "a - fes «unstage» d'aquest tros i tota la resta de trossos del fitxer\n"
-#~ "d - no facis «unstage» d'aquest tros o de cap altre restant del fitxer"
-
-#~ msgid ""
-#~ "y - apply this hunk to index\n"
-#~ "n - do not apply this hunk to index\n"
-#~ "q - quit; do not apply this hunk or any of the remaining ones\n"
-#~ "a - apply this hunk and all later hunks in the file\n"
-#~ "d - do not apply this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - aplica aquest tros a l'índex\n"
-#~ "n - no apliquis aquest tros a l'índex\n"
-#~ "q - surt; no apliquis aquest tros ni cap dels pendents\n"
-#~ "a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
-#~ "d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-#~ msgid ""
-#~ "y - discard this hunk from worktree\n"
-#~ "n - do not discard this hunk from worktree\n"
-#~ "q - quit; do not discard this hunk or any of the remaining ones\n"
-#~ "a - discard this hunk and all later hunks in the file\n"
-#~ "d - do not discard this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - descarta aquest tros de l'arbre de treball\n"
-#~ "n - no descartis aquest tros des de l'arbre de treball\n"
-#~ "q - surt; no descartis aquest tros ni cap dels pendents\n"
-#~ "a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
-#~ "d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-#~ msgid ""
-#~ "y - discard this hunk from index and worktree\n"
-#~ "n - do not discard this hunk from index and worktree\n"
-#~ "q - quit; do not discard this hunk or any of the remaining ones\n"
-#~ "a - discard this hunk and all later hunks in the file\n"
-#~ "d - do not discard this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - descarta aquest tros de l'índex i de l'arbre de treball\n"
-#~ "n - no descartis aquest tros des de l'índex i de l'arbre de treball\n"
-#~ "q - surt; no descartis aquest tros ni cap dels pendents\n"
-#~ "a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
-#~ "d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-#~ msgid ""
-#~ "y - apply this hunk to index and worktree\n"
-#~ "n - do not apply this hunk to index and worktree\n"
-#~ "q - quit; do not apply this hunk or any of the remaining ones\n"
-#~ "a - apply this hunk and all later hunks in the file\n"
-#~ "d - do not apply this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - aplica aquest tros a l'índex i l'arbre de treball\n"
-#~ "n - no apliquis aquest tros des de l'índex i de l'arbre de treball\n"
-#~ "q - surt; no apliquis aquest tros ni cap dels pendents\n"
-#~ "a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
-#~ "d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-#~ msgid ""
-#~ "y - apply this hunk to worktree\n"
-#~ "n - do not apply this hunk to worktree\n"
-#~ "q - quit; do not apply this hunk or any of the remaining ones\n"
-#~ "a - apply this hunk and all later hunks in the file\n"
-#~ "d - do not apply this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - aplica aquest tros a l'arbre de treball\n"
-#~ "n - no apliquis aquest tros a l'arbre de treball\n"
-#~ "q - surt; no apliquis aquest tros ni cap dels pendents\n"
-#~ "a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
-#~ "d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
-
-#~ msgid ""
-#~ "g - select a hunk to go to\n"
-#~ "/ - search for a hunk matching the given regex\n"
-#~ "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"
-#~ "s - split the current hunk into smaller hunks\n"
-#~ "e - manually edit the current hunk\n"
-#~ "? - print help\n"
-#~ msgstr ""
-#~ "g - selecciona el tros on voleu anar\n"
-#~ "/ - cerca un tros que coincideixi amb l'expressió regular donada\n"
-#~ "j - deixa aquest tros sense decidir, veure el tros sense decidir següent\n"
-#~ "J - deixa aquest tros sense decidir, veure el tros següent\n"
-#~ "k - deixa aquest tros sense decidir, veure el tros sense decidir "
-#~ "anterior\n"
-#~ "K - deixa aquest tros sense decidir, veure el tros anterior\n"
-#~ "s - divideix el tros actual en trossos més petits\n"
-#~ "e - edita manualment el tros actual\n"
-#~ "? - mostra l'ajuda\n"
-
-#~ msgid "The selected hunks do not apply to the index!\n"
-#~ msgstr "Els trossos seleccionats no apliquen a l'índex\n"
-
-#, perl-format
-#~ msgid "ignoring unmerged: %s\n"
-#~ msgstr "s'està ignorant %s no fusionat\n"
-
-#~ msgid "No other hunks to goto\n"
-#~ msgstr "No hi ha altres trossos on anar-hi\n"
-
-#, perl-format
-#~ msgid "Invalid number: '%s'\n"
-#~ msgstr "Número no vàlid: «%s»\n"
-
-#, perl-format
-#~ msgid "Sorry, only %d hunk available.\n"
-#~ msgid_plural "Sorry, only %d hunks available.\n"
-#~ msgstr[0] "Només %d tros disponible.\n"
-#~ msgstr[1] "Només %d trossos disponibles.\n"
-
-#~ msgid "No other hunks to search\n"
-#~ msgstr "No hi ha cap altre tros a cercar\n"
-
-#, perl-format
-#~ msgid "Malformed search regexp %s: %s\n"
-#~ msgstr "Expressió regular de cerca mal formada %s: %s\n"
-
-#~ msgid "No hunk matches the given pattern\n"
-#~ msgstr "No hi ha trossos que coincideixin amb el patró donat\n"
-
-#~ msgid "No previous hunk\n"
-#~ msgstr "Sense tros previ\n"
-
-#~ msgid "No next hunk\n"
-#~ msgstr "No hi ha tros següent\n"
-
-#~ msgid "Sorry, cannot split this hunk\n"
-#~ msgstr "No es pot dividir aquest tros\n"
-
-#, perl-format
-#~ msgid "Split into %d hunk.\n"
-#~ msgid_plural "Split into %d hunks.\n"
-#~ msgstr[0] "Divideix en %d tros.\n"
-#~ msgstr[1] "Divideix en %d trossos.\n"
-
-#~ msgid "Sorry, cannot edit this hunk\n"
-#~ msgstr "No es pot editar aquest tros\n"
-
-#~ msgid ""
-#~ "status        - show paths with changes\n"
-#~ "update        - add working tree state to the staged set of changes\n"
-#~ "revert        - revert staged set of changes back to the HEAD version\n"
-#~ "patch         - pick hunks and update selectively\n"
-#~ "diff          - view diff between HEAD and index\n"
-#~ "add untracked - add contents of untracked files to the staged set of "
-#~ "changes\n"
-#~ msgstr ""
-#~ "status        - mostra els camins amb canvis\n"
-#~ "update        - afegeix l'estat de l'arbre de treball al conjunt de "
-#~ "canvis «staged»\n"
-#~ "revert        - reverteix el conjunt de canvis de «staged» a la versió "
-#~ "HEAD\n"
-#~ "patch         - selecciona trossos i actualitza'ls selectivament\n"
-#~ "diff          - mostra la diferència entre HEAD i l'índex\n"
-#~ "add untracked - afegeix el contingut dels fitxers no seguits al conjunt "
-#~ "de canvis «staged»\n"
-
-#~ msgid "missing --"
-#~ msgstr "manca --"
-
-#, perl-format
-#~ msgid "unknown --patch mode: %s"
-#~ msgstr "desconegut --patch mode: %s"
-
-#, perl-format
-#~ msgid "invalid argument %s, expecting --"
-#~ msgstr "argument %s no vàlid, s'esperava --"
-
-#, c-format
-#~ msgid "unable to normalize object directory: %s"
-#~ msgstr "no s'ha pogut normalitzar el directori de l'objecte: %s"
diff --git a/po/de.po b/po/de.po
index 7559a8c..37d6c80 100644
--- a/po/de.po
+++ b/po/de.po
@@ -8,8 +8,8 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-03 17:13+0100\n"
-"PO-Revision-Date: 2023-03-03 13:46+0100\n"
+"POT-Creation-Date: 2024-02-17 18:07+0100\n"
+"PO-Revision-Date: 2024-02-17 18:14+0100\n"
 "Last-Translator: Ralf Thielow <ralf.thielow@gmail.com>\n"
 "Language-Team: German\n"
 "Language: de\n"
@@ -17,7 +17,7 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n!=1);\n"
-"X-Generator: Poedit 3.2.2\n"
+"X-Generator: Poedit 3.4.1\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -664,9 +664,9 @@
 msgstr ""
 "Reverten ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "%s ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr ""
+"Rebase ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -688,6 +688,24 @@
 msgid "Exiting because of unfinished merge."
 msgstr "Beende wegen nicht abgeschlossenem Merge."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Abweichende Branches können nicht vorgespult werden, benutzen Sie "
+"stattdessen:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"oder:\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Vorspulen nicht möglich, breche ab."
 
@@ -798,6 +816,12 @@
 msgid "'%s' outside a repository"
 msgstr "'%s' außerhalb eines Repositories"
 
+msgid "failed to read patch"
+msgstr "Patch konnte nicht gelesen werden"
+
+msgid "patch too large"
+msgstr "Patch zu groß"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Kann regulären Ausdruck für Zeitstempel %s nicht verarbeiten"
@@ -1144,6 +1168,10 @@
 msgstr "kann '%s' nicht öffnen"
 
 #, c-format
+msgid "cannot unlink '%s'"
+msgstr "Kann '%s' nicht löschen."
+
+#, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Patch-Bereich #%d sauber angewendet."
 
@@ -1341,6 +1369,11 @@
 msgstr "kann '%s' nicht lesen"
 
 #, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+"Pfadspezifikation '%s' findet Dateien außerhalb des aktuellen Verzeichnisses"
+
+#, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "Pfadspezifikation '%s' stimmt mit keinen Dateien überein"
 
@@ -1356,9 +1389,6 @@
 msgid "not a tree object: %s"
 msgstr "Kein Tree-Objekt: %s"
 
-msgid "current working directory is untracked"
-msgstr "aktuelles Arbeitsverzeichnis ist unversioniert"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Datei nicht gefunden: %s"
@@ -1444,6 +1474,10 @@
 msgstr "Unerwartete Option --output"
 
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "zusätzlicher Befehlszeilenparameter '%s'"
+
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "Unbekanntes Archivformat '%s'"
 
@@ -1485,6 +1519,17 @@
 msgid "ignoring overly large gitattributes blob '%s'"
 msgstr "ignoriere übermäßig großen gitattribute-Blob '%s'"
 
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "ungültiges --attr-source oder GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "konnte '%s' nicht lesen"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "kann %s nicht lesen"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Ungültiger Inhalt bzgl. Anführungszeichen in Datei '%s': %s"
@@ -1591,10 +1636,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents und --reverse funktionieren gemeinsam nicht."
 
-msgid "cannot use --contents with final commit object name"
-msgstr ""
-"kann --contents nicht mit endgültigem Namen des Commit-Objektes benutzen"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse und --first-parent zusammen erfordern die Angabe eines "
@@ -1669,12 +1710,10 @@
 msgid "not tracking: ambiguous information for ref '%s'"
 msgstr "kein Tracking: mehrdeutige Informationen für Referenz '%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
@@ -1717,9 +1756,10 @@
 msgstr "Branch '%s' existiert bereits"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"kann Aktualisierung des Branches '%s' nicht erzwingen, ausgecheckt in '%s'"
+"Aktualisierung des vom Arbeitsverzeichnis '%2$s' verwendete Branch '%1$s' "
+"kann nicht erzwungen werden"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1779,12 +1819,8 @@
 msgstr "Submodul '%s': kann Branch nicht erzeugen: '%s'"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' ist bereits in '%s' ausgecheckt"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD des Arbeitsverzeichnisses %s ist nicht aktualisiert"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' wird bereits von Arbeitsverzeichnis in '%s' verwendet"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<Optionen>] [--] <Pfadspezifikation>..."
@@ -1793,17 +1829,6 @@
 msgid "cannot chmod %cx '%s'"
 msgstr "kann chmod %cx '%s' nicht ausführen"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "unerwarteter Differenz-Status %c"
-
-msgid "updating files failed"
-msgstr "Aktualisierung der Dateien fehlgeschlagen"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "lösche '%s'\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr ""
 "Nicht zum Commit vorgemerkte Änderungen nach Aktualisierung der Staging-Area:"
@@ -1815,25 +1840,22 @@
 "Die Einstellung add.interactive.useBuiltin wurde entfernt!\n"
 "Siehe den Eintrag in 'git help config' für Details."
 
-msgid "Could not read the index"
-msgstr "Konnte den Index nicht lesen"
-
-msgid "Could not write patch"
-msgstr "Konnte Patch nicht schreiben"
+msgid "could not read the index"
+msgstr "konnte den Index nicht lesen"
 
 msgid "editing patch failed"
 msgstr "Bearbeitung des Patches fehlgeschlagen"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Konnte Verzeichnis '%s' nicht lesen"
+msgid "could not stat '%s'"
+msgstr "Konnte '%s' nicht lesen."
 
-msgid "Empty patch. Aborted."
-msgstr "Leerer Patch. Abgebrochen."
+msgid "empty patch. aborted"
+msgstr "leerer Patch. Abgebrochen"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Konnte '%s' nicht anwenden."
+msgid "could not apply '%s'"
+msgstr "konnte '%s' nicht anwenden"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
@@ -1972,6 +1994,9 @@
 msgid "index file corrupt"
 msgstr "Index-Datei beschädigt"
 
+msgid "unable to write new index file"
+msgstr "Konnte neue Index-Datei nicht schreiben."
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "ungültige Aktion '%s' für '%s'"
@@ -2186,9 +2211,6 @@
 "Sie können `git rm` auf Dateien ausführen, um \"von denen gelöscht\" für\n"
 "diese zu akzeptieren."
 
-msgid "unable to write new index file"
-msgstr "Konnte neue Index-Datei nicht schreiben."
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "Konnte Objekt '%s' nicht parsen."
@@ -2207,11 +2229,6 @@
 msgid "failed to read '%s'"
 msgstr "Fehler beim Lesen von '%s'"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr ""
-"die Optionen '%s=%s' und '%s=%s' können nicht gemeinsam verwendet werden"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<Optionen>] [(<mbox> | <E-Mail-Verzeichnis>)...]"
 
@@ -2251,9 +2268,6 @@
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "--keep-cr an git-mailsplit für mbox-Format übergeben"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "kein --keep-cr an git-mailsplit übergeben, unabhängig von am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "alles vor einer Scheren-Zeile entfernen"
 
@@ -2366,11 +2380,11 @@
 msgstr "git archive: erwartete eine Spülung (flush)"
 
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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}=<Begriff> --term-{old,good}=<Begriff>] [--"
-"no-checkout] [--first-parent] [<schlecht> [<gut>...]] [--] "
+"git bisect start [--term-(new|bad)=<Begriff> --term-(old|good)=<Begriff>]    "
+"[--no-checkout] [--first-parent] [<schlecht> [<gut>...]] [--]    "
 "[<Pfadspezifikation>...]"
 
 msgid "git bisect (good|bad) [<rev>...]"
@@ -2385,8 +2399,8 @@
 msgid "git bisect replay <logfile>"
 msgstr "git bisect replay <Logdatei>"
 
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <Programm>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <Programm> [<Argument>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2808,49 +2822,59 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"entferne Branch '%s', der zusammengeführt wurde mit\n"
-"         '%s', aber noch nicht mit HEAD zusammengeführt wurde."
+"Löschen des Branches '%s', der mit\n"
+"         '%s', aber noch nicht in HEAD zusammengeführt wurde"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "entferne Branch '%s' nicht, der noch nicht zusammengeführt wurde mit\n"
-"         '%s', obwohl er mit HEAD zusammengeführt wurde."
+"         '%s', obwohl er mit HEAD zusammengeführt wurde"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Konnte Commit-Objekt für '%s' nicht nachschlagen."
+msgid "couldn't look up commit object for '%s'"
+msgstr "konnte Commit-Objekt für '%s' nicht nachschlagen"
 
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
+msgid "the branch '%s' is not fully merged"
+msgstr "der Branch '%s' ist nicht vollständig zusammengeführt"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
 msgstr ""
-"Der Branch '%s' ist nicht vollständig zusammengeführt.\n"
-"Wenn Sie sicher sind diesen Branch zu entfernen, führen Sie 'git branch -D "
-"%s' aus."
+"Wenn Sie sicher sind, dass Sie den Branch löschen wollen, führen Sie 'git "
+"branch -D %s' aus."
 
-msgid "Update of config-file failed"
+msgid "update of config-file failed"
 msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
 
 msgid "cannot use -a with -d"
 msgstr "kann -a nicht mit -d benutzen"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Kann Branch '%s' nicht entfernen, ausgecheckt in '%s'."
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"kann Branch '%s' nicht löschen, der in Arbeitsverzeichnis '%s' verwendet wird"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
+msgid "remote-tracking branch '%s' not found"
 msgstr "Remote-Tracking-Branch '%s' nicht gefunden"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "Branch '%s' nicht gefunden."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"Branch '%s' nicht gefunden.\n"
+"Haben Sie --remote vergessen?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "Branch '%s' nicht gefunden"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2871,52 +2895,56 @@
 msgstr "HEAD (%s) wurde nicht unter \"refs/heads/\" gefunden!"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Branch %s wird auf %s umgesetzt"
+msgid "branch %s is being rebased at %s"
+msgstr "Rebase von Branch %s in %s im Gange"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Binäre Suche von Branch %s zu %s im Gange"
+msgid "branch %s is being bisected at %s"
+msgstr "Binäre Suche von Branch %s in %s im Gange"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD des Arbeitsverzeichnisses %s ist nicht aktualisiert"
+
+#, c-format
+msgid "invalid branch name: '%s'"
 msgstr "Ungültiger Branchname: '%s'"
 
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Noch kein Commit in Branch '%s'."
+msgid "no commit on branch '%s' yet"
+msgstr "Noch kein Commit in Branch '%s'"
 
 #, c-format
-msgid "No branch named '%s'."
-msgstr "Branch '%s' nicht vorhanden."
+msgid "no branch named '%s'"
+msgstr "kein Branch mit dem Namen '%s'"
 
-msgid "Branch rename failed"
+msgid "branch rename failed"
 msgstr "Umbenennung des Branches fehlgeschlagen"
 
-msgid "Branch copy failed"
+msgid "branch copy failed"
 msgstr "Kopie des Branches fehlgeschlagen"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Kopie eines falsch benannten Branches '%s' erstellt."
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "Kopie eines falsch benannten Branches '%s' erstellt"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
+msgid "renamed a misnamed branch '%s' away"
 msgstr "falsch benannten Branch '%s' umbenannt"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Branch umbenannt zu %s, aber HEAD ist nicht aktualisiert!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "Branch wird in %s umbenannt, aber HEAD wird nicht aktualisiert"
 
-msgid "Branch is renamed, but update of config-file failed"
+msgid "branch is renamed, but update of config-file failed"
 msgstr ""
-"Branch ist umbenannt, aber die Aktualisierung der Konfigurationsdatei ist "
-"fehlgeschlagen."
+"Branch wurde umbenannt, aber die Aktualisierung der Konfigurationsdatei\n"
+"ist fehlgeschlagen"
 
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr ""
-"Branch wurde kopiert, aber die Aktualisierung der Konfigurationsdatei ist\n"
-"fehlgeschlagen."
+"Branch wurde kopiert, aber die Aktualisierung der Konfigurationsdatei\n"
+"ist fehlgeschlagen"
 
 #, c-format
 msgid ""
@@ -2983,6 +3011,9 @@
 msgstr ""
 "einen Branch verschieben/umbenennen, auch wenn das Ziel bereits existiert"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "keinen Zeilenumbruch nach leer formatierten Referenzen ausgeben"
+
 msgid "copy a branch and its reflog"
 msgstr "einen Branch und dessen Reflog kopieren"
 
@@ -3028,8 +3059,8 @@
 msgid "format to use for the output"
 msgstr "für die Ausgabe zu verwendendes Format"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Konnte HEAD nicht als gültige Referenz auflösen."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "HEAD konnte nicht als gültige Referenz aufgelöst werden"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD wurde nicht unter \"refs/heads\" gefunden!"
@@ -3047,18 +3078,18 @@
 msgid "branch name required"
 msgstr "Branchname erforderlich"
 
-msgid "Cannot give description to detached HEAD"
+msgid "cannot give description to detached HEAD"
 msgstr "zu losgelöstem HEAD kann keine Beschreibung hinterlegt werden"
 
 msgid "cannot edit description of more than one branch"
 msgstr "Beschreibung von mehr als einem Branch kann nicht bearbeitet werden"
 
-msgid "cannot copy the current branch while not on any."
+msgid "cannot copy the current branch while not on any"
 msgstr ""
 "Kann den aktuellen Branch nicht kopieren, solange Sie sich auf keinem "
 "befinden."
 
-msgid "cannot rename the current branch while not on any."
+msgid "cannot rename the current branch while not on any"
 msgstr ""
 "Kann aktuellen Branch nicht umbenennen, solange Sie sich auf keinem befinden."
 
@@ -3073,10 +3104,10 @@
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"Konnte keinen neuen Upstream-Branch von HEAD zu %s setzen, da dieser auf\n"
-"keinen Branch zeigt."
+"konnte den Upstream von HEAD nicht auf %s setzen, wenn er auf keinen Zweig "
+"verweist"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3091,17 +3122,17 @@
 "zu viele Argumente angegeben, um Konfiguration zu Upstream-Branch zu "
 "entfernen"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
-"Konnte Konfiguration zu Upstream-Branch von HEAD nicht entfernen, da dieser\n"
-"auf keinen Branch zeigt."
+"konnte den Upstream von HEAD nicht aufheben, wenn er nicht auf einen Zweig "
+"verweist"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Branch '%s' hat keinen Upstream-Branch gesetzt"
+msgid "branch '%s' has no upstream information"
+msgstr "Branch '%s' hat keine Upstream-Informationen"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "Die Optionen -a und -r bei 'git branch' können nicht mit einem Branchnamen "
@@ -3110,7 +3141,7 @@
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "Die '--set-upstream' Option wird nicht länger unterstützt.\n"
 "Bitte benutzen Sie stattdessen '--track' oder '--set-upstream-to'."
@@ -3190,6 +3221,10 @@
 msgstr "ein Suffix im strftime-Format für den/die Dateinamen angeben"
 
 #, c-format
+msgid "unknown argument `%s'"
+msgstr "unbekanntes Argument `%s'"
+
+#, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "konnte vorangehende Verzeichnisse für '%s' nicht erstellen"
 
@@ -3212,12 +3247,10 @@
 msgstr "Neuer Bericht unter '%s' erstellt.\n"
 
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<Version>] <Datei> <git-rev-list-Argumente>"
 
 msgid "git bundle verify [-q | --quiet] <file>"
@@ -3238,11 +3271,11 @@
 msgid "show progress meter"
 msgstr "Fortschrittsanzeige anzeigen"
 
-msgid "show progress meter during object writing phase"
-msgstr "Forschrittsanzeige während des Schreibens von Objekten anzeigen"
+msgid "historical; same as --progress"
+msgstr "historisch; dasselbe wie --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "ähnlich zu --all-progress wenn Fortschrittsanzeige darstellt wird"
+msgid "historical; does nothing"
+msgstr "historisch; kein Effekt"
 
 msgid "specify bundle format version"
 msgstr "Version des Paket-Formats angeben"
@@ -3298,17 +3331,6 @@
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <Objekt>"
 
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-
-msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
@@ -3316,6 +3338,17 @@
 "             [<Commit>:<Pfad|Commit-Referenz> | --path=<Pfad|Commit-"
 "Referenz> <Commit>]"
 
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
 msgid "Check object existence or emit object contents"
 msgstr "Überprüfen von Objektexistenz oder Ausgeben von Objekt-Inhalten"
 
@@ -3353,6 +3386,9 @@
 msgid "stdin is NUL-terminated"
 msgstr "stdin endet mit NUL"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin und stdout sind NUL-beendet"
+
 msgid "read commands from stdin"
 msgstr "Befehle von der Standard-Eingabe lesen"
 
@@ -3615,6 +3651,12 @@
 msgstr "'%s' oder '%s' kann nicht mit %s verwendet werden"
 
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr ""
+"'%s', '%s' oder '%s' können beim Auschecken aus einem Verzeichnis nicht "
+"verwendet werden"
+
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "Pfad '%s' ist nicht zusammengeführt"
 
@@ -3874,8 +3916,8 @@
 msgid "new-branch"
 msgstr "neuer Branch"
 
-msgid "new unparented branch"
-msgstr "neuer Branch ohne Eltern-Commit"
+msgid "new unborn branch"
+msgstr "neuer ungeborener Branch"
 
 msgid "update ignored files (default)"
 msgstr "ignorierte Dateien aktualisieren (Standard)"
@@ -4129,9 +4171,6 @@
 "clean.requireForce standardmäßig auf \"true\" gesetzt und weder -i, -n noch -"
 "f gegeben; \"clean\" verweigert"
 
-msgid "-x and -X cannot be used together"
-msgstr "-x und -X können nicht gemeinsam verwendet werden"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<Optionen>] [--] <Repository> [<Verzeichnis>]"
 
@@ -4225,6 +4264,9 @@
 msgid "separate git dir from working tree"
 msgstr "Git-Verzeichnis vom Arbeitsverzeichnis separieren"
 
+msgid "specify the reference format to use"
+msgstr "das zu verwendende Referenzformat angeben"
+
 msgid "key=value"
 msgstr "Schlüssel=Wert"
 
@@ -4237,12 +4279,6 @@
 msgid "option to transmit"
 msgstr "Option übertragen"
 
-msgid "use IPv4 addresses only"
-msgstr "nur IPv4-Adressen benutzen"
-
-msgid "use IPv6 addresses only"
-msgstr "nur IPv6-Adressen benutzen"
-
 msgid "apply partial clone filters to submodules"
 msgstr "partielle Klonfilter auf Submodule anwenden"
 
@@ -4275,6 +4311,11 @@
 msgstr "%s existiert und ist kein Verzeichnis"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr ""
+"'%s' ist eine symbolische Verknüpfung, verweigere das Klonen mit --local"
+
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "Fehler beim Starten der Iteration über '%s'"
 
@@ -4349,12 +4390,9 @@
 msgid "You must specify a repository to clone."
 msgstr "Sie müssen ein Repository zum Klonen angeben."
 
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri ist inkompatibel mit --depth, --shallow-since und --shallow-"
-"exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "unbekanntes Speicherformat für Referenzen '%s'"
 
 #, c-format
 msgid "repository '%s' does not exist"
@@ -4496,7 +4534,7 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <Verzeichnis>] [--append]\n"
 "                       [--split[=<Strategie>]] [--reachable | --stdin-packs "
@@ -4520,6 +4558,10 @@
 msgstr "Konnte Commit-Graph '%s' nicht öffnen."
 
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "konnte die Commit-Graph-Kette '%s' nicht öffnen"
+
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "nicht erkanntes --split Argument, %s"
 
@@ -4701,6 +4743,9 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "Aktualisierung der Dateien fehlgeschlagen"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "Fehler beim Entpacken des Tree-Objektes von HEAD."
 
@@ -4719,9 +4764,6 @@
 msgid "Failed to update main cache tree"
 msgstr "Konnte Haupt-Cache-Verzeichnis nicht aktualisieren"
 
-msgid "unable to write new_index file"
-msgstr "Konnte new_index Datei nicht schreiben"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "Kann keinen Teil-Commit durchführen, während ein Merge im Gange ist."
 
@@ -4761,8 +4803,8 @@
 "der aktuellen Commit-Beschreibung verwendet wird."
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "Konnte Commit %s nicht nachschlagen"
+msgid "could not lookup commit '%s'"
+msgstr "konnte Commit '%s' nicht nachschlagen"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5131,13 +5173,13 @@
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"Das Repository wurde aktualisiert, aber die \"new_index\"-Datei\n"
+"Das Repository wurde aktualisiert, aber die neue Index-Datei\n"
 "konnte nicht geschrieben werden. Prüfen Sie, dass Ihre Festplatte nicht\n"
 "voll und Ihr Kontingent nicht aufgebraucht ist und führen Sie\n"
-"anschließend \"git restore HEAD --staged :/\" zur Wiederherstellung aus."
+"anschließend \"git restore --staged :/\" zur Wiederherstellung aus."
 
 msgid "git config [<options>]"
 msgstr "git config [<Optionen>]"
@@ -5828,6 +5870,178 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel kann nicht negativ sein"
 
+msgid "couldn't find remote ref HEAD"
+msgstr "konnte Remote-Referenz von HEAD nicht finden"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Von %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "Objekt %s nicht gefunden"
+
+msgid "[up to date]"
+msgstr "[aktuell]"
+
+msgid "[rejected]"
+msgstr "[zurückgewiesen]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "fetch kann in den ausgecheckten Branch nicht durchgeführt werden"
+
+msgid "[tag update]"
+msgstr "[Tag Aktualisierung]"
+
+msgid "unable to update local ref"
+msgstr "kann lokale Referenz nicht aktualisieren"
+
+msgid "would clobber existing tag"
+msgstr "würde bestehende Tags verändern"
+
+msgid "[new tag]"
+msgstr "[neues Tag]"
+
+msgid "[new branch]"
+msgstr "[neuer Branch]"
+
+msgid "[new ref]"
+msgstr "[neue Referenz]"
+
+msgid "forced update"
+msgstr "Aktualisierung erzwungen"
+
+msgid "non-fast-forward"
+msgstr "kein Vorspulen"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "kann '%s' nicht öffnen"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"Normalerweise zeigt 'fetch' welche Branches eine erzwungene Aktualisierung\n"
+"hatten, aber diese Überprüfung wurde deaktiviert. Um diese wieder zu\n"
+"aktivieren, nutzen Sie die Option '--show-forced-updates' oder führen\n"
+"Sie 'git config fetch.showForcedUpdates true' aus."
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"Es brauchte %.2f Sekunden, um erzwungene Aktualisierungen zu überprüfen.\n"
+"Sie können die Option '--no-show-forced-updates' benutzen oder\n"
+"'git config fetch.showForcedUpdates false' ausführen, um diese Überprüfung\n"
+"zu umgehen.\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s hat nicht alle erforderlichen Objekte gesendet\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr ""
+"%s zurückgewiesen, da Root-Commits von Repositories mit unvollständiger\n"
+"Historie (shallow) nicht aktualisiert werden dürfen."
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"Einige lokale Referenzen konnten nicht aktualisiert werden; versuchen Sie\n"
+"'git remote prune %s', um jeden älteren, widersprüchlichen Branch zu löschen."
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s wird unreferenziert)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s wurde unreferenziert)"
+
+msgid "[deleted]"
+msgstr "[gelöscht]"
+
+msgid "(none)"
+msgstr "(nichts)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "Anfordern in Branch '%s' verweigert, ausgecheckt in '%s'"
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "Option \"%s\" Wert \"%s\" ist nicht gültig für %s"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "Option \"%s\" wird ignoriert für %s\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s ist kein gültiges Objekt"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "das Objekt %s ist nicht vorhanden"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "mehrere Branches erkannt, inkompatibel mit --set-upstream"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"konnte keinen Upstream-Branch von HEAD auf '%s' von '%s' setzen, da dieser "
+"auf keinen Branch zeigt."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "setze keinen Upstream für einen entfernten Remote-Tracking-Branch"
+
+msgid "not setting upstream for a remote tag"
+msgstr "setze keinen Upstream für einen Tag eines Remote-Repositories"
+
+msgid "unknown branch type"
+msgstr "unbekannter Branch-Typ"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"kein Quell-Branch gefunden;\n"
+"Sie müssen bei der Option --set-upstream genau einen Branch angeben"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Fordere an von %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "konnte %s nicht anfordern"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "Konnte '%s' nicht anfordern (Exit-Code: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"kein Remote-Repository angegeben; bitte geben Sie entweder eine URL\n"
+"oder den Namen des Remote-Repositories an, von welchem neue\n"
+"Commits angefordert werden sollen"
+
+msgid "you need to specify a tag name"
+msgstr "Sie müssen den Namen des Tags angeben"
+
 msgid "fetch from all remotes"
 msgstr "fordert von allen Remote-Repositories an"
 
@@ -5945,178 +6159,6 @@
 msgid "accept refspecs from stdin"
 msgstr "akzeptiere Refspecs von der Standard-Eingabe"
 
-msgid "couldn't find remote ref HEAD"
-msgstr "konnte Remote-Referenz von HEAD nicht finden"
-
-#, c-format
-msgid "object %s not found"
-msgstr "Objekt %s nicht gefunden"
-
-msgid "[up to date]"
-msgstr "[aktuell]"
-
-msgid "[rejected]"
-msgstr "[zurückgewiesen]"
-
-msgid "can't fetch into checked-out branch"
-msgstr "fetch kann in den ausgecheckten Branch nicht durchgeführt werden"
-
-msgid "[tag update]"
-msgstr "[Tag Aktualisierung]"
-
-msgid "unable to update local ref"
-msgstr "kann lokale Referenz nicht aktualisieren"
-
-msgid "would clobber existing tag"
-msgstr "würde bestehende Tags verändern"
-
-msgid "[new tag]"
-msgstr "[neues Tag]"
-
-msgid "[new branch]"
-msgstr "[neuer Branch]"
-
-msgid "[new ref]"
-msgstr "[neue Referenz]"
-
-msgid "forced update"
-msgstr "Aktualisierung erzwungen"
-
-msgid "non-fast-forward"
-msgstr "kein Vorspulen"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "kann '%s' nicht öffnen"
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"Normalerweise zeigt 'fetch' welche Branches eine erzwungene Aktualisierung\n"
-"hatten, aber diese Überprüfung wurde deaktiviert. Um diese wieder zu\n"
-"aktivieren, nutzen Sie die Option '--show-forced-updates' oder führen\n"
-"Sie 'git config fetch.showForcedUpdates true' aus."
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"Es brauchte %.2f Sekunden, um erzwungene Aktualisierungen zu überprüfen.\n"
-"Sie können die Option '--no-show-forced-updates' benutzen oder\n"
-"'git config fetch.showForcedUpdates false' ausführen, um diese Überprüfung\n"
-"zu umgehen.\n"
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s hat nicht alle erforderlichen Objekte gesendet\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr ""
-"%s zurückgewiesen, da Root-Commits von Repositories mit unvollständiger\n"
-"Historie (shallow) nicht aktualisiert werden dürfen."
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "Von %.*s\n"
-
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"Einige lokale Referenzen konnten nicht aktualisiert werden; versuchen Sie\n"
-"'git remote prune %s', um jeden älteren, widersprüchlichen Branch zu löschen."
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s wird unreferenziert)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s wurde unreferenziert)"
-
-msgid "[deleted]"
-msgstr "[gelöscht]"
-
-msgid "(none)"
-msgstr "(nichts)"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "Anfordern in Branch '%s' verweigert, ausgecheckt in '%s'"
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "Option \"%s\" Wert \"%s\" ist nicht gültig für %s"
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "Option \"%s\" wird ignoriert für %s\n"
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s ist kein gültiges Objekt"
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr "das Objekt %s ist nicht vorhanden"
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "mehrere Branches erkannt, inkompatibel mit --set-upstream"
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"konnte keinen Upstream-Branch von HEAD auf '%s' von '%s' setzen, da dieser "
-"auf keinen Branch zeigt."
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "setze keinen Upstream für einen entfernten Remote-Tracking-Branch"
-
-msgid "not setting upstream for a remote tag"
-msgstr "setze keinen Upstream für einen Tag eines Remote-Repositories"
-
-msgid "unknown branch type"
-msgstr "unbekannter Branch-Typ"
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"kein Quell-Branch gefunden;\n"
-"Sie müssen bei der Option --set-upstream genau einen Branch angeben"
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "Fordere an von %s\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr "konnte %s nicht anfordern"
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "Konnte '%s' nicht anfordern (Exit-Code: %d)\n"
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"kein Remote-Repository angegeben; bitte geben Sie entweder eine URL\n"
-"oder den Namen des Remote-Repositories an, von welchem neue\n"
-"Commits angefordert werden sollen"
-
-msgid "you need to specify a tag name"
-msgstr "Sie müssen den Namen des Tags angeben"
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only benötigt einen oder mehrere --negotiation-tip=*"
 
@@ -6239,6 +6281,12 @@
 msgid "print only refs which don't contain the commit"
 msgstr "nur Referenzen ausgeben, die diesen Commit nicht enthalten"
 
+msgid "read reference patterns from stdin"
+msgstr "Referenzmuster von Standard-Eingabe lesen"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "unbekannte Argumente mit --stdin geliefert"
+
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<Konfiguration> [--] <Argumente>"
 
@@ -6251,6 +6299,10 @@
 msgid "missing --config=<config>"
 msgstr "Option --config=<Konfiguration> fehlt"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "ungültige Konfiguration --config=%s"
+
 msgid "unknown"
 msgstr "unbekannt"
 
@@ -6397,19 +6449,28 @@
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "Notiz: %s zeigt auf einen ungeborenen Branch (%s)"
 
-msgid "Checking cache tree"
-msgstr "Prüfe Cache-Verzeichnis"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Prüfe Cache-Verzeichnis von %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: Ungültiger SHA1-Zeiger in Cache-Verzeichnis"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: ungültiger SHA1-Zeiger im Cache-Verzeichnis von %s"
 
 msgid "non-tree in cache-tree"
 msgstr "non-tree in Cache-Verzeichnis"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: Ungültiger sha1-Zeiger in resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: ungültiger SHA1-Zeiger in resolve-undo von %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "rev-Index für Pack-Datei '%s' kann nicht geladen werden"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "ungültiger Rev-Index für Pack-Datei '%s'"
 
 msgid ""
 "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
@@ -6596,6 +6657,9 @@
 msgid "pack unreferenced objects separately"
 msgstr "unreferenzierte Objekte separat verpacken"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "mit --cruft, die Größe neuer Cruft-Pakete begrenzen"
+
 msgid "be more thorough (increased runtime)"
 msgstr "mehr Gründlichkeit (erhöht Laufzeit)"
 
@@ -6779,12 +6843,6 @@
 msgid "'crontab' died"
 msgstr "'crontab' abgebrochen"
 
-msgid "failed to start systemctl"
-msgstr "Fehler beim Starten von systemctl"
-
-msgid "failed to run systemctl"
-msgstr "Fehler beim Ausführen von systemctl"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "Fehler beim Löschen von '%s'"
@@ -6793,6 +6851,12 @@
 msgid "failed to flush '%s'"
 msgstr "Flush bei '%s' fehlgeschlagen."
 
+msgid "failed to start systemctl"
+msgstr "Fehler beim Starten von systemctl"
+
+msgid "failed to run systemctl"
+msgstr "Fehler beim Ausführen von systemctl"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "nicht erkanntes --scheduler Argument '%s'"
@@ -6816,6 +6880,9 @@
 msgid "scheduler to trigger git maintenance run"
 msgstr "Scheduler, um \"git maintenance run\" auzuführen"
 
+msgid "failed to set up maintenance schedule"
+msgstr "Fehler beim Einrichten des Wartungsplans"
+
 msgid "failed to add repo to global config"
 msgstr "Repository konnte nicht zur globalen Konfiguration hinzugefügt werden"
 
@@ -6833,7 +6900,6 @@
 msgid "invalid number of threads specified (%d) for %s"
 msgstr "ungültige Anzahl von Threads (%d) für %s angegeben"
 
-#. #-#-#-#-#  grep.c.po  #-#-#-#-#
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
@@ -6847,6 +6913,10 @@
 msgstr "konnte \"Tree\"-Objekt (%s) nicht lesen"
 
 #, c-format
+msgid "unable to read tree %s"
+msgstr "konnte \"Tree\"-Objekt (%s) nicht lesen"
+
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "kann \"grep\" nicht mit Objekten des Typs %s durchführen"
 
@@ -6890,8 +6960,8 @@
 msgid "search in subdirectories (default)"
 msgstr "in Unterverzeichnissen suchen (Standard)"
 
-msgid "descend at most <depth> levels"
-msgstr "höchstens <Tiefe> Ebenen durchlaufen"
+msgid "descend at most <n> levels"
+msgstr "höchstens <n> Ebenen absteigen"
 
 msgid "use extended POSIX regular expressions"
 msgstr "erweiterte reguläre Ausdrücke aus POSIX verwenden"
@@ -7268,10 +7338,6 @@
 msgstr "SHA1 KOLLISION MIT %s GEFUNDEN !"
 
 #, c-format
-msgid "unable to read %s"
-msgstr "kann %s nicht lesen"
-
-#, c-format
 msgid "cannot read existing object info %s"
 msgstr "Kann existierende Informationen zu Objekt %s nicht lesen."
 
@@ -7408,85 +7474,16 @@
 msgid "fsck error in pack objects"
 msgstr "fsck Fehler beim Packen von Objekten"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "kann Vorlage '%s' nicht lesen"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "kann Verzeichnis '%s' nicht öffnen"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "kann Verweis '%s' nicht lesen"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "kann symbolische Verknüpfung '%s' auf '%s' nicht erstellen"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "kann '%s' nicht nach '%s' kopieren"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "ignoriere Vorlage %s"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "Keine Vorlagen in %s gefunden."
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "kopiere keine Vorlagen von '%s': %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "ungültiger initialer Branchname: '%s'"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "kann nicht mit Dateityp %d umgehen"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "Konnte %s nicht nach %s verschieben"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "Versuch, das Repository mit einem anderen Hash zu reinitialisieren"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s existiert bereits"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "Neu-Initialisierung: --initial-branch=%s ignoriert"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Bestehendes verteiltes Git-Repository in %s%s neuinitialisiert\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Bestehendes Git-Repository in %s%s neuinitialisiert\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Leeres verteiltes Git-Repository in %s%s initialisiert\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Leeres Git-Repository in %s%s initialisiert\n"
-
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<Vorlagenverzeichnis>]\n"
 "         [--separate-git-dir <Git-Verzeichnis>] [--object-format=<Format>]\n"
+"         [--ref-format=<Format>]\n"
 "         [-b <Branchname> | --initial-branch=<Branchname>]\n"
 "         [--shared[=<Berechtigungen>]] [<Verzeichnis>]"
 
@@ -7530,11 +7527,12 @@
 
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <Token>[(=|:)<Wert>])...]\n"
+"                       [(--trailer (<Schlüssel>|<Schlüssel-Alias>)"
+"[(=|:)<Wert>])...]\n"
 "                       [--parse] [<Datei>...]"
 
 msgid "edit files in place"
@@ -7543,6 +7541,9 @@
 msgid "trim empty trailers"
 msgstr "kürzt leere Anhänge"
 
+msgid "placement"
+msgstr "Platzierung"
+
 msgid "where to place the new trailer"
 msgstr "wo der neue Anhang platziert wird"
 
@@ -7555,17 +7556,17 @@
 msgid "output only the trailers"
 msgstr "nur Anhänge ausgeben"
 
-msgid "do not apply config rules"
-msgstr "Regeln aus Konfiguration nicht anwenden"
+msgid "do not apply trailer.* configuration variables"
+msgstr "trailer.* Konfigurationsvariablen nicht anwenden"
 
-msgid "join whitespace-continued values"
-msgstr "durch Leerzeichen fortgesetzte Werte verbinden"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "mehrzeilige Trailer als einzeilige Werte umformatieren"
 
-msgid "set parsing options"
-msgstr "Optionen für das Parsen setzen"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "Alias für --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "--- nicht speziell behandeln"
+msgid "do not treat \"---\" as the end of input"
+msgstr "\"---\" nicht als Ende der Eingabe behandeln"
 
 msgid "trailer(s) to add"
 msgstr "Anhang/Anhänge hinzufügen"
@@ -7655,6 +7656,10 @@
 msgid "not a range"
 msgstr "Kein Commit-Bereich."
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "konnte Datei mit Branchbeschreibung '%s' nicht lesen"
+
 msgid "cover letter needs email format"
 msgstr "Anschreiben benötigt E-Mail-Format"
 
@@ -7754,6 +7759,9 @@
 msgstr ""
 "Erzeuge Teile des Deckblattes basierend auf der Beschreibung des Branches"
 
+msgid "use branch description from file"
+msgstr "Branchbeschreibung aus Datei verwenden"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "nutze [<Präfix>] statt [PATCH]"
 
@@ -7918,6 +7926,10 @@
 "manuell an.\n"
 
 #, c-format
+msgid "could not get object info about '%s'"
+msgstr "konnte Objekt-Informationen über '%s' nicht bestimmen"
+
+#, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "ungültiges ls-files-Format: Element '%s' fängt nicht mit '(' an"
 
@@ -8067,10 +8079,6 @@
 msgstr "git ls-tree [<Optionen>] <Commit-Referenz> [<Pfad>...]"
 
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "konnte Objekt-Informationen über '%s' nicht bestimmen"
-
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "ungültiges ls-tree-Format: Element '%s' fängt nicht mit '(' an"
 
@@ -8196,9 +8204,19 @@
 "git merge-file [<Optionen>] [-L <Name1> [-L <orig> [-L <Name2>]]] <Datei1> "
 "<orig-Datei> <Datei2>"
 
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"Option diff-algorithm akzeptiert: \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+
 msgid "send results to standard output"
 msgstr "Ergebnisse zur Standard-Ausgabe senden"
 
+msgid "use object IDs instead of filenames"
+msgstr "Objekt-IDs anstelle von Dateinamen verwenden"
+
 msgid "use a diff3 based merge"
 msgstr "einen diff3 basierten Merge verwenden"
 
@@ -8214,6 +8232,12 @@
 msgid "for conflicts, use a union version"
 msgstr "bei Konflikten eine gemeinsame Variante verwenden"
 
+msgid "<algorithm>"
+msgstr "<Algorithmus>"
+
+msgid "choose a diff algorithm"
+msgstr "einen Algorithmus für Änderungen wählen"
+
 msgid "for conflicts, use this marker size"
 msgstr "bei Konflikten diese Kennzeichnungslänge verwenden"
 
@@ -8224,6 +8248,13 @@
 msgstr "Beschriftung für Datei1/orig-Datei/Datei2 setzen"
 
 #, c-format
+msgid "object '%s' does not exist"
+msgstr "Objekt '%s' existiert nicht"
+
+msgid "Could not write object file"
+msgstr "konnte Objektdatei nicht schreiben"
+
+#, c-format
 msgid "unknown option %s"
 msgstr "unbekannte Option: %s"
 
@@ -8285,11 +8316,18 @@
 msgid "specify a merge-base for the merge"
 msgstr "Merge-Basis für den Merge angeben"
 
+msgid "option=value"
+msgstr "Option=Wert"
+
+msgid "option for selected merge strategy"
+msgstr "Option für ausgewählte Merge-Strategie"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge ist mit allen anderen Optionen inkompatibel"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base ist inkompatibel mit --stdin"
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "unbekannte Strategie-Option: -X%s"
 
 #, c-format
 msgid "malformed input line: '%s'."
@@ -8360,12 +8398,6 @@
 msgid "merge strategy to use"
 msgstr "zu verwendende Merge-Strategie"
 
-msgid "option=value"
-msgstr "Option=Wert"
-
-msgid "option for selected merge strategy"
-msgstr "Option für ausgewählte Merge-Strategie"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "Commit-Beschreibung zusammenführen (für einen Merge, der kein Vorspulen war)"
@@ -8427,10 +8459,6 @@
 msgstr "Es wird nur der Merge von zwei Branches behandelt."
 
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "unbekannte Strategie-Option: -X%s"
-
-#, c-format
 msgid "unable to write %s"
 msgstr "konnte %s nicht schreiben"
 
@@ -8735,8 +8763,8 @@
 msgid "can not move directory into itself"
 msgstr "kann Verzeichnis nicht in sich selbst verschieben"
 
-msgid "cannot move directory over file"
-msgstr "kann Verzeichnis nicht über Datei verschieben"
+msgid "destination already exists"
+msgstr "Ziel existiert bereits"
 
 msgid "source directory is empty"
 msgstr "Quellverzeichnis ist leer"
@@ -8815,22 +8843,26 @@
 msgstr "git notes [--ref <Notiz-Referenz>] [list [<Objekt>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <Notiz-Referenz>] add [-f] [--allow-empty] [-m "
-"<Beschreibung> | -F <Datei> | (-c | -C) <Objekt>] [<Objekt>]"
+"git notes [--ref <Notiz-Referenz>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<Absatz-Unterbrechung>] [--[no-]stripspace] [-m "
+"<Nachricht> | -F <Datei> | (-c | -C) <Objekt>] [<Objekt>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <Notiz-Referenz>] copy [-f] <von-Objekt> <nach-Objekt>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <Notiz-Referenz>] append [--allow-empty] [-m <Beschreibung> "
-"| -F <Datei> | (-c | -C) <Objekt>] [<Objekt>]"
+"git notes [--ref <Notiz-Referenz>] append [--allow-empty] [--"
+"[no-]separator|--separator=<Absatz-Unterbrechnung>] [--[no-]stripspace] [-m "
+"<Nachricht> | -F <Datei> | (-c | -C) <Objekt>] [<Object>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <Notiz-Referenz>] edit [--allow-empty] [<Objekt>]"
@@ -8965,6 +8997,15 @@
 msgid "replace existing notes"
 msgstr "existierende Notizen ersetzen"
 
+msgid "<paragraph-break>"
+msgstr "<Absatz-Unterbrechung>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "<Absatz-Unterbrechung> zwischen Absätzen einfügen"
+
+msgid "remove unnecessary whitespace"
+msgstr "unnötigen Whitespace entfernen"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9243,6 +9284,10 @@
 msgstr "Inkonsistenz mit der Anzahl von Deltas"
 
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "ungültiger Wert für pack.allowPackReuse: '%s'"
+
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -9326,6 +9371,12 @@
 msgid "bad index version '%s'"
 msgstr "ungültige Index-Version '%s'"
 
+msgid "show progress meter during object writing phase"
+msgstr "Forschrittsanzeige während des Schreibens von Objekten anzeigen"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "ähnlich zu --all-progress wenn Fortschrittsanzeige darstellt wird"
+
 msgid "<version>[,<offset>]"
 msgstr "<Version>[,<Offset>]"
 
@@ -9486,9 +9537,6 @@
 msgstr ""
 "--thin kann nicht benutzt werden, um ein indizierbares Paket zu erstellen."
 
-msgid "cannot use --filter without --stdout"
-msgstr "Kann --filter nicht ohne --stdout benutzen."
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "kann --filter nicht mit --stdin-packs benutzen"
 
@@ -9502,19 +9550,16 @@
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "kann --stdin-packs nicht mit --cruft benutzen"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "kann --max-pack-size nicht mit --cruft benutzen"
-
 msgid "Enumerating objects"
 msgstr "Objekte aufzählen"
 
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Gesamt %<PRIu32> (Delta %<PRIu32>), Wiederverwendet %<PRIu32> (Delta "
-"%<PRIu32>), Pack wiederverwendet %<PRIu32>"
+"%<PRIu32>), Paket wiederverwendet %<PRIu32> (von %<PRIuMAX>)"
 
 msgid ""
 "'git pack-redundant' is nominated for removal.\n"
@@ -9530,8 +9575,14 @@
 "Sie es immer noch verwenden, indem Sie eine E-Mail an\n"
 "<git@vger.kernel.org> senden. Danke.\n"
 
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "Ausführung ohne --i-still-use-this verweigert"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <Muster>] [--exclude <Muster>]"
 
 msgid "pack everything"
 msgstr "alles packen"
@@ -9539,6 +9590,12 @@
 msgid "prune loose refs (default)"
 msgstr "lose Referenzen entfernen (Standard)"
 
+msgid "references to include"
+msgstr "einzubeziehende Referenzen"
+
+msgid "references to exclude"
+msgstr "Referenzen zum Ausschluss"
+
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
 
@@ -9598,6 +9655,12 @@
 msgid "number of submodules pulled in parallel"
 msgstr "Anzahl der parallel mit 'pull' zu verarbeitenden Submodule"
 
+msgid "use IPv4 addresses only"
+msgstr "nur IPv4-Adressen benutzen"
+
+msgid "use IPv6 addresses only"
+msgstr "nur IPv6-Adressen benutzen"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9712,7 +9775,7 @@
 msgid "pull with rebase"
 msgstr "Pull mit Rebase"
 
-msgid "please commit or stash them."
+msgid "Please commit or stash them."
 msgstr "Bitte committen Sie die Änderungen oder benutzen Sie \"stash\"."
 
 #, c-format
@@ -9873,35 +9936,37 @@
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze Ihres aktuellen\n"
-"Branches hinter seinem externen Gegenstück zurückgefallen ist. Führen Sie\n"
-"die externen Änderungen zusammen (z. B. 'git pull ...') bevor Sie \"push\"\n"
-"erneut ausführen.\n"
-"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
-"für weitere Details."
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie\n"
+"die externen Änderungen integrieren wollen, verwenden Sie 'git pull' bevor\n"
+"Sie erneut push ausführen.\n"
+"Siehe auch den Abschnitt 'Note about fast-forwards' in 'git push --help' "
+"für\n"
+"weitere Informationen."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
-"Branches hinter seinem externen Gegenstück zurückgefallen ist. Checken Sie\n"
-"diesen Branch aus und führen Sie die externen Änderungen zusammen\n"
-"(z. B. 'git pull ...') bevor Sie erneut \"push\" ausführen.\n"
-"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
-"für weitere Details."
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie die\n"
+"externen Änderungen integrieren wollen, verwenden Sie 'git pull'\n"
+"bevor Sie erneut push ausführen.\n"
+"Siehe auch den Abschnitt 'Note about fast-forwards' in 'git push --help' "
+"für\n"
+"weitere Informationen."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil das Remote-Repository Commits "
@@ -9909,11 +9974,12 @@
 "die lokal nicht vorhanden sind. Das wird üblicherweise durch einen \"push\" "
 "von\n"
 "Commits auf dieselbe Referenz von einem anderen Repository aus verursacht.\n"
-"Vielleicht müssen Sie die externen Änderungen zusammenführen (z. B. 'git "
-"pull ...')\n"
-"bevor Sie erneut \"push\" ausführen.\n"
-"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
-"für weitere Details."
+"Wenn Sie die externen Änderungen integrieren wollen, verwenden Sie 'git "
+"pull'\n"
+"bevor Sie erneut push ausführen.\n"
+"Siehe auch den Abschnitt 'Note about fast-forwards' in 'git push --help' "
+"für\n"
+"weitere Informationen."
 
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
@@ -9930,15 +9996,21 @@
 "die Option '--force' zu verwenden.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Aktualisierungen wurden zurückgewiesen, weil die Spitze des Remote-\n"
-"Tracking-Branches seit dem letzen Checkout aktualisiert wurde. Sie möchten\n"
-"diese Änderungen vielleicht lokal integrieren (z. B. 'git pull ...') bevor\n"
-"Sie die Änderungen erzwingen.\n"
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze des Remote-"
+"Tracking-\n"
+"Branches seit dem letzten Auschecken aktualisiert wurde. Wenn Sie die "
+"externen\n"
+"Änderungen integrieren wollen, verwenden Sie 'git pull' bevor Sie erneut "
+"push\n"
+"ausführen.\n"
+"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help' für "
+"weitere\n"
+"Informationen."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9962,8 +10034,8 @@
 msgid "repository"
 msgstr "Repository"
 
-msgid "push all refs"
-msgstr "alle Referenzen versenden"
+msgid "push all branches"
+msgstr "alle Branches versenden"
 
 msgid "mirror all refs"
 msgstr "alle Referenzen spiegeln"
@@ -9971,8 +10043,10 @@
 msgid "delete refs"
 msgstr "Referenzen löschen"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "Tags versenden (kann nicht mit --all oder --mirror verwendet werden)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"Tags versenden (kann nicht mit --all, --branches oder --mirror verwendet "
+"werden)"
 
 msgid "force updates"
 msgstr "Aktualisierung erzwingen"
@@ -10242,6 +10316,10 @@
 "ausführen."
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Unbekannter rebase-merges Modus: %s"
+
+#, c-format
 msgid "could not switch to %s"
 msgstr "Konnte nicht zu %s wechseln."
 
@@ -10258,6 +10336,15 @@
 "nicht erkannter leerer Typ '%s'; gültige Werte sind \"drop\", \"keep\", und "
 "\"ask\"."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges mit einem leeren String-Argument ist veraltet und wird in "
+"einer zukünftigen Version von Git nicht mehr funktionieren. Verwenden Sie "
+"stattdessen --rebase-merges ohne ein Argument, was dasselbe bewirkt."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10483,19 +10570,12 @@
 msgid "switch `C' expects a numerical value"
 msgstr "Schalter `C' erwartet einen numerischen Wert."
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Unbekannter Modus: %s"
-
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy erfordert --merge oder --interactive"
-
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"apply-Optionen sind mit rebase.autosquash nicht kompatibel. Erwägen Sie das "
-"Hinzufügen von --no-autosquash"
+"apply-Optionen sind nicht kompatibel mit rebase.rebaseMerges. Erwägen Sie "
+"das Hinzufügen von --no-rebase-merges"
 
 msgid ""
 "apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
@@ -10541,9 +10621,6 @@
 msgid "Does not point to a valid commit '%s'"
 msgstr "'%s' zeigt auf keinen gültigen Commit."
 
-msgid "Please commit or stash them."
-msgstr "Bitte committen Sie die Änderungen oder benutzen Sie \"stash\"."
-
 msgid "HEAD is up to date."
 msgstr "HEAD ist aktuell."
 
@@ -10806,11 +10883,12 @@
 msgid "fetch the remote branches"
 msgstr "die Remote-Branches anfordern"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "alle Tags und verbundene Objekte beim Anfordern importieren"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "oder fordere gar keine Tags an (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"alle Tags und zugehörigen Objekte beim Abruf importieren\n"
+"oder überhaupt keine Tags abrufen (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "Branch(es) zur Übernahme"
@@ -11209,6 +11287,10 @@
 msgid "could not remove stale bitmap: %s"
 msgstr "konnte veraltete Bitmap nicht entfernen: %s"
 
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "Pack-Präfix %s fängt nicht mit objdir %s an"
+
 msgid "pack everything in a single pack"
 msgstr "alles in eine einzige Pack-Datei packen"
 
@@ -11221,8 +11303,8 @@
 msgid "approxidate"
 msgstr "Datumsangabe"
 
-msgid "with -C, expire objects older than this"
-msgstr "mit -C, Objekte älter als angegeben verfallen lassen"
+msgid "with --cruft, expire objects older than this"
+msgstr "mit --cruft, Objekte verfallen lassen, die älter sind als das"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "redundante Pakete entfernen und \"git-prune-packed\" ausführen"
@@ -11287,17 +11369,20 @@
 msgid "pack prefix to store a pack containing pruned objects"
 msgstr "pack-Präfix zum Speichern eines Pakets mit gelöschten Objekten"
 
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "Paketpräfix, um ein Paket mit herausgefilterten Objekten zu speichern"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "kann Pack-Dateien in precious-objects Repository nicht löschen"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "Option '%s' kann nur zusammen mit '%s' verwendet werden"
+
 msgid "Nothing new to pack."
 msgstr "Nichts Neues zum Packen."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "Pack-Präfix %s fängt nicht mit objdir %s an"
-
-#, c-format
 msgid "renaming pack to '%s' failed"
 msgstr "Umbenennung des Pakets in '%s' fehlgeschlagen"
 
@@ -11498,6 +11583,77 @@
 msgid "only one pattern can be given with -l"
 msgstr "Mit -l kann nur ein Muster angegeben werden"
 
+msgid "need some commits to replay"
+msgstr "zum erneuten Abspielen werden Commits benötigt"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto und --advance sind inkompatibel"
+
+msgid "all positive revisions given must be references"
+msgstr "alle angegebenen positiven Commits müssen Referenzen sein"
+
+msgid "argument to --advance must be a reference"
+msgstr "Argument für --advance muss eine Referenz sein"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"kann Ziel nicht mit mehreren Quellen erweitern, da die Reihenfolge unklar "
+"wäre"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"kann nicht implizit bestimmen, ob es sich um eine --advance oder --onto "
+"Operation handelt"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"kann Ziel nicht mit mehreren Quell-Branches erweitern, da die Reihenfolge "
+"unklar wäre"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "kann nicht implizit die richtige Basis für --onto bestimmen"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTELL!) git replay ([--contained] --onto <neue-Basis> | --advance "
+"<Branch>) <Commitbereich>..."
+
+msgid "make replay advance given branch"
+msgstr "angegebenen Branch durch neues Abspielen erweitern"
+
+msgid "replay onto given commit"
+msgstr "auf angegebenen Commit neu abspielen"
+
+msgid "advance all branches contained in revision-range"
+msgstr "alle Branches erweitern, die in Commitbereich liegen"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "Option --onto oder --advance erforderlich"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"einige Optionen für das Abgehen von Commits werden außer Kraft gesetzt, da "
+"das '%s' Bit in 'struct rev_info' erzwungen wird"
+
+msgid "error preparing revisions"
+msgstr "Fehler beim Vorbereiten der Commits"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "erneutes Abspielen bis zum Root-Commit wird noch nicht unterstützt!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "erneutes Abspielen von Merge-Commits wird noch nicht unterstützt!"
+
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
@@ -11709,18 +11865,12 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "unbekannter Modus für --abbrev-ref: %s"
 
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden kann nicht zusammen mit --branches verwendet werden"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden kann nicht zusammen mit --tags verwendet werden"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden kann nicht zusammen mit --remotes verwendet werden"
-
 msgid "this operation must be run in a work tree"
 msgstr "Diese Operation muss in einem Arbeitsverzeichnis ausgeführt werden."
 
+msgid "Could not read the index"
+msgstr "Konnte den Index nicht lesen"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "unbekannter Modus für --show-object-format: %s"
@@ -11902,6 +12052,9 @@
 msgid "remote name"
 msgstr "Name des Remote-Repositories"
 
+msgid "push all refs"
+msgstr "alle Referenzen versenden"
+
 msgid "use stateless RPC protocol"
 msgstr "zustandsloses RPC-Protokoll verwenden"
 
@@ -12067,23 +12220,44 @@
 msgstr "Unbekannter Hash-Algorithmus"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<Muster>...]"
 
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<Referenz>...]"
+
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<Muster>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <Referenz>"
+
+msgid "reference does not exist"
+msgstr "Referenz nicht vorhanden"
+
+msgid "failed to look up reference"
+msgstr "Fehler beim Nachschlagen der Referenz"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "nur Tags anzeigen (kann mit \"heads\" kombiniert werden)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "nur Branches anzeigen (kann mit \"tags\" kombiniert werden)"
 
+msgid "check for reference existence without resolving"
+msgstr "Prüfung auf Vorhandensein einer Referenz, ohne diese aufzulösen"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr "strengere Referenzprüfung, erfordert exakten Referenzpfad"
 
@@ -12107,10 +12281,11 @@
 "Repository befinden"
 
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) "
-"[<Optionen>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<Optionen>]"
 
 msgid "this worktree is not sparse"
 msgstr "dieses Arbeitsverzeichnis ist nicht partiell"
@@ -12241,6 +12416,24 @@
 msgid "error while refreshing working directory"
 msgstr "Fehler während der Aktualisierung des Arbeitsverzeichnisses."
 
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <Datei>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "Eingabe- und Ausgabedateien durch ein NUL-Zeichen abschließen"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"bei Verwendung mit --rules-file werden die Muster als Cone-Modus Muster "
+"interpretiert"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "Muster aus <Datei> anstelle der aktuellen Muster verwenden"
+
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<log-Optionen>]"
 
@@ -12774,6 +12967,10 @@
 msgstr "Überspringe Submodul '%s'"
 
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "kann Submodul '%s' nicht ohne URL klonen"
+
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Fehler beim Klonen von '%s'. Weiterer Versuch geplant"
 
@@ -12910,6 +13107,9 @@
 "shallow] [--reference <Repository>] [--recursive] [--[no-]single-branch] "
 "[--] [<Pfad>...]"
 
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Konnte HEAD nicht als gültige Referenz auflösen."
+
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<Optionen>] [<Pfad>...]"
 
@@ -13393,6 +13593,9 @@
 msgid "write index in this format"
 msgstr "Index-Datei in diesem Format schreiben"
 
+msgid "report on-disk index format version"
+msgstr "Bericht über die Version des Indexformats auf der Festplatte"
+
 msgid "enable or disable split index"
 msgstr "aufgeteilten Index aktivieren oder deaktivieren"
 
@@ -13419,6 +13622,14 @@
 msgid "clear fsmonitor valid bit"
 msgstr "\"fsmonitor valid\"-Bit löschen"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: war %d, wurde auf %d gesetzt"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13547,11 +13758,12 @@
 
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason "
 "<Zeichenkette>]]\n"
-"                 [-b <neuer-Branch>] <Pfad> [<Commit-Angabe>]"
+"                 [--orphan] [(-b | -B) <neuer-Branch>] <Pfad> [<Commit-"
+"Angabe>]"
 
 msgid "git worktree list [-v | --porcelain [-z]]"
 msgstr "git worktree list [-v | --porcelain [-z]]"
@@ -13574,6 +13786,37 @@
 msgid "git worktree unlock <worktree>"
 msgstr "git worktree unlock <Arbeitsverzeichnis>"
 
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Kein möglicher Quell-Branch, der auf '--orphan' schließen lässt"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, welches einen neuen\n"
+"ungeborenen Branch (Branch ohne Commits) für dieses Repository erzeugt,\n"
+"können Sie dies mit der Option --orphan tun:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, welches einen neuen\n"
+"ungeborenen Branch (Branch ohne Commits) für dieses Repository erzeugt,\n"
+"können Sie dies mit der Option --orphan tun:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "Entferne %s/%s: %s"
@@ -13633,6 +13876,10 @@
 msgstr "initialisiere"
 
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "konnte erstelltes Arbeitsverzeichnis '%s' nicht finden"
+
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "Bereite Arbeitsverzeichnis vor (neuer Branch '%s')"
 
@@ -13645,9 +13892,33 @@
 msgstr "Bereite Arbeitsverzeichnis vor (checke '%s' aus)"
 
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "unerreichbar: ungültige Referenz: %s"
+
+#, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Bereite Arbeitsverzeichnis vor (losgelöster HEAD %s)"
 
+#, c-format
+msgid ""
+"HEAD points to an invalid (or orphaned) reference.\n"
+"HEAD path: '%s'\n"
+"HEAD contents: '%s'"
+msgstr ""
+"HEAD zeigt auf eine ungültige (oder verwaiste) Referenz.\n"
+"HEAD-Pfad: '%s'\n"
+"HEAD Inhalte: '%s'"
+
+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 ""
+"Es gibt keine lokalen oder entfernten Referenzen, obwohl mindestens ein "
+"Remote-Repository\n"
+"vorhanden ist. Angehalten. Verwenden Sie 'add -f', um eine entfernte "
+"Referenz zu überschreiben\n"
+"oder rufen Sie diese zuerst ab"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "<Branch> auschecken, auch wenn dieser bereits in einem anderen "
@@ -13659,6 +13930,9 @@
 msgid "create or reset a branch"
 msgstr "Branch erstellen oder umsetzen"
 
+msgid "create unborn branch"
+msgstr "ungeborenen Branch erzeugen"
+
 msgid "populate the new working tree"
 msgstr "das neue Arbeitsverzeichnis auschecken"
 
@@ -13681,6 +13955,10 @@
 msgstr ""
 "die Optionen '%s', '%s' und '%s' können nicht gemeinsam verwendet werden"
 
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "Option '%s' und commit-ish können nicht gemeinsam verwendet werden"
+
 msgid "added with --lock"
 msgstr "mit --lock hinzugefügt"
 
@@ -13920,6 +14198,14 @@
 msgstr[0] "Das Paket benötigt diese Referenz:"
 msgstr[1] "Das Paket benötigt diese %<PRIuMAX> Referenzen:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Das Paket verwendet diesen Hash-Algorithmus: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Das Paket verwendet diesen Filter: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "konnte dup für Descriptor des Pakets nicht ausführen"
 
@@ -13955,6 +14241,10 @@
 msgstr "abschließende Chunk-ID erscheint eher als erwartet"
 
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "Chunk id %<PRIx32> nicht %d-byte-aligned"
+
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "unzulässige(r) Chunk-Offset(s) %<PRIx64> und %<PRIx64>"
 
@@ -14011,10 +14301,8 @@
 msgid "Move objects and refs by archive"
 msgstr "Objekte und Referenzen über ein Archiv verteilen"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Inhalt oder Informationen zu Typ und Größe für Repository-Objekte "
-"bereitstellen"
+msgid "Provide contents or details of repository objects"
+msgstr "Bereitstellung von Inhalten oder Details von Repository-Objekten"
 
 msgid "Display gitattributes information"
 msgstr "gitattributes Informationen darstellen"
@@ -14159,9 +14447,10 @@
 msgid "A portable graphical interface to Git"
 msgstr "eine portable grafische Schnittstelle zu Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
+msgid "Compute object ID and optionally create an object from a file"
 msgstr ""
-"von einer Datei die Objekt-ID berechnen und optional ein Blob erstellen"
+"Berechnung der Objekt-ID und optionales Erstellen eines Objekts aus einer "
+"Datei"
 
 msgid "Display help information about Git"
 msgstr "Hilfsinformationen über Git anzeigen"
@@ -14319,6 +14608,11 @@
 msgid "Create, list, delete refs to replace objects"
 msgstr "Referenzen für ersetzende Objekte erstellen, auflisten, löschen"
 
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPERIMENTELL: Commits auf neuer Basis abspielen, funktioniert auch mit Bare-"
+"Repositories"
+
 msgid "Generates a summary of pending changes"
 msgstr "eine Übersicht über ausstehende Änderungen generieren"
 
@@ -14445,8 +14739,8 @@
 msgid "Display version information about Git"
 msgstr "Versionsinformationen über Git anzeigen"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Logs mit dem Unterschied, den jeder Commit einführt, anzeigen"
+msgid "Show logs with differences each commit introduces"
+msgstr "Logs mit den Unterschieden anzeigen, den jeder Commit einführt"
 
 msgid "Manage multiple working trees"
 msgstr "mehrere Arbeitsverzeichnisse verwalten"
@@ -14563,6 +14857,32 @@
 msgid "commit-graph file is too small"
 msgstr "Commit-Graph-Datei ist zu klein"
 
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "Commit-Graph OID fanout Chunk hat die falsche Größe"
+
+msgid "commit-graph fanout values out of order"
+msgstr "Commit-Graph fanout-Werte sind nicht in Ordnung"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "Commit-Graph OID Lookup Chunk hat die falsche Größe"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "Commit-Graph Commit Daten Chunk hat die falsche Größe"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "Commit-Graph Generations Chunk hat die falsche Größe"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "Commit-Graph changed-path Index Chunk ist zu klein"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"ignoriere zu kleinen Chunk für geänderte Pfade (%<PRIuMAX> < %<PRIuMAX>) in "
+"Commit-Graph-Datei"
+
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr "Commit-Graph-Signatur %X stimmt nicht mit Signatur %X überein"
@@ -14579,13 +14899,33 @@
 msgid "commit-graph file is too small to hold %u chunks"
 msgstr "Commit-Graph-Datei ist zu klein, um %u Chunks zu enthalten"
 
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "Commit-Graph benötigter OID fanout Chunk fehlt oder ist beschädigt"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "Commit-Graph benötigter OID lookup Chunk fehlt oder ist beschädigt"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"Commit-Graph erforderlicher Commit-Daten Chunk fehlt oder ist beschädigt"
+
 msgid "commit-graph has no base graphs chunk"
 msgstr "Commit-Graph hat keinen Basis-Graph-Chunk"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "Commit-Graph Basis-Graph-Chunk ist zu klein"
+
 msgid "commit-graph chain does not match"
 msgstr "Commit-Graph Verkettung stimmt nicht überein"
 
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "Anzahl der Commits im Basisgraph zu hoch: %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "Commit-Graph Chain-Datei zu klein"
+
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr "Ungültige Commit-Graph Verkettung: Zeile '%s' ist kein Hash"
 
@@ -14602,6 +14942,12 @@
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "Commit-Graph erfordert Überlaufgenerierungsdaten, aber hat keine"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "Commit-Graph Überlaufgenerierungsdaten sind zu klein"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "commit-graph extra-edges Zeiger außerhalb der Grenzen"
+
 msgid "Loading known commits in commit graph"
 msgstr "Lade bekannte Commits in Commit-Graph"
 
@@ -14668,6 +15014,17 @@
 msgid "failed to rename temporary commit-graph file"
 msgstr "konnte temporäre Commit-Graph-Datei nicht umbenennen"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"Graphen mit %<PRIuMAX>, %<PRIuMAX> Commits können nicht zusammengeführt "
+"werden"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr ""
+"Graph %s kann nicht zusammengeführt werden, zu viele Commits: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Durchsuche zusammengeführte Commits"
 
@@ -14699,9 +15056,6 @@
 msgid "failed to parse commit %s from commit-graph"
 msgstr "konnte Commit %s von Commit-Graph nicht parsen"
 
-msgid "Verifying commits in commit graph"
-msgstr "Commit in Commit-Graph überprüfen"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14725,20 +15079,6 @@
 msgstr "Commit-Graph Vorgänger-Liste für Commit %s endet zu früh"
 
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"Commit-Graph hat Generationsnummer null für Commit %s, aber sonst ungleich "
-"null"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"Commit-Graph hat Generationsnummer ungleich null für Commit %s, aber sonst "
-"null"
-
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr "Commit-Graph Erstellung für Commit %s ist %<PRIuMAX> < %<PRIuMAX>"
 
@@ -14748,6 +15088,17 @@
 "Commit-Datum für Commit %s in Commit-Graph ist %<PRIuMAX> != %<PRIuMAX>"
 
 #, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"Commit-Graph hat sowohl Null- als auch Nicht-Null-Generationen (z. B. "
+"Commits '%s' und '%s')"
+
+msgid "Verifying commits in commit graph"
+msgstr "Commit in Commit-Graph überprüfen"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s ist kein Commit!"
 
@@ -14772,6 +15123,11 @@
 "\"git config advice.graftFileDeprecated false\" ausführen."
 
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"Commit %s existiert im Commit-Graphen, aber nicht in der Objektdatenbank"
+
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr ""
 "Commit %s hat eine nicht vertrauenswürdige GPG-Signatur, angeblich von %s."
@@ -15181,8 +15537,8 @@
 msgid "bad zlib compression level %d"
 msgstr "ungültiger zlib Komprimierungsgrad %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar sollte nur ein Zeichen sein"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar sollte nur ein ASCII-Zeichen sein"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -15219,10 +15575,6 @@
 msgid "unable to resolve config blob '%s'"
 msgstr "Konnte Blob '%s' für Konfiguration nicht auflösen."
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "Fehler beim Parsen von %s."
-
 msgid "unable to parse command-line config"
 msgstr ""
 "Konnte die über die Befehlszeile angegebene Konfiguration nicht parsen."
@@ -15300,6 +15652,11 @@
 msgstr "Ungültiger Sektionsname: %s"
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"Ausführung mit übermäßig langer Zeile in '%s' bei Zeile %<PRIuMAX> verweigert"
+
+#, c-format
 msgid "missing value for '%s'"
 msgstr "Fehlender Wert für '%s'"
 
@@ -15697,9 +16054,6 @@
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base funktioniert nicht mit Bereichen"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base funktioniert nur mit Commits"
-
 msgid "unable to get HEAD"
 msgstr "konnte HEAD nicht bekommen"
 
@@ -15709,6 +16063,12 @@
 msgid "multiple merge bases found"
 msgstr "mehrere Merge-Basen gefunden"
 
+msgid "cannot compare stdin to a directory"
+msgstr "kann stdin nicht mit einem Verzeichnis vergleichen"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "kann eine benannte Pipe nicht mit einem Verzeichnis vergleichen"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<Optionen>] <Pfad> <Pfad>"
 
@@ -15755,6 +16115,10 @@
 msgstr "Unbekannter Wert in Konfigurationsvariable 'diff.submodule': '%s'"
 
 #, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "Unbekannter Wert für Konfiguration '%s': %s"
+
+#, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
@@ -15766,6 +16130,13 @@
 msgid "external diff died, stopping at %s"
 msgstr "externes Diff-Programm unerwartet beendet, angehalten bei %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow erfordert genau eine Pfadspezifikation"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "Magie von Pfadspezifikationen wird von --follow nicht unterstützt: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr ""
@@ -15785,9 +16156,6 @@
 "die Optionen '%s' und '%s' können nicht gemeinsam verwendet werden, nutzen "
 "Sie '%s' mit '%s' und '%s'"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow erfordert genau eine Pfadspezifikation"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "Ungültiger --stat Wert: %s"
@@ -15832,13 +16200,6 @@
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "ungültiger Modus '%s' in --color-moved-ws"
 
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"Option diff-algorithm akzeptiert: \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "ungültiges Argument für %s"
@@ -15882,8 +16243,8 @@
 msgid "output only the last line of --stat"
 msgstr "nur die letzte Zeile von --stat ausgeben"
 
-msgid "<param1,param2>..."
-msgstr "<Parameter1,Parameter2>..."
+msgid "<param1>,<param2>..."
+msgstr "<Parameter1>,<Parameter2>..."
 
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
@@ -15894,8 +16255,8 @@
 msgid "synonym for --dirstat=cumulative"
 msgstr "Synonym für --dirstat=cumulative"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "Synonym für --dirstat=files,Parameter1,Parameter2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "Synonym für --dirstat=files,<Parameter1>,<Parameter2>..."
 
 msgid "warn if changes introduce conflict markers or whitespace errors"
 msgstr ""
@@ -15980,6 +16341,9 @@
 msgid "do not show any source or destination prefix"
 msgstr "keine Quell- oder Ziel-Präfixe anzeigen"
 
+msgid "use default prefixes a/ and b/"
+msgstr "Standardpräfixe a/ und b/ verwenden"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "Kontext zwischen Unterschied-Blöcken bis zur angegebenen Anzahl von Zeilen "
@@ -16075,12 +16439,6 @@
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "Änderungen durch Nutzung des Algorithmus \"Histogram Diff\" erzeugen"
 
-msgid "<algorithm>"
-msgstr "<Algorithmus>"
-
-msgid "choose a diff algorithm"
-msgstr "einen Algorithmus für Änderungen wählen"
-
 msgid "<text>"
 msgstr "<Text>"
 
@@ -16294,6 +16652,14 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "Hinweis: Warte auf das Schließen der Datei durch Ihren Editor...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "Konnte nicht nach '%s' schreiben."
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "Konnte '%s' nicht editieren."
+
 msgid "Filtering content"
 msgstr "Filtere Inhalt"
 
@@ -16591,6 +16957,10 @@
 msgstr "kein Konfigurationsschlüssel für --config-env angegeben\n"
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "keine Attributquelle für --attr-source angegeben\n"
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "Unbekannte Option: %s\n"
 
@@ -17121,12 +17491,12 @@
 "sind vorhanden:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
+msgid "failed to execute internal merge"
 msgstr "Fehler bei Ausführung des internen Merges"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Konnte %s nicht zur Datenbank hinzufügen"
+msgid "unable to add %s to database"
+msgstr "konnte %s nicht zur Datenbank hinzufügen"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17282,7 +17652,7 @@
 #. conflict in a submodule. The first argument is the submodule
 #. name, and the second argument is the abbreviated id of the
 #. commit that needs to be merged.  For example:
-#.  - go to submodule (mysubmodule), and either merge commit abc1234"
+#. - go to submodule (mysubmodule), and either merge commit abc1234"
 #.
 #, c-format
 msgid ""
@@ -17584,6 +17954,19 @@
 msgstr "Multi-Pack-Index OID fanout hat die falsche Größe"
 
 #, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"Ungültige oid fanout Reihenfolge: fanout[%d] = %<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "multi-pack-index OID-Lookup-Chunk hat die falsche Größe"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "multi-pack-index Object-Offset-Chunk hat die falsche Größe"
+
+#, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "Multi-Pack-Index-Datei %s ist zu klein."
 
@@ -17600,17 +17983,24 @@
 msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "Multi-Pack-Index Hash-Version %u stimmt nicht mit Version %u überein"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher Pack-Namen Chunk"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"multi-pack-index erforderlicher Pack-Name Chunk fehlt oder ist beschädigt"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher OID fanout Chunk"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"multi-pack-index erforderlicher OID-Fanout-Chunk fehlt oder ist beschädigt"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher OID lookup Chunk"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"multi-pack-index erforderlicher OID Lookup Chunk fehlt oder ist beschädigt"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "Multi-Pack-Index fehlt erforderlicher Objekt offset Chunk"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"multi-pack-index benötigte Objekt Offsets Chunk fehlt oder ist beschädigt"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "multi-pack-index Pack-Name Chunk ist zu klein"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17620,10 +18010,20 @@
 msgid "bad pack-int-id: %u (%u total packs)"
 msgstr "Ungültige pack-int-id: %u (%u Pakete insgesamt)"
 
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX enthält keinen BTMP-Chunk"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "konnte Bitmap-Paket nicht laden %<PRIu32>"
+
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr ""
 "Multi-Pack-Index speichert einen 64-Bit Offset, aber off_t ist zu klein"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr "multi-pack-index großer Offset außerhalb der Grenzen"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "Fehler beim Hinzufügen von Packdatei '%s'"
@@ -17702,13 +18102,6 @@
 msgid "Looking for referenced packfiles"
 msgstr "Suche nach referenzierten Pack-Dateien"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"Ungültige oid fanout Reihenfolge: fanout[%d] = %<PRIx32> > %<PRIx32> = "
-"fanout[%d]"
-
 msgid "the midx contains no oid"
 msgstr "das midx enthält keine oid"
 
@@ -18023,7 +18416,7 @@
 #. TRANSLATORS: This is a line of ambiguous commit
 #. object output. E.g.:
 #. *
-#.    "deadbeef commit 2021-01-01 - Some Commit Message"
+#. "deadbeef commit 2021-01-01 - Some Commit Message"
 #.
 #, c-format
 msgid "%s commit %s - %s"
@@ -18032,7 +18425,7 @@
 #. TRANSLATORS: This is a line of ambiguous
 #. tag object output. E.g.:
 #. *
-#.    "deadbeef tag 2022-01-01 - Some Tag Message"
+#. "deadbeef tag 2022-01-01 - Some Tag Message"
 #. *
 #. The second argument is the YYYY-MM-DD found
 #. in the tag.
@@ -18048,7 +18441,7 @@
 #. tag object output where we couldn't parse
 #. the tag itself. E.g.:
 #. *
-#.    "deadbeef [bad tag, could not parse it]"
+#. "deadbeef [bad tag, could not parse it]"
 #.
 #, c-format
 msgid "%s [bad tag, could not parse it]"
@@ -18244,6 +18637,9 @@
 msgid "could not open pack %s"
 msgstr "konnte Paket '%s' nicht öffnen"
 
+msgid "could not determine MIDX preferred pack"
+msgstr "konnte das von MIDX bevorzugte Paket nicht ermitteln"
+
 #, c-format
 msgid "preferred pack (%s) is invalid"
 msgstr "bevorzugtes Paket (%s) ist ungültig"
@@ -18267,6 +18663,12 @@
 "fehlerhafte ewah-Bitmap: abgeschnittener Header für Bitmap des Commits \"%s\""
 
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"Paket kann nicht geladen werden: '%s', Deaktivierung der Paket-"
+"Wiederverwendung"
+
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "Objekt '%s' nicht im Typ Bitmaps gefunden"
 
@@ -18304,6 +18706,10 @@
 msgstr "Festplattennutzung von '%s' kann nicht abgerufen werden"
 
 #, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "Bitmap-Datei '%s' hat eine ungültige Prüfsumme"
+
+#, c-format
 msgid "mtimes file %s is too small"
 msgstr "mtimes-Datei %s ist zu klein"
 
@@ -18343,6 +18749,19 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "Reverse-Index-Datei %s hat nicht unterstützte Hash-ID %<PRIu32>"
 
+msgid "invalid checksum"
+msgstr "ungültige Prüfsumme"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "ungültige rev-index Position bei %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "multi-pack-index Reverse-Index Chunk hat die falsche Größe"
+
+msgid "could not determine preferred pack"
+msgstr "konnte das bevorzugte Paket nicht bestimmen"
+
 msgid "cannot both write and verify reverse index"
 msgstr ""
 "Reverse-Index kann nicht gleichzeitig geschrieben und verifiziert werden"
@@ -18395,14 +18814,6 @@
 msgstr "%s erfordert einen Wert."
 
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s ist inkompatibel mit %s."
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: inkompatibel mit etwas anderem"
-
-#, c-format
 msgid "%s takes no value"
 msgstr "%s erwartet keinen Wert"
 
@@ -18487,6 +18898,10 @@
 msgid "-NUM"
 msgstr "-NUM"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "Gegenteil von --no-%s"
+
 msgid "expiry-date"
 msgstr "Verfallsdatum"
 
@@ -18518,6 +18933,14 @@
 "Mit der Option --pathspec-from-file sind Pfade durch NUL-Zeichen getrennt"
 
 #, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "falscher boolescher Wert von Umgebungsvariable '%s' für '%s'"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "Fehler beim Parsen von %s."
+
+#, c-format
 msgid "Could not make %s writable by group"
 msgstr "Konnte Gruppenschreibrecht für %s nicht setzen."
 
@@ -18566,6 +18989,10 @@
 msgstr "%s: 'literal' und 'glob' sind inkompatibel"
 
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' liegt außerhalb des Verzeichnisbaums"
+
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: '%s' liegt außerhalb des Repositories von '%s'"
 
@@ -18695,6 +19122,13 @@
 msgstr "Konnte Log für '%s' nicht parsen."
 
 #, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "ungültiger zusätzlicher Schrotthinweis: '%s'"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "zusätzliche neue Objekte konnten nicht aufgezählt werden"
+
+#, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr ""
 "Dateialias '%s' wird nicht hinzugefügt ('%s' existiert bereits im Index)."
@@ -18717,10 +19151,6 @@
 msgstr "Konnte '%s' nicht dem Index hinzufügen."
 
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "konnte '%s' nicht lesen"
-
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "'%s' scheint eine Datei und ein Verzeichnis zu sein"
 
@@ -18828,10 +19258,6 @@
 msgstr "Konvertierung zu einem Sparse-Index fehlgeschlagen"
 
 #, c-format
-msgid "could not stat '%s'"
-msgstr "Konnte '%s' nicht lesen."
-
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "konnte Git-Verzeichnis nicht öffnen: %s"
 
@@ -18847,6 +19273,14 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: Kann nicht auf Stufe #0 wechseln."
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "unerwarteter Differenz-Status %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "lösche '%s'\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -19048,6 +19482,22 @@
 msgstr "Positiver Wert erwartet contents:lines=%s"
 
 #, c-format
+msgid "argument expected for %s"
+msgstr "Argument erwartet für %s"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "positiver Wert erwartet %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "kann %s=%s nicht vollständig parsen"
+
+#, c-format
+msgid "value expected %s="
+msgstr "Wert erwartet %s="
+
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "positiver Wert erwartet '%s' in %%(%s)"
 
@@ -19072,6 +19522,10 @@
 msgstr "Positive Breitenangabe für %%(align) erwartet"
 
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "erwartetes Format: %%(ahead-behind:<Commit>)"
+
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "Fehlerhafter Feldname: %.*s"
 
@@ -19117,6 +19571,9 @@
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s kann nicht mit --python, --shell, --tcl verwendet werden"
 
+msgid "failed to run 'describe'"
+msgstr "'describe' konnte nicht ausgeführt werden"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(kein Branch, Rebase von %s)"
@@ -19178,6 +19635,9 @@
 msgid "field name to sort on"
 msgstr "sortiere nach diesem Feld"
 
+msgid "exclude refs which match pattern"
+msgstr "Ausschluss von Referenzen, die dem Muster entsprechen"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "Kein Reflog: %s"
@@ -19269,10 +19729,6 @@
 msgstr "kann '%s' und '%s' nicht zur selben Zeit verarbeiten"
 
 #, c-format
-msgid "could not remove reference %s"
-msgstr "konnte Referenz %s nicht löschen"
-
-#, c-format
 msgid "could not delete reference %s: %s"
 msgstr "konnte Referenz %s nicht entfernen: %s"
 
@@ -19466,16 +19922,13 @@
 "\n"
 "Neither worked, so we gave up. You must fully qualify the ref."
 msgstr ""
-"Das angegebene Ziel ist kein vollständiger Referenzname (startet mit \"refs/"
-"\").\n"
-"Wir versuchten zu erraten, was Sie meinten, mit:\n"
+"Das angegebene Ziel ist kein vollständiger Referenzname (startet mit\n"
+"\"refs/\"). Wir versuchten zu erraten, was Sie meinten, mit:\n"
 "\n"
 "- Suche einer Referenz, die mit '%s' übereinstimmt, auf der Remote-Seite\n"
-"- Prüfung, ob die versendete <Quelle> ('%s') eine Referenz in \"refs/{heads,"
-"tags}\"\n"
-"  ist, in dessen Falle wir einen entsprechenden refs/{heads,tags} Präfix "
-"auf\n"
-"  der Remote-Seite hinzufügen würden.\n"
+"- Prüfung, ob die versendete <Quelle> ('%s') eine Referenz in\n"
+"  \"refs/{heads,tags}/\" ist, in dessen Falle wir einen entsprechenden\n"
+"  refs/{heads,tags}/ Präfix auf der Remote-Seite hinzufügen würden.\n"
 "\n"
 "Keines hat funktioniert, sodass wir aufgegeben haben. Sie müssen die\n"
 "Referenz mit vollqualifizierten Namen angeben."
@@ -19635,10 +20088,11 @@
 "Ihr Branch und '%s' sind divergiert,\n"
 "und haben jeweils %d und %d unterschiedliche Commits.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
 msgstr ""
-"  (benutzen Sie \"git pull\", um Ihren Branch mit dem Remote-Branch "
-"zusammenzuführen)\n"
+"  (verwenden Sie \"git pull\", wenn Sie den Remote-Branch in Ihren "
+"integrieren wollen)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19711,10 +20165,6 @@
 msgstr "Keine aufgezeichnete Konfliktauflösung für '%s'."
 
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "Kann '%s' nicht löschen."
-
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Preimage für '%s' aktualisiert."
 
@@ -19754,6 +20204,10 @@
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<Pack-Datei> wird nicht länger unterstützt"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "ungültige Option '%s' im Modus --stdin"
+
 msgid "your current branch appears to be broken"
 msgstr "Ihr aktueller Branch scheint fehlerhaft zu sein."
 
@@ -19840,8 +20294,15 @@
 msgid "only download metadata for the branch that will be checked out"
 msgstr "lade nur Metadaten des Branches herunter, der ausgecheckt wird"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<Optionen>] [--] <Repository> [<Verzeichnis>]"
+msgid "create repository within 'src' directory"
+msgstr "Repository im Verzeichnis 'src' erstellen"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <Haupt-Branch>] [--full-clone]\n"
+"\t[--[no-]src] <URL> [<Eintragung>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19892,12 +20353,28 @@
 msgstr "konnte veraltetes scalar.repo '%s' nicht entfernen"
 
 #, c-format
-msgid "removing stale scalar.repo '%s'"
-msgstr "entferne veraltetes scalar.repo '%s'"
+msgid "removed stale scalar.repo '%s'"
+msgstr "veraltetes scalar.repo '%s' entfernt"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "Git-Repository entfernt in '%s'"
+msgid "repository at '%s' has different owner"
+msgstr "Repository bei '%s' hat anderen Eigentümer"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "Repository bei '%s' hat ein Formatproblem"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "Kein Repository in '%s' gefunden"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"um dieses Repository von Scalar abzumelden, führen Sie aus\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -20053,10 +20530,6 @@
 msgstr "Konnte '%s' nicht sperren"
 
 #, c-format
-msgid "could not write to '%s'"
-msgstr "Konnte nicht nach '%s' schreiben."
-
-#, c-format
 msgid "could not write eol to '%s'"
 msgstr "Konnte EOL nicht nach '%s' schreiben."
 
@@ -20312,10 +20785,6 @@
 msgstr "%s: kann Eltern-Commit %s nicht parsen"
 
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "Konnte '%s' nicht zu '%s' umbenennen."
-
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "Konnte \"revert\" nicht auf %s... (%s) ausführen"
 
@@ -20424,9 +20893,6 @@
 msgid "could not create sequencer directory '%s'"
 msgstr "konnte \"sequencer\"-Verzeichnis '%s' nicht erstellen"
 
-msgid "could not lock HEAD"
-msgstr "konnte HEAD nicht sperren"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "kein \"cherry-pick\" oder \"revert\" im Gange"
 
@@ -20523,20 +20989,20 @@
 "\n"
 "ausführen.\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "Der Index und/oder das Arbeitsverzeichnis wurde geändert.\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "und Änderungen am Index und/oder am Arbeitsverzeichnis vorgenommen.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "Ausführung erfolgreich: %s\n"
-"Aber Änderungen in Index oder Arbeitsverzeichnis verblieben.\n"
+"Aber es sind Änderungen im Index oder Arbeitsverzeichnis verblieben.\n"
 "Committen Sie Ihre Änderungen oder benutzen Sie \"stash\".\n"
 "Führen Sie dann aus:\n"
 "\n"
@@ -20647,6 +21113,9 @@
 msgid "Autostash exists; creating a new stash entry."
 msgstr "Automatischer Stash existiert; ein neuer Stash-Eintrag wird erstellt."
 
+msgid "autostash reference is a symref"
+msgstr "Referenz für autostash ist eine symbolische Referenz"
+
 msgid "could not detach HEAD"
 msgstr "konnte HEAD nicht loslösen"
 
@@ -20680,14 +21149,14 @@
 "    git rebase --continue\n"
 
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Rebase (%d/%d)%s"
-
-#, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Angehalten bei %s... %.*s\n"
 
 #, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Rebase (%d/%d)%s"
+
+#, c-format
 msgid "unknown command %d"
 msgstr "Unbekannter Befehl %d"
 
@@ -20931,6 +21400,83 @@
 msgstr "setsid fehlgeschlagen"
 
 #, c-format
+msgid "cannot stat template '%s'"
+msgstr "kann Vorlage '%s' nicht lesen"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kann Verzeichnis '%s' nicht öffnen"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kann Verweis '%s' nicht lesen"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kann symbolische Verknüpfung '%s' auf '%s' nicht erstellen"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kann '%s' nicht nach '%s' kopieren"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignoriere Vorlage %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "keine Vorlagen in %s gefunden"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "kopiere keine Vorlagen von '%s': %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "ungültiger initialer Branchname: '%s'"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "Neu-Initialisierung: --initial-branch=%s ignoriert"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kann nicht mit Dateityp %d umgehen"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "konnte %s nicht nach %s verschieben"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "Versuch, das Repository mit einem anderen Hash zu reinitialisieren"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"Versuch, das Repository mit einem anderen Referenzspeicherformat neu zu "
+"initialisieren"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s existiert bereits"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Bestehendes verteiltes Git-Repository in %s%s neuinitialisiert\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Bestehendes Git-Repository in %s%s neuinitialisiert\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Leeres verteiltes Git-Repository in %s%s initialisiert\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Leeres Git-Repository in %s%s initialisiert\n"
+
+#, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
 msgstr "Index-Eintrag ist ein Verzeichnis, aber nicht partiell (%08x)"
 
@@ -20982,10 +21528,6 @@
 msgstr[1] "%u Bytes/s"
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr "Konnte '%s' nicht editieren."
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "Ignoriere verdächtigen Submodulnamen: %s"
 
@@ -21187,12 +21729,6 @@
 "Anzahl der Einträge im Cache-Verzeichnis, die ungültig gemacht werden sollen "
 "(Standardwert 0)"
 
-msgid "unhandled options"
-msgstr "unbehandelte Optionen"
-
-msgid "error preparing revisions"
-msgstr "Fehler beim Vorbereiten der Commits"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "Commit %s ist nicht als erreichbar gekennzeichnet."
@@ -21348,9 +21884,6 @@
 msgid "invalid remote service path"
 msgstr "ungültiger Remote-Service Pfad."
 
-msgid "operation not supported by protocol"
-msgstr "die Operation wird von dem Protokoll nicht unterstützt"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "kann keine Verbindung zu Subservice %s herstellen"
@@ -21481,10 +22014,6 @@
 msgstr "Unterstützung für Protokoll v2 noch nicht implementiert."
 
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "Unbekannter Wert für Konfiguration '%s': %s"
-
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "Übertragungsart '%s' nicht erlaubt."
 
@@ -21537,6 +22066,9 @@
 msgid "could not retrieve server-advertised bundle-uri list"
 msgstr "konnte die vom Server angekündigte bundle-uri-Liste nicht abrufen"
 
+msgid "operation not supported by protocol"
+msgstr "die Operation wird von dem Protokoll nicht unterstützt"
+
 msgid "too-short tree object"
 msgstr "zu kurzes Tree-Objekt"
 
@@ -21944,6 +22476,9 @@
 msgid "unable to get current working directory"
 msgstr "konnte aktuelles Arbeitsverzeichnis nicht bekommen"
 
+msgid "unable to get random bytes"
+msgstr "konnte keine Zufallsbytes abrufen"
+
 msgid "Unmerged paths:"
 msgstr "Nicht zusammengeführte Pfade:"
 
@@ -22422,6 +22957,10 @@
 msgstr ""
 "%s nicht möglich: Die Staging-Area enthält nicht committete Änderungen."
 
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "unbekannter Stil '%s' für '%s' angegeben"
+
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
 "merge"
@@ -22626,13 +23165,13 @@
 "möchten.\n"
 
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Fehler beim Öffnen von %s: %s"
-
-#, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "Fehler beim Öffnen von %s.final: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Fehler beim Öffnen von %s: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "E-Mail mit Zusammenfassung ist leer, wird ausgelassen\n"
 
@@ -22794,13 +23333,17 @@
 msgstr "(%s) Konnte '%s' nicht ausführen"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Füge %s: %s hinzu von: '%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Fehlerhafte Ausgabe von '%s'"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) Fehler beim Schließen der Pipe nach '%s'"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Füge %s: %s hinzu von: '%s'\n"
+
 msgid "cannot send message as 7bit"
 msgstr "kann Nachricht nicht als 7bit versenden"
 
@@ -22837,377 +23380,3 @@
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Wollen Sie %s wirklich versenden? [y|N]: "
-
-#~ msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-#~ msgstr "git bisect--helper --bisect-state (bad|new) [<Commit>]"
-
-#~ msgid "won't bisect on cg-seek'ed tree"
-#~ msgstr ""
-#~ "binäre Suche auf einem durch 'cg-seek' geändertem Verzeichnis nicht "
-#~ "möglich"
-
-#~ msgid "--bisect-terms requires 0 or 1 argument"
-#~ msgstr "--bisect-terms benötigt 0 oder 1 Argument"
-
-#~ msgid "--bisect-next requires 0 arguments"
-#~ msgstr "--bisect-next benötigt 0 Argumente"
-
-#~ msgid "--bisect-log requires 0 arguments"
-#~ msgstr "--bisect-log benötigt 0 Argumente"
-
-#~ msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-#~ msgstr "git env--helper --type=[bool|ulong] <Optionen> <Umgebungsvariable>"
-
-#~ msgid "default for git_env_*(...) to fall back on"
-#~ msgstr "Standard für git_env_*(...), um darauf zurückzugreifen"
-
-#~ msgid "be quiet only use git_env_*() value as exit code"
-#~ msgstr ""
-#~ "Ausgaben unterdrücken; nur git_env_*() Werte als Exit-Code verwenden"
-
-#, c-format
-#~ msgid ""
-#~ "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-#~ msgstr ""
-#~ "Option `--default' erwartet einen booleschen Wert bei `--type=bool`, "
-#~ "nicht `%s`"
-
-#, c-format
-#~ msgid ""
-#~ "option `--default' expects an unsigned long value with `--type=ulong`, "
-#~ "not `%s`"
-#~ msgstr ""
-#~ "Option `--default' erwartet einen vorzeichenlosen Long-Wert bei `--"
-#~ "type=ulong`, nicht `%s`"
-
-#, c-format
-#~ msgid "%s doesn't support --super-prefix"
-#~ msgstr "%s unterstützt kein --super-prefix"
-
-#, c-format
-#~ msgid "no prefix given for --super-prefix\n"
-#~ msgstr "Kein Präfix für --super-prefix angegeben.\n"
-
-#, c-format
-#~ msgid "failed to read object %s"
-#~ msgstr "Konnte Objekt %s nicht lesen."
-
-#~ msgid "file write error"
-#~ msgstr "Fehler beim Schreiben einer Datei."
-
-#~ msgid "corrupt commit"
-#~ msgstr "fehlerhafter Commit"
-
-#~ msgid "corrupt tag"
-#~ msgstr "fehlerhaftes Tag"
-
-#, c-format
-#~ msgid "%%(objecttype) does not take arguments"
-#~ msgstr "%%(objecttype) akzeptiert keine Argumente"
-
-#, c-format
-#~ msgid "%%(deltabase) does not take arguments"
-#~ msgstr "%%(deltabase) akzeptiert keine Argumente"
-
-#, c-format
-#~ msgid "%%(body) does not take arguments"
-#~ msgstr "%%(body) akzeptiert keine Argumente"
-
-#, c-format
-#~ msgid "unrecognized email option: %s"
-#~ msgstr "nicht erkannte E-Mail Option: %s"
-
-#, c-format
-#~ msgid ""
-#~ "It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-#~ "may speed it up, but you have to be careful not to forget to add\n"
-#~ "new files yourself (see 'git help status')."
-#~ msgstr ""
-#~ "Es dauerte %.2f Sekunden die unversionierten Dateien zu bestimmen.\n"
-#~ "'status -uno' könnte das beschleunigen, aber Sie müssen darauf achten,\n"
-#~ "neue Dateien selbstständig hinzuzufügen (siehe 'git help status')."
-
-#, perl-format
-#~ msgid "%12s %12s %s"
-#~ msgstr "%28s %25s %s"
-
-#, perl-format
-#~ msgid "touched %d path\n"
-#~ msgid_plural "touched %d paths\n"
-#~ msgstr[0] "%d Pfad angefasst\n"
-#~ msgstr[1] "%d Pfade angefasst\n"
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for staging."
-#~ msgstr ""
-#~ "Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-#~ "Patch-Block direkt zum Hinzufügen zur Staging-Area markiert."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for stashing."
-#~ msgstr ""
-#~ "Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-#~ "Patch-Block direkt zum Hinzufügen zum Stash markiert."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for unstaging."
-#~ msgstr ""
-#~ "Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-#~ "Patch-Block direkt zum Entfernen aus der Staging-Area markiert."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for applying."
-#~ msgstr ""
-#~ "Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-#~ "Patch-Block direkt zum Anwenden markiert."
-
-#~ msgid ""
-#~ "If the patch applies cleanly, the edited hunk will immediately be\n"
-#~ "marked for discarding."
-#~ msgstr ""
-#~ "Wenn der Patch sauber angewendet werden kann, wird der bearbeitete\n"
-#~ "Patch-Block direkt zum Verwerfen markiert."
-
-#, perl-format
-#~ msgid "failed to open hunk edit file for writing: %s"
-#~ msgstr ""
-#~ "Fehler beim Öffnen von Editier-Datei eines Patch-Blocks zum Schreiben: %s"
-
-#, perl-format
-#~ msgid ""
-#~ "---\n"
-#~ "To remove '%s' lines, make them ' ' lines (context).\n"
-#~ "To remove '%s' lines, delete them.\n"
-#~ "Lines starting with %s will be removed.\n"
-#~ msgstr ""
-#~ "---\n"
-#~ "Um '%s' Zeilen zu entfernen, machen Sie aus diesen ' ' Zeilen (Kontext).\n"
-#~ "Um '%s' Zeilen zu entfernen, löschen Sie diese.\n"
-#~ "Zeilen, die mit %s beginnen, werden entfernt.\n"
-
-#, perl-format
-#~ msgid "failed to open hunk edit file for reading: %s"
-#~ msgstr ""
-#~ "Fehler beim Öffnen von Editier-Datei eines Patch-Blocks zum Lesen: %s"
-
-#~ msgid ""
-#~ "y - stage this hunk\n"
-#~ "n - do not stage this hunk\n"
-#~ "q - quit; do not stage this hunk or any of the remaining ones\n"
-#~ "a - stage this hunk and all later hunks in the file\n"
-#~ "d - do not stage this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block zum Commit vormerken\n"
-#~ "n - diesen Patch-Block nicht zum Commit vormerken\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht zum Commit "
-#~ "vormerken\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke dieser Datei zum Commit "
-#~ "vormerken\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke in dieser Datei nicht zum "
-#~ "Commit vormerken"
-
-#~ msgid ""
-#~ "y - stash this hunk\n"
-#~ "n - do not stash this hunk\n"
-#~ "q - quit; do not stash this hunk or any of the remaining ones\n"
-#~ "a - stash this hunk and all later hunks in the file\n"
-#~ "d - do not stash this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block stashen\n"
-#~ "n - diesen Patch-Block nicht stashen\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht stashen\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke dieser Datei stashen\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht stashen"
-
-#~ msgid ""
-#~ "y - unstage this hunk\n"
-#~ "n - do not unstage this hunk\n"
-#~ "q - quit; do not unstage this hunk or any of the remaining ones\n"
-#~ "a - unstage this hunk and all later hunks in the file\n"
-#~ "d - do not unstage this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block unstashen\n"
-#~ "n - diesen Patch-Block nicht unstashen\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht unstashen\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke dieser Datei unstashen\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht unstashen"
-
-#~ msgid ""
-#~ "y - apply this hunk to index\n"
-#~ "n - do not apply this hunk to index\n"
-#~ "q - quit; do not apply this hunk or any of the remaining ones\n"
-#~ "a - apply this hunk and all later hunks in the file\n"
-#~ "d - do not apply this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block auf den Index anwenden\n"
-#~ "n - diesen Patch-Block nicht auf den Index anwenden\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht auf den "
-#~ "Index anwenden\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke dieser Datei auf den Index "
-#~ "anwenden\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht auf den "
-#~ "Index anwenden"
-
-#~ msgid ""
-#~ "y - discard this hunk from worktree\n"
-#~ "n - do not discard this hunk from worktree\n"
-#~ "q - quit; do not discard this hunk or any of the remaining ones\n"
-#~ "a - discard this hunk and all later hunks in the file\n"
-#~ "d - do not discard this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block im Arbeitsverzeichnis verwerfen\n"
-#~ "n - diesen Patch-Block im Arbeitsverzeichnis nicht verwerfen\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht im "
-#~ "Arbeitsverzeichnis verwerfen\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke dieser Datei im "
-#~ "Arbeitsverzeichnis verwerfen\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke dieser Datei nicht im "
-#~ "Arbeitsverzeichnis verwerfen"
-
-#~ msgid ""
-#~ "y - discard this hunk from index and worktree\n"
-#~ "n - do not discard this hunk from index and worktree\n"
-#~ "q - quit; do not discard this hunk or any of the remaining ones\n"
-#~ "a - discard this hunk and all later hunks in the file\n"
-#~ "d - do not discard this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block im Index und Arbeitsverzeichnis verwerfen\n"
-#~ "n - diesen Patch-Block nicht im Index und Arbeitsverzeichnis verwerfen\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht im Index "
-#~ "und Arbeitsverzeichnis verwerfen\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke in der Datei verwerfen\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke in der Datei nicht verwerfen"
-
-#~ msgid ""
-#~ "y - apply this hunk to index and worktree\n"
-#~ "n - do not apply this hunk to index and worktree\n"
-#~ "q - quit; do not apply this hunk or any of the remaining ones\n"
-#~ "a - apply this hunk and all later hunks in the file\n"
-#~ "d - do not apply this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block im Index und auf Arbeitsverzeichnis anwenden\n"
-#~ "n - diesen Patch-Block nicht im Index und auf Arbeitsverzeichnis "
-#~ "anwenden\n"
-#~ "q - Beenden; diesen oder alle verbleibenden Patch-Blöcke nicht anwenden\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke in der Datei anwenden\n"
-#~ "d - diesen oder alle weiteren Patch-Blöcke in der Datei nicht anwenden"
-
-#~ msgid ""
-#~ "y - apply this hunk to worktree\n"
-#~ "n - do not apply this hunk to worktree\n"
-#~ "q - quit; do not apply this hunk or any of the remaining ones\n"
-#~ "a - apply this hunk and all later hunks in the file\n"
-#~ "d - do not apply this hunk or any of the later hunks in the file"
-#~ msgstr ""
-#~ "y - diesen Patch-Block auf das Arbeitsverzeichnis anwenden\n"
-#~ "n - diesen Patch-Block nicht auf das Arbeitsverzeichnis anwenden\n"
-#~ "q - Beenden; diesen und alle verbleibenden Patch-Blöcke nicht anwenden\n"
-#~ "a - diesen und alle weiteren Patch-Blöcke in der Datei anwenden\n"
-#~ "d - diesen und alle weiteren Patch-Blöcke in der Datei nicht anwenden"
-
-#~ msgid ""
-#~ "g - select a hunk to go to\n"
-#~ "/ - search for a hunk matching the given regex\n"
-#~ "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"
-#~ "s - split the current hunk into smaller hunks\n"
-#~ "e - manually edit the current hunk\n"
-#~ "? - print help\n"
-#~ msgstr ""
-#~ "g - Patch-Block zum Hinspringen auswählen\n"
-#~ "/ - nach Patch-Block suchen, der gegebenem regulärem Ausdruck entspricht\n"
-#~ "j - diesen Patch-Block unbestimmt lassen, nächsten unbestimmten Patch-"
-#~ "Block anzeigen\n"
-#~ "J - diesen Patch-Block unbestimmt lassen, nächsten Patch-Block anzeigen\n"
-#~ "k - diesen Patch-Block unbestimmt lassen, vorherigen unbestimmten Patch-"
-#~ "Block anzeigen\n"
-#~ "K - diesen Patch-Block unbestimmt lassen, vorherigen Patch-Block "
-#~ "anzeigen\n"
-#~ "s - aktuellen Patch-Block in kleinere Patch-Blöcke aufteilen\n"
-#~ "e - aktuellen Patch-Block manuell editieren\n"
-#~ "? - Hilfe anzeigen\n"
-
-#~ msgid "The selected hunks do not apply to the index!\n"
-#~ msgstr ""
-#~ "Die ausgewählten Patch-Blöcke können nicht auf den Index angewendet "
-#~ "werden!\n"
-
-#, perl-format
-#~ msgid "ignoring unmerged: %s\n"
-#~ msgstr "ignoriere nicht zusammengeführte Datei: %s\n"
-
-#~ msgid "No other hunks to goto\n"
-#~ msgstr "Keine anderen Patch-Blöcke verbleibend\n"
-
-#, perl-format
-#~ msgid "Invalid number: '%s'\n"
-#~ msgstr "Ungültige Nummer: '%s'\n"
-
-#, perl-format
-#~ msgid "Sorry, only %d hunk available.\n"
-#~ msgid_plural "Sorry, only %d hunks available.\n"
-#~ msgstr[0] "Entschuldigung, nur %d Patch-Block verfügbar.\n"
-#~ msgstr[1] "Entschuldigung, nur %d Patch-Blöcke verfügbar.\n"
-
-#~ msgid "No other hunks to search\n"
-#~ msgstr "Keine anderen Patch-Blöcke zum Durchsuchen\n"
-
-#, perl-format
-#~ msgid "Malformed search regexp %s: %s\n"
-#~ msgstr "Fehlerhafter regulärer Ausdruck für Suche %s: %s\n"
-
-#~ msgid "No hunk matches the given pattern\n"
-#~ msgstr "Kein Patch-Block entspricht dem angegebenen Muster\n"
-
-#~ msgid "No previous hunk\n"
-#~ msgstr "Kein vorheriger Patch-Block\n"
-
-#~ msgid "No next hunk\n"
-#~ msgstr "Kein folgender Patch-Block\n"
-
-#~ msgid "Sorry, cannot split this hunk\n"
-#~ msgstr "Entschuldigung, kann diesen Patch-Block nicht aufteilen.\n"
-
-#, perl-format
-#~ msgid "Split into %d hunk.\n"
-#~ msgid_plural "Split into %d hunks.\n"
-#~ msgstr[0] "In %d Patch-Block aufgeteilt.\n"
-#~ msgstr[1] "In %d Patch-Blöcke aufgeteilt.\n"
-
-#~ msgid "Sorry, cannot edit this hunk\n"
-#~ msgstr "Entschuldigung, kann diesen Patch-Block nicht bearbeiten.\n"
-
-#~ msgid ""
-#~ "status        - show paths with changes\n"
-#~ "update        - add working tree state to the staged set of changes\n"
-#~ "revert        - revert staged set of changes back to the HEAD version\n"
-#~ "patch         - pick hunks and update selectively\n"
-#~ "diff          - view diff between HEAD and index\n"
-#~ "add untracked - add contents of untracked files to the staged set of "
-#~ "changes\n"
-#~ msgstr ""
-#~ "status        - Pfade mit Änderungen anzeigen\n"
-#~ "update        - Zustand des Arbeitsverzeichnisses den zum Commit "
-#~ "vorgemerkten Änderungen hinzufügen\n"
-#~ "revert        - zum Commit vorgemerkte Änderungen auf HEAD Version "
-#~ "zurücksetzen\n"
-#~ "patch         - Patch-Blöcke auswählen und selektiv aktualisieren\n"
-#~ "diff          - Unterschiede zwischen HEAD und Index anzeigen\n"
-#~ "add untracked - Inhalte von unversionierten Dateien zum Commit vormerken\n"
-
-#~ msgid "missing --"
-#~ msgstr "-- fehlt"
-
-#, perl-format
-#~ msgid "unknown --patch mode: %s"
-#~ msgstr "Unbekannter --patch Modus: %s"
-
-#, perl-format
-#~ msgid "invalid argument %s, expecting --"
-#~ msgstr "ungültiges Argument %s, erwarte --"
diff --git a/po/fr.po b/po/fr.po
index f032441..736a90f 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -21,6 +21,7 @@
 #   bypass           |  éviter d'utiliser
 #   to checkout      |  extraire
 #   cherry-pick      |  picorer
+#   chunk            |  tronçon
 #   to commit        |  valider
 #   commit-ish       |  commit ou apparenté
 #   config file      |  fichier de configuration
@@ -29,6 +30,7 @@
 #   debugging        |  débogage
 #   to deflate       |  compresser
 #   email            |  courriel
+#   enlistment       |  enrôlement
 #   entry            |  élément
 #   fanout           |  dispersion
 #   fast-forward     |  avance rapide
@@ -78,8 +80,8 @@
 msgstr ""
 "Project-Id-Version: git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-01 01:20+0000\n"
-"PO-Revision-Date: 2023-03-02 18:44+0100\n"
+"POT-Creation-Date: 2024-02-16 19:18+0100\n"
+"PO-Revision-Date: 2024-02-16 19:19+0100\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"
@@ -716,9 +718,8 @@
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Impossible d'annuler car vous avez des fichiers non fusionnés."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "%s n'est pas possible car vous avez des fichiers non fusionnés."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Impossible de rebaser, car vous avez des fichiers non fusionnés."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -739,6 +740,24 @@
 msgid "Exiting because of unfinished merge."
 msgstr "Abandon à cause d'une fusion non terminée."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Des branches divergentes ne peuvent pas être gérées en avance rapide, vous "
+"devez soit :\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"ou :\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Pas possible d'avancer rapidement, abandon."
 
@@ -850,6 +869,12 @@
 msgid "'%s' outside a repository"
 msgstr "'%s' hors d'un dépôt"
 
+msgid "failed to read patch"
+msgstr "impossible de lire la rustine"
+
+msgid "patch too large"
+msgstr "la rustine est trop grosse"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Impossible de préparer la regexp d'horodatage %s"
@@ -1200,6 +1225,10 @@
 msgstr "impossible d'ouvrir %s"
 
 #, c-format
+msgid "cannot unlink '%s'"
+msgstr "impossible de délier '%s'"
+
+#, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Section n°%d appliquée proprement."
 
@@ -1391,6 +1420,12 @@
 msgstr "impossible de lire '%s'"
 
 #, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+"le spécificateur de chemin '%s' correspond à des fichiers hors du répertoire "
+"actuel"
+
+#, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "le chemin '%s' ne correspond à aucun fichier"
 
@@ -1406,9 +1441,6 @@
 msgid "not a tree object: %s"
 msgstr "objet arbre invalide : %s"
 
-msgid "current working directory is untracked"
-msgstr "l'arbre de travail actuel est non-suivi"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Fichier non trouvé : %s"
@@ -1494,6 +1526,10 @@
 msgstr "Option --output inattendue"
 
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "paramètre de commande supplémentaire '%s'"
+
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "Format d'archive inconnu '%s'"
 
@@ -1535,6 +1571,17 @@
 msgid "ignoring overly large gitattributes blob '%s'"
 msgstr "blob gitattributes trop gros ignoré '%s'"
 
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "mauvais --attr-source ou GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "fstat de '%s' impossible"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "impossible de lire %s"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Contenu mal cité dans le fichier '%s' : %s"
@@ -1641,9 +1688,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents et --reverse ne font pas bon ménage."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "on ne peut pas utiliser --contents avec un nom d'objet commit final"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse et --first-parent ensemble nécessitent la spécification d'un "
@@ -1762,9 +1806,10 @@
 msgstr "Une branche nommée '%s' existe déjà"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"impossible de forcer la mise à jour de la branche '%s' extraite dans '%s'"
+"impossible de forcer la mise à jour de la branche '%s' utilisée par l'arbre-"
+"de-travail dans '%s'"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1824,12 +1869,8 @@
 msgstr "sous-module '%s' : impossible de créer la branche '%s'"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' est déjà extrait dans '%s'"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "la HEAD de la copie de travail %s n'est pas mise à jour"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' est déjà utilisé par l'arbre-de-travail dans '%s'"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<options>] [--] <chemin>..."
@@ -1838,17 +1879,6 @@
 msgid "cannot chmod %cx '%s'"
 msgstr "impossible de chmod %cx '%s'"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "état de diff inattendu %c"
-
-msgid "updating files failed"
-msgstr "échec de la mise à jour des fichiers"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "suppression de '%s'\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Modifications non indexées après rafraîchissement de l'index :"
 
@@ -1859,25 +1889,22 @@
 "le réglage add.interactive.useBuiltin a été supprimé !\n"
 "Référez-vous à cette entrée dans 'git help config' pour plus de détails."
 
-msgid "Could not read the index"
-msgstr "Impossible de lire l'index"
-
-msgid "Could not write patch"
-msgstr "Impossible d'écrire le patch"
+msgid "could not read the index"
+msgstr "impossible de lire l'index"
 
 msgid "editing patch failed"
 msgstr "échec de l'édition du patch"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Stat de '%s' impossible"
+msgid "could not stat '%s'"
+msgstr "impossible de stat '%s'"
 
-msgid "Empty patch. Aborted."
-msgstr "Patch vide. Abandon."
+msgid "empty patch. aborted"
+msgstr "rustine vide. abandon"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Impossible d'appliquer '%s'"
+msgid "could not apply '%s'"
+msgstr "impossible d'appliquer '%s'"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
@@ -2011,6 +2038,9 @@
 msgid "index file corrupt"
 msgstr "fichier d'index corrompu"
 
+msgid "unable to write new index file"
+msgstr "impossible d'écrire le nouveau fichier d'index"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "action invalide '%s' pour '%s'"
@@ -2220,9 +2250,6 @@
 "Vous pouvez lancer 'git rm' sur un fichier \"supprimé par eux\" pour "
 "accepter son état."
 
-msgid "unable to write new index file"
-msgstr "impossible d'écrire le nouveau fichier d'index"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "Impossible d'analyser l'objet '%s'."
@@ -2241,10 +2268,6 @@
 msgid "failed to read '%s'"
 msgstr "échec de la lecture de '%s'"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "les options '%s=%s' et '%s=%s' ne peuvent pas être utilisées ensemble"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<options>] [(<mbox> | <Maildir>)...]"
 
@@ -2284,10 +2307,6 @@
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "passer l'option --keep-cr à git-mailsplit fpour le format mbox"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"ne pas passer l'option --keep-cr à git-mailsplit indépendamment de am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "retirer tout le contenu avant la ligne des ciseaux"
 
@@ -2402,7 +2421,7 @@
 msgstr "git archive : vidage attendu"
 
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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}=<terme> --term-{old,good}=<terme>]    [--"
@@ -2421,8 +2440,8 @@
 msgid "git bisect replay <logfile>"
 msgstr "git bisect replay <fichier-journal>"
 
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <cmd>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <cmd>  [<arg>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2849,48 +2868,58 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "suppression de la branche '%s' qui a été fusionnée dans\n"
-"         '%s', mais pas dans HEAD."
+"         '%s', mais pas encore dans HEAD"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"branche '%s' non supprimée car elle n'a pas été fusionnée dans\n"
-"         '%s', même si elle est fusionnée dans HEAD."
+"branche '%s' non supprimée car elle n'a pas encore été fusionnée dans\n"
+"         '%s', même si elle est fusionnée dans HEAD"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Impossible de rechercher l'objet commit pour '%s'"
+msgid "couldn't look up commit object for '%s'"
+msgstr "impossible de rechercher l'objet commit pour '%s'"
 
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"La branche '%s' n'est pas totalement fusionnée.\n"
-"Si vous souhaitez réellement la supprimer, lancez 'git branch -D %s'."
+msgid "the branch '%s' is not fully merged"
+msgstr "la branche '%s' n'est pas complètement fusionnée"
 
-msgid "Update of config-file failed"
-msgstr "Échec de la mise à jour du fichier de configuration"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Si vous souhaitez réellement la supprimer, lancez 'git branch -D %s'"
+
+msgid "update of config-file failed"
+msgstr "échec de la mise à jour du fichier de configuration"
 
 msgid "cannot use -a with -d"
 msgstr "impossible d'utiliser -a avec -d"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Impossible de supprimer la branche '%s' extraite dans '%s'"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"impossible de supprimer la branche '%s' utilisée par l'arbre-de-travail dans "
+"'%s'"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "branche de suivi '%s' non trouvée."
+msgid "remote-tracking branch '%s' not found"
+msgstr "branche de suivi '%s' non trouvée"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "branche '%s' non trouvée."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"branche '%s' non trouvée.\n"
+"Avez-vous oublié --remote ?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "branche '%s' non trouvée"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2911,51 +2940,55 @@
 msgstr "HEAD (%s) pointe hors de refs/heads/"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "La branche %s est en cours de rebasage sur %s"
+msgid "branch %s is being rebased at %s"
+msgstr "la branche %s est en cours de rebasage sur %s"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "La branche %s est en cours de bissection sur %s"
+msgid "branch %s is being bisected at %s"
+msgstr "la branche %s est en cours de bissection sur %s"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Nom de branche invalide : '%s'"
+msgid "HEAD of working tree %s is not updated"
+msgstr "la HEAD de la copie de travail %s n'est pas mise à jour"
 
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Aucun commit sur la branche '%s'."
+msgid "invalid branch name: '%s'"
+msgstr "nom de branche invalide : '%s'"
 
 #, c-format
-msgid "No branch named '%s'."
-msgstr "Aucune branche nommée '%s'."
-
-msgid "Branch rename failed"
-msgstr "Échec de renommage de la branche"
-
-msgid "Branch copy failed"
-msgstr "Échec de copie de la branche"
+msgid "no commit on branch '%s' yet"
+msgstr "aucun commit encore sur la branche '%s'"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Création d'une copie d'une branche mal nommée '%s'"
+msgid "no branch named '%s'"
+msgstr "aucune branche nommée '%s'"
+
+msgid "branch rename failed"
+msgstr "échec de renommage de la branche"
+
+msgid "branch copy failed"
+msgstr "échec de copie de la branche"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Renommage d'une branche mal nommée '%s'"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "création d'une copie d'une branche mal nommée '%s'"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "La branche a été renommée en %s, mais HEAD n'est pas mise à jour !"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "renommage d'une branche mal nommée '%s'"
 
-msgid "Branch is renamed, but update of config-file failed"
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "la branche a été renommée en %s, mais HEAD n'est pas mise à jour"
+
+msgid "branch is renamed, but update of config-file failed"
 msgstr ""
-"La branche est renommée, mais la mise à jour du fichier de configuration a "
+"la branche est renommée, mais la mise à jour du fichier de configuration a "
 "échoué"
 
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr ""
-"La branche est copiée, mais la mise à jour du fichier de configuration a "
+"la branche est copiée, mais la mise à jour du fichier de configuration a "
 "échoué"
 
 #, c-format
@@ -3022,6 +3055,9 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "déplacer/renommer une branche, même si la cible existe"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "ne pas générer de nouvelle ligne après des réfs formatées vides"
+
 msgid "copy a branch and its reflog"
 msgstr "copier une branche et son reflog"
 
@@ -3067,8 +3103,8 @@
 msgid "format to use for the output"
 msgstr "format à utiliser pour la sortie"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Échec de résolution de HEAD comme référence valide."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "échec de résolution de HEAD comme référence valide"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD non trouvée sous refs/heads !"
@@ -3086,17 +3122,17 @@
 msgid "branch name required"
 msgstr "le nom de branche est requis"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Impossible de décrire une HEAD détachée"
+msgid "cannot give description to detached HEAD"
+msgstr "impossible de décrire une HEAD détachée"
 
 msgid "cannot edit description of more than one branch"
 msgstr "impossible d'éditer la description de plus d'une branche"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "impossible de copier la branche actuelle, il n'y en a pas."
+msgid "cannot copy the current branch while not on any"
+msgstr "impossible de copier la branche actuelle, il n'y en a pas"
 
-msgid "cannot rename the current branch while not on any."
-msgstr "impossible de renommer la branche actuelle, il n'y en a pas."
+msgid "cannot rename the current branch while not on any"
+msgstr "impossible de renommer la branche actuelle, il n'y en a pas"
 
 msgid "too many branches for a copy operation"
 msgstr "trop de branches pour une opération de copie"
@@ -3109,10 +3145,10 @@
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
 "impossible de spécifier une branche amont de HEAD par %s qui ne pointe sur "
-"aucune branche."
+"aucune branche"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3125,29 +3161,29 @@
 msgid "too many arguments to unset upstream"
 msgstr "trop d'arguments pour désactiver un amont"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
 "impossible de désactiver une branche amont de HEAD quand elle ne pointe sur "
-"aucune branche."
+"aucune branche"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "La branche '%s' n'a aucune information de branche amont"
+msgid "branch '%s' has no upstream information"
+msgstr "la branche '%s' n'a aucune information de branche amont"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Les options -a et -r de 'git branch' n'ont pas de sens avec un nom de "
+"les options -a et -r de 'git branch' n'ont pas de sens avec un nom de "
 "branche.\n"
 "Vouliez-vous plutôt dire -a|-r --list <motif> ?"
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "l'option '--set-upstream' est obsolète. Utilisez '--track' ou '--set-"
-"upstream-to' à la place."
+"upstream-to' à la place"
 
 msgid "git version:\n"
 msgstr "version git ::\n"
@@ -3225,6 +3261,10 @@
 msgstr "spécifier une suffixe au format strftime pour le(s) nom(s) de fichier"
 
 #, c-format
+msgid "unknown argument `%s'"
+msgstr "argument inconnu '%s'"
+
+#, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "impossible de créer les répertoires de premier niveau pour '%s'"
 
@@ -3247,13 +3287,11 @@
 msgstr "Nouveau rapport créé à '%s'.\n"
 
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
-"                  [--version=<version>] <file> <args-de-git-rev-list>"
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <fichier> <args-de-git-rev-list>"
 
 msgid "git bundle verify [-q | --quiet] <file>"
 msgstr "git bundle verify [-q | --quiet] <fichier>"
@@ -3273,11 +3311,11 @@
 msgid "show progress meter"
 msgstr "afficher la barre de progression"
 
-msgid "show progress meter during object writing phase"
-msgstr "afficher la barre de progression durant la phase d'écrite des objets"
+msgid "historical; same as --progress"
+msgstr "option historique ; identique à --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "similaire à --all-progress quand la barre de progression est affichée"
+msgid "historical; does nothing"
+msgstr "option historique ; ne fait rien"
 
 msgid "specify bundle format version"
 msgstr "spécifier la version du format de colis"
@@ -3333,23 +3371,23 @@
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objet>"
 
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-
-msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<chemin|arbresque> | --path=<chemin|arbresque> <rev>]"
 
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
 msgid "Check object existence or emit object contents"
 msgstr "Vérifie l'existence d'un objet ou émettre le contenu de l'objet"
 
@@ -3386,6 +3424,9 @@
 msgid "stdin is NUL-terminated"
 msgstr "l'entrée se termine par NUL"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "l'entrée et la sortie standard se terminent par NUL"
+
 msgid "read commands from stdin"
 msgstr "lire les commandes depuis l'entrée standard"
 
@@ -3648,6 +3689,12 @@
 msgstr "'%s' ou '%s' ne peut pas être utilisé avec %s"
 
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr ""
+"'%s', '%s' ou '%s' ne peuvent pas être utilisés lors de l'extraction d'un "
+"arbre"
+
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "le chemin '%s' n'est pas fusionné"
 
@@ -3902,8 +3949,8 @@
 msgid "new-branch"
 msgstr "nouvelle branche"
 
-msgid "new unparented branch"
-msgstr "nouvelle branche sans parent"
+msgid "new unborn branch"
+msgstr "nouvelle branche non née"
 
 msgid "update ignored files (default)"
 msgstr "mettre à jour les fichiers ignorés (par défaut)"
@@ -4157,9 +4204,6 @@
 "clean.requireForce à true par défaut et ni -i, -n ou -f fourni ; refus de "
 "nettoyer"
 
-msgid "-x and -X cannot be used together"
-msgstr "-x et -X ne peuvent pas être utilisés ensemble"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<options>] [--] <dépôt> [<répertoire>]"
 
@@ -4250,6 +4294,9 @@
 msgid "separate git dir from working tree"
 msgstr "séparer le répertoire git de la copie de travail"
 
+msgid "specify the reference format to use"
+msgstr "spécifier le format de réference à utiliser"
+
 msgid "key=value"
 msgstr "clé=valeur"
 
@@ -4262,12 +4309,6 @@
 msgid "option to transmit"
 msgstr "option à transmettre"
 
-msgid "use IPv4 addresses only"
-msgstr "n'utiliser que des adresses IPv4"
-
-msgid "use IPv6 addresses only"
-msgstr "n'utiliser que des adresses IPv6"
-
 msgid "apply partial clone filters to submodules"
 msgstr "appliquer les filtres de clone partiel aux sous-modules"
 
@@ -4301,6 +4342,10 @@
 msgstr "%s existe et n'est pas un répertoire"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' est un lien symbolique, refus de cloner avec --local"
+
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "échec du démarrage un itérateur sur '%s'"
 
@@ -4374,12 +4419,9 @@
 msgid "You must specify a repository to clone."
 msgstr "Vous devez spécifier un dépôt à cloner."
 
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri est incompatible avec --depth, --shallow-since, et --shallow-"
-"exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "Format de stockage de réf inconnu '%s'"
 
 #, c-format
 msgid "repository '%s' does not exist"
@@ -4513,14 +4555,14 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <rép>] [--append]\n"
 "                       [--split[=<stratégie>]] [--reachable | --stdin-packs "
 "| --stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <options de division>"
+"                       <options-de-division>"
 
 msgid "dir"
 msgstr "répertoire"
@@ -4537,6 +4579,10 @@
 msgstr "Impossible d'ouvrir le graphe de commit '%s'"
 
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "impossible d'ouvrir le graphe de commit '%s'"
+
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "argument de --split non reconnu, %s"
 
@@ -4715,6 +4761,9 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "échec de la mise à jour des fichiers"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "échec du dépaquetage de l'objet arbre HEAD"
 
@@ -4733,9 +4782,6 @@
 msgid "Failed to update main cache tree"
 msgstr "Impossible de mettre à jour l'arbre de cache principal"
 
-msgid "unable to write new_index file"
-msgstr "impossible d'écrire le fichier new_index"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "impossible de faire une validation partielle pendant une fusion."
 
@@ -4774,8 +4820,8 @@
 "qui n'est pas utilisé dans le message de validation actuel"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "impossible de rechercher le commit %s"
+msgid "could not lookup commit '%s'"
+msgstr "impossible de rechercher le commit '%s'"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5145,11 +5191,11 @@
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"le dépôt a été mis à jour, mais impossible d'écrire le fichier\n"
-"new_index. Vérifiez que le disque n'est pas plein ou que le quota\n"
+"le dépôt a été mis à jour, mais impossible d'écrire le nouveau fichier\n"
+"d'index. Vérifiez que le disque n'est pas plein ou que le quota\n"
 "n'a pas été dépassé, puis lancez \"git restore --staged :/\" pour réparer."
 
 msgid "git config [<options>]"
@@ -5841,6 +5887,175 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel ne peut pas être négatif"
 
+msgid "couldn't find remote ref HEAD"
+msgstr "impossible de trouver la référence HEAD distante"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Depuis %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "objet %s non trouvé"
+
+msgid "[up to date]"
+msgstr "[à jour]"
+
+msgid "[rejected]"
+msgstr "[rejeté]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "impossible de récupérer dans la branche extraite"
+
+msgid "[tag update]"
+msgstr "[mise à jour de l'étiquette]"
+
+msgid "unable to update local ref"
+msgstr "impossible de mettre à jour la référence locale"
+
+msgid "would clobber existing tag"
+msgstr "écraserait l'étiquette existante"
+
+msgid "[new tag]"
+msgstr "[nouvelle étiquette]"
+
+msgid "[new branch]"
+msgstr "[nouvelle branche]"
+
+msgid "[new ref]"
+msgstr "[nouvelle référence]"
+
+msgid "forced update"
+msgstr "mise à jour forcée"
+
+msgid "non-fast-forward"
+msgstr "pas en avance rapide"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "impossible d'ouvrir '%s'"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"fetch indique normalement quelles branches ont subi une mise à jour forcée,\n"
+"mais ceci a été désactivé. Pour ré-activer, utilisez le drapeau\n"
+"'--show-forced-updates' ou lancez 'git config fetch.showForcedUpdates true'"
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"%.2f secondes ont été nécessaires pour vérifier les mises à jour forcées ;\n"
+"Vous pouvez utiliser '--no-show-forced-updates' ou lancer\n"
+"'git config fetch.showForcedUpdates false' pour éviter cette vérification\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s n'a pas envoyé tous les objets nécessaires\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr ""
+"%s rejeté parce que les  mises à jour de racines superficielles ne sont pas "
+"permises"
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"des références locales n'ont pas pu être mises à jour ; essayez de lancer\n"
+" 'git remote prune %s' pour supprimer des branches anciennes en conflit"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s sera en suspens)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s est devenu en suspens)"
+
+msgid "[deleted]"
+msgstr "[supprimé]"
+
+msgid "(none)"
+msgstr "(aucun(e))"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "refus de récupérer dans la branche '%s' extraite dans '%s'"
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "la valeur \"%2$s\" de l'option \"%1$s\" est invalide pour %3$s"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "l'option \"%s\" est ignorée pour %s\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s n'est pas un objet valide"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "l'objet %s n'existe pas"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "branches multiples détectées, imcompatible avec --set-upstream"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"impossible de régler la branche amont de HEAD à '%s' depuis '%s' qui ne "
+"pointe sur aucune branche."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "dépôt amont non défini pour la branche de suivi à distance"
+
+msgid "not setting upstream for a remote tag"
+msgstr "dépôt amont non défini pour l'étiquette distante"
+
+msgid "unknown branch type"
+msgstr "type de branche inconnu"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"aucune branche source trouvée.\n"
+"Vous devez spécifier exactement une branche avec l'option --set-upstream"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Récupération de %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "impossible de récupérer %s"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "impossible de récupérer '%s' (code de sortie : %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"Aucun dépôt distant spécifié. Veuillez spécifier une URL ou un nom\n"
+"distant depuis lesquels les nouvelles révisions devraient être récupérées"
+
+msgid "you need to specify a tag name"
+msgstr "Vous devez spécifier un nom d'étiquette"
+
 msgid "fetch from all remotes"
 msgstr "récupérer depuis tous les dépôts distants"
 
@@ -5954,175 +6169,6 @@
 msgid "accept refspecs from stdin"
 msgstr "lire les spécificateurs de référence depuis l'entrée standard"
 
-msgid "couldn't find remote ref HEAD"
-msgstr "impossible de trouver la référence HEAD distante"
-
-#, c-format
-msgid "object %s not found"
-msgstr "objet %s non trouvé"
-
-msgid "[up to date]"
-msgstr "[à jour]"
-
-msgid "[rejected]"
-msgstr "[rejeté]"
-
-msgid "can't fetch into checked-out branch"
-msgstr "impossible de récupérer dans la branche extraite"
-
-msgid "[tag update]"
-msgstr "[mise à jour de l'étiquette]"
-
-msgid "unable to update local ref"
-msgstr "impossible de mettre à jour la référence locale"
-
-msgid "would clobber existing tag"
-msgstr "écraserait l'étiquette existante"
-
-msgid "[new tag]"
-msgstr "[nouvelle étiquette]"
-
-msgid "[new branch]"
-msgstr "[nouvelle branche]"
-
-msgid "[new ref]"
-msgstr "[nouvelle référence]"
-
-msgid "forced update"
-msgstr "mise à jour forcée"
-
-msgid "non-fast-forward"
-msgstr "pas en avance rapide"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "impossible d'ouvrir '%s'"
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"fetch indique normalement quelles branches ont subi une mise à jour forcée,\n"
-"mais ceci a été désactivé. Pour ré-activer, utilisez le drapeau\n"
-"'--show-forced-updates' ou lancez 'git config fetch.showForcedUpdates true'"
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"%.2f secondes ont été nécessaires pour vérifier les mises à jour forcées ;\n"
-"Vous pouvez utiliser '--no-show-forced-updates' ou lancer\n"
-"'git config fetch.showForcedUpdates false' pour éviter cette vérification\n"
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s n'a pas envoyé tous les objets nécessaires\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr ""
-"%s rejeté parce que les  mises à jour de racines superficielles ne sont pas "
-"permises"
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "Depuis %.*s\n"
-
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"des références locales n'ont pas pu être mises à jour ; essayez de lancer\n"
-" 'git remote prune %s' pour supprimer des branches anciennes en conflit"
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s sera en suspens)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s est devenu en suspens)"
-
-msgid "[deleted]"
-msgstr "[supprimé]"
-
-msgid "(none)"
-msgstr "(aucun(e))"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "refus de récupérer dans la branche '%s' extraite dans '%s'"
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "la valeur \"%2$s\" de l'option \"%1$s\" est invalide pour %3$s"
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "l'option \"%s\" est ignorée pour %s\n"
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s n'est pas un objet valide"
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr "l'objet %s n'existe pas"
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "branches multiples détectées, imcompatible avec --set-upstream"
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"impossible de régler la branche amont de HEAD à '%s' depuis '%s' qui ne "
-"pointe sur aucune branche."
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "dépôt amont non défini pour la branche de suivi à distance"
-
-msgid "not setting upstream for a remote tag"
-msgstr "dépôt amont non défini pour l'étiquette distante"
-
-msgid "unknown branch type"
-msgstr "type de branche inconnu"
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"aucune branche source trouvée.\n"
-"Vous devez spécifier exactement une branche avec l'option --set-upstream"
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "Récupération de %s\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr "impossible de récupérer %s"
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "impossible de récupérer '%s' (code de sortie : %d)\n"
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"Aucun dépôt distant spécifié. Veuillez spécifier une URL ou un nom\n"
-"distant depuis lesquels les nouvelles révisions devraient être récupérées"
-
-msgid "you need to specify a tag name"
-msgstr "Vous devez spécifier un nom d'étiquette"
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only nécessite au moins un --negotiation-tip=*"
 
@@ -6238,6 +6284,12 @@
 msgid "print only refs which don't contain the commit"
 msgstr "afficher seulement les références qui ne contiennent pas le commit"
 
+msgid "read reference patterns from stdin"
+msgstr "lire les motifs de références depuis l'entrée standard"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "arguments inconnus fournis avec l'option --stdin"
+
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<config> [--] <arguments>"
 
@@ -6250,6 +6302,10 @@
 msgid "missing --config=<config>"
 msgstr "--config=<config> manquant"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "config incorrecte --config=%s"
+
 msgid "unknown"
 msgstr "inconnu"
 
@@ -6396,19 +6452,28 @@
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "note : %s pointe sur une branche non-née (%s)"
 
-msgid "Checking cache tree"
-msgstr "Vérification de l'arbre cache"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Vérification de l'arbre cache de %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s : pointer sha1 invalide dans l'arbre de cache"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s : pointer sha1 invalide dans l'arbre de cache de %s"
 
 msgid "non-tree in cache-tree"
 msgstr "non-arbre dans l'arbre de cache"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s : pointeur sha1 invalide dans resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s : pointeur sha1 invalide dans resolve-undo de %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "impossible de charger le rev-index pour le paquet '%s'"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "rev-index invalide pour le paquet '%s'"
 
 msgid ""
 "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
@@ -6596,6 +6661,9 @@
 msgid "pack unreferenced objects separately"
 msgstr "empaqueter les objets non référencés séparément"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "avec --cruft, limiter la taille des nouveaux paquets déchets"
+
 msgid "be more thorough (increased runtime)"
 msgstr "être plus consciencieux (durée de traitement allongée)"
 
@@ -6775,12 +6843,6 @@
 msgid "'crontab' died"
 msgstr "'crontab' est mort"
 
-msgid "failed to start systemctl"
-msgstr "échec du démarrage de systemctl"
-
-msgid "failed to run systemctl"
-msgstr "échec pour lancer systemctl"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "échec de la suppression de '%s'"
@@ -6789,6 +6851,12 @@
 msgid "failed to flush '%s'"
 msgstr "échec du flush de '%s'"
 
+msgid "failed to start systemctl"
+msgstr "échec du démarrage de systemctl"
+
+msgid "failed to run systemctl"
+msgstr "échec pour lancer systemctl"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "argument '%s' de --scheduler non reconnu"
@@ -6814,6 +6882,9 @@
 msgid "scheduler to trigger git maintenance run"
 msgstr "planificateur qui lancera les maintenances git"
 
+msgid "failed to set up maintenance schedule"
+msgstr "impossible de mettre en place la planification de maintenance"
+
 msgid "failed to add repo to global config"
 msgstr "échec de l'ajout du dépôt à la config globale"
 
@@ -6845,6 +6916,10 @@
 msgstr "impossible de lire l'arbre (%s)"
 
 #, c-format
+msgid "unable to read tree %s"
+msgstr "impossible de lire l'arbre %s"
+
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "impossible de faire un grep sur un objet de type %s"
 
@@ -6888,8 +6963,8 @@
 msgid "search in subdirectories (default)"
 msgstr "rechercher dans les sous-répertoires (défaut)"
 
-msgid "descend at most <depth> levels"
-msgstr "descendre au plus de <profondeur> dans l'arborescence"
+msgid "descend at most <n> levels"
+msgstr "descendre au plus de <n> niveaux"
 
 msgid "use extended POSIX regular expressions"
 msgstr "utiliser des expressions régulières étendues POSIX"
@@ -7263,10 +7338,6 @@
 msgstr "COLLISION SHA1 TROUVÉE AVEC %s !"
 
 #, c-format
-msgid "unable to read %s"
-msgstr "impossible de lire %s"
-
-#, c-format
 msgid "cannot read existing object info %s"
 msgstr "impossible de lire l'information existante de l'objet %s"
 
@@ -7404,86 +7475,17 @@
 msgid "fsck error in pack objects"
 msgstr "erreur de fsck dans les objets paquets"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "impossible de faire un stat du modèle '%s'"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "impossible d'ouvrir le répertoire '%s'"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "impossible de lire le lien '%s'"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "impossible de créer un lien symbolique de '%s' '%s'"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "impossible de copier '%s' vers '%s'"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "modèle %s ignoré"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "modèles non trouvés dans %s"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "pas de copie des modèles depuis '%s' : %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "nom de branche initiale invalide : '%s'"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "impossible de traiter le fichier de type %d"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "impossible de déplacer %s vers %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "essai de réinitialisation du dépôt avec une empreinte différente"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s existe déjà"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-initialisation : --initial-branch=%s ignoré"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Dépôt Git existant partagé réinitialisé dans %s%s\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Dépôt Git existant réinitialisé dans %s%s\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Dépôt Git vide partagé initialisé dans %s%s\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Dépôt Git vide initialisé dans %s%s\n"
-
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<répertoire-modèle>]\n"
-"         [--separate-git-dir <rép-git>] [--object-format=<format>]\\n\"\n"
-"         [-b <nom-de-branche> | --initial-branch=<nom-de-branche>]\\n\"\n"
+"git init [-q | --quiet] [--bare] [--template=<répertoire-de-modèles>]\n"
+"         [--separate-git-dir <rép-git>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
+"         [-b <nom-de-branch> | --initial-branch=<nom-de-branche>]\n"
 "         [--shared[=<permissions>]] [<répertoire>]"
 
 msgid "permissions"
@@ -7526,11 +7528,12 @@
 
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <symbole>[(=|:)<valeur>])...]\n"
+"                       [(--trailer (<clé>|<alias-de-clé>)"
+"[(=|:)<valeur>])...]\n"
 "                       [--parse] [<fichier>...]"
 
 msgid "edit files in place"
@@ -7539,6 +7542,9 @@
 msgid "trim empty trailers"
 msgstr "éliminer les lignes de fin vides"
 
+msgid "placement"
+msgstr "placement"
+
 msgid "where to place the new trailer"
 msgstr "où placer les nouvelles lignes terminales"
 
@@ -7551,17 +7557,19 @@
 msgid "output only the trailers"
 msgstr "éliminer les lignes terminales vides"
 
-msgid "do not apply config rules"
-msgstr "ne pas appliquer les règles de la configuration"
+msgid "do not apply trailer.* configuration variables"
+msgstr "ne pas appliquer les variables de configuration trailer.*"
 
-msgid "join whitespace-continued values"
-msgstr "joindre les valeurs continuées avec des caractères blancs"
+msgid "reformat multiline trailer values as single-line values"
+msgstr ""
+"reformatter les valeurs de ligne terminales multi-lignes en valeurs sur une "
+"seule ligne"
 
-msgid "set parsing options"
-msgstr "paramètres d'analyse"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "alias pour --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "ne pas traiter spécialement ---"
+msgid "do not treat \"---\" as the end of input"
+msgstr "ne pas traiter \"---\" comme la fin d'entrée"
 
 msgid "trailer(s) to add"
 msgstr "ligne(s) de fin à ajouter"
@@ -7651,6 +7659,10 @@
 msgid "not a range"
 msgstr "ceci n'est pas une plage"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "lecture du fichier de description de branche '%s' impossible"
+
 msgid "cover letter needs email format"
 msgstr "la lettre de motivation doit être au format courriel"
 
@@ -7757,6 +7769,9 @@
 "générer des parties de la lettre d'introduction à partir de la description "
 "de la branche"
 
+msgid "use branch description from file"
+msgstr "utiliser la description de branche dans le fichier"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "utiliser [<préfixe>] au lieu de [PATCH]"
 
@@ -7923,6 +7938,10 @@
 "<branche_amont> manuellement.\n"
 
 #, c-format
+msgid "could not get object info about '%s'"
+msgstr "impossible d'obtenir l'information d'objet pour '%s'"
+
+#, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "mauvais format ls-files : l'élément '%s' ne commence pas par '('"
 
@@ -8070,10 +8089,6 @@
 msgstr "git ls-tree [<options>] <arbre ou apparenté> [<chemin>...]"
 
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "impossible d'obtenir l'information d'objet pour '%s'"
-
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "mauvais format ls-tree : l'élément '%s' ne commence pas par '('"
 
@@ -8200,9 +8215,19 @@
 "git merge-file [<options>] [-L <nom1> [-L <orig> [-L <nom2>]]] <fichier1> "
 "<fichier-orig> <fichier2>"
 
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"l'option diff-algorithm accept \"myers\", \"minimal\", \"patience\" et "
+"\"histogram\""
+
 msgid "send results to standard output"
 msgstr "envoyer les résultats sur la sortie standard"
 
+msgid "use object IDs instead of filenames"
+msgstr "utiliser les IDs d'objet au lieu de noms de fichier"
+
 msgid "use a diff3 based merge"
 msgstr "utiliser une fusion basée sur diff3"
 
@@ -8218,6 +8243,12 @@
 msgid "for conflicts, use a union version"
 msgstr "pour les conflits, utiliser l'ensemble des versions"
 
+msgid "<algorithm>"
+msgstr "<algorithme>"
+
+msgid "choose a diff algorithm"
+msgstr "choisir un algorithme de différence"
+
 msgid "for conflicts, use this marker size"
 msgstr "pour les conflits, utiliser cette taille de marqueur"
 
@@ -8228,6 +8259,13 @@
 msgstr "définir les labels pour fichier1/fichier-orig/fichier2"
 
 #, c-format
+msgid "object '%s' does not exist"
+msgstr "l'objet '%s' n'existe pas"
+
+msgid "Could not write object file"
+msgstr "impossible d'écrire le fichier d'objet"
+
+#, c-format
 msgid "unknown option %s"
 msgstr "option inconnue %s"
 
@@ -8288,11 +8326,18 @@
 msgid "specify a merge-base for the merge"
 msgstr "spécifier une base de fusion pour la fusion"
 
+msgid "option=value"
+msgstr "option=valeur"
+
+msgid "option for selected merge strategy"
+msgstr "option pour la stratégie de fusion sélectionnée"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge est incompatible avec d'autres options"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base est incompatible avec --stdin"
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "option de stratégie inconnue : -X%s"
 
 #, c-format
 msgid "malformed input line: '%s'."
@@ -8362,12 +8407,6 @@
 msgid "merge strategy to use"
 msgstr "stratégie de fusion à utiliser"
 
-msgid "option=value"
-msgstr "option=valeur"
-
-msgid "option for selected merge strategy"
-msgstr "option pour la stratégie de fusion sélectionnée"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "message de validation de la fusion (pour une fusion sans avance rapide)"
@@ -8429,10 +8468,6 @@
 msgstr "Impossible de gérer autre chose que la fusion de deux têtes."
 
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "option de stratégie inconnue : -X%s"
-
-#, c-format
 msgid "unable to write %s"
 msgstr "impossible d'écrire %s"
 
@@ -8733,8 +8768,8 @@
 msgid "can not move directory into itself"
 msgstr "impossible de déplacer un répertoire dans lui-même"
 
-msgid "cannot move directory over file"
-msgstr "impossible de déplacer un répertoire sur un fichier"
+msgid "destination already exists"
+msgstr "la destination existe déjà"
 
 msgid "source directory is empty"
 msgstr "le répertoire source est vide"
@@ -8815,22 +8850,26 @@
 msgstr "git notes [--ref <références-notes>] [list [<object>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <références-notes>] add [-f] [--allow-empty] [-m <message> "
-"| -F <fichier> | (-c | -C) <objet>] [<objet>]"
+"git notes [--ref <référence-notes>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<coupure-paragraphe>] [--[no-]stripspace] [-m "
+"<message> | -F <fichier> | (-c | -C) <objet>] [<objet>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <références-notes>] copy [-f] <depuis-objet> <vers-objet>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <références-notes>] append [--allow-empty] [-m <message> | -"
-"F <fichier> | (-c | -C) <objet>] [<objet>]"
+"git notes [--ref <références-notes>] append [--allow-empty] [--"
+"[no-]separator|--separator=<coupure-paragraphe>] [--[no-]stripspace]-m "
+"<message> | -F <fichier> | (-c | -C) <objet>] [<objet>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <références-notes>] edit [--allow-empty] [<objet>]"
@@ -8962,6 +9001,15 @@
 msgid "replace existing notes"
 msgstr "remplacer les notes existantes"
 
+msgid "<paragraph-break>"
+msgstr "<séparateur-paragraphe>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "insérer <séparateur-paragraphe> entre les paragraphes"
+
+msgid "remove unnecessary whitespace"
+msgstr "retirer les espaces inutiles"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9238,6 +9286,10 @@
 msgstr "inconsistance dans le compte de delta"
 
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "valeur invalide de pack.allowPackReuse : '%s'"
+
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -9321,6 +9373,12 @@
 msgid "bad index version '%s'"
 msgstr "mauvaise version d'index '%s'"
 
+msgid "show progress meter during object writing phase"
+msgstr "afficher la barre de progression durant la phase d'écrite des objets"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "similaire à --all-progress quand la barre de progression est affichée"
+
 msgid "<version>[,<offset>]"
 msgstr "<version>[,<décalage>]"
 
@@ -9475,9 +9533,6 @@
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin ne peut pas être utilisé pour construire un paquet indexable"
 
-msgid "cannot use --filter without --stdout"
-msgstr "impossible d'utiliser --filter sans --stdout"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "impossible d'utiliser --filter avec --stdin-packs"
 
@@ -9491,19 +9546,16 @@
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "impossible d'utiliser --stdin-packs avec --cruft"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "impossible d'utiliser --max-pack-size avec --cruft"
-
 msgid "Enumerating objects"
 msgstr "Énumération des objets"
 
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Total %<PRIu32> (delta %<PRIu32>), réutilisés %<PRIu32> (delta %<PRIu32>), "
-"réutilisés du pack %<PRIu32>"
+"réutilisés du paquet %<PRIu32> (depuis %<PRIuMAX>)"
 
 msgid ""
 "'git pack-redundant' is nominated for removal.\n"
@@ -9518,8 +9570,14 @@
 "sur la ligne de commande pour nous avertir par\n"
 "un courriel à <git@vger.kernel.org>. Merci.\n"
 
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "refus de lancer sans --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <motif>] [--exclude <motif>]"
 
 msgid "pack everything"
 msgstr "empaqueter tout"
@@ -9527,6 +9585,12 @@
 msgid "prune loose refs (default)"
 msgstr "élaguer les références perdues (défaut)"
 
+msgid "references to include"
+msgstr "références à inclure"
+
+msgid "references to exclude"
+msgstr "références à exclure"
+
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
 
@@ -9584,6 +9648,12 @@
 msgid "number of submodules pulled in parallel"
 msgstr "nombre de sous-modules tirés en parallèle"
 
+msgid "use IPv4 addresses only"
+msgstr "n'utiliser que des adresses IPv4"
+
+msgid "use IPv6 addresses only"
+msgstr "n'utiliser que des adresses IPv6"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9694,8 +9764,8 @@
 msgid "pull with rebase"
 msgstr "tirer avec un rebasage"
 
-msgid "please commit or stash them."
-msgstr "veuillez les valider ou les remiser."
+msgid "Please commit or stash them."
+msgstr "Veuillez les valider ou les remiser."
 
 #, c-format
 msgid ""
@@ -9854,46 +9924,43 @@
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées car la pointe de la branche courante est "
+"Les mises à jour ont été rejetées car le sommet de la branche actuelle est "
 "derrière\n"
-"son homologue distant. Intégrez les changements distants (par exemple 'git "
-"pull ...')\n"
-"avant de pousser à nouveau.\n"
+"son homologue distant. Si vous souhaitez intégrer les changements distants,\n"
+"utilisez 'git pull' avant de pousser à nouveau.\n"
 "Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
 "d'information."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées car la pointe de la branche courante est "
+"Les mises à jour ont été rejetées, car le sommet d'une branche poussée est "
 "derrière\n"
-"son homologue distant. Extrayez cette branche et intégrez les changements "
-"distants\n"
-"(par exemple 'git pull ...') avant de pousser à nouveau.\n"
+"son homologue distant. Si vous souhaitez intégrer les changements distants,\n"
+"\"utilisez 'git pull' avant de pousser à nouveau.\n"
 "Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
 "d'information."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées car la branche distante contient du "
-"travail que\n"
-"vous n'avez pas en local. Ceci est généralement causé par un autre dépôt "
-"poussé\n"
-"vers la même référence. Vous pourriez intégrer d'abord les changements "
-"distants\n"
-"(par exemple 'git pull ...') avant de pousser à nouveau.\n"
+"Les mises à jour ont été rejetées car le distant contient du travail que "
+"vous\n"
+"n'avez pas localement. La cause probable est que quelqu'un a déjà poussé sur "
+"la même réf.\n"
+"depuis un autre dépôt. Si vous souhaitez intégrer les changements distants,\n"
+"\"utilisez 'git pull' avant de pousser à nouveau.\n"
 "Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
 "d'information."
 
@@ -9914,15 +9981,18 @@
 "vers un objet qui n'est pas un commit, sans utiliser l'option '--force'.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Les mises à jour ont été rejetées, car la pointe de la branche\n"
-"de suivi a été mise à jour depuis la dernière extraction. Intégrez\n"
-"ces changements localement (par exemple 'git pull ...') avant de\n"
-"forcer à nouveau une mise à jour.\n"
+"Les mises à jour ont été rejetées, car le sommet de la branche de suivi à "
+"distance a\n"
+"été mis à jour depuis la dernière extraction. Si vous souhaitez intégrer les "
+"changements distants,\n"
+"\"utilisez 'git pull' avant de pousser à nouveau.\n"
+"Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus "
+"d'information."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9946,8 +10016,8 @@
 msgid "repository"
 msgstr "dépôt"
 
-msgid "push all refs"
-msgstr "pousser toutes les références"
+msgid "push all branches"
+msgstr "pousser toutes les branches"
 
 msgid "mirror all refs"
 msgstr "refléter toutes les références"
@@ -9955,9 +10025,10 @@
 msgid "delete refs"
 msgstr "supprimer les références"
 
-msgid "push tags (can't be used with --all or --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
 msgstr ""
-"pousser les étiquettes (ne peut pas être utilisé avec --all ou --mirror)"
+"pousser les étiquettes (ne peut pas être utilisé avec --all, --branches ou --"
+"mirror)"
 
 msgid "force updates"
 msgstr "forcer les mises à jour"
@@ -10225,6 +10296,10 @@
 "Résultat, git ne peut pas les rebaser."
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Mode de rebase-merges inconnu : %s"
+
+#, c-format
 msgid "could not switch to %s"
 msgstr "impossible de basculer vers %s"
 
@@ -10240,6 +10315,15 @@
 "type vide non connu '%s' ; les valeurs valides sont \"drop\" (abandonner), "
 "\"keep\" (garder) et \"ask\" (demander)."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges avec un argument chaîne vide est obsolète et cessera de "
+"fonctionner dans une version future de Git. Utilisez à la place --rebase-"
+"merges sans argument, qui a le même effet."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10460,19 +10544,12 @@
 msgid "switch `C' expects a numerical value"
 msgstr "l'option `C' attend un valeur numérique"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Mode inconnu : %s"
-
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy requiert --merge ou --interactive"
-
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"les options d'application sont incompatibles avec rebase.autosquash. "
-"Considérez l'ajout de --no-autosquash"
+"les options d'application sont incompatibles avec rebase.rebaseMerges. "
+"Considérez l'ajout de --no-rebase-merges"
 
 msgid ""
 "apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
@@ -10518,9 +10595,6 @@
 msgid "Does not point to a valid commit '%s'"
 msgstr "Ne pointe pas sur une validation valide : '%s'"
 
-msgid "Please commit or stash them."
-msgstr "Veuillez les valider ou les remiser."
-
 msgid "HEAD is up to date."
 msgstr "HEAD est à jour."
 
@@ -10780,12 +10854,13 @@
 msgid "fetch the remote branches"
 msgstr "rapatrier les branches distantes"
 
-msgid "import all tags and associated objects when fetching"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
 msgstr ""
-"importer toutes les étiquettes et les objets associés lors du rapatriement"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "ou ne rapatrier aucune étiquette (--no-tags)"
+"importer toutes les étiquettes et les objets associés lors de la "
+"récupération\n"
+"ou ne récupérer aucune étiquette (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "branche(s) à suivre"
@@ -11184,6 +11259,10 @@
 msgid "could not remove stale bitmap: %s"
 msgstr "impossible de revenir la bitmap obsolète : %s"
 
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "le préfixe %s ne commence pas avec objdir %s"
+
 msgid "pack everything in a single pack"
 msgstr "empaqueter tout dans un seul paquet"
 
@@ -11197,8 +11276,8 @@
 msgid "approxidate"
 msgstr "date approximative"
 
-msgid "with -C, expire objects older than this"
-msgstr "avec -C, faire expirer les objets plus vieux que celui-ci"
+msgid "with --cruft, expire objects older than this"
+msgstr "avec --cruft, faire expirer les objets plus vieux que celui-ci"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "supprimer les paquets redondants et lancer git-prune-packed"
@@ -11261,17 +11340,20 @@
 msgid "pack prefix to store a pack containing pruned objects"
 msgstr "préfixe de paquet pour stocker un paquet contenant les objets élagués"
 
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "préfixe de paquet pour stocker un paquet contenant les objets filtrés"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "impossible de supprimer les paquets dans un dépôt d'objets précieux"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "l'option '%s' ne peut être utilisé qu'avec '%s'"
+
 msgid "Nothing new to pack."
 msgstr "Rien de neuf à empaqueter."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "le préfixe %s ne commence pas avec objdir %s"
-
-#, c-format
 msgid "renaming pack to '%s' failed"
 msgstr "le renommage du paquet en '%s' a échoué"
 
@@ -11471,6 +11553,77 @@
 msgid "only one pattern can be given with -l"
 msgstr "-l n'accepte qu'un motifs"
 
+msgid "need some commits to replay"
+msgstr "commits requis pour pouvoir rejouer"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto et --advance sont incompatibles"
+
+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"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"impossible de déterminer implicitement s'il y a une opération --advance ou --"
+"onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"impossible d'avancer la cible sur des branches sources multiples parce que "
+"l'ordre ne serait pas total"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "impossible de déterminer implicitement une base correcte pour --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <nouvelle-base> | --advance "
+"<branche>) <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é"
+
+msgid "advance all branches contained in revision-range"
+msgstr "avancer toutes les branches contenues dans la plage-de-révisions"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "une option --onto ou --advance est obligatoire"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"certaines options de parcours de révs seront surchargées car le bit '%s' "
+"dans 'struct rev_info' sera forcé"
+
+msgid "error preparing revisions"
+msgstr "erreur lors de la préparation des révisions"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "rejouer jusqu'au 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é !"
+
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
@@ -11682,18 +11835,12 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "mode inconnu pour --abbrev-ref : %s"
 
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden ne peut être utilisé avec --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden ne peut pas être utilisé avec --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden ne peut pas être utilisé avec --remotes"
-
 msgid "this operation must be run in a work tree"
 msgstr "cette opération doit être effectuée dans un arbre de travail"
 
+msgid "Could not read the index"
+msgstr "Impossible de lire l'index"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "mode inconnu pour --show-object-format : %s"
@@ -11871,6 +12018,9 @@
 msgid "remote name"
 msgstr "nom distant"
 
+msgid "push all refs"
+msgstr "pousser toutes les références"
+
 msgid "use stateless RPC protocol"
 msgstr "utiliser un protocole RPC sans état"
 
@@ -12040,23 +12190,44 @@
 msgstr "Algorithme d'empreinte inconnu"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<motif>...]"
 
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<motif>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <réf>"
+
+msgid "reference does not exist"
+msgstr "la référence n'existe pas"
+
+msgid "failed to look up reference"
+msgstr "échec de la recherche de la référence"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "afficher seulement les étiquettes (peut être combiné avec heads)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "afficher seulement les têtes (peut être combiné avec tags)"
 
+msgid "check for reference existence without resolving"
+msgstr "vérifier l'existence de la référence sans la résoudre"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr ""
 "vérification de référence plus stricte, nécessite un chemin de référence "
@@ -12081,9 +12252,11 @@
 "local"
 
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 
 msgid "this worktree is not sparse"
 msgstr "cet arbre de travail n'est pas clairsemé"
@@ -12213,6 +12386,24 @@
 msgid "error while refreshing working directory"
 msgstr "erreur lors du rafraîchissement du répertoire de travail"
 
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <fichier>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "terminer les fichiers en entrée et en sortie par un caractère NUL"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"quand utilisé avec --rules-file, interpréter les motifs comme des motifs en "
+"mode cone"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "utiliser les motifs dans <fichier> plutôt que les actuels."
+
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<options-de-log>]"
 
@@ -12419,7 +12610,7 @@
 msgstr "remiser seulement les modifications indexées"
 
 msgid "stash in patch mode"
-msgstr "remiser une mode rustine"
+msgstr "remiser en mode rustine"
 
 msgid "quiet mode"
 msgstr "mode silencieux"
@@ -12746,6 +12937,10 @@
 msgstr "Sous-module '%s' non traité"
 
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "impossible de cloner le sous-module '%s' sans une URL"
+
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Impossible de cloner '%s'. Réessai prévu"
 
@@ -12879,6 +13074,9 @@
 "[no-]recommend-shallow] [--reference <dépôt>] [--recursive] [--[no-]single-"
 "branch] [--] [<chemin>...]"
 
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Échec de résolution de HEAD comme référence valide."
+
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<options>] [<chemin>...]"
 
@@ -13357,6 +13555,9 @@
 msgid "write index in this format"
 msgstr "écrire l'index dans ce format"
 
+msgid "report on-disk index format version"
+msgstr "afficher la version du format d'index sur disque"
+
 msgid "enable or disable split index"
 msgstr "activer ou désactiver l'index scindé"
 
@@ -13382,6 +13583,14 @@
 msgid "clear fsmonitor valid bit"
 msgstr "effacer le bit de validité fsmonitor"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version : précédemment %d, mis à %d"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13508,10 +13717,11 @@
 
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <chaîne>]]\n"
-"                 [-b <nouvelle-branche>] <chemin> [<commit-esque>]"
+"                 [--orphan] [(-b | -B) <nouvelle-branche>] <chemin> [<commit-"
+"esque>]"
 
 msgid "git worktree list [-v | --porcelain [-z]]"
 msgstr "git worktree list [-v | --porcelain [-z]]"
@@ -13534,6 +13744,37 @@
 msgid "git worktree unlock <worktree>"
 msgstr "git worktree unlock <arbre-de-travail>"
 
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Aucune branche source possible, activation de '--orphan'"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Si vous vouliez créer un arbre-de-travail contenant une nouvelle branche\n"
+"non-née (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
+"en utilisant le drapeau --orphan :\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Si vous vouliez créer un arbre-de-travail contenant une nouvelle branche\n"
+"non-née (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
+"en utilisant le drapeau --orphan :\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "Suppression de %s/%s : %s"
@@ -13592,6 +13833,10 @@
 msgstr "initialisation"
 
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "impossible de trouver l'arbre-de-travail créé '%s'"
+
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "Préparation de l'arbre de travail (nouvelle branche '%s')"
 
@@ -13606,9 +13851,32 @@
 msgstr "Préparation de l'arbre de travail (extraction de '%s')"
 
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "non joignable : référence invalide : %s"
+
+#, c-format
 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 ""
+"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 ""
+"Aucune réf locale ou distant n'existe malgré la présence d'au moins un "
+"distant,\n"
+"on arrête ; utilisez 'add -f' pour passe outre ou récupérer le distant en "
+"premier"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "extraire la <branche> même si elle est déjà extraite dans une autre copie de "
@@ -13620,6 +13888,9 @@
 msgid "create or reset a branch"
 msgstr "créer ou réinitialiser une branche"
 
+msgid "create unborn branch"
+msgstr "créer une branche non née"
+
 msgid "populate the new working tree"
 msgstr "remplissage de la nouvelle copie de travail"
 
@@ -13639,6 +13910,11 @@
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "les options '%s', '%s' et '%s' ne peuvent pas être utilisées ensemble"
 
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr ""
+"l'option '%s' et des commit-esques ne peuvent pas être utilisés ensemble"
+
 msgid "added with --lock"
 msgstr "ajouté avec --lock"
 
@@ -13876,6 +14152,14 @@
 msgstr[0] "Le colis exige cette référence :"
 msgstr[1] "Le colis exige ces %<PRIuMAX> références :"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "algorithme d'empreinte du colis : %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Le colis utilise ce filtre : %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "impossible de dupliquer le descripteur de liasse"
 
@@ -13911,16 +14195,20 @@
 msgstr "l'identifiant de terminaison de tronçon apparaît plus tôt qu'attendu"
 
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "id de tronçon %<PRIx32> non alignés sur %d octets"
+
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
-msgstr "décalage(s) de section incorrect(s) %<PRIx64> et %<PRIx64>"
+msgstr "décalage(s) de tronçon incorrect(s) %<PRIx64> et %<PRIx64>"
 
 #, c-format
 msgid "duplicate chunk ID %<PRIx32> found"
-msgstr "ID de section dupliqué %<PRIx32>"
+msgstr "ID de tronçon dupliqué %<PRIx32>"
 
 #, c-format
 msgid "final chunk has non-zero id %<PRIx32>"
-msgstr "la section finale a un id non nul %<PRIx32>"
+msgstr "le tronçon final a un id non nul %<PRIx32>"
 
 msgid "invalid hash version"
 msgstr "version d'empreinte invalide"
@@ -13965,10 +14253,8 @@
 msgid "Move objects and refs by archive"
 msgstr "Déplacer les objets et références par archive"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Fournir le contenu ou l'information de type et taille pour les objets du "
-"dépôt"
+msgid "Provide contents or details of repository objects"
+msgstr "Fournir le contenu ou les détails des objets du dépôt"
 
 msgid "Display gitattributes information"
 msgstr "Afficher les informations gitattributes"
@@ -14111,9 +14397,9 @@
 msgid "A portable graphical interface to Git"
 msgstr "Une interface graphique portable pour Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
+msgid "Compute object ID and optionally create an object from a file"
 msgstr ""
-"Calculer l'ID d'objet et créer optionnellement un blob depuis un fichier"
+"Calculer l'ID d'objet et créer optionnellement un objet depuis un fichier"
 
 msgid "Display help information about Git"
 msgstr "Afficher l'information d'aide à propos de Git"
@@ -14268,6 +14554,11 @@
 msgid "Create, list, delete refs to replace objects"
 msgstr "Créer, lister, supprimer des référence pour remplacer des objets"
 
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPÉRIMENTAL ; rejoue des commits sur une nouvelle base, fonctionne aussi "
+"avec les dépôts nus"
+
 msgid "Generates a summary of pending changes"
 msgstr "Générer une résumé des modifications en attentes"
 
@@ -14392,7 +14683,7 @@
 msgid "Display version information about Git"
 msgstr "Afficher l'information de version à propos de Git"
 
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "Afficher les journaux avec la différence que chaque commit introduit"
 
 msgid "Manage multiple working trees"
@@ -14429,7 +14720,7 @@
 msgstr "le format du fichier de colis"
 
 msgid "Chunk-based file formats"
-msgstr "format de fichier avec des sections"
+msgstr "format de fichier avec des tronçons"
 
 msgid "Git commit-graph format"
 msgstr "format de graphe de commit de Git"
@@ -14510,6 +14801,35 @@
 msgid "commit-graph file is too small"
 msgstr "le graphe de commit est trop petit"
 
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr ""
+"le tronçon de distribution d'oid du graphe de commit n'a pas la bonne taille"
+
+msgid "commit-graph fanout values out of order"
+msgstr "les valeurs de distribution du graphe de commit sont désordonnées"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr ""
+"le tronçon de recherche de l'OID du graphe de commits n'a pas la bonne taille"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "le tronçon de données du graphe de commit n'a pas la bonne taille"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "le tronçon des générations du graphe de commit n'a pas la bonne taille"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr ""
+"le tronçon d'index des chemins modifiés du graphe de commit est trop petit"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"tronçon de chemin modifié dans le fichier de graphe de commits trop petit "
+"((%<PRIuMAX> < %<PRIuMAX>)) ignoré"
+
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr ""
@@ -14527,15 +14847,40 @@
 
 #, c-format
 msgid "commit-graph file is too small to hold %u chunks"
-msgstr "le graphe de commit est trop petit pour contenir %u sections"
+msgstr "le graphe de commit est trop petit pour contenir %u tronçons"
+
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"le tronçon de distribution des OID requis du graphe de commits est manquant "
+"ou corrompu"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"le tronçon de recherche OID requis par le graphe de commits est manquant ou "
+"corrompu"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"le tronçon d'étalement OID requis par le graphe de commits est manquant ou "
+"corrompu"
 
 msgid "commit-graph has no base graphs chunk"
-msgstr "le graphe de commit n'a pas de section de graphes de base"
+msgstr "le graphe de commit n'a pas de tronçon de graphes de base"
+
+msgid "commit-graph base graphs chunk is too small"
+msgstr "le tronçon de graphes de base du graphe de commit est trop petit"
 
 msgid "commit-graph chain does not match"
 msgstr "la chaîne de graphe de commit ne correspond pas"
 
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "nombre de commits dans le graphe de base trop haut : %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "la chaine du graphe de commit est trop petite"
+
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr ""
 "chaîne de graphe de commit invalide : la ligne '%s' n'est pas une empreinte"
@@ -14557,6 +14902,14 @@
 "le graphe de commits requiert des données de génération de débordement mais "
 "n'en contient pas"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr ""
+"les données de génération de débordement du graphe de commits sont trop "
+"petites"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "pointeur hors-gamme d'arêtes supplémentaires du graphe de commits"
+
 msgid "Loading known commits in commit graph"
 msgstr "Lecture des commits connus dans un graphe de commit"
 
@@ -14626,6 +14979,15 @@
 msgid "failed to rename temporary commit-graph file"
 msgstr "impossible de renommer le fichier temporaire de graphe de commits"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr ""
+"impossible de fusionner des graphes avec %<PRIuMAX>, %<PRIuMAX> commits"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "impossible de fusionner le graphe %s, trop de commits : %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Analyse des commits de fusion"
 
@@ -14658,9 +15020,6 @@
 msgid "failed to parse commit %s from commit-graph"
 msgstr "échec de l'analyse le commit %s depuis le graphe de commits"
 
-msgid "Verifying commits in commit graph"
-msgstr "Verification des commits dans le graphe de commits"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14688,20 +15047,6 @@
 "la liste de parents du graphe de commit pour le commit %s se termine trop tôt"
 
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"le graphe de commit a un numéro de génération nul pour le commit %s, mais "
-"non-nul ailleurs"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"le graphe de commit a un numéro de génération non-nul pour le commit %s, "
-"mais nul ailleurs"
-
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
 "la génération du graphe de commit pour le commit %s est %<PRIuMAX> < "
@@ -14714,6 +15059,17 @@
 "%<PRIuMAX> != %<PRIuMAX>"
 
 #, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"le graphe de commit a des numéros de génération à la fois nuls et non-nuls "
+"(par ex. les commits %s et %s)"
+
+msgid "Verifying commits in commit graph"
+msgstr "Verification des commits dans le graphe de commits"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s n'est pas un commit !"
 
@@ -14737,6 +15093,12 @@
 "\"git config advice.graftFileDeprecated false\""
 
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"le commit '%s' existe dans la graphe de commit mais pas dans la base de "
+"données d'objets"
+
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "La validation %s a une signature GPG non fiable, prétendument par %s."
 
@@ -15146,8 +15508,8 @@
 msgid "bad zlib compression level %d"
 msgstr "niveau de compression zlib incorrect %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar ne devrait être qu'un unique caractère"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar ne devrait être qu'un unique caractère ASCII"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -15183,10 +15545,6 @@
 msgid "unable to resolve config blob '%s'"
 msgstr "impossible de résoudre le blob de config '%s'"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "échec de l'analyse de %s"
-
 msgid "unable to parse command-line config"
 msgstr "lecture de la configuration de ligne de commande impossible"
 
@@ -15262,6 +15620,12 @@
 msgstr "nom de section invalide : %s"
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"refus de travailler avec des lignes trop longues dans '%s' à la ligne "
+"%<PRIuMAX>"
+
+#, c-format
 msgid "missing value for '%s'"
 msgstr "valeur manquante pour '%s'"
 
@@ -15663,9 +16027,6 @@
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base ne fonctionne pas avec des plages"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base ne fonctionne qu'avec des commits"
-
 msgid "unable to get HEAD"
 msgstr "impossible d'acquérir HEAD"
 
@@ -15675,6 +16036,12 @@
 msgid "multiple merge bases found"
 msgstr "bases multiples de fusion trouvées"
 
+msgid "cannot compare stdin to a directory"
+msgstr "impossible de comparer stdin à un répertoire"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "impossible de réparer un tuyau nommé à un répertoire"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<options>] <chemin> <chemin>"
 
@@ -15723,6 +16090,10 @@
 "Valeur inconnue pour la variable de configuration 'diff.submodule' : '%s'"
 
 #, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "valeur inconnue pour la config '%s' : %s"
+
+#, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
@@ -15734,6 +16105,14 @@
 msgid "external diff died, stopping at %s"
 msgstr "l'application de diff externe a disparu, arrêt à %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow a besoin d'une spécification de chemin unique"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr ""
+"le spécificateur magique de chemin n'est pas pris en charge par --follow : %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr ""
@@ -15752,9 +16131,6 @@
 "les options '%s' et '%s' ne peuvent pas être utilisées ensemble, utilisez "
 "'%s' avec '%s' et '%s'"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow a besoin d'une spécification de chemin unique"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "valeur invalide de --stat : %s"
@@ -15799,13 +16175,6 @@
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "mode invalide '%s' dans --color-moved-ws"
 
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"l'option diff-algorithm accept \"myers\", \"minimal\", \"patience\" et "
-"\"histogram\""
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "argument invalide pour %s"
@@ -15849,8 +16218,8 @@
 msgid "output only the last line of --stat"
 msgstr "afficher seulement la dernière ligne de --stat"
 
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
 
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
@@ -15861,8 +16230,8 @@
 msgid "synonym for --dirstat=cumulative"
 msgstr "synonyme pour --dirstat=cumulative"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "synonyme pour --dirstat=files,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "synonyme pour --dirstat=files,<param1>,<param2>..."
 
 msgid "warn if changes introduce conflict markers or whitespace errors"
 msgstr ""
@@ -15950,6 +16319,9 @@
 msgid "do not show any source or destination prefix"
 msgstr "n'afficher aucun préfixe, ni de source, ni de destination"
 
+msgid "use default prefixes a/ and b/"
+msgstr "utiliser les préfixes par défaut a/ et b/"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "afficher le contexte entre les sections à concurrence du nombre de ligne "
@@ -16047,12 +16419,6 @@
 msgstr ""
 "générer un diff en utilisant l'algorithme de différence \"histogramme\""
 
-msgid "<algorithm>"
-msgstr "<algorithme>"
-
-msgid "choose a diff algorithm"
-msgstr "choisir un algorithme de différence"
-
 msgid "<text>"
 msgstr "<texte>"
 
@@ -16271,6 +16637,14 @@
 "suggestion : en attente de la fermeture du fichier par votre éditeur de "
 "texte…%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "impossible d'écrire dans '%s'"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "impossible d'éditer '%s'"
+
 msgid "Filtering content"
 msgstr "Filtrage du contenu"
 
@@ -16570,6 +16944,10 @@
 msgstr "aucune clé de configuration fournie pour --config-env\n"
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "aucune source d'attribut fournie pour --attr-source\n"
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "option inconnue : %s\n"
 
@@ -17103,12 +17481,12 @@
 "existent :\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "Échec à l'exécution de la fusion interne"
+msgid "failed to execute internal merge"
+msgstr "échec à l'exécution de la fusion interne"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Impossible d'ajouter %s à la base de données"
+msgid "unable to add %s to database"
+msgstr "impossible d'ajouter %s à la base de données"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17551,6 +17929,21 @@
 msgstr "l'étalement de l'OID d'index multi-paquet n'a pas la bonne taille"
 
 #, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"étalement oid en désordre : étalement[%d] = %<PRIx32> > %<PRIx32> = "
+"étalement[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr ""
+"le tronçon de recherche de l'OID d'index multi-paquet n'a pas la bonne taille"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr ""
+"le tronçon de décalage de l'OID d'index multi-paquet n'a pas la bonne taille"
+
+#, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "le fichier d'index multi-paquet %s est trop petit"
 
@@ -17570,17 +17963,28 @@
 "la version d'empreinte d'index multi-paquet %u ne correspond pas à la "
 "version %u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "index multi-paquet manque de tronçon de nom de paquet"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"le tronçon de nom de paquet requis par l'index multi-paquet est manquant ou "
+"corrompu"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "index multi-paquet manque de tronçon de d'étalement OID requis"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"le tronçon d'étalement OID requis de l'index multi-paquet est manquant ou "
+"corrompu"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "index multi-paquet manque de tronçon de recherche OID requis"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"le tronçon de recherche OID requis de l'index multi-paquet est manquant ou "
+"corrompu"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "index multi-paquet manque de tronçon de décalage d'objet requis"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"le tronçon de décalage d'objet requis de l'index multi-paquet est manquant "
+"ou corrompu"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "le tronçon de nom de paquet de l'index multi-paquet est trop petit"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17591,9 +17995,19 @@
 msgid "bad pack-int-id: %u (%u total packs)"
 msgstr "mauvais pack-int-id : %u (%u paquets au total)"
 
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "le MIDX ne contient pas de tronçon BTMP"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "impossible d'ouvrir le paquet bitmappé %<PRIu32>"
+
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr ""
-"l'index multi-paquet stock un décalage en 64-bit, mais off_t est trop petit"
+"l'index multi-paquet stocke un décalage en 64-bit, mais off_t est trop petit"
+
+msgid "multi-pack-index large offset out of bounds"
+msgstr "le grand décalage de l'index-multi-paquet est hors limite"
 
 #, c-format
 msgid "failed to add packfile '%s'"
@@ -17673,13 +18087,6 @@
 msgid "Looking for referenced packfiles"
 msgstr "Recherche de fichiers paquets référencés"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"étalement oid en désordre : étalement[%d] = %<PRIx32> > %<PRIx32> = "
-"étalement[%d]"
-
 msgid "the midx contains no oid"
 msgstr "le midx ne contient aucun oid"
 
@@ -18206,6 +18613,9 @@
 msgid "could not open pack %s"
 msgstr "impossible d'ouvrir le paquet '%s'"
 
+msgid "could not determine MIDX preferred pack"
+msgstr "impossible de déterminer le paquet préféré de MIDX"
+
 #, c-format
 msgid "preferred pack (%s) is invalid"
 msgstr "le paquet préféré (%s) est invalide"
@@ -18228,6 +18638,10 @@
 msgstr "bitmap ewah corrompue : entête tronqué pour la bitmap du commit '%s'"
 
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "impossible de charger le paquet : '%s', pack-reuse désactivé"
+
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "objet '%s' non trouvé dans les bitmaps de type"
 
@@ -18265,6 +18679,10 @@
 msgstr "impossible de récupérer l'utilisation du disque de '%s'"
 
 #, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "le fichier bitmap '%s' a une somme de contrôle invalide"
+
+#, c-format
 msgid "mtimes file %s is too small"
 msgstr "le fichier de mtimes %s est trop petit"
 
@@ -18304,6 +18722,20 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "le fichier d'index inverse %s a un id d'empreinte non géré %<PRIu32>"
 
+msgid "invalid checksum"
+msgstr "somme de contrôle invalide"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "position de rev-index invalide à %<PRIu64> : %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+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"
 
@@ -18356,14 +18788,6 @@
 msgstr "%s a besoin d'une valeur"
 
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s est incompatible avec %s"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s est incompatible avec toute autre option"
-
-#, c-format
 msgid "%s takes no value"
 msgstr "%s n'accepte aucune valeur"
 
@@ -18446,6 +18870,10 @@
 msgid "-NUM"
 msgstr "-NUM"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "opposé de --no-%s"
+
 msgid "expiry-date"
 msgstr "date-d'expiration"
 
@@ -18477,6 +18905,14 @@
 "caractère NUL"
 
 #, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "valeur booléenne d'environnement invalide '%s' pour '%s'"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "échec de l'analyse de %s"
+
+#, c-format
 msgid "Could not make %s writable by group"
 msgstr "Impossible de rendre %s inscriptible pour le groupe"
 
@@ -18527,6 +18963,10 @@
 msgstr "%s : 'literal' et 'glob' sont incompatibles"
 
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' est hors de l'arbre de répertoire"
+
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s : '%s' est hors du dépôt à '%s'"
 
@@ -18660,6 +19100,13 @@
 msgstr "impossible d'analyser le journal pour '%s'"
 
 #, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "sommet supplémentaire dégénéré invalide : '%s'"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "impossible d'énumérer les objets récents additionnels"
+
+#, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr "pas d'ajout d'alias de fichier '%s'(« %s » existe déjà dans l'index)"
 
@@ -18681,10 +19128,6 @@
 msgstr "impossible d'ajouter '%s' à l'index"
 
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "fstat de '%s' impossible"
-
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "'%s' existe à la fois comme un fichier et un répertoire"
 
@@ -18792,10 +19235,6 @@
 msgstr "échec de conversion d'un index clairsemé"
 
 #, c-format
-msgid "could not stat '%s'"
-msgstr "impossible de stat '%s'"
-
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "impossible d'ouvrir le répertoire git : %s"
 
@@ -18811,6 +19250,14 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s : impossible de revenir à l'étape 0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "état de diff inattendu %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "suppression de '%s'\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -19014,6 +19461,22 @@
 msgstr "valeur positive attendue contents:lines=%s"
 
 #, c-format
+msgid "argument expected for %s"
+msgstr "argument attendu pour %s"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "valeur positive attendue %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "impossible d'analyser complètement %s=%s"
+
+#, c-format
+msgid "value expected %s="
+msgstr "valeur attendue %s="
+
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "valeur positive attendue '%s' dans %%(%s)"
 
@@ -19038,6 +19501,10 @@
 msgstr "valeur positive attendue avec l'atome %%(align)"
 
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "format attendu : %%(ahead-behind:<commit-esque>)"
+
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "nom de champ malformé %.*s"
 
@@ -19083,6 +19550,9 @@
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s ne peut pas être utilisé avec --python, --shell, --tcl"
 
+msgid "failed to run 'describe'"
+msgstr "échec pour lancer 'describe'"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(aucune branche, rebasage de %s)"
@@ -19144,6 +19614,9 @@
 msgid "field name to sort on"
 msgstr "nom du champ servant à trier"
 
+msgid "exclude refs which match pattern"
+msgstr "exclure les références correspondant à <motif>"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "'%s' n'est pas un journal de références"
@@ -19231,10 +19704,6 @@
 msgstr "impossible de traiter '%s' et '%s' en même temps"
 
 #, c-format
-msgid "could not remove reference %s"
-msgstr "impossible de supprimer la référence %s"
-
-#, c-format
 msgid "could not delete reference %s: %s"
 msgstr "impossible de supprimer la référence %s : %s"
 
@@ -19429,7 +19898,7 @@
 "Neither worked, so we gave up. You must fully qualify the ref."
 msgstr ""
 "La destination que vous avez fournie n'est pas un nom de référence complète\n"
-"(c'est-à-dire commençant par \"ref/\"). Essai d'approximation par :\n"
+"(c'est-à-dire commençant par \"refs/\"). Essai d'approximation par :\n"
 "\n"
 "- Recherche d'une référence qui correspond à '%s' sur le serveur distant.\n"
 "- Vérification si la <source> en cours de poussée ('%s')\n"
@@ -19597,9 +20066,10 @@
 "Votre branche et '%s' ont divergé,\n"
 "et ont %d et %d commits différents chacune respectivement.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
 msgstr ""
-"  (utilisez \"git pull\" pour fusionner la branche distante dans la vôtre)\n"
+"  (utilisez \"git pull\" pour intégrer la branche distante avec la vôtre)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19672,10 +20142,6 @@
 msgstr "aucune résolution enregistrée pour '%s'"
 
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "impossible de délier '%s'"
-
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Pré-image mise à jour pour '%s'"
 
@@ -19716,6 +20182,10 @@
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<fichier-paquet> n'est plus géré"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "option invalide '%s' en mode --stdin"
+
 msgid "your current branch appears to be broken"
 msgstr "votre branche actuelle semble cassée"
 
@@ -19802,8 +20272,16 @@
 msgid "only download metadata for the branch that will be checked out"
 msgstr "ne télécharger les méta-données que pour la branche qui sera extraite"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<options>] [--] <dépôt> [<répertoire>]"
+msgid "create repository within 'src' directory"
+msgstr "Créer un dépôt dans le repertoire 'src'"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <branche-principale>] [--full-"
+"clone]\n"
+"\t[--[no-]src] <url> [<enrôlement>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19854,12 +20332,28 @@
 msgstr "impossible de supprimé le scalar.repo obsolète '%s'"
 
 #, c-format
-msgid "removing stale scalar.repo '%s'"
+msgid "removed stale scalar.repo '%s'"
 msgstr "suppression du scalar.repo obsolète '%s'"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "dépôt git parti dans '%s'"
+msgid "repository at '%s' has different owner"
+msgstr "le dépôt dans '%s' a un propriétaire différent"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "le dépôt dans '%s' a un problème de format"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "dépôt non trouvé dans '%s'"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"pour désinscrire ce dépôt de Scalar, lancez\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -20014,10 +20508,6 @@
 msgstr "impossible de verrouiller '%s'"
 
 #, c-format
-msgid "could not write to '%s'"
-msgstr "impossible d'écrire dans '%s'"
-
-#, c-format
 msgid "could not write eol to '%s'"
 msgstr "impossible d'écrire la fin de ligne dans '%s'"
 
@@ -20271,10 +20761,6 @@
 msgstr "%s : impossible d'analyser le commit parent %s"
 
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "impossible de renommer '%s' en '%s'"
-
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "impossible d'annuler %s... %s"
 
@@ -20382,9 +20868,6 @@
 msgid "could not create sequencer directory '%s'"
 msgstr "impossible de créer le répertoire de séquenceur '%s'"
 
-msgid "could not lock HEAD"
-msgstr "impossible de verrouiller HEAD"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "aucun picorage ou retour en cours"
 
@@ -20480,20 +20963,20 @@
 "git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "et a mis à jour l'index ou l'arbre de travail\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "et a mis à jour l'index ou l'arbre de travail.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "l'exécution a réussi : %s\n"
-"mais a laissé des modifications dans l'index ou la copie de travail\n"
+"mais a laissé des modifications dans l'index ou la copie de travail.\n"
 "Validez ou remisez vos modification, puis lancez\n"
 "\n"
 "  git rebase --continue\n"
@@ -20603,6 +21086,9 @@
 msgstr ""
 "Un remisage automatique existe ; création d'une nouvelle entrée de remisage."
 
+msgid "autostash reference is a symref"
+msgstr "la référence d'auto-remisage est une symref"
+
 msgid "could not detach HEAD"
 msgstr "impossible de détacher HEAD"
 
@@ -20635,14 +21121,14 @@
 "    git rebase --continue\n"
 
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Rebasage (%d/%d)%s"
-
-#, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Arrêt à %s... %.*s\n"
 
 #, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Rebasage (%d/%d)%s"
+
+#, c-format
 msgid "unknown command %d"
 msgstr "commande inconnue %d"
 
@@ -20886,6 +21372,83 @@
 msgstr "échec du setsid"
 
 #, c-format
+msgid "cannot stat template '%s'"
+msgstr "impossible de faire un stat du modèle '%s'"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "impossible d'ouvrir le répertoire '%s'"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "impossible de lire le lien '%s'"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "impossible de créer un lien symbolique de '%s' '%s'"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "impossible de copier '%s' vers '%s'"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "modèle %s ignoré"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "modèles non trouvés dans %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "pas de copie des modèles depuis '%s' : %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "nom de branche initiale invalide : '%s'"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-initialisation : --initial-branch=%s ignoré"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "impossible de traiter le fichier de type %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "impossible de déplacer %s vers %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "essai de réinitialisation du dépôt avec une empreinte différente"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"essai de réinitialisation du dépôt avec un format de stockage de références "
+"différent"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s existe déjà"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Dépôt Git existant partagé réinitialisé dans %s%s\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Dépôt Git existant réinitialisé dans %s%s\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Dépôt Git vide partagé initialisé dans %s%s\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Dépôt Git vide initialisé dans %s%s\n"
+
+#, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
 msgstr "l'entrée d'index est un répertoire, mais pas clairsemé (%08x)"
 
@@ -20937,10 +21500,6 @@
 msgstr[1] "%u octets/s"
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr "impossible d'éditer '%s'"
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "nom de sous-module suspicieux %s ignoré"
 
@@ -21142,12 +21701,6 @@
 msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr "nombre d'entrées dans l'arbre de cache à invalider (par défaut, 0)"
 
-msgid "unhandled options"
-msgstr "options non gérées"
-
-msgid "error preparing revisions"
-msgstr "erreur lors de la préparation des révisions"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "le commit %s n'est pas marqué joignable"
@@ -21307,9 +21860,6 @@
 msgid "invalid remote service path"
 msgstr "chemin de service distant invalide"
 
-msgid "operation not supported by protocol"
-msgstr "option non supportée par le protocole"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "impossible de se connecter au sous-service %s"
@@ -21442,10 +21992,6 @@
 msgstr "le support du protocole v2 n'est pas encore implanté"
 
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "valeur inconnue pour la config '%s' : %s"
-
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "transport '%s' non permis"
 
@@ -21499,6 +22045,9 @@
 msgstr ""
 "impossible de récupérer la liste de bundle-uris annoncée par le serveur"
 
+msgid "operation not supported by protocol"
+msgstr "option non supportée par le protocole"
+
 msgid "too-short tree object"
 msgstr "objet arbre trop court"
 
@@ -21899,6 +22448,9 @@
 msgid "unable to get current working directory"
 msgstr "impossible d'accéder au répertoire de travail courant"
 
+msgid "unable to get random bytes"
+msgstr "impossible d'acquérir des octets aléatoires"
+
 msgid "Unmerged paths:"
 msgstr "Chemins non fusionnés :"
 
@@ -22345,6 +22897,10 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "%s impossible : votre index contient des modifications non validées."
 
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "style inconnu '%s' pour '%s'"
+
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
 "merge"
@@ -22538,13 +23094,13 @@
 "Effacez le corps si vous ne souhaitez pas envoyer un résumé.\n"
 
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Échec à l'ouverture de %s : %s"
-
-#, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "Échec à l'ouverture de %s.final : %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Échec à l'ouverture de %s : %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Le courriel de résumé étant vide, il a été ignoré\n"
 
@@ -22698,13 +23254,17 @@
 msgstr "(%s) Impossible d'exécuter '%s'"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Ajout de %s : %s depuis : '%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Sortie malformée depuis '%s'"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) échec de la fermeture du pipe vers '%s'"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Ajout de %s : %s depuis : '%s'\n"
+
 msgid "cannot send message as 7bit"
 msgstr "impossible d'envoyer un message comme 7bit"
 
@@ -22741,3 +23301,159 @@
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Souhaitez-vous réellement envoyer %s ?[y|N] : "
+
+#~ msgid "-x and -X cannot be used together"
+#~ msgstr "-x et -X ne peuvent pas être utilisés ensemble"
+
+#~ msgid ""
+#~ "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+#~ "exclude"
+#~ msgstr ""
+#~ "--bundle-uri est incompatible avec --depth, --shallow-since, et --shallow-"
+#~ "exclude"
+
+#~ msgid "--merge-base is incompatible with --stdin"
+#~ msgstr "--merge-base est incompatible avec --stdin"
+
+#~ msgid ""
+#~ "apply options are incompatible with rebase.autoSquash.  Consider adding --"
+#~ "no-autosquash"
+#~ msgstr ""
+#~ "les options d'application sont incompatibles avec rebase.autoSquash. "
+#~ "Considérez l'ajout de --no-autosquash"
+
+#~ msgid "--exclude-hidden cannot be used together with --branches"
+#~ msgstr "--exclude-hidden ne peut être utilisé avec --branches"
+
+#~ msgid "--exclude-hidden cannot be used together with --tags"
+#~ msgstr "--exclude-hidden ne peut pas être utilisé avec --tags"
+
+#~ msgid "--exclude-hidden cannot be used together with --remotes"
+#~ msgstr "--exclude-hidden ne peut pas être utilisé avec --remotes"
+
+#, c-format
+#~ msgid "only one of '%s', '%s' or '%s' can be given"
+#~ msgstr "les options '%s', '%s' et '%s' sont mutuellement exclusives"
+
+#, c-format
+#~ msgid "'%s' and '%s' cannot be used together"
+#~ msgstr "'%s' et '%s' ne peuvent pas être utilisées ensemble"
+
+#, c-format
+#~ msgid "options '%s', and '%s' cannot be used together"
+#~ msgstr "les options '%s' et '%s' ne peuvent pas être utilisées ensemble"
+
+#~ msgid "<commit-ish>"
+#~ msgstr "<commit-esque>"
+
+#, c-format
+#~ msgid "%s is incompatible with %s"
+#~ msgstr "%s est incompatible avec %s"
+
+#, c-format
+#~ msgid "could not remove reference %s"
+#~ msgstr "impossible de supprimer la référence %s"
+
+#~ msgid "unhandled options"
+#~ msgstr "options non gérées"
+
+#, c-format
+#~ msgid "options '%s=%s' and '%s=%s' cannot be used together"
+#~ msgstr ""
+#~ "les options '%s=%s' et '%s=%s' ne peuvent pas être utilisées ensemble"
+
+#, c-format
+#~ msgid "%s : incompatible with something else"
+#~ msgstr "%s est incompatible avec toute autre option"
+
+#~ msgid "Could not write patch"
+#~ msgstr "Impossible d'écrire le patch"
+
+#, c-format
+#~ msgid "Could not stat '%s'"
+#~ msgstr "Stat de '%s' impossible"
+
+#, c-format
+#~ msgid "Cannot delete branch '%s' checked out at '%s'"
+#~ msgstr "Impossible de supprimer la branche '%s' extraite dans '%s'"
+
+#~ msgid "unable to write new_index file"
+#~ msgstr "impossible d'écrire le fichier new_index"
+
+#~ msgid "do not apply config rules"
+#~ msgstr "ne pas appliquer les règles de la configuration"
+
+#~ msgid "join whitespace-continued values"
+#~ msgstr "joindre les valeurs continuées avec des caractères blancs"
+
+#~ msgid "set parsing options"
+#~ msgstr "paramètres d'analyse"
+
+#~ msgid "cannot move directory over file"
+#~ msgstr "impossible de déplacer un répertoire sur un fichier"
+
+#~ msgid "cannot use --filter without --stdout"
+#~ msgstr "impossible d'utiliser --filter sans --stdout"
+
+#~ msgid "cannot use --max-pack-size with --cruft"
+#~ msgstr "impossible d'utiliser --max-pack-size avec --cruft"
+
+#~ msgid "--strategy requires --merge or --interactive"
+#~ msgstr "--strategy requiert --merge ou --interactive"
+
+#, c-format
+#~ msgid ""
+#~ "commit-graph has generation number zero for commit %s, but non-zero "
+#~ "elsewhere"
+#~ msgstr ""
+#~ "le graphe de commit a un numéro de génération nul pour le commit %s, mais "
+#~ "non-nul ailleurs"
+
+#~ msgid "--merge-base only works with commits"
+#~ msgstr "--merge-base ne fonctionne qu'avec des commits"
+
+#~ msgid "scalar clone [<options>] [--] <repo> [<dir>]"
+#~ msgstr "scalar clone [<options>] [--] <dépôt> [<répertoire>]"
+
+#, c-format
+#~ msgid "could not rename '%s' to '%s'"
+#~ msgstr "impossible de renommer '%s' en '%s'"
+
+#, c-format
+#~ msgid "It is not possible to %s because you have unmerged files."
+#~ msgstr "%s n'est pas possible car vous avez des fichiers non fusionnés."
+
+#~ msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
+#~ msgstr ""
+#~ "ne pas passer l'option --keep-cr à git-mailsplit indépendamment de am."
+#~ "keepcr"
+
+#~ msgid ""
+#~ "Updates were rejected because the tip of the remote-tracking\n"
+#~ "branch has been updated since the last checkout. You may want\n"
+#~ "to integrate those changes locally (e.g., 'git pull ...')\n"
+#~ "before forcing an update.\n"
+#~ msgstr ""
+#~ "Les mises à jour ont été rejetées, car la pointe de la branche\n"
+#~ "de suivi a été mise à jour depuis la dernière extraction. Intégrez\n"
+#~ "ces changements localement (par exemple 'git pull ...') avant de\n"
+#~ "forcer à nouveau une mise à jour.\n"
+
+#~ msgid "or do not fetch any tag at all (--no-tags)"
+#~ msgstr "ou ne rapatrier aucune étiquette (--no-tags)"
+
+#~ msgid "current working directory is untracked"
+#~ msgstr "l'arbre de travail actuel est non-suivi"
+
+#~ msgid "cannot use --contents with final commit object name"
+#~ msgstr "on ne peut pas utiliser --contents avec un nom d'objet commit final"
+
+#~ msgid "please commit or stash them."
+#~ msgstr "veuillez les valider ou les remiser."
+
+#, c-format
+#~ msgid "Unknown mode: %s"
+#~ msgstr "Mode inconnu : %s"
+
+#~ msgid "could not lock HEAD"
+#~ msgstr "impossible de verrouiller HEAD"
diff --git a/po/id.po b/po/id.po
index e468b6d..3e8b212 100644
--- a/po/id.po
+++ b/po/id.po
@@ -7,8 +7,8 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-02 15:18+0700\n"
-"PO-Revision-Date: 2023-03-02 17:52+0700\n"
+"POT-Creation-Date: 2024-02-16 09:36+0700\n"
+"PO-Revision-Date: 2024-02-16 10:45+0700\n"
 "Last-Translator: Bagas Sanjaya <bagasdotme@gmail.com>\n"
 "Language-Team: Indonesian\n"
 "Language: id\n"
@@ -764,9 +764,8 @@
 msgstr "Pembalikkan tidak mungkin sebab Anda punya berkas tak tergabung."
 
 #: advice.c
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "Tidak mungkin untuk %s sebab Anda punya berkas tak tergabung."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Pendasaran ulang tidak mungkin sebab Anda punya berkas tak tergabung."
 
 #: advice.c
 msgid ""
@@ -793,6 +792,25 @@
 msgstr "Keluar karena penggabungan belum selesai."
 
 #: advice.c
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Cabang yang menyebar tidak dapat dimajucepatkan, Anda perlu salah satu "
+"dari:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"atau:\n"
+"\n"
+"\tgit rebase\n"
+
+#: advice.c
 msgid "Not possible to fast-forward, aborting."
 msgstr "Tidak mungkin untuk maju cepat, batalkan."
 
@@ -891,7 +909,7 @@
 msgstr "tanda kutip tak ditutup"
 
 #: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
+#: builtin/receive-pack.c builtin/tag.c t/helper/test-pkt-line.c
 msgid "too many arguments"
 msgstr "terlalu banyak argumen"
 
@@ -905,14 +923,16 @@
 msgid "unrecognized whitespace ignore option '%s'"
 msgstr "opsi abai spasi putih tidak dikenal '%s'"
 
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout.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.c builtin/pack-objects.c builtin/push.c
-#: builtin/rebase.c builtin/repack.c builtin/reset.c builtin/rev-list.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/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/pack-objects.c builtin/rebase.c
+#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.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 "Opsi '%s' dan '%s' tidak dapat digunakan bersamaan"
@@ -923,6 +943,14 @@
 msgstr "'%s' di luar repositori"
 
 #: apply.c
+msgid "failed to read patch"
+msgstr "gagal membaca tambalan"
+
+#: apply.c
+msgid "patch too large"
+msgstr "tambalan terlalu besar"
+
+#: apply.c
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Tidak dapat menyiapkan ekspresi reguler stempel waktu %s"
@@ -1339,6 +1367,11 @@
 msgid "cannot open %s"
 msgstr "tidak dapat membuka %s"
 
+#: apply.c rerere.c
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "tidak dapat batal taut '%s'"
+
 #: apply.c
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1389,7 +1422,7 @@
 msgstr[0] "%d baris diterapkan setelah memperbaiki kesalahan spasi putih."
 msgstr[1] "%d baris diterapkan setelah memperbaiki kesalahan spasi putih."
 
-#: apply.c builtin/add.c builtin/mv.c builtin/rm.c
+#: apply.c builtin/mv.c builtin/rm.c
 msgid "Unable to write new index file"
 msgstr "Tidak dapat menulis berkas indeks baru"
 
@@ -1579,6 +1612,11 @@
 msgid "cannot read '%s'"
 msgstr "tidak dapat membaca '%s'"
 
+#: archive.c
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "spek jalur '%s' mencocoki berkas di luar direktori saat ini"
+
 #: archive.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1600,10 +1638,6 @@
 msgstr "bukan objek pohon: %s"
 
 #: archive.c
-msgid "current working directory is untracked"
-msgstr "direktori kerja saat ini tak terlacak"
-
-#: archive.c
 #, c-format
 msgid "File not found: %s"
 msgstr "Berkas tidak ditemukan: %s"
@@ -1721,6 +1755,11 @@
 
 #: archive.c
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "parameter konfigurasi tambahan: '%s'"
+
+#: archive.c
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "Format arsip tidak dikenal '%s'"
 
@@ -1771,6 +1810,21 @@
 msgid "ignoring overly large gitattributes blob '%s'"
 msgstr "mengabaikan blob gitattributes '%s' yang terlalu besar"
 
+#: attr.c
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "--attr-source atau GIT_ATTR_SOURCE jelek"
+
+#: attr.c read-cache.c
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "tidak dapat men-stat '%s'"
+
+#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
+#: builtin/pack-objects.c combine-diff.c rerere.c
+#, c-format
+msgid "unable to read %s"
+msgstr "tidak dapat membaca %s"
+
 #: bisect.c
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
@@ -1895,17 +1949,13 @@
 msgstr "--contents dan --reverse tidak dapat dipadu dengan baik."
 
 #: blame.c
-msgid "cannot use --contents with final commit object name"
-msgstr "tidak dapat menggunakan --contents dengan nama objek komit final"
-
-#: blame.c
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse dan --first-parent bersama-sama butuh komit terbaru yang disebutkan"
 
 #: blame.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c ref-filter.c
-#: remote.c sequencer.c submodule.c
+#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c remote.c
+#: sequencer.c submodule.c
 msgid "revision walk setup failed"
 msgstr "persiapan jalan revisi gagal"
 
@@ -2037,8 +2087,10 @@
 
 #: branch.c
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "tidak dapat memperbarui paksa cabang '%s' yang ter-check out pada '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"tidak dapat memperbarui paksa cabang '%s' yang yang digunakan oleh pohon "
+"kerja pada '%s'"
 
 #: branch.c
 #, c-format
@@ -2108,13 +2160,8 @@
 
 #: branch.c
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' sudah di-checkout pada '%s'"
-
-#: branch.c
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD dari pohon kerja %s tidak diperbarui"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' sudah digunakan oleh pohon kerja di '%s'"
 
 #: builtin/add.c
 msgid "git add [<options>] [--] <pathspec>..."
@@ -2126,20 +2173,6 @@
 msgstr "tidak dapat chmod %cx '%s'"
 
 #: builtin/add.c
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "status diff tak diharapkan %c"
-
-#: builtin/add.c builtin/commit.c
-msgid "updating files failed"
-msgstr "gagal memperbarui berkas"
-
-#: builtin/add.c
-#, c-format
-msgid "remove '%s'\n"
-msgstr "hapus '%s'\n"
-
-#: builtin/add.c
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Perubahan tak tergelar setelah menyegarkan indeks:"
 
@@ -2151,31 +2184,27 @@
 "setelan add.interactive.useBuiltin sudah dihapus!\n"
 "Selengkapnya lihat entrinya di 'git help config'."
 
-#: builtin/add.c builtin/rev-parse.c
-msgid "Could not read the index"
-msgstr "Tidak dapat membaca indeks"
-
 #: builtin/add.c
-msgid "Could not write patch"
-msgstr "Tidak dapat menulis tambalan"
+msgid "could not read the index"
+msgstr "tidak dapat membaca indeks"
 
 #: builtin/add.c
 msgid "editing patch failed"
 msgstr "Gagal menyunting tambalan"
 
-#: builtin/add.c
+#: builtin/add.c read-cache.c
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Tidak dapat men-stat '%s'"
+msgid "could not stat '%s'"
+msgstr "tidak dapat men-stat '%s'"
 
 #: builtin/add.c
-msgid "Empty patch. Aborted."
-msgstr "Tambalan kosong. Dibatalkan."
+msgid "empty patch. aborted"
+msgstr "tambalan kosong, dibatalkan"
 
 #: builtin/add.c
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Tidak dapat terapkan '%s'"
+msgid "could not apply '%s'"
+msgstr "tidak dapat menerapkan '%s'"
 
 #: builtin/add.c
 msgid "The following paths are ignored by one of your .gitignore files:\n"
@@ -2336,14 +2365,19 @@
 msgid "index file corrupt"
 msgstr "berkas indeks rusak"
 
+#: builtin/add.c builtin/am.c builtin/checkout.c builtin/clone.c
+#: builtin/commit.c builtin/stash.c merge.c rerere.c
+msgid "unable to write new index file"
+msgstr "gagal menulis berkas indeks baru"
+
 #: builtin/am.c builtin/mailinfo.c mailinfo.c
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "tindakan jelek '%s' untuk '%s'"
 
 #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
-#: sequencer.c setup.c
+#: builtin/pull.c config.c diff-merges.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c
 #, c-format
 msgid "invalid value for '%s': '%s'"
 msgstr "nilai tidak valid untuk '%s': '%s'"
@@ -2386,7 +2420,7 @@
 msgid "could not open '%s' for reading"
 msgstr "tidak dapat membuka '%s' untuk dibaca"
 
-#: builtin/am.c builtin/rebase.c sequencer.c strbuf.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
 msgstr "tidak dapat membuka '%s' untuk ditulis"
@@ -2506,8 +2540,7 @@
 msgid "applying to an empty history"
 msgstr "menerapkan ke sebuah riwayat kosong"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
-#: t/helper/test-fast-rebase.c
+#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
 msgid "failed to write commit object"
 msgstr "gagal menulis objek komit"
 
@@ -2599,11 +2632,6 @@
 "Anda mungkin jalankan `git rm` pada berkas untuk menerima \"penghapusan oleh "
 "mereka\" untuk itu."
 
-#: builtin/am.c builtin/checkout.c builtin/clone.c builtin/stash.c merge.c
-#: rerere.c
-msgid "unable to write new index file"
-msgstr "gagal menulis berkas indeks baru"
-
 #: builtin/am.c builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
@@ -2627,11 +2655,6 @@
 msgstr "gagal membaca '%s'"
 
 #: builtin/am.c
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "Opsi '%s=%s' dan '%s=%s' tidak dapat digunakan bersamaan"
-
-#: builtin/am.c
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<opsi>] [(<mbox> | <Maildir>)...]"
 
@@ -2685,11 +2708,6 @@
 msgstr "lewatkan opsi --keep-cr ke git-mailsplit untuk format mbox"
 
 #: builtin/am.c
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"jangan lewatkan opsi --keep-cr ke git-mailsplit tak bergantung pada am.keepcr"
-
-#: builtin/am.c
 msgid "strip everything before a scissors line"
 msgstr "copot semuanya sebelum garis gunting"
 
@@ -2708,8 +2726,9 @@
 msgstr "n"
 
 #: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
-#: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.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/replace.c builtin/tag.c
+#: builtin/verify-tag.c
 msgid "format"
 msgstr "format"
 
@@ -2840,10 +2859,10 @@
 
 #: builtin/bisect.c
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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}=<istilah> --term-{old, good}=<istilah>] "
+"git bisect start [--term-(new,bad)=<istilah> --term-(old, good)=<istilah>] "
 "[--no-checkout] [--first-parent] [<jelek> [<bagus>...]] [--] [<jalur>...]"
 
 #: builtin/bisect.c
@@ -2863,8 +2882,8 @@
 msgstr "git bisect replay <berkas log>"
 
 #: builtin/bisect.c
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <perintah>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <perintah> [<argumen>...]"
 
 #: builtin/bisect.c
 #, c-format
@@ -3383,37 +3402,38 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "menghapus cabang '%s' yang sudah digabungkan ke\n"
-"         '%s', tapi belum digabungkan ke HEAD."
+"         '%s', tapi belum digabungkan ke HEAD"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "tidak menghapus cabang '%s' yang belum digabungkan ke\n"
-"         '%s', walaupun tergabung ke HEAD."
+"         '%s', walaupun tergabung ke HEAD"
 
 #: builtin/branch.c
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Tidak dapat mencari objek komit untuk '%s'"
+msgid "couldn't look up commit object for '%s'"
+msgstr "tidak dapat mencari objek komit untuk '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"Cabang '%s' belum sepenuhnya tergabung.\n"
-"Kalau Anda yakin ingin menghapusnya, jalankan 'git branch -D %s'."
+msgid "the branch '%s' is not fully merged"
+msgstr "cabang '%s' belum sepenuhnya digabungkan"
 
 #: builtin/branch.c
-msgid "Update of config-file failed"
-msgstr "Pembaruan berkas konfigurasi gagal"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Jika anda yakin untuk menghapusnya, lakukan 'git branch -D %s'"
+
+#: builtin/branch.c
+msgid "update of config-file failed"
+msgstr "pembaruan berkas konfigurasi gagal"
 
 #: builtin/branch.c
 msgid "cannot use -a with -d"
@@ -3421,18 +3441,28 @@
 
 #: builtin/branch.c
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Tidak dapat menghapus cabang '%s' yang ter-checkout pada '%s'"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"tidak dapat menghapus cabang '%s' yang digunakan oleh pohon kerja di '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "cabang pelacak remote '%s' tidak ditemukan."
+msgid "remote-tracking branch '%s' not found"
+msgstr "cabang pelacak remote '%s' tidak ditemukan"
 
 #: builtin/branch.c
 #, c-format
-msgid "branch '%s' not found."
-msgstr "cabang '%s' tidak ditemukan."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"cabang '%s' tidak ditemukan.\n"
+"Mungkin anda lupa menambahkan --remote?"
+
+#: builtin/branch.c
+#, c-format
+msgid "branch '%s' not found"
+msgstr "cabang '%s' tidak ditemukan"
 
 #: builtin/branch.c
 #, c-format
@@ -3459,59 +3489,64 @@
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Cabang %s sedang didasarkan ulang pada %s"
+msgid "branch %s is being rebased at %s"
+msgstr "cabang %s sedang didasarkan ulang pada %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Cabang %s sedang dibagi dua pada %s"
+msgid "branch %s is being bisected at %s"
+msgstr "cabang %s sedang dibagi dua pada %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Nama cabang tidak valid: '%s'"
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD dari pohon kerja %s tidak diperbarui"
 
 #: builtin/branch.c
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Belum ada komit pada cabang '%s'."
+msgid "invalid branch name: '%s'"
+msgstr "nama cabang tidak valid: '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "No branch named '%s'."
-msgstr "Tidak ada cabang bernama '%s'."
-
-#: builtin/branch.c
-msgid "Branch rename failed"
-msgstr "Penggantian nama cabang gagal"
-
-#: builtin/branch.c
-msgid "Branch copy failed"
-msgstr "Penyalinan cabang gagal"
+msgid "no commit on branch '%s' yet"
+msgstr "belum ada komit pada cabang '%s'."
 
 #: builtin/branch.c
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Salinan cabang salah nama '%s' dibuat"
+msgid "no branch named '%s'"
+msgstr "tidak ada cabang bernama '%s'"
+
+#: builtin/branch.c
+msgid "branch rename failed"
+msgstr "penggantian nama cabang gagal"
+
+#: builtin/branch.c
+msgid "branch copy failed"
+msgstr "penyalinan cabang gagal"
 
 #: builtin/branch.c
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Cabang salah nama '%s' berganti nama"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "salinan cabang salah nama '%s' dibuat"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Cabang berganti nama ke %s, tapi HEAD tidak diperbarui!"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "cabang salah nama '%s' berganti nama"
 
 #: builtin/branch.c
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Cabang berganti nama, tapi pembaruan berkas konfigurasi gagal"
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "cabang berganti nama ke %s, tapi HEAD tidak diperbarui"
 
 #: builtin/branch.c
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Cabang disalin, tapi pembaruan berkas konfigurasi gagal"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "cabang berganti nama, tapi pembaruan berkas konfigurasi gagal"
+
+#: builtin/branch.c
+msgid "branch is copied, but update of config-file failed"
+msgstr "cabang disalin, tapi pembaruan berkas konfigurasi gagal"
 
 #: builtin/branch.c
 #, c-format
@@ -3596,6 +3631,10 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "pindah/ganti nama cabang, walaupun target ada"
 
+#: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
+msgid "do not output a newline after empty formatted refs"
+msgstr "jangan keluarkan baris baru setelah referensi terformat kosong"
+
 #: builtin/branch.c
 msgid "copy a branch and its reflog"
 msgstr "salin cabang dan reflog-nya"
@@ -3657,9 +3696,9 @@
 msgid "format to use for the output"
 msgstr "format yang digunakan untuk keluaran"
 
-#: builtin/branch.c builtin/submodule--helper.c submodule.c
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Gagal menguraikan HEAD sebagai referensi valid."
+#: builtin/branch.c
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "gagal menguraikan HEAD sebagai referensi valid."
 
 #: builtin/branch.c builtin/clone.c
 msgid "HEAD not found below refs/heads!"
@@ -3682,20 +3721,20 @@
 msgstr "nama cabang diperlukan"
 
 #: builtin/branch.c
-msgid "Cannot give description to detached HEAD"
-msgstr "Tidak dapat memberikan deskripsi ke HEAD terpisah"
+msgid "cannot give description to detached HEAD"
+msgstr "tidak dapat memberikan deskripsi ke HEAD terpisah"
 
 #: builtin/branch.c
 msgid "cannot edit description of more than one branch"
 msgstr "tidak dapat menyunting deskripsi lebih dari satu cabang"
 
 #: builtin/branch.c
-msgid "cannot copy the current branch while not on any."
-msgstr "tidak dapat menyalin cabang saat ini ketika tidak ada."
+msgid "cannot copy the current branch while not on any"
+msgstr "tidak dapat menyalin cabang saat ini ketika tidak ada"
 
 #: builtin/branch.c
-msgid "cannot rename the current branch while not on any."
-msgstr "tidak dapat mengganti nama cabang saat ini ketika tidak ada."
+msgid "cannot rename the current branch while not on any"
+msgstr "tidak dapat mengganti nama cabang saat ini ketika tidak ada"
 
 #: builtin/branch.c
 msgid "too many branches for a copy operation"
@@ -3712,10 +3751,9 @@
 #: builtin/branch.c
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"tidak dapat menyetel hulu HEAD ke %s ketika itu tak menunjuk pada cabang "
-"apapun."
+"tidak dapat menyetel hulu HEAD ke %s ketika tak menunjuk pada cabang apapun"
 
 #: builtin/branch.c
 #, c-format
@@ -3732,31 +3770,30 @@
 msgstr "terlalu banyak argumen untuk batal-setel hulu"
 
 #: builtin/branch.c
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
-"tidak dapat membatal-setel hulu HEAD ketika itu tak menunjuk pada cabang "
-"apapun."
+"tidak dapat membatal-setel hulu HEAD ketika tak menunjuk pada cabang apapun"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Cabang '%s' tidak ada informasi hulu"
+msgid "branch '%s' has no upstream information"
+msgstr "cabang '%s' tidak ada informasi hulu"
 
 #: builtin/branch.c
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Opsi -a dan -r tidak mengambil nama cabang.\n"
-"Mungkin maksud Anda gunakan: -a|-r --list <pola>?"
+"opsi -a dan -r tidak mengambil nama cabang.\n"
+"Mungkin maksud Anda menggunakan: -a|-r --list <pola>?"
 
 #: builtin/branch.c
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "opsi '--set-upstream' tidak lagi didukung. Mohon gunakan '--track' atau '--"
-"set-upstream-to' sebagai gantinya."
+"set-upstream-to' sebagai gantinya"
 
 #: builtin/bugreport.c
 msgid "git version:\n"
@@ -3842,6 +3879,11 @@
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "sebutkan akhiran format strftime untuk nama(-nama) berkas"
 
+#: builtin/bugreport.c
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "argumen tidak dikenal `%s'"
+
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
 msgid "could not create leading directories for '%s'"
@@ -3872,12 +3914,10 @@
 
 #: builtin/bundle.c
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [-all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<versi>] <berkas> <argumen git-rev-list>"
 
 #: builtin/bundle.c
@@ -3904,13 +3944,13 @@
 msgid "show progress meter"
 msgstr "perlihatkan meteran perkembangan"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "show progress meter during object writing phase"
-msgstr "perlihatkan meteran perkembangan saat fase penulisan objek"
+#: builtin/bundle.c
+msgid "historical; same as --progress"
+msgstr "opsi bersejarah; sama dengan --progress"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "sama seperti --all-progress ketika meteran perkembangan diperlihatkan"
+#: builtin/bundle.c
+msgid "historical; does nothing"
+msgstr "opsi bersejarah: tidak apa-apa"
 
 #: builtin/bundle.c
 msgid "specify bundle format version"
@@ -3983,18 +4023,6 @@
 
 #: builtin/cat-file.c
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-
-#: builtin/cat-file.c
-msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
@@ -4003,6 +4031,18 @@
 "<revisi>"
 
 #: builtin/cat-file.c
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-z]"
+
+#: builtin/cat-file.c
 msgid "Check object existence or emit object contents"
 msgstr "Periksa keberadaan objek atau keluarkan isi objek"
 
@@ -4051,6 +4091,10 @@
 msgstr "stdin diakhiri dengan NUL"
 
 #: builtin/cat-file.c
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin dan stdout diakhiri dengan NUL"
+
+#: builtin/cat-file.c
 msgid "read commands from stdin"
 msgstr "baca perintah dari masukan standar"
 
@@ -4386,6 +4430,11 @@
 
 #: builtin/checkout.c
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "'%s', '%s', atau '%s' tidak dapat digunakan ketika men-checkout pohon"
+
+#: builtin/checkout.c
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "jalur '%s' tak tergabung"
 
@@ -4412,7 +4461,7 @@
 msgid "HEAD is now at"
 msgstr "HEAD sekarang berada di"
 
-#: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
+#: builtin/checkout.c builtin/clone.c
 msgid "unable to update HEAD"
 msgstr "tidak dapat memperbarui HEAD"
 
@@ -4588,8 +4637,8 @@
 "Consider \"git merge --quit\" or \"git worktree add\"."
 msgstr ""
 "tidak dapat mengganti cabang saat penggabungan\n"
-"Pertimbangkan untuk menggunakan \"git merge --quit\" atau \"git worktree add"
-"\"."
+"Pertimbangkan untuk menggunakan \"git merge --quit\" atau \"git worktree "
+"add\"."
 
 #: builtin/checkout.c
 msgid ""
@@ -4605,8 +4654,8 @@
 "Consider \"git rebase --quit\" or \"git worktree add\"."
 msgstr ""
 "tidak dapat mengganti cabang saat pendasaran ulang\n"
-"Pertimbangkan untuk menggunakan \"git rebase --quit\" atau \"git worktree add"
-"\"."
+"Pertimbangkan untuk menggunakan \"git rebase --quit\" atau \"git worktree "
+"add\"."
 
 #: builtin/checkout.c
 msgid ""
@@ -4623,8 +4672,8 @@
 "Consider \"git revert --quit\" or \"git worktree add\"."
 msgstr ""
 "tidak dapat mengganti cabang saat pembalikan\n"
-"Pertimbangkan untuk menggunakan \"git revert --quit\" atau \"git worktree add"
-"\"."
+"Pertimbangkan untuk menggunakan \"git revert --quit\" atau \"git worktree "
+"add\"."
 
 #: builtin/checkout.c
 msgid "you are switching branch while bisecting"
@@ -4683,8 +4732,8 @@
 msgstr "cabang baru"
 
 #: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "cabang baru tanpa induk"
+msgid "new unborn branch"
+msgstr "cabang yatim baru"
 
 #: builtin/checkout.c builtin/merge.c
 msgid "update ignored files (default)"
@@ -4752,7 +4801,7 @@
 msgid "you must specify path(s) to restore"
 msgstr "Anda harus sebutkan jalur untuk dipulihkan"
 
-#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
 #: builtin/submodule--helper.c builtin/worktree.c
 msgid "branch"
 msgstr "cabang"
@@ -4963,7 +5012,8 @@
 msgstr "hapus keseluruhan direktori"
 
 #: builtin/clean.c builtin/describe.c builtin/grep.c builtin/log.c
-#: builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c builtin/show-ref.c
+#: ref-filter.h
 msgid "pattern"
 msgstr "pola"
 
@@ -4995,10 +5045,6 @@
 "clean.requireForce asal ke true dan baik -i, -n, atau -f tidak diberikan; "
 "menolak membersihkan"
 
-#: builtin/clean.c
-msgid "-x and -X cannot be used together"
-msgstr "-x dan -X tidak dapat digunakan bersamaan"
-
 #: builtin/clone.c
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<opsi>] [--] <repo> [<direktori>]"
@@ -5077,7 +5123,7 @@
 msgid "path to git-upload-pack on the remote"
 msgstr "jalur ke git-upload-pack pada remote"
 
-#: builtin/clone.c builtin/fetch.c builtin/grep.c builtin/pull.c
+#: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "depth"
 msgstr "kedalaman"
 
@@ -5090,6 +5136,7 @@
 msgstr "buat klon dangkal sejak waktu yang disebutkan"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
 msgid "revision"
 msgstr "revisi"
 
@@ -5117,6 +5164,10 @@
 msgid "separate git dir from working tree"
 msgstr "pisahkan direktori git dari pohon kerja"
 
+#: builtin/clone.c builtin/init-db.c
+msgid "specify the reference format to use"
+msgstr "sebutkan format referensi untuk digunakan"
+
 #: builtin/clone.c
 msgid "key=value"
 msgstr "kunci=nilai"
@@ -5135,14 +5186,6 @@
 msgid "option to transmit"
 msgstr "opsi untuk transmisi"
 
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv4 addresses only"
-msgstr "gunakan hanya alamat IPv4"
-
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv6 addresses only"
-msgstr "gunakan hanya alamat IPv6"
-
 #: builtin/clone.c
 msgid "apply partial clone filters to submodules"
 msgstr "terapkan saringan kloning parsial ke submodul"
@@ -5181,6 +5224,11 @@
 
 #: builtin/clone.c
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' tautan simbolik, menolak mengkloning dengan --local"
+
+#: builtin/clone.c
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "gagal memulai iterator pada '%s'"
 
@@ -5270,13 +5318,10 @@
 msgid "You must specify a repository to clone."
 msgstr "Anda harus sebutkan repositori untuk diklon."
 
-#: builtin/clone.c
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri tidak kompatibel dengan --depth, --shallow-since, dan --shallow-"
-"exclude"
+#: builtin/clone.c builtin/init-db.c setup.c
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "format penyimpanan referensi tidak dikenal '%s'"
 
 #: builtin/clone.c
 #, c-format
@@ -5442,13 +5487,13 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <direktori>] [--append]\n"
 "                       [--split[=<strategi>]] [--reachable | --stdin-packs | "
 "--stdin-commits]\n"
-"                       [--changed-paths] [--[no-]max-new-filters <n>] [--[-"
-"no-]progress]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
 "                       <opsi pemisahan>"
 
 #: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
@@ -5470,6 +5515,11 @@
 
 #: builtin/commit-graph.c
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "tidak dapat membuka grafik komit '%s'"
+
+#: builtin/commit-graph.c
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "argumen --split tidak dikenal, %s"
 
@@ -5685,6 +5735,10 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+#: builtin/commit.c read-cache.c
+msgid "updating files failed"
+msgstr "gagal memperbarui berkas"
+
 #: builtin/commit.c
 msgid "failed to unpack HEAD tree object"
 msgstr "gagal membuka objek pohon HEAD"
@@ -5710,10 +5764,6 @@
 msgstr "gagal memperbarui tembolok pohon utama"
 
 #: builtin/commit.c
-msgid "unable to write new_index file"
-msgstr "tidak dapat menulis berkas new_index"
-
-#: builtin/commit.c
 msgid "cannot do a partial commit during a merge."
 msgstr "tidak dapat melakukan komit sebagian selama penggabungan."
 
@@ -5762,8 +5812,8 @@
 
 #: builtin/commit.c builtin/merge-tree.c
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "tidak dapat mencari komit %s"
+msgid "could not lookup commit '%s'"
+msgstr "tidak dapat mencari komit '%s'"
 
 #: builtin/commit.c builtin/shortlog.c
 #, c-format
@@ -5983,7 +6033,7 @@
 msgid "version"
 msgstr "versi"
 
-#: builtin/commit.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "keluaran yang dapat dibaca mesin"
 
@@ -6211,11 +6261,11 @@
 #: builtin/commit.c
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
 "repositori sudah diperbarui, tetapi tidak dapat menulis\n"
-"berkas new_index. Periksa bahwa disk tidak penuh dan kuota\n"
+"berkas indeks baru. Periksa bahwa disk tidak penuh dan kuota\n"
 "tidak terlampaui, lalu \"git restore --staged :/\" untuk pulihkan."
 
 #: builtin/config.c
@@ -7078,6 +7128,216 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel tidak dapat bernilai negatif"
 
+#: builtin/fetch.c
+msgid "couldn't find remote ref HEAD"
+msgstr "tidak dapat menemukan referensi remote HEAD"
+
+#: builtin/fetch.c
+#, c-format
+msgid "From %.*s\n"
+msgstr "Dari %.*s\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "object %s not found"
+msgstr "objek %s tidak ditemukan"
+
+#: builtin/fetch.c
+msgid "[up to date]"
+msgstr "[terkini]"
+
+#: builtin/fetch.c
+msgid "[rejected]"
+msgstr "[tertolak]"
+
+#: builtin/fetch.c
+msgid "can't fetch into checked-out branch"
+msgstr "tidak dapat mengambil ke dalam cabang ter-checkout"
+
+#: builtin/fetch.c
+msgid "[tag update]"
+msgstr "[pembaruan tag]"
+
+#: builtin/fetch.c
+msgid "unable to update local ref"
+msgstr "tidak dapat memperbarui referensi lokal"
+
+#: builtin/fetch.c
+msgid "would clobber existing tag"
+msgstr "akan klob tag yang ada"
+
+#: builtin/fetch.c
+msgid "[new tag]"
+msgstr "[tag baru]"
+
+#: builtin/fetch.c
+msgid "[new branch]"
+msgstr "[cabang baru]"
+
+#: builtin/fetch.c
+msgid "[new ref]"
+msgstr "[referensi baru]"
+
+#: builtin/fetch.c
+msgid "forced update"
+msgstr "pembaruan terpaksa"
+
+#: builtin/fetch.c
+msgid "non-fast-forward"
+msgstr "bukan-maju-cepat"
+
+#: builtin/fetch.c builtin/grep.c sequencer.c
+#, c-format
+msgid "cannot open '%s'"
+msgstr "tidak dapat membuka '%s'"
+
+#: builtin/fetch.c
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"fetch secara normal mengindikasikan cabang mana ada pembaruan terpaksa,\n"
+"tapi pemeriksaan tersebut sudah dinonaktifkan. Untuk aktifkan kembali, "
+"gunakan\n"
+"bendera '--show-forced-updates' atau jalankan 'git config fetch."
+"showForcedUpdates true'."
+
+#: builtin/fetch.c
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"Butuh waktu %2.f detik untuk memeriksa pembaruan terpaksa; Anda dapat\n"
+"menggunakan '--no-show-forced-updates' atau jalankan\n"
+"'git config fetch.showForcedUpdates false'\n"
+"untuk menghindari pemeriksaan ini.\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s tidak mengirim semua objek yang diperlukan\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr "tolak %s karena akar dangkal tidak diperkenankan untuk diperbarui"
+
+#: builtin/fetch.c
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"beberapa referensi lokal tidak dapat diperbarui; coba jalankan\n"
+" 'git remote prune %s' untuk hapus cabang yang lama dan berkonflik"
+
+#: builtin/fetch.c
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s akan menjadi terjuntai)"
+
+#: builtin/fetch.c
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s telah menjadi terjuntai)"
+
+#: builtin/fetch.c
+msgid "[deleted]"
+msgstr "[dihapus]"
+
+#: builtin/fetch.c builtin/remote.c
+msgid "(none)"
+msgstr "(tidak ada)"
+
+#: builtin/fetch.c
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "menolak mengambil ke dalam cabang '%s' yang ter-checkout pada '%s'"
+
+#: builtin/fetch.c
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "opsi \"%s\" nilai \"%s\" tidak valid untuk %s"
+
+#: builtin/fetch.c
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "opsi \"%s\" diabaikan untuk %s\n"
+
+#: builtin/fetch.c object-file.c
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s bukan sebuah objek valid"
+
+#: builtin/fetch.c
+#, c-format
+msgid "the object %s does not exist"
+msgstr "objek '%s' tidak ada"
+
+#: builtin/fetch.c
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "banyak cabang terdeteksi, tidak kompatibel dengan --set-upstream"
+
+#: builtin/fetch.c
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"tidak dapat menyetel hulu HEAD ke %s dari '%s' ketika itu tak menunjuk pada "
+"cabang apapun."
+
+#: builtin/fetch.c
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "tidak setel hulu untuk cabang remote pelacak remote"
+
+#: builtin/fetch.c
+msgid "not setting upstream for a remote tag"
+msgstr "tidak setel hulu untuk tag remote"
+
+#: builtin/fetch.c
+msgid "unknown branch type"
+msgstr "tipe cabang tidak diketahui"
+
+#: builtin/fetch.c
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"cabang sumber tidak ditemukan;\n"
+"Anda harus sebutkan tepat satu cabang dengan opsi --set-upstream."
+
+#: builtin/fetch.c
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Mengambil %s\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "could not fetch %s"
+msgstr "tidak dapat mengambil %s"
+
+#: builtin/fetch.c
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "tidak dapat mengambil '%s' (kode keluar: %d)\n"
+
+#: builtin/fetch.c
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"repositori remote tidak disebutkan; mohon sebutkan baik URL atau nama\n"
+"remote yang mana revisi baru sebaiknya diambil"
+
+#: builtin/fetch.c
+msgid "you need to specify a tag name"
+msgstr "Anda perlu sebutkan sebuah nama tag"
+
 #: builtin/fetch.c builtin/pull.c
 msgid "fetch from all remotes"
 msgstr "ambil dari semua remote"
@@ -7219,216 +7479,6 @@
 msgstr "terima spek referensi dari masukan standar"
 
 #: builtin/fetch.c
-msgid "couldn't find remote ref HEAD"
-msgstr "tidak dapat menemukan referensi remote HEAD"
-
-#: builtin/fetch.c
-#, c-format
-msgid "object %s not found"
-msgstr "objek %s tidak ditemukan"
-
-#: builtin/fetch.c
-msgid "[up to date]"
-msgstr "[terkini]"
-
-#: builtin/fetch.c
-msgid "[rejected]"
-msgstr "[tertolak]"
-
-#: builtin/fetch.c
-msgid "can't fetch into checked-out branch"
-msgstr "tidak dapat mengambil ke dalam cabang ter-checkout"
-
-#: builtin/fetch.c
-msgid "[tag update]"
-msgstr "[pembaruan tag]"
-
-#: builtin/fetch.c
-msgid "unable to update local ref"
-msgstr "tidak dapat memperbarui referensi lokal"
-
-#: builtin/fetch.c
-msgid "would clobber existing tag"
-msgstr "akan klob tag yang ada"
-
-#: builtin/fetch.c
-msgid "[new tag]"
-msgstr "[tag baru]"
-
-#: builtin/fetch.c
-msgid "[new branch]"
-msgstr "[cabang baru]"
-
-#: builtin/fetch.c
-msgid "[new ref]"
-msgstr "[referensi baru]"
-
-#: builtin/fetch.c
-msgid "forced update"
-msgstr "pembaruan terpaksa"
-
-#: builtin/fetch.c
-msgid "non-fast-forward"
-msgstr "bukan-maju-cepat"
-
-#: builtin/fetch.c builtin/grep.c sequencer.c
-#, c-format
-msgid "cannot open '%s'"
-msgstr "tidak dapat membuka '%s'"
-
-#: builtin/fetch.c
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"fetch secara normal mengindikasikan cabang mana ada pembaruan terpaksa,\n"
-"tapi pemeriksaan tersebut sudah dinonaktifkan. Untuk aktifkan kembali, "
-"gunakan\n"
-"bendera '--show-forced-updates' atau jalankan 'git config fetch."
-"showForcedUpdates true'."
-
-#: builtin/fetch.c
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"Butuh waktu %2.f detik untuk memeriksa pembaruan terpaksa; Anda dapat\n"
-"menggunakan '--no-show-forced-updates' atau jalankan\n"
-"'git config fetch.showForcedUpdates false'\n"
-"untuk menghindari pemeriksaan ini.\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s tidak mengirim semua objek yang diperlukan\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr "tolak %s karena akar dangkal tidak diperkenankan untuk diperbarui"
-
-#: builtin/fetch.c
-#, c-format
-msgid "From %.*s\n"
-msgstr "Dari %.*s\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"beberapa referensi lokal tidak dapat diperbarui; coba jalankan\n"
-" 'git remote prune %s' untuk hapus cabang yang lama dan berkonflik"
-
-#: builtin/fetch.c
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s akan menjadi terjuntai)"
-
-#: builtin/fetch.c
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s telah menjadi terjuntai)"
-
-#: builtin/fetch.c
-msgid "[deleted]"
-msgstr "[dihapus]"
-
-#: builtin/fetch.c builtin/remote.c
-msgid "(none)"
-msgstr "(tidak ada)"
-
-#: builtin/fetch.c
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "menolak mengambil ke dalam cabang '%s' yang ter-checkout pada '%s'"
-
-#: builtin/fetch.c
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "opsi \"%s\" nilai \"%s\" tidak valid untuk %s"
-
-#: builtin/fetch.c
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "opsi \"%s\" diabaikan untuk %s\n"
-
-#: builtin/fetch.c object-file.c
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s bukan sebuah objek valid"
-
-#: builtin/fetch.c
-#, c-format
-msgid "the object %s does not exist"
-msgstr "objek '%s' tidak ada"
-
-#: builtin/fetch.c
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "banyak cabang terdeteksi, tidak kompatibel dengan --set-upstream"
-
-#: builtin/fetch.c
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"tidak dapat menyetel hulu HEAD ke %s dari '%s' ketika itu tak menunjuk pada "
-"cabang apapun."
-
-#: builtin/fetch.c
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "tidak setel hulu untuk cabang remote pelacak remote"
-
-#: builtin/fetch.c
-msgid "not setting upstream for a remote tag"
-msgstr "tidak setel hulu untuk tag remote"
-
-#: builtin/fetch.c
-msgid "unknown branch type"
-msgstr "tipe cabang tidak diketahui"
-
-#: builtin/fetch.c
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"cabang sumber tidak ditemukan;\n"
-"Anda harus sebutkan tepat satu cabang dengan opsi --set-upstream."
-
-#: builtin/fetch.c
-#, c-format
-msgid "Fetching %s\n"
-msgstr "Mengambil %s\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid "could not fetch %s"
-msgstr "tidak dapat mengambil %s"
-
-#: builtin/fetch.c
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "tidak dapat mengambil '%s' (kode keluar: %d)\n"
-
-#: builtin/fetch.c
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"repositori remote tidak disebutkan; mohon sebutkan baik URL atau nama\n"
-"remote yang mana revisi baru sebaiknya diambil"
-
-#: builtin/fetch.c
-msgid "you need to specify a tag name"
-msgstr "Anda perlu sebutkan sebuah nama tag"
-
-#: builtin/fetch.c
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only perlu satu atau lebih --negotiation-tip=*"
 
@@ -7576,6 +7626,14 @@
 msgid "print only refs which don't contain the commit"
 msgstr "hanya cetak referensi yang tidak berisi komit"
 
+#: builtin/for-each-ref.c
+msgid "read reference patterns from stdin"
+msgstr "baca pola referensi dari masukan standar"
+
+#: builtin/for-each-ref.c
+msgid "unknown arguments supplied with --stdin"
+msgstr "argumen tidak dikenal diberikan dengan --stdin"
+
 #: builtin/for-each-repo.c
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<konfigurasi> [--] <argumen perintah>"
@@ -7592,6 +7650,11 @@
 msgid "missing --config=<config>"
 msgstr "kehilangan --config=<config>"
 
+#: builtin/for-each-repo.c
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "dapat konfigurasi jelek --config=%s"
+
 #: builtin/fsck.c
 msgid "unknown"
 msgstr "tidak dikenal"
@@ -7777,13 +7840,14 @@
 msgstr "catatan: %s menunjuk ke cabang yang belum lahir (%s)"
 
 #: builtin/fsck.c
-msgid "Checking cache tree"
-msgstr "Memeriksa pohon tembolok"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Memeriksa pohon tembolok %s"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: penunjuk sha1 tidak valid pada pohon tembolok"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: penunjuk sha1 tidak valid pada pohon tembolok %s"
 
 #: builtin/fsck.c
 msgid "non-tree in cache-tree"
@@ -7791,8 +7855,18 @@
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: penunjuk sha1 tidak valid di resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: penunjuk sha1 tidak valid di resolve-undo %s"
+
+#: builtin/fsck.c
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "tidak dapat memuat indeks balik untuk pak '%s'"
+
+#: builtin/fsck.c
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "indeks balik tidak valid untuk pak '%s'"
 
 #: builtin/fsck.c
 msgid ""
@@ -8000,7 +8074,7 @@
 msgid "failed to parse '%s' value '%s'"
 msgstr "gagal menguraikan nilai '%s' '%s'"
 
-#: builtin/gc.c builtin/init-db.c
+#: builtin/gc.c setup.c
 #, c-format
 msgid "cannot stat '%s'"
 msgstr "tidak dapat men-stat '%s'"
@@ -8028,6 +8102,10 @@
 msgid "pack unreferenced objects separately"
 msgstr "pak objek tak terujuk secara terpisah"
 
+#: builtin/gc.c builtin/repack.c
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "batasi ukuran pak sisa dengan --cruft"
+
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
 msgstr "jadi lebih cermat (waktu yang dijalankan bertambah)"
@@ -8249,14 +8327,6 @@
 msgid "'crontab' died"
 msgstr "'crontab' mati"
 
-#: builtin/gc.c
-msgid "failed to start systemctl"
-msgstr "gagal memulai systemctl"
-
-#: builtin/gc.c
-msgid "failed to run systemctl"
-msgstr "gagal menjalankan systemctl"
-
 #: builtin/gc.c builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
@@ -8268,6 +8338,14 @@
 msgstr "gagal membilas '%s'"
 
 #: builtin/gc.c
+msgid "failed to start systemctl"
+msgstr "gagal memulai systemctl"
+
+#: builtin/gc.c
+msgid "failed to run systemctl"
+msgstr "gagal menjalankan systemctl"
+
+#: builtin/gc.c
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "argumen --scheduler tidak dikenal '%s'"
@@ -8298,6 +8376,10 @@
 msgstr "penjadwal untuk memicu git maintenance run"
 
 #: builtin/gc.c
+msgid "failed to set up maintenance schedule"
+msgstr "gagal mempersiapkan jadwal pemeliharaan"
+
+#: builtin/gc.c
 msgid "failed to add repo to global config"
 msgstr "gagal menambahkan repositori ke konfigurasi global"
 
@@ -8336,6 +8418,11 @@
 
 #: builtin/grep.c
 #, c-format
+msgid "unable to read tree %s"
+msgstr "tidak dapat membaca pohon %s"
+
+#: builtin/grep.c
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "tidak dapan men-grep dari objek dengan tipe %s"
 
@@ -8393,7 +8480,7 @@
 msgstr "cari dalam subdirektori (asali)"
 
 #: builtin/grep.c
-msgid "descend at most <depth> levels"
+msgid "descend at most <n> levels"
 msgstr "turun paling banyak <kedalaman> tingkat"
 
 #: builtin/grep.c
@@ -8863,11 +8950,6 @@
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "TUMBUKAN SHA1 DITEMUKAN DENGAN %s !"
 
-#: builtin/index-pack.c builtin/pack-objects.c
-#, c-format
-msgid "unable to read %s"
-msgstr "tidak dapat membaca %s"
-
 #: builtin/index-pack.c
 #, c-format
 msgid "cannot read existing object info %s"
@@ -9024,7 +9106,7 @@
 msgid "bad %s"
 msgstr "%s jelek"
 
-#: builtin/index-pack.c builtin/init-db.c
+#: builtin/index-pack.c builtin/init-db.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "algoritma hash tak dikenal '%s'"
@@ -9042,103 +9124,16 @@
 msgstr "kesalahan fsck dalam objek paket"
 
 #: builtin/init-db.c
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "tidak dapat men-stat templat '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "tidak dapat membuka direktori '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "tidak dapat membaca tautan '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "tidak dapat menautkan simbolik '%s' '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "tidak dapat menyalin '%s' ke '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "ignoring template %s"
-msgstr "mengabaikan templat %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "templates not found in %s"
-msgstr "templat tidak ditemukan di %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "tidak menyalin templat dari '%s': %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "nama cabang asal salah: '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "tidak dapat menangani tipe berkas %d"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "tidak dapat memindahkan %s ke %s"
-
-#: builtin/init-db.c
-msgid "attempt to reinitialize repository with different hash"
-msgstr "mencoba menginisialisasi ulang repositori dengan hash yang berbeda"
-
-#: builtin/init-db.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s sudah ada"
-
-#: builtin/init-db.c
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s diabaikan"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Repositori berbagi Git yang sudah ada diinisialisasi ulang di %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Repositori Git diinisialisasi ulang di %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Repositori berbagi Git kosong diinisialisasi di %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Repositori Git kosong dinisialisasi di %s%s\n"
-
-#: builtin/init-db.c
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<direktori templat>]\n"
 "         [--separate-git-dir <direktori git>] [--object-format=<format>]\n"
+"\t  [--ref-format=<format>]\n"
 "         [-b <nama cabang> | --initial-branch=<nama cabang>]\n"
 "         [--shared[=<perizinan>]] [<direktori>]"
 
@@ -9194,11 +9189,12 @@
 #: builtin/interpret-trailers.c
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<nilai>])...]\n"
+"                       [(--trailer (<kunci>|<alias kunci>)"
+"[(=|:)<nilai>])...]\n"
 "                       [--parse] [<berkas>...]"
 
 #: builtin/interpret-trailers.c
@@ -9210,6 +9206,10 @@
 msgstr "pangkas trailer kosong"
 
 #: builtin/interpret-trailers.c
+msgid "placement"
+msgstr "penempatan"
+
+#: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
 msgstr "dimana trailer baru ditempatkan"
 
@@ -9226,20 +9226,20 @@
 msgstr "keluarkan hanya trailer"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply config rules"
-msgstr "jangan terapkan aturan konfigurasi"
+msgid "do not apply trailer.* configuration variables"
+msgstr "jangan terapkan variabel konfigurasi trailer.*"
 
 #: builtin/interpret-trailers.c
-msgid "join whitespace-continued values"
-msgstr "gabungkan nilai yang dilanjutkan oleh spasi"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "format ulang nilai trailer multibaris sebagai nilai satu baris."
 
 #: builtin/interpret-trailers.c
-msgid "set parsing options"
-msgstr "setel opsi penguraian"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "alias untuk --only-trailers --only-input --unfold"
 
 #: builtin/interpret-trailers.c
-msgid "do not treat --- specially"
-msgstr "jangan memperlakukan khusus ---"
+msgid "do not treat \"---\" as the end of input"
+msgstr "jangan perlakukan \"---\" sebagai ujung masukan"
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
@@ -9298,7 +9298,7 @@
 "lacak evolusi rentang baris <awal>,<akhir> atau fungsi :<nama fungsi> dalam "
 "<berkas>"
 
-#: builtin/log.c builtin/shortlog.c bundle.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "argumen tidak dikenal: %s"
@@ -9354,6 +9354,11 @@
 msgstr "bukan sebuah rentang"
 
 #: builtin/log.c
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "tidak dapat membaca berkas deskripsi cabang '%s'"
+
+#: builtin/log.c
 msgid "cover letter needs email format"
 msgstr "sampul surat butuh format email"
 
@@ -9482,6 +9487,10 @@
 msgstr "buat bagian dari sampul surat berdasarkan deskripsi cabang"
 
 #: builtin/log.c
+msgid "use branch description from file"
+msgstr "gunakan deskripsi cabang dari berkas"
+
+#: builtin/log.c
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "gunakan [<prefix>] daripada [PATCH]"
 
@@ -9690,6 +9699,11 @@
 "Tidak dapat menemukan cabang remote terlacak, mohon sebutkan <hulu>\n"
 "secara manual.\n"
 
+#: builtin/ls-files.c builtin/ls-tree.c
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "tidak dapat mendapatkan info objek tentang '%s'"
+
 #: builtin/ls-files.c
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
@@ -9879,11 +9893,6 @@
 
 #: builtin/ls-tree.c
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "tidak dapat mendapatkan info objek tentang '%s'"
-
-#: builtin/ls-tree.c
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "format ls-tree jelek: elemen '%s' tidak dimulai dengan '('"
 
@@ -10045,11 +10054,23 @@
 "git merge-file [<opsi>] [-L <nama 1> [-L <asli> [-L <nama 2>]]] <berkas 1> "
 "<berkas asli> <berkas 2>"
 
+#: builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"opsi diff-algorithm terima \"myers\", \"minimal\", \"patience\" dan "
+"\"histogram\""
+
 #: builtin/merge-file.c
 msgid "send results to standard output"
 msgstr "kirim hasil ke keluaran standar"
 
 #: builtin/merge-file.c
+msgid "use object IDs instead of filenames"
+msgstr "gunakan ID objek daripada nama berkas"
+
+#: builtin/merge-file.c
 msgid "use a diff3 based merge"
 msgstr "gunakan penggabungan berdasarkan diff3"
 
@@ -10069,6 +10090,14 @@
 msgid "for conflicts, use a union version"
 msgstr "untuk konflik, gunakan versi bersatu"
 
+#: builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<algoritma>"
+
+#: builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "pilih algoritma diff"
+
 #: builtin/merge-file.c
 msgid "for conflicts, use this marker size"
 msgstr "untuk konflik, gunakan ukuran penanda ini"
@@ -10081,6 +10110,15 @@
 msgid "set labels for file1/orig-file/file2"
 msgstr "setel label untuk file1/orig-file/file2"
 
+#: builtin/merge-file.c
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "objek '%s' tidak ada"
+
+#: builtin/merge-file.c
+msgid "Could not write object file"
+msgstr "Tidak dapat menulis berkas objek"
+
 #: builtin/merge-recursive.c
 #, c-format
 msgid "unknown option %s"
@@ -10160,13 +10198,22 @@
 msgid "specify a merge-base for the merge"
 msgstr "harus menyebutkan sebuah dasar penggabungan untuk penggabungan"
 
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option=value"
+msgstr "opsi=nilai"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option for selected merge strategy"
+msgstr "opsi untuk strategi penggabungan yang dipilih"
+
 #: builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge tidak kompatibel dengan semua opsi lainnya"
 
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base tidak kompatibel dengan --stdin"
+#: builtin/merge-tree.c builtin/merge.c
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "opsi strategi tidak dikenal: -X%s"
 
 #: builtin/merge-tree.c builtin/notes.c
 #, c-format
@@ -10256,14 +10303,6 @@
 msgid "merge strategy to use"
 msgstr "strategi penggabungan yang digunakan"
 
-#: builtin/merge.c builtin/pull.c
-msgid "option=value"
-msgstr "opsi=nilai"
-
-#: builtin/merge.c builtin/pull.c
-msgid "option for selected merge strategy"
-msgstr "opsi untuk strategi penggabungan yang dipilih"
-
 #: builtin/merge.c
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "pesan komit penggabungan (untuk penggabungan bukan maju cepat)"
@@ -10333,7 +10372,7 @@
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "Untai branch.%s.mergeoptions jelek: %s"
 
-#: builtin/merge.c builtin/stash.c merge-recursive.c
+#: builtin/merge.c merge-recursive.c
 msgid "Unable to write index."
 msgstr "Tidak dapat menulis indeks."
 
@@ -10343,11 +10382,6 @@
 
 #: builtin/merge.c
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "opsi strategi tidak dikenal: -X%s"
-
-#: builtin/merge.c t/helper/test-fast-rebase.c
-#, c-format
 msgid "unable to write %s"
 msgstr "tidak dapat menulis %s"
 
@@ -10422,7 +10456,7 @@
 msgid "Bad value '%s' in environment '%s'"
 msgstr "Nilai jelek '%s' dalam lingkungan '%s'"
 
-#: builtin/merge.c read-cache.c strbuf.c wrapper.c
+#: builtin/merge.c editor.c read-cache.c wrapper.c
 #, c-format
 msgid "could not close '%s'"
 msgstr "tidak dapat menutup '%s'"
@@ -10714,8 +10748,8 @@
 msgstr "tidak dapat memindahkan direktori ke dirinya sendiri"
 
 #: builtin/mv.c
-msgid "cannot move directory over file"
-msgstr "tidak dapat memindahkan direktori ke berkas"
+msgid "destination already exists"
+msgstr "tujuan sudah ada"
 
 #: builtin/mv.c
 msgid "source directory is empty"
@@ -10819,11 +10853,13 @@
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referensi catatan>] add [-f] [--allow-empty] [-m <pesan | -"
-"F <berkas> | (-c | -C) <objek>] [<objek>]"
+"git notes [--ref <referensi catatan>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<pemisah paragraf>] [--[no-]stripspace] [-m "
+"<pesan | -F <berkas> | (-c | -C) <objek>] [<objek>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -10832,11 +10868,13 @@
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <referensi catatan>] append [--alow-empty] [-m <pesan> | -F "
-"<berkas> | (-c | -C) <objek>] [<objek>]"
+"git notes [--ref <referensi catatan>] append [--alow-empty] [--"
+"[no]separator|--separator=<pemisah paragraf>] [--[no-]stripspace] [-m "
+"<pesan> | -F <berkas> | (-c | -C) <objek>] [<objek>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -11007,6 +11045,18 @@
 msgstr "timpa catatan yang sudah ada"
 
 #: builtin/notes.c
+msgid "<paragraph-break>"
+msgstr "<pemisah paragraf>"
+
+#: builtin/notes.c
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "masukkan <pemisah paragraf> di antara paragraf"
+
+#: builtin/notes.c
+msgid "remove unnecessary whitespace"
+msgstr "hapus spasi yang tidak diperlukan"
+
+#: builtin/notes.c
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -11278,7 +11328,7 @@
 msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
 msgstr "%<PRIu32> objek ditulish ketika mengharapkan %<PRIu32>"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repack.c
 msgid "disabling bitmap writing, as some objects are not being packed"
 msgstr "menonaktifkan penulisan bitmap, saat beberapa objek tidak sedang dipak"
 
@@ -11345,6 +11395,11 @@
 
 #: builtin/pack-objects.c
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "nilai pack.allowPackReuse tidak valid: '%s'"
+
+#: builtin/pack-objects.c
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -11447,6 +11502,14 @@
 msgstr "versi indeks jelek '%s'"
 
 #: builtin/pack-objects.c
+msgid "show progress meter during object writing phase"
+msgstr "perlihatkan meteran perkembangan saat fase penulisan objek"
+
+#: builtin/pack-objects.c
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "sama seperti --all-progress ketika meteran perkembangan diperlihatkan"
+
+#: builtin/pack-objects.c
 msgid "<version>[,<offset>]"
 msgstr "<versi>[,<offset>]"
 
@@ -11643,10 +11706,6 @@
 "--thin tidak dapat digunakan untuk membangun sebuah pak yang dapat diindeks"
 
 #: builtin/pack-objects.c
-msgid "cannot use --filter without --stdout"
-msgstr "tidak dapat menggunakan --filter tanpa --stdout"
-
-#: builtin/pack-objects.c
 msgid "cannot use --filter with --stdin-packs"
 msgstr "tidak dapat menggunakan --filter dengan --stdin-packs"
 
@@ -11663,10 +11722,6 @@
 msgstr "tidak dapat menggunakan --stdin-packs dengan --cruft"
 
 #: builtin/pack-objects.c
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "tidak dapat menggunakan --max-pack-size dengan --cruft"
-
-#: builtin/pack-objects.c
 msgid "Enumerating objects"
 msgstr "Menghitung objek"
 
@@ -11674,10 +11729,10 @@
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Total %<PRIu32> (delta %<PRIu32>), digunakan ulang %<PRIu32> (delta "
-"%<PRIu32>), pak yang digunakan ulang %<PRIu32>"
+"%<PRIu32>), pak yang digunakan ulang %<PRIu32> (dari %<PRIuMAX>)"
 
 #: builtin/pack-redundant.c
 msgid ""
@@ -11693,9 +11748,16 @@
 "Anda masih menggunakannya dengan mengirimkan surel ke\n"
 "<git@vger.kernel.org>. Terima kasih.\n"
 
+#: builtin/pack-redundant.c
+msgid "refusing to run without --i-still-use-this"
+msgstr "menolak menjalankan tanpa --i-still-use-this"
+
 #: builtin/pack-refs.c
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <pola>] [--exclude <pola>]"
 
 #: builtin/pack-refs.c
 msgid "pack everything"
@@ -11705,6 +11767,14 @@
 msgid "prune loose refs (default)"
 msgstr "pangkas referensi longgar (asali)"
 
+#: builtin/pack-refs.c
+msgid "references to include"
+msgstr "referensi untuk ditambahkan"
+
+#: builtin/pack-refs.c
+msgid "references to exclude"
+msgstr "referensi untuk dikecualikan"
+
 #: builtin/patch-id.c
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
@@ -11781,6 +11851,14 @@
 msgid "number of submodules pulled in parallel"
 msgstr "nomor submodul ditarik dalam paralel"
 
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "gunakan hanya alamat IPv4"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "gunakan hanya alamat IPv6"
+
 #: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -11909,9 +11987,9 @@
 msgid "pull with rebase"
 msgstr "tarik dengan pendasaran ulang"
 
-#: builtin/pull.c
-msgid "please commit or stash them."
-msgstr "mohon komit atau stase."
+#: builtin/pull.c builtin/rebase.c
+msgid "Please commit or stash them."
+msgstr "Mohon komit atau stase."
 
 #: builtin/pull.c
 #, c-format
@@ -12086,39 +12164,39 @@
 #: builtin/push.c
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Pembaruan ditolak karena ujung dari cabang Anda saat ini di belakang\n"
-"pasangan remotenya. Integrasikan perubahan remote (seperti\n"
-"'git pull') sebelum dorong lagi.\n"
+"pasangan remotenya. Jika Anda ingin mengintegrasikan perubahan remote,\n"
+"lakukan 'git pull' sebelum mendorong lagi.\n"
 "Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Pembaruan ditolak karena ujung dari cabang yang didorong di belakang\n"
-"pasangan remotenya. Check out cabang itu dan integrasikan perubahan\n"
-"remote (seperti 'git pull') sebelum dorong lagi.\n"
+"pasangan remotenya. Jika anda ingin mengintegrasikan perubahan\n"
+"remote, lakukan 'git pull' sebelum mendorong lagi.\n"
 "Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Pembaruan ditolak karena remote berisi kerja yang Anda tidak punya\n"
+"Pembaruan ditolak karena remote berisi karya yang Anda tidak punya\n"
 "di lokal. Ini biasanya disebabkan repositori yang lain dorong ke\n"
-"referensi yang sama. Mungkin Anda ingin integrasikan terlebih dahulu\n"
-"perubahan remote (seperti 'git pull') sebelum dorong lagi.\n"
+"referensi yang sama. Jika Anda ingin mengintegrasikan perubahan remote\n"
+"lakukan 'git pull' sebelum mendorong lagi.\n"
 "Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
@@ -12137,15 +12215,15 @@
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Pembaruan ditolak karena ujung dari cabang pelacak remote\n"
-"sudah diperbarui sejak checkout terakhir. Mungkin Anda ingin\n"
-"integrasikan perubahan tersebut ke lokal (seperti 'git pull...')\n"
-"sebelum memaksa pembaruan.\n"
+"Pembaruan ditolak karena ujung dari cabang Anda saat ini sudah diperbarui\n"
+"sejak checkout terakhir. Jika Anda ingin mengintegrasikan perubahan remote,\n"
+"lakukan 'git pull' sebelum mendorong lagi.\n"
+"Lihat 'Note about fast-forwards' di 'git push --help' untuk selengkapnya."
 
 #: builtin/push.c
 #, c-format
@@ -12174,9 +12252,9 @@
 msgid "repository"
 msgstr "repositori"
 
-#: builtin/push.c builtin/send-pack.c
-msgid "push all refs"
-msgstr "dorong semua referensi"
+#: builtin/push.c
+msgid "push all branches"
+msgstr "dorong semua cabang"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "mirror all refs"
@@ -12187,8 +12265,10 @@
 msgstr "hapus referensi"
 
 #: builtin/push.c
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "dorong tag (tidak dapat digunakan dengan --all atau --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"dorong tag (tidak dapat digunakan bersamaan dengan --all, --branches, atau --"
+"mirror)"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "force updates"
@@ -12520,6 +12600,11 @@
 
 #: builtin/rebase.c
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Mode rebase-merges tidak dikenal: %s"
+
+#: builtin/rebase.c
+#, c-format
 msgid "could not switch to %s"
 msgstr "tidak dapat mengganti ke %s"
 
@@ -12530,13 +12615,23 @@
 #: builtin/rebase.c
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
 msgstr ""
 "tipe kosong tak dikenali '%s'; nilai yang valid adalah \"drop\", \"keep\", "
 "dan \"ask\"."
 
 #: builtin/rebase.c
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"--rebase-merges dengan argumen untai kosong usang dan akan berhenti bekerja "
+"pada versi Git berikutnya. Sebagai gantinya, gunakan --rebase-merges tanpa "
+"argumen, yang melakukan hal yang sama."
+
+#: builtin/rebase.c
 #, c-format
 msgid ""
 "%s\n"
@@ -12756,7 +12851,7 @@
 msgstr ""
 "Aksi --edit-todo hanya dapat digunakan selama pendasaran ulang interaktif."
 
-#: builtin/rebase.c t/helper/test-fast-rebase.c
+#: builtin/rebase.c
 msgid "Cannot read HEAD"
 msgstr "Tidak dapat membaca HEAD"
 
@@ -12803,21 +12898,12 @@
 msgstr "tombol `C' harap nilai numerik"
 
 #: builtin/rebase.c
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Mode tidak dikenal: %s"
-
-#: builtin/rebase.c
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy butuh --merge atau --interactive"
-
-#: builtin/rebase.c
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"opsi penerapan tidak kompatibel dengan rebase.autosquash. "
-"Pertimbangkanmenambahkan --no-autosquash"
+"opsi penerapan tidak kompatibel dengan rebase.rebaseMerges. "
+"Pertimbangkanmenambahkan --no-rebase-merges"
 
 #: builtin/rebase.c
 msgid ""
@@ -12875,10 +12961,6 @@
 msgstr "Tidak menunjuk pada komit yang valid '%s'"
 
 #: builtin/rebase.c
-msgid "Please commit or stash them."
-msgstr "Mohon komit atau stase."
-
-#: builtin/rebase.c
 msgid "HEAD is up to date."
 msgstr "HEAD terbaru."
 
@@ -13189,12 +13271,12 @@
 msgstr "ambil cabang remote"
 
 #: builtin/remote.c
-msgid "import all tags and associated objects when fetching"
-msgstr "impor semua tag dan objek yang terkait ketika mengambil"
-
-#: builtin/remote.c
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "atau jangan mengambil tag apapun (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"impor semua tag dan objek yang terkait ketika mengambil atau jangan\n"
+"mengambil tag apapun (--no-tags)"
 
 #: builtin/remote.c
 msgid "branch(es) to track"
@@ -13313,10 +13395,10 @@
 "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
 "to delete them, use:"
 msgstr[0] ""
-"Catatan: Sebuah cabang diluar hierarki refs/remotes tidak dihapus;\n"
+"Catatan: Sebuah cabang diluar hierarki refs/remotes/ tidak dihapus;\n"
 "untuk menghapusnya, gunakan:"
 msgstr[1] ""
-"Catatan: Beberapa cabang diluar hierarki refs/remotes tidak dihapus;\n"
+"Catatan: Beberapa cabang diluar hierarki refs/remotes/ tidak dihapus;\n"
 "untuk menghapusnya, gunakan:"
 
 #: builtin/remote.c
@@ -13677,6 +13759,11 @@
 msgstr "tidak dapt memindahkan bitmap basi: %s"
 
 #: builtin/repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "nama berkas paket %s tidak diawali dengan %s"
+
+#: builtin/repack.c
 msgid "pack everything in a single pack"
 msgstr "pak semuanya dalam satu pak"
 
@@ -13693,8 +13780,8 @@
 msgstr "tanggal aproksimasi"
 
 #: builtin/repack.c
-msgid "with -C, expire objects older than this"
-msgstr "dengan -C, jangan kadaluarsakan objek lebih lama dari ini"
+msgid "with --cruft, expire objects older than this"
+msgstr "dengan --cruft, kadaluarsakan objek yang lebih tua dari ini"
 
 #: builtin/repack.c
 msgid "remove redundant packs, and run git-prune-packed"
@@ -13779,17 +13866,21 @@
 msgstr "awalan pak untuk menyimpan pak berisi objek terpangkas"
 
 #: builtin/repack.c
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "awalan pak untuk menyimpan pak berisi objek tersaring"
+
+#: builtin/repack.c
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "tidak dapat menghapus pak dalam repositori objek berharga"
 
 #: builtin/repack.c
-msgid "Nothing new to pack."
-msgstr "Tidak ada yang baru untuk dipak."
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "opsi '%s' tidak dapat digunakan bersamaan dengan '%s'"
 
 #: builtin/repack.c
-#, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "nama berkas paket %s tidak diawali dengan %s"
+msgid "Nothing new to pack."
+msgstr "Tidak ada yang baru untuk dipak."
 
 #: builtin/repack.c
 #, c-format
@@ -14044,6 +14135,94 @@
 msgid "only one pattern can be given with -l"
 msgstr "hanya satu pola yang dapat diberikan dengan -l"
 
+#: builtin/replay.c
+msgid "need some commits to replay"
+msgstr "butuh beberapa komit untuk dimainkan ulang"
+
+#: builtin/replay.c
+msgid "--onto and --advance are incompatible"
+msgstr "--onto dan --advance tidak kompatibel"
+
+#: builtin/replay.c
+msgid "all positive revisions given must be references"
+msgstr "semua revisi positif yang diberikan haruslah referensi"
+
+#: builtin/replay.c
+msgid "argument to --advance must be a reference"
+msgstr "argumen pada --advance harus sebuah referensi"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"tidak dapat memajukan target dengan banyak sumber karena pengurutannya akan "
+"menjadi tidak jelas"
+
+#: builtin/replay.c
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"tidak dapat menentukan secara tidak langsung apakah ini operasi --advance "
+"atau --onto"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"tidak dapat memajukan target dengan banyak cabang sumber karena "
+"pengurutannya akan menjadi tidak jelas"
+
+#: builtin/replay.c
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "tidak dapat menentukan secara tidak langsung dasar untuk --onto"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EKSPERIMENTAL!) git replay ([--contained] --onto <dasar baru> | --advance "
+"<cabang>) <rentang revisi>..."
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "buat pemainan ulang memajukan caban yang diberikan"
+
+#: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "mainkan ulang pada komit yang diberikan"
+
+#: builtin/replay.c
+msgid "advance all branches contained in revision-range"
+msgstr "majukan semua cabang yang berada pada rentang komit"
+
+#: builtin/replay.c
+msgid "option --onto or --advance is mandatory"
+msgstr "opsi --onto atau --advance diwajibkan"
+
+#: builtin/replay.c
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"beberapa opsi jalan revisi akan ditimpa oleh karena bit '%s' di 'struct "
+"rev_info' akan dipaksakan"
+
+#: builtin/replay.c
+msgid "error preparing revisions"
+msgstr "kesalahan menyiapkan revisi"
+
+#: builtin/replay.c
+msgid "replaying down to root commit is not supported yet!"
+msgstr "memainkan ulang ke komit akar belum didukung!"
+
+#: builtin/replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "memainkan ulang komit penggabungan belum didukung!"
+
 #: builtin/rerere.c
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
@@ -14306,23 +14485,15 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "mode untuk --abbrev-ref tidak dikenal: %s"
 
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden tidak dapat digunakan bersamaan dengan --branches"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden tidak dapat digunakan bersamaan dengan --tags"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden tidak dapat digunakan dengan --remotes"
-
 #: builtin/rev-parse.c setup.c
 msgid "this operation must be run in a work tree"
 msgstr "operasi ini harus dijalankan di dalam pohon kerja"
 
 #: builtin/rev-parse.c
+msgid "Could not read the index"
+msgstr "Tidak dapat membaca indeks"
+
+#: builtin/rev-parse.c
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "mode untuk --show-object-format tidak dikenal: %s"
@@ -14539,6 +14710,10 @@
 msgstr "nama remote"
 
 #: builtin/send-pack.c
+msgid "push all refs"
+msgstr "dorong semua referensi"
+
+#: builtin/send-pack.c
 msgid "use stateless RPC protocol"
 msgstr "gunakan protokol RPC nirkeadaan"
 
@@ -14750,11 +14925,21 @@
 
 #: builtin/show-ref.c
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pola>...]"
+
+#: builtin/show-ref.c
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pola>...]"
 
@@ -14763,6 +14948,18 @@
 msgstr "git show-ref --exclude-existing[=<pola>]"
 
 #: builtin/show-ref.c
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <referensi>"
+
+#: builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "referensi tidak ada"
+
+#: builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "gagal mencari referensi"
+
+#: builtin/show-ref.c
 msgid "only show tags (can be combined with heads)"
 msgstr "hanya perlihatkan tag (bisa dikombinasikan dengan kepala)"
 
@@ -14771,6 +14968,10 @@
 msgstr "hanya perlihatkan kepala (bisa dikombinasikan dengan tag)"
 
 #: builtin/show-ref.c
+msgid "check for reference existence without resolving"
+msgstr "periksa adanya referensi tanpa penguraian"
+
+#: builtin/show-ref.c
 msgid "stricter reference checking, requires exact ref path"
 msgstr "pemeriksaan referensi lebih ketat, butuh jalur referensi eksak"
 
@@ -14798,9 +14999,11 @@
 
 #: builtin/sparse-checkout.c
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<opsi>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<opsi>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -14951,6 +15154,27 @@
 msgid "error while refreshing working directory"
 msgstr "kesalahan saat menyegarkan direktori kerja"
 
+#: builtin/sparse-checkout.c
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no]-cone] [--rules-"
+"file <file>]"
+
+#: builtin/sparse-checkout.c
+msgid "terminate input and output files by a NUL character"
+msgstr "akhiri berkas masukan dan keluaran oleh satu karakter NUL"
+
+#: builtin/sparse-checkout.c
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"ketika digunakan dengan --rules-file tafsirkan pola sebagai pola mode kerucut"
+
+#: builtin/sparse-checkout.c
+msgid "use patterns in <file> instead of the current ones."
+msgstr "gunakan pola di dalam <berkas> daripada yang saat ini."
+
 #: builtin/stash.c
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<opsi log>]"
@@ -15601,6 +15825,11 @@
 
 #: builtin/submodule--helper.c
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "tidak dapat mengkloning submodul '%s' tanpa URL"
+
+#: builtin/submodule--helper.c
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "Gagal mengkloning '%s'. Percobaan ulang dijadwalkan"
 
@@ -15764,6 +15993,10 @@
 "shallow] [--reference <repositori>] [--recursive] [--[no-]single-branch] "
 "[--] [<jalur>...]"
 
+#: builtin/submodule--helper.c submodule.c
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Gagal menguraikan HEAD sebagai referensi valid."
+
 #: builtin/submodule--helper.c
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<opsi>] [<jalur>...]"
@@ -16346,6 +16579,10 @@
 msgstr "tulis indeks dalam format ini"
 
 #: builtin/update-index.c
+msgid "report on-disk index format version"
+msgstr "laporkan versi format indeks pada-disk"
+
+#: builtin/update-index.c
 msgid "enable or disable split index"
 msgstr "aktifkan atau nonaktifkan indeks terpisah"
 
@@ -16378,6 +16615,16 @@
 msgstr "bersihkan bita fsmonitor valid"
 
 #: builtin/update-index.c
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#: builtin/update-index.c
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: sebelumnya %d, disetel ke %d"
+
+#: builtin/update-index.c
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -16531,10 +16778,10 @@
 #: builtin/worktree.c
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <untai>]]\n"
-"                 [-b <cabang baru>] <jalur> [<mirip komit>]"
+"                 [--orphan] [(-b | -B) <cabang baru>] <jalur> [<mirip komit>]"
 
 #: builtin/worktree.c
 msgid "git worktree list [-v | --porcelain [-z]]"
@@ -16565,6 +16812,38 @@
 msgstr "git worktree unlock <worktree>"
 
 #: builtin/worktree.c
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Tidak ada cabang sumber yang mungkin, menyimpulkan '--orphan'"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Jika maksud Anda ingin membuat pohon kerja berisi sebuah cabang yatim baru\n"
+"(cabang tanpa komit) untuk repositori ini, Anda dapat melakukannya dengan\n"
+"opsi --orphan:\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Jika maksud Anda ingin membuat pohon kerja berisi sebuah cabang yatim baru\n"
+"(cabang tanpa komit) untuk repositori ini, Anda dapat melakukannya dengan\n"
+"opsi --orphan:\n"
+"    git worktree add --orphan %s\n"
+
+#: builtin/worktree.c
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "Menghapus %s/%s: %s"
@@ -16633,6 +16912,11 @@
 
 #: builtin/worktree.c
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "tidak dapat menemukan pohon kerja yang dibuat '%s'"
+
+#: builtin/worktree.c
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "Menyiapkan pohon kerja (cabang baru '%s')"
 
@@ -16649,10 +16933,35 @@
 
 #: builtin/worktree.c
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "tidak dapat dicapat: referensi tidak valid: %s"
+
+#: builtin/worktree.c
+#, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 msgstr "Menyiapkan pohon kerja (HEAD terpisah %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 menunjuk pada referensi tidak valid (atau yatim).\n"
+"Jalur HEAD: '%s'\n"
+"Isi HEAD: '%s'"
+
+#: builtin/worktree.c
+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 ""
+"Tidak ada referensi lokal atau remote yang ada meskipun salah satu remote\n"
+"ada, berhenti; gunakan 'add -f' untuk menimpa atau mengambil remote\n"
+"terlebih dahulu"
+
+#: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "checkout <cabang> bahkan jika sudah di-checkout pada pohon kerja lainnya"
@@ -16666,6 +16975,10 @@
 msgstr "buat atau setel ulang sebuah cabang"
 
 #: builtin/worktree.c
+msgid "create unborn branch"
+msgstr "buat cabang belum lahir/yatim"
+
+#: builtin/worktree.c
 msgid "populate the new working tree"
 msgstr "isikan pohon kerja baru"
 
@@ -16691,6 +17004,11 @@
 msgstr "Opsi '%s', '%s', dan '%s' tidak dapat digunakan bersamaan"
 
 #: builtin/worktree.c
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "opsi '%s' dan mirip-komit tidak dapat digunakan bersamaan"
+
+#: builtin/worktree.c
 msgid "added with --lock"
 msgstr "tambahkan dengan --lock"
 
@@ -16976,6 +17294,16 @@
 msgstr[1] "Bundel membutuhkan %<PRIuMAX> referensi berikut:"
 
 #: bundle.c
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Bundel menggunakan algoritma hash ini: %s"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Bundel menggunakan penyaring ini: %s"
+
+#: bundle.c
 msgid "unable to dup bundle descriptor"
 msgstr "tidak dapat men-dup pendeskripsi bundel"
 
@@ -17021,6 +17349,11 @@
 
 #: chunk-format.c
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "id bingkah %<PRIx32> tidak terata %d-bita"
+
+#: chunk-format.c
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "offset bingkah %<PRIx64> dan %<PRIx64> tidak tepat"
 
@@ -17090,9 +17423,8 @@
 msgstr "Pindahkan objek dan referensi oleh arsip"
 
 #: command-list.h
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Sediakan isi atau informasi tipe dan ukuran berkas untuk objek repositori"
+msgid "Provide contents or details of repository objects"
+msgstr "Sediakan isi atau detail objek repositori"
 
 #: command-list.h
 msgid "Display gitattributes information"
@@ -17278,8 +17610,8 @@
 msgstr "Sebuah antarmuka grafis Git portabel"
 
 #: command-list.h
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Hitung ID objek dan buat blob dari berkas (opsional)"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Hitung ID objek dan buat objeck dari berkas (opsional)"
 
 #: command-list.h
 msgid "Display help information about Git"
@@ -17477,6 +17809,12 @@
 msgstr "Buat, daftar, hapus referensi untuk mengganti objek"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EKSPERIMENTAL: Mainkan ulang komit pada dasar baru, dan juga bekerja pada "
+"repositori bare"
+
+#: command-list.h
 msgid "Generates a summary of pending changes"
 msgstr "Buat ringkasan perubahan tertunda"
 
@@ -17638,8 +17976,8 @@
 msgstr "Perlihatkan info versi Git"
 
 #: command-list.h
-msgid "Show logs with difference each commit introduces"
-msgstr "Perlihatkan log dengan perbedaan yang dimasukkan setiap komit"
+msgid "Show logs with differences each commit introduces"
+msgstr "Perlihatkan log dengan perbedaan yang diperkenalkan pada setiap komit"
 
 #: command-list.h
 msgid "Manage multiple working trees"
@@ -17794,6 +18132,39 @@
 msgstr "berkas grafik komit terlalu kecil"
 
 #: commit-graph.c
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "bingkah oid grafik komit kipas keluar salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph fanout values out of order"
+msgstr "nilai kipas keluar grafik komit tidak berurutan"
+
+#: commit-graph.c
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "bingkah OID pencarian grafik komit salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "bingkah data grafik komit salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph generations chunk is wrong size"
+msgstr "bingkah generasi grafik komit salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "bingkah indeks grafik komit jalur berubah terlalu kecil"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"mengabaikan bingkah jalur berubah yang terlalu kecil (%<PRIuMAX> < "
+"%<PRIuMAX>) di dalam berkas grafik komit"
+
+#: commit-graph.c
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr "tanda tangan grafik komit %X tidak cocok dengan tanda tangan %X"
@@ -17814,15 +18185,41 @@
 msgstr "berkas grafik komit terlalu kecil untuk menyimpan %u bingkah"
 
 #: commit-graph.c
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"bingkah grafik komit OID kipas keluar yang diperlukan hilang atau rusak"
+
+#: commit-graph.c
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "bingkah grafik komit OID pencarian yang diperlukan hilang atau rusak"
+
+#: commit-graph.c
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "bingkah grafik komit data komit yang diperlukan hilang atau rusak"
+
+#: commit-graph.c
 msgid "commit-graph has no base graphs chunk"
 msgstr "grafik komit tidak punya bingkah grafik dasar"
 
 #: commit-graph.c
+msgid "commit-graph base graphs chunk is too small"
+msgstr "bingkah grafik komit dasar terlalu kecil"
+
+#: commit-graph.c
 msgid "commit-graph chain does not match"
 msgstr "rantai grafik komit tidak cocok"
 
 #: commit-graph.c
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "jumlah komit pada grafik dasar terlalu tinggi: %<PRIuMAX>"
+
+#: commit-graph.c
+msgid "commit-graph chain file too small"
+msgstr "berkas rantai grafik komit terlalu kecil"
+
+#: commit-graph.c
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr "rantai grafik komit tidak cocok: baris '%s' bukan sebuah hash"
 
@@ -17844,6 +18241,14 @@
 msgstr "grafik komit memerlukan pembuatan data meluap tapi tidak punya"
 
 #: commit-graph.c
+msgid "commit-graph overflow generation data is too small"
+msgstr "data generasi luapan grafik komit terlalu kecil"
+
+#: commit-graph.c
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "penunjuk tepi tambahan grafik komit di luar batas"
+
+#: commit-graph.c
 msgid "Loading known commits in commit graph"
 msgstr "Memuat komit yang dikenal di grafik komit"
 
@@ -17929,6 +18334,16 @@
 msgstr "gagal menamai ulang berkas grafik komit sementara"
 
 #: commit-graph.c
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "tidak dapat menggabungkan grafik dengan %<PRIuMAX>, %<PRIuMAX> komit"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "tidak dapat menggabungkan grafik %s, terlalu banyak komit: %<PRIuMAX>"
+
+#: commit-graph.c
 msgid "Scanning merged commits"
 msgstr "Memindai komit tergabung"
 
@@ -17964,10 +18379,6 @@
 msgstr "gagal menguraikan komit %s dari grafik komit"
 
 #: commit-graph.c
-msgid "Verifying commits in commit graph"
-msgstr "Memverifikasi komit di dalam grafik komit"
-
-#: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr "gagal menguraikan komit %s dari basis data objek untuk grafik komit"
@@ -17994,22 +18405,6 @@
 
 #: commit-graph.c
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"grafik komit punya angka pembuatan nol untuk komit %s, tapi bukan nol di "
-"tempat lain"
-
-#: commit-graph.c
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"grafik komit punya angka pembuatan bukan nol untuk komit %s, tapi nol di "
-"tempat lain"
-
-#: commit-graph.c
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr "pembuatan grafik komit untuk komit %s yaitu %<PRIuMAX> < %<PRIuMAX>"
 
@@ -18020,6 +18415,19 @@
 "tanggal komit untuk komit %s di dalam grafik komit yaitu %<PRIuMAX> != "
 "%<PRIuMAX>"
 
+#: commit-graph.c
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"grafik komit punya baik generasi nol dan bukan nol (seperti komit '%s' dan "
+"'%s')"
+
+#: commit-graph.c
+msgid "Verifying commits in commit graph"
+msgstr "Memverifikasi komit di dalam grafik komit"
+
 #: commit.c
 #, c-format
 msgid "%s %s is not a commit!"
@@ -18047,6 +18455,12 @@
 
 #: commit.c
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"komit %s ada pada grafik komit tapi tidak ada di dalam basis data objek"
+
+#: commit.c
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "Komit %s punya tandatangan GPG tak dipercaya, dituduh sebagai %s."
 
@@ -18547,8 +18961,8 @@
 msgstr "level kompresi zlib jelek %d"
 
 #: config.c
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar harusnya hanya satu karakter"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar seharusnya hanya satu karakter ASCII"
 
 #: config.c
 #, c-format
@@ -18594,11 +19008,6 @@
 msgstr "tidak dapat menguraikan blob konfigurasi '%s'"
 
 #: config.c
-#, c-format
-msgid "failed to parse %s"
-msgstr "gagal menguraikan %s"
-
-#: config.c
 msgid "unable to parse command-line config"
 msgstr "gagal menguraikan konfigurasi baris perintah"
 
@@ -18688,6 +19097,13 @@
 
 #: config.c
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"menolak bekerja dengan baris yang terlalu panjang di '%s' pada baris "
+"%<PRIuMAX>"
+
+#: config.c
+#, c-format
 msgid "missing value for '%s'"
 msgstr "nilai hilang untuk '%s'"
 
@@ -19171,10 +19587,6 @@
 msgstr "--merge-base tidak bekerja dengan rentang"
 
 #: diff-lib.c
-msgid "--merge-base only works with commits"
-msgstr "--merge-base hanya bekerja dengan komit"
-
-#: diff-lib.c
 msgid "unable to get HEAD"
 msgstr "tidak dapat mendapatkan HEAD"
 
@@ -19187,6 +19599,14 @@
 msgstr "banyak dasar penggabungan ditemukan"
 
 #: diff-no-index.c
+msgid "cannot compare stdin to a directory"
+msgstr "tidak dapat membandingkan masukan standar dan sebuah direktori"
+
+#: diff-no-index.c
+msgid "cannot compare a named pipe to a directory"
+msgstr "tidak dapat membandingkan pipa bernama dan sebuah direktori"
+
+#: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<opsi>] <jalur> <jalur>"
 
@@ -19239,6 +19659,11 @@
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 msgstr "Nilai tidak dikenal untuk variabel konfigurasi 'diff.submodule': '%s'"
 
+#: diff.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "nilai tidak dikenal untuk konfigurasi '%s': %s"
+
 #: diff.c
 #, c-format
 msgid ""
@@ -19253,6 +19678,15 @@
 msgid "external diff died, stopping at %s"
 msgstr "diff eksternal mati, berhenti pada %s"
 
+#: diff.c
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow butuh tepatnya satu spek jalur"
+
+#: diff.c
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "spek jalur ajaib tidak didukung oleh --follow: %s"
+
 #: diff.c parse-options.c
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -19273,10 +19707,6 @@
 "dan '%s'"
 
 #: diff.c
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow butuh tepatnya satu spek jalur"
-
-#: diff.c
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "nilai --stat tidak valid: %s"
@@ -19331,14 +19761,6 @@
 msgstr "mode tidak valid '%s' dalam --color-moved-ws"
 
 #: diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"opsi diff-algorithm terima \"myers\", \"minimal\", \"patience\" dan "
-"\"histogram\""
-
-#: diff.c
 #, c-format
 msgid "invalid argument to %s"
 msgstr "argumen tidak valid ke %s"
@@ -19395,8 +19817,8 @@
 msgstr "keluarkan hanya baris terakhir --stat"
 
 #: diff.c
-msgid "<param1,param2>..."
-msgstr "<parameter 1,parameter 2>..."
+msgid "<param1>,<param2>..."
+msgstr "<parameter 1>,<parameter 2>..."
 
 #: diff.c
 msgid ""
@@ -19409,8 +19831,8 @@
 msgstr "sinonim untuk --dirstat=cumulative"
 
 #: diff.c
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "sinonim untuk --dirstat=files,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "sinonim untuk --dirstat=files,<parameter 1>,<parameter 2>..."
 
 #: diff.c
 msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19518,6 +19940,10 @@
 msgstr "jangan perlihatkan prefiks sumber atau tujuan apapun"
 
 #: diff.c
+msgid "use default prefixes a/ and b/"
+msgstr "gunakan awalan asali a/ dan b/"
+
+#: diff.c
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 "perlihatkan konteks diantara bingkah diff hingga jumlah baris yang disebutkan"
@@ -19639,14 +20065,6 @@
 msgstr "buat diff menggunakan algoritma \"diff histogram\""
 
 #: diff.c
-msgid "<algorithm>"
-msgstr "<algoritma>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "pilih algoritma diff"
-
-#: diff.c
 msgid "<text>"
 msgstr "<teks>"
 
@@ -19901,6 +20319,16 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "petunjuk: Menunggu penyunting Anda untuk menutup berkas...%c"
 
+#: editor.c sequencer.c wrapper.c
+#, c-format
+msgid "could not write to '%s'"
+msgstr "tidak dapat menulis ke '%s'"
+
+#: editor.c
+#, c-format
+msgid "could not edit '%s'"
+msgstr "tidak dapat menyunting '%s'"
+
 #: entry.c
 msgid "Filtering content"
 msgstr "Menyaring isi"
@@ -20273,6 +20701,11 @@
 
 #: git.c
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "tidak ada atribut yang diberikan untuk --attr-source\n"
+
+#: git.c
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "opsi tidak dikenal: %s\n"
 
@@ -20910,13 +21343,13 @@
 "%s"
 
 #: merge-ort.c merge-recursive.c
-msgid "Failed to execute internal merge"
-msgstr "Gagal menjalankan penggabungan internal"
+msgid "failed to execute internal merge"
+msgstr "gagal menjalankan penggabungan internal"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Tidak dapat menambahkan %s ke basis data"
+msgid "unable to add %s to database"
+msgstr "tidak dapat menambahkan %s ke basis data"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
@@ -21322,8 +21755,8 @@
 #: merge-recursive.c
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "KONFLIK (penamaan ulang/penamaan ulang): Penamaan ulang \"%s\"->\"%s\" di "
 "dalam cabang \"%s\" penamaan ulang \"%s\"->\"%s\" di \"%s\"%s"
@@ -21441,6 +21874,22 @@
 
 #: midx.c
 #, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"kipas-keluar oid tidak berurutan: fanout[%d] =%<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
+#: midx.c
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "bingkah pencarian OID kipas-keluar indeks multipak salah ukuran"
+
+#: midx.c
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "bingkah offset OID kipas-keluar indeks multipak salah ukuran"
+
+#: midx.c
+#, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "berkas indeks multipak %s terlalu kecil"
 
@@ -21461,20 +21910,26 @@
 msgstr "versi hash indeks multipak %u tidak cocok dengan versi %u"
 
 #: midx.c
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "indeks multipak kehilangan bingkah pack-name yang diperlukan"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "bingkah nama pak indeks multipak yang diperlukan hilang atau rusak"
 
 #: midx.c
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "indeks multipak kehilangan bingkah OID kipas keluar yang diperlukan"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"bingkah OID kipas-keluar indeks multipak yang diperlukan hilang atau rusak"
 
 #: midx.c
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "indeks multipak kehilangan bingkah pencarian OID yang diperlukan"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"bingkah pencarian OID indeks multipak yang diperlukan  hilang atau rusak"
 
 #: midx.c
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "indeks multipak kehilangan bingkah offset objek yang diperlukan"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr "bingkah offset objek indeks multipak yang diperlukan hilang atau rusak"
+
+#: midx.c
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "bingkah nama pak indeks multipak terlalu kecil"
 
 #: midx.c
 #, c-format
@@ -21487,10 +21942,23 @@
 msgstr "pack-int-id jelek: %u (total pak %u)"
 
 #: midx.c
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX tidak berisi bingkah BTMP"
+
+#: midx.c
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "tidak dapat membuka pak terbitmap %<PRIu32>"
+
+#: midx.c
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "indeks multipak simpan offset 64-bit, tapi off_t terlalu kecil"
 
 #: midx.c
+msgid "multi-pack-index large offset out of bounds"
+msgstr "offset indeks multipak besar di luar jangkauan"
+
+#: midx.c
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "gagal menambah berkas pak '%s'"
@@ -21590,14 +22058,6 @@
 msgstr "Mencari berkas pak yang direferensikan"
 
 #: midx.c
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"kipas-keluar oid tidak berurutan: fanout[%d] =%<PRIx32> > %<PRIx32> = "
-"fanout[%d]"
-
-#: midx.c
 msgid "the midx contains no oid"
 msgstr "midx tidak berisi oid"
 
@@ -22234,6 +22694,10 @@
 msgid "could not open pack %s"
 msgstr "tidak dapat membuka '%s'"
 
+#: pack-bitmap.c t/helper/test-read-midx.c
+msgid "could not determine MIDX preferred pack"
+msgstr "tidak dapat menentukan pak MIDX terpilih"
+
 #: pack-bitmap.c
 #, c-format
 msgid "preferred pack (%s) is invalid"
@@ -22259,6 +22723,11 @@
 
 #: pack-bitmap.c
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "tidak dapat memuat pak: '%s', mematikan penggunaan ulang pak"
+
+#: pack-bitmap.c
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "objek '%s' tidak ditemukan di bitmap tipe"
 
@@ -22304,6 +22773,11 @@
 msgid "unable to get disk usage of '%s'"
 msgstr "tidak dapat mendapatkan penggunaan disk dari '%s'"
 
+#: pack-bitmap.c
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "checksum berkas bitmap '%s' tidak valid"
+
 #: pack-mtimes.c
 #, c-format
 msgid "mtimes file %s is too small"
@@ -22354,6 +22828,23 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "berkas indeks balik %s punya id hash tidak didukung %<PRIu32>"
 
+#: pack-revindex.c
+msgid "invalid checksum"
+msgstr "checksum tidak valid"
+
+#: pack-revindex.c
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "posisi indeks balik tidak valid pada %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+#: pack-revindex.c
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "bingkah indeks balik multipak salah ukuran"
+
+#: pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "tidak dapat menentukan pak terpilih"
+
 #: pack-write.c
 msgid "cannot both write and verify reverse index"
 msgstr "tidak dapat kedua-duanya menulis dan memverifikasi indeks balik"
@@ -22419,16 +22910,6 @@
 
 #: parse-options.c
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s tidak kompatibel dengan %s"
-
-#: parse-options.c
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s : tidak kompatibel dengan sesuatu yang lain"
-
-#: parse-options.c
-#, c-format
 msgid "%s takes no value"
 msgstr "%s tidak mengambil nilai apapun"
 
@@ -22528,6 +23009,11 @@
 msgid "-NUM"
 msgstr "-NUM"
 
+#: parse-options.c
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "lawan dari --no-%s"
+
 #: parse-options.h
 msgid "expiry-date"
 msgstr "tanggal kadaluarsa"
@@ -22566,6 +23052,16 @@
 msgstr ""
 "dengan --pathspec-from-file, elemen spek jalur dipisahkan dengan karakter NUL"
 
+#: parse.c
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "nilai lingkungan boolean '%s' jelek untuk '%s'"
+
+#: parse.c
+#, c-format
+msgid "failed to parse %s"
+msgstr "gagal menguraikan %s"
+
 #: path.c
 #, c-format
 msgid "Could not make %s writable by group"
@@ -22628,6 +23124,11 @@
 
 #: pathspec.c
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' di luar pohon direktori"
+
+#: pathspec.c
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: '%s' di luar repositori pada '%s'"
 
@@ -22787,6 +23288,15 @@
 msgid "could not parse log for '%s'"
 msgstr "tidak dapat menguraikan log untuk '%s'"
 
+#: reachable.c
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "ujung sampah tambahan tidak valid: '%s'"
+
+#: reachable.c
+msgid "unable to enumerate additional recent objects"
+msgstr "tidak dapat menghitung objeck terkini tambahan"
+
 #: read-cache.c
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
@@ -22816,11 +23326,6 @@
 
 #: read-cache.c
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "tidak dapat men-stat '%s'"
-
-#: read-cache.c
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "'%s' muncul baik sebagai sebuah berkas dan sebagai sebuah direktori"
 
@@ -22954,11 +23459,6 @@
 
 #: read-cache.c
 #, c-format
-msgid "could not stat '%s'"
-msgstr "tidak dapat men-stat '%s'"
-
-#: read-cache.c
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "tidak dapat membuka direktori git: %s"
 
@@ -22977,6 +23477,16 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: tidak dapat menurunkan ke tahap #0"
 
+#: read-cache.c
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "status diff tak diharapkan %c"
+
+#: read-cache.c
+#, c-format
+msgid "remove '%s'\n"
+msgstr "hapus '%s'\n"
+
 #: rebase-interactive.c
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
@@ -23200,6 +23710,26 @@
 
 #: ref-filter.c
 #, c-format
+msgid "argument expected for %s"
+msgstr "argumen diharapkan untuk %s"
+
+#: ref-filter.c
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "nilai positif %s=%s diharapkan"
+
+#: ref-filter.c
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "tidak dapat menguraikan penuh %s=%s"
+
+#: ref-filter.c
+#, c-format
+msgid "value expected %s="
+msgstr "nilai %s= diharapkan"
+
+#: ref-filter.c
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "nilai positif '%s' diharapkan di %%(%s)"
 
@@ -23230,6 +23760,11 @@
 
 #: ref-filter.c
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "format yang diharapkan: %%(ahead-behind:<mirip komit>)"
+
+#: ref-filter.c
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "nama bidang rusak: %.*s"
 
@@ -23286,6 +23821,10 @@
 msgstr "--format=%.*s tidak dapat digunakan dengan --python, --shell, --tcl"
 
 #: ref-filter.c
+msgid "failed to run 'describe'"
+msgstr "gagal menjalankan 'describe'"
+
+#: ref-filter.c
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(tanpa cabang, mendasarkan ulang %s)"
@@ -23362,6 +23901,10 @@
 msgid "field name to sort on"
 msgstr "nama bidang untuk diurutkan"
 
+#: ref-filter.h
+msgid "exclude refs which match pattern"
+msgstr "hanya gunakan referensi yang cocok dengan pola"
+
 #: reflog.c
 #, c-format
 msgid "not a reflog: %s"
@@ -23467,17 +24010,12 @@
 msgid "cannot process '%s' and '%s' at the same time"
 msgstr "tidak dapat memproses '%s' dan '%s' pada waktu yang bersamaan"
 
-#: refs/files-backend.c
-#, c-format
-msgid "could not remove reference %s"
-msgstr "tidak dapat menghapus referensi %s"
-
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
 #, c-format
 msgid "could not delete reference %s: %s"
 msgstr "tidak dapat menghapus referensi %s: %s"
 
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
 #, c-format
 msgid "could not delete references: %s"
 msgstr "tidak dapat menghapus referensi: %s"
@@ -23712,9 +24250,9 @@
 msgstr ""
 "Tujuan yang Anda berikan bukan nama referensi penuh (seperti \n"
 "dimulai dengan \"refs/\"). Kami mencoba menebak maksud Anda dengan:\n"
-"- Cari referensi yang cocok dengan '%s' pada sisi remote.\n"
-"- Perika apakah <src> yang sedang didorong ('%s') adalah \n"
-"  referensi pada \"refs/{heads,tags}\". Bila demikian kami \n"
+"- mencari referensi yang cocok dengan '%s' pada sisi remote.\n"
+"- memeriksa apakah <src> yang sedang didorong ('%s') adalah \n"
+"  referensi pada \"refs/{heads,tags}/\". Bila demikian kami \n"
 "  menambahkan awalan refs/{heads,tags}/ yang bersesuaian pada sisi \n"
 "  remote.\n"
 "\n"
@@ -23902,8 +24440,11 @@
 "dan masing-masing punya %d dan %d komit berbeda.\n"
 
 #: remote.c
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (gunakan \"git pull\" untuk gabungkan cabang remote ke milik Anda)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (gunakan \"git pull\" jika Anda ingin mengintegrasikan cabang remote pada\n"
+"   milik Anda)\n"
 
 #: remote.c
 #, c-format
@@ -23995,11 +24536,6 @@
 
 #: rerere.c
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "tidak dapat batal taut '%s'"
-
-#: rerere.c
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "Pracitra diperbarui untuk '%s'"
 
@@ -24051,6 +24587,11 @@
 msgstr "--unpacked=<berkas pak> tidak didukung lagi"
 
 #: revision.c
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "opsi tidak valid: '%s' di mode --stdin"
+
+#: revision.c
 msgid "your current branch appears to be broken"
 msgstr "sepertinya cabang Anda saat ini rusak"
 
@@ -24162,8 +24703,16 @@
 msgstr "hanya unduh metadata untuk cabang yang akan dicheckout"
 
 #: scalar.c
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<options>] [--] <repositori> [<direktori>]"
+msgid "create repository within 'src' directory"
+msgstr "salin repositori di dalam direktori 'src'"
+
+#: scalar.c
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <cabang utama>] [--full-clone]\n"
+"\t[--[-no-]src] <url> [<pendaftaran>]"
 
 #: scalar.c
 #, c-format
@@ -24229,13 +24778,32 @@
 
 #: scalar.c
 #, c-format
-msgid "removing stale scalar.repo '%s'"
+msgid "removed stale scalar.repo '%s'"
 msgstr "menghapus scalar.repo basi '%s'"
 
 #: scalar.c
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "repositori git pergi di '%s'"
+msgid "repository at '%s' has different owner"
+msgstr "repositori pada '%s' berpemilik lain"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "repositori pada '%s' ada masalah format"
+
+#: scalar.c
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "repositori tidak ditemukan di '%s'"
+
+#: scalar.c
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"untuk mencabut repositori ini dari Scalar, jalankan\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 #: scalar.c
 msgid ""
@@ -24419,11 +24987,6 @@
 msgid "could not lock '%s'"
 msgstr "tidak dapat mengunci '%s'"
 
-#: sequencer.c strbuf.c wrapper.c
-#, c-format
-msgid "could not write to '%s'"
-msgstr "tidak dapat menulis ke '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not write eol to '%s'"
@@ -24629,7 +25192,7 @@
 msgid "corrupt author: missing date information"
 msgstr "pengarang rusak: informasi tanggal hilang"
 
-#: sequencer.c t/helper/test-fast-rebase.c
+#: sequencer.c
 #, c-format
 msgid "could not update %s"
 msgstr "tidak dapat memperbarui %s"
@@ -24726,11 +25289,6 @@
 
 #: sequencer.c
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "tidak dapat menamai ulang '%s' ke '%s'"
-
-#: sequencer.c
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "tidak dapat membalikkan %s... %s"
 
@@ -24867,10 +25425,6 @@
 msgstr "tidak dapat membuat direktori pembaris '%s'"
 
 #: sequencer.c
-msgid "could not lock HEAD"
-msgstr "tidak dapat mengunci HEAD"
-
-#: sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "tidak ada pemetikan ceri atau pembalikkan yang sedang berjalan"
 
@@ -24986,21 +25540,21 @@
 "\n"
 
 #: sequencer.c
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "dan buat perubahan ke indeks dan/atau pohon kerja\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "dan buat perubahan ke indeks dan/atau pohon kerja.\n"
 
 #: sequencer.c
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "eksekusi berhasil: %s\n"
-"tapi meninggalkan perubahan ke indeks dan/atau pohon kerja\n"
+"tapi meninggalkan perubahan ke indeks dan/atau pohon kerja.\n"
 "Komit atau stase perubahan Anda, lalu jalankan\n"
 "\n"
 "  git rebase --continue\n"
@@ -25133,6 +25687,10 @@
 msgstr "Stase otomatis sudah ada; membuat entri stase baru."
 
 #: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "referensi autostase itu referensi simbolik"
+
+#: sequencer.c
 msgid "could not detach HEAD"
 msgstr "tidak dapat melepas HEAD"
 
@@ -25169,13 +25727,13 @@
 
 #: sequencer.c
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Mendasarkan ulang (%d/%d)%s"
+msgid "Stopped at %s...  %.*s\n"
+msgstr "Berhenti pada %s... %.*s\n"
 
 #: sequencer.c
 #, c-format
-msgid "Stopped at %s...  %.*s\n"
-msgstr "Berhenti pada %s... %.*s\n"
+msgid "Rebasing (%d/%d)%s"
+msgstr "Mendasarkan ulang (%d/%d)%s"
 
 #: sequencer.c
 #, c-format
@@ -25467,6 +26025,102 @@
 msgid "setsid failed"
 msgstr "setsid gagal"
 
+#: setup.c
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "tidak dapat men-stat templat '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "tidak dapat membuka direktori '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "tidak dapat membaca tautan '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "tidak dapat menautkan simbolik '%s' '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "tidak dapat menyalin '%s' ke '%s'"
+
+#: setup.c
+#, c-format
+msgid "ignoring template %s"
+msgstr "mengabaikan templat %s"
+
+#: setup.c
+#, c-format
+msgid "templates not found in %s"
+msgstr "templat tidak ditemukan di %s"
+
+#: setup.c
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "tidak menyalin templat dari '%s': %s"
+
+#: setup.c
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "nama cabang asal salah: '%s'"
+
+#: setup.c
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: --initial-branch=%s diabaikan"
+
+#: setup.c
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "tidak dapat menangani tipe berkas %d"
+
+#: setup.c
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "tidak dapat memindahkan %s ke %s"
+
+#: setup.c
+msgid "attempt to reinitialize repository with different hash"
+msgstr "mencoba menginisialisasi ulang repositori dengan hash yang berbeda"
+
+#: setup.c
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"mencoba menginisialisasi ulang repositori dengan format penyimpanan "
+"referensi yang berbeda"
+
+#: setup.c
+#, c-format
+msgid "%s already exists"
+msgstr "%s sudah ada"
+
+#: setup.c
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Repositori berbagi Git yang sudah ada diinisialisasi ulang di %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Repositori Git diinisialisasi ulang di %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Repositori berbagi Git kosong diinisialisasi di %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Repositori Git kosong dinisialisasi di %s%s\n"
+
 #: sparse-index.c
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -25528,11 +26182,6 @@
 msgstr[0] "%u bita/detik"
 msgstr[1] "%u bita/detik"
 
-#: strbuf.c
-#, c-format
-msgid "could not edit '%s'"
-msgstr "tidak dapat menyunting '%s'"
-
 #: submodule-config.c
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
@@ -25776,14 +26425,6 @@
 msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr "jumlah entri di dalam pohon tembolok untuk dinirvalidasi (asali 0)"
 
-#: t/helper/test-fast-rebase.c
-msgid "unhandled options"
-msgstr "opsi tak tertangani"
-
-#: t/helper/test-fast-rebase.c
-msgid "error preparing revisions"
-msgstr "kesalahan menyiapkan revisi"
-
 #: t/helper/test-reach.c
 #, c-format
 msgid "commit %s is not marked reachable"
@@ -25981,10 +26622,6 @@
 msgid "invalid remote service path"
 msgstr "jalur layanan remote tidak valid"
 
-#: transport-helper.c transport.c
-msgid "operation not supported by protocol"
-msgstr "operasi tidak didukung oleh protokol"
-
 #: transport-helper.c
 #, c-format
 msgid "can't connect to subservice %s"
@@ -26150,11 +26787,6 @@
 
 #: transport.c
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "nilai tidak dikenal untuk konfigurasi '%s': %s"
-
-#: transport.c
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "transportasi '%s' tidak diperbolehkan"
 
@@ -26213,6 +26845,10 @@
 msgid "could not retrieve server-advertised bundle-uri list"
 msgstr "tidak dapat menerima daftar bundle-uri teriklankan server"
 
+#: transport.c
+msgid "operation not supported by protocol"
+msgstr "operasi tidak didukung oleh protokol"
+
 #: tree-walk.c
 msgid "too-short tree object"
 msgstr "objek pohon terlalu pendek"
@@ -26673,6 +27309,10 @@
 msgid "unable to get current working directory"
 msgstr "tidak dapat mendapatkan direktori kerja saat ini"
 
+#: wrapper.c
+msgid "unable to get random bytes"
+msgstr "tidak dapat mendapatkan bita acak"
+
 #: wt-status.c
 msgid "Unmerged paths:"
 msgstr "Jalur yang tak tergabung:"
@@ -27230,6 +27870,11 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "tidak dapat %s: indeks Anda berisi perubahan yang belum dikomit."
 
+#: xdiff-interface.c
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "nilai gaya tidak dikenal '%s' diberikan untuk '%s'"
+
 #: git-merge-octopus.sh git-merge-resolve.sh
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
@@ -27452,13 +28097,13 @@
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Gagal membuka %s: %s"
+msgid "Failed to open %s.final: %s"
+msgstr "Gagal membuka %s.final: %s"
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s.final: %s"
-msgstr "Gagal membuka %s.final: %s"
+msgid "Failed to open %s: %s"
+msgstr "Gagal membuka %s: %s"
 
 #: git-send-email.perl
 msgid "Summary email is empty, skipping it\n"
@@ -27643,8 +28288,8 @@
 
 #: git-send-email.perl
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Menambahkan %s: %s dari: '%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Baris masukan salah format: '%s'"
 
 #: git-send-email.perl
 #, perl-format
@@ -27652,6 +28297,11 @@
 msgstr "(%s) gagal menutup pipa ke '%s'"
 
 #: git-send-email.perl
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Menambahkan %s: %s dari: '%s'\n"
+
+#: git-send-email.perl
 msgid "cannot send message as 7bit"
 msgstr "tidak dapat mengirim pesan sebagai 7bit"
 
diff --git a/po/ru.po b/po/ru.po
index 803208c..3e56eb5 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -4,28 +4,28 @@
 #
 # Translators:
 # Alexander Golubev <fatzer2@gmail.com>, 2020
-# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2022
+# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2023
 # insolor, 2014
 # insolor, 2014
 # Sergey Alyoshin <alyoshin.s@gmail.com>, 2020-2021
 # Sergey Kuznetsov <votkinsk@gmail.com>, 2021-2022
-# Чук Таблицоменделеев <aurum444an@gmail.com>, 2019
+# Чук Таблицоменделеев (Aurum79) <aurum444an@gmail.com>, 2019
 msgid ""
 msgstr ""
 "Project-Id-Version: Git Russian Localization Project\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-06-26 20:30+0800\n"
+"POT-Creation-Date: 2023-06-30 12:55+0300\n"
 "PO-Revision-Date: 2014-02-18 23:30+0000\n"
-"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2022\n"
-"Language-Team: Russian (http://www.transifex.com/djm00n/git-po-ru/language/"
+"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2023\n"
+"Language-Team: Russian (http://app.transifex.com/djm00n/git-po-ru/language/"
 "ru/)\n"
-"Language: ru\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
-"%100>=11 && n%100<=14)? 2 : 3);\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || "
+"(n%100>=11 && n%100<=14)? 2 : 3);\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -53,7 +53,7 @@
 msgid "could not write index"
 msgstr "не удалось записать индекс"
 
-#, c-format, perl-format
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "обновлён %d путь\n"
@@ -61,7 +61,7 @@
 msgstr[2] "обновлено %d путей\n"
 msgstr[3] "обновлено %d пути\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "примечание: %s теперь неотслеживаемый.\n"
 
@@ -75,7 +75,7 @@
 msgid "Could not parse HEAD^{tree}"
 msgstr "Не удалось разобрать HEAD^{tree}"
 
-#, c-format, perl-format
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "обращены изменения %d пути\n"
@@ -90,7 +90,7 @@
 msgid "Add untracked"
 msgstr "Добавить неотслеживаемый"
 
-#, c-format, perl-format
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "добавлен %d путь\n"
@@ -188,19 +188,19 @@
 msgid "Bye.\n"
 msgstr "До свидания.\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage mode change [y,n,q,a,d%s,?]? "
 msgstr "Проиндексировать изменение режима доступа [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "Проиндексировать удаление [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "Добавить к индексу [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Индексировать этот блок [y,n,q,a,d%s,?]? "
 
@@ -224,19 +224,19 @@
 "a - индексировать этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "Спрятать изменение режима доступа [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "Спрятать удаление файла [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "Добавить к спрятанному [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "Спрятать этот блок [y,n,q,a,d%s,?]? "
 
@@ -259,19 +259,19 @@
 "a - спрятать этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "Убрать изменения режима доступа из индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "Убрать удаление из индекса [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "Убрать из индекса [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "Убрать из индекса этот блок [y,n,q,a,d%s,?]? "
 
@@ -289,25 +289,25 @@
 "a - unstage this hunk and all later hunks in the file\n"
 "d - do not unstage this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - убрать из индекса этот блок\n"
+"y - убрать этот блок из индекса\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - убрать из индекса этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "Применить изменение режима доступа к индексу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "Применить удаление к индексу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "Применить добавление к индексу [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "Принять этот блок в индекс [y,n,q,a,d%s,?]? "
 
@@ -331,21 +331,21 @@
 "a - принять этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить изменения режима доступа в рабочем каталоге [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить удаление в рабочем каталоге [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить добавления из рабочего каталога [y,n,q,a,d%s,?]?"
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
-msgstr "Отбросить этот блок из рабочего дерева [y,n,q,a,d%s,?]?"
+msgstr "Отбросить этот блок из рабочего каталога [y,n,q,a,d%s,?]?"
 
 msgid ""
 "If the patch applies cleanly, the edited hunk will immediately be marked for "
@@ -360,29 +360,29 @@
 "a - discard this hunk and all later hunks in the file\n"
 "d - do not discard this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - отбросить этот блок из рабочего дерева\n"
+"y - отбросить этот блок из рабочего каталога\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - отбросить этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, 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, perl-format
+#, c-format
 msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить удаление в индексе и рабочем каталоге [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Отменить добавление в индекс и рабочий каталог [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
-msgstr "Отбросить этот блок из индекса рабочего дерева [y,n,q,a,d%s,?]? "
+msgstr "Отбросить этот блок из индекса рабочего каталога [y,n,q,a,d%s,?]? "
 
 msgid ""
 "y - discard this hunk from index and worktree\n"
@@ -391,27 +391,27 @@
 "a - discard this hunk and all later hunks in the file\n"
 "d - do not discard this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - отбросить этот блок из рабочего дерева\n"
+"y - отбросить этот блок из рабочего каталога\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - отбросить этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
-#, c-format, perl-format
+#, 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, perl-format
+#, c-format
 msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Применить удаление к индексу и рабочему каталогу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply addition to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Применить добавление к индексу и рабочему каталогу [y,n,q,a,d%s,?]? "
 
-#, c-format, perl-format
+#, c-format
 msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
 msgstr "Принять этот блок в индекс и рабочий каталог [y,n,q,a,d%s,?]? "
 
@@ -422,12 +422,28 @@
 "a - apply this hunk and all later hunks in the file\n"
 "d - do not apply this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - принять этот блок в индекс и рабочее дерево\n"
+"y - принять этот блок в индекс и рабочий каталог\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - принять этот и остальные блоки файла\n"
 "d - пропустить этот и остальные блоки файла\n"
 
+#, c-format
+msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+
+#, c-format
+msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+
+#, c-format
+msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+
+#, c-format
+msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
+msgstr "Принять этот блок в рабочий каталог [y,n,q,a,d%s,?]? "
+
 msgid ""
 "y - apply this hunk to worktree\n"
 "n - do not apply this hunk to worktree\n"
@@ -435,7 +451,7 @@
 "a - apply this hunk and all later hunks in the file\n"
 "d - do not apply this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - принять этот блок в рабочее дерево\n"
+"y - принять этот блок в рабочий каталог\n"
 "n - пропустить этот блок\n"
 "q - выход; пропустить этот и все последующие блоки\n"
 "a - принять этот и остальные блоки файла\n"
@@ -445,10 +461,6 @@
 msgid "could not parse hunk header '%.*s'"
 msgstr "не удалось разобрать заголовок блока изменений «%.*s»"
 
-#, c-format
-msgid "could not parse colored hunk header '%.*s'"
-msgstr "не удалось разобрать цветной заголовок блока изменений «%.*s»"
-
 msgid "could not parse diff"
 msgstr "не удалось разобрать список изменений (diff)"
 
@@ -457,15 +469,18 @@
 
 #, c-format
 msgid "failed to run '%s'"
-msgstr ""
+msgstr "не удалось запустить «%s»"
 
 msgid "mismatched output from interactive.diffFilter"
-msgstr ""
+msgstr "несовпадение вывода из interactive.diffFilter"
 
 msgid ""
 "Your filter must maintain a one-to-one correspondence\n"
 "between its input and output lines."
 msgstr ""
+"Ваш фильтр должен поддерживать соответствие\n"
+"один-к-одному между его входными и выходными\n"
+"строками."
 
 #, c-format
 msgid ""
@@ -504,8 +519,6 @@
 "Чтобы убрать «%c»-строки, удалите их.\n"
 "Строки, начинающиеся с %cбудут удалены.\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -517,25 +530,16 @@
 "и блок останется без изменений.\n"
 
 msgid "could not parse hunk header"
-msgstr ""
+msgstr "не удалось разобрать заголовок блока изменений"
 
 msgid "'git apply --cached' failed"
-msgstr ""
+msgstr "сбой при выполнении «git apply --cached»"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
-#.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
@@ -543,7 +547,7 @@
 "«нет»!) [y/n]? "
 
 msgid "The selected hunks do not apply to the index!"
-msgstr ""
+msgstr "Выбранные блоки не применяются без ошибок к индексу!"
 
 msgid "Apply them to the worktree anyway? "
 msgstr "Все равно применить их к рабочему каталогу? "
@@ -562,15 +566,24 @@
 "e - manually edit the current hunk\n"
 "? - print help\n"
 msgstr ""
+"j - не принимать решение по этому блоку, перейти на следующий нерешенный\n"
+"J - не принимать решение по этому блоку, перейти на следующий\n"
+"k - не принимать решение по этому блоку, перейти на предыдущий нерешенный\n"
+"K - не принимать решение по этому блоку, перейти на предыдущий\n"
+"g - выбрать блок изменений на который нужно перейти\n"
+"/ - поиск блока изменений с помощью регулярного выражения\n"
+"s - разделить текущий блок на блоки меньшего размера\n"
+"e - вручную отредактировать текущий блок\n"
+"? - вывести справку\n"
 
 msgid "No previous hunk"
-msgstr ""
+msgstr "Нет предыдущего блока"
 
 msgid "No next hunk"
-msgstr ""
+msgstr "Не следующего блока"
 
 msgid "No other hunks to goto"
-msgstr ""
+msgstr "Нет других блоков для перехода"
 
 msgid "go to which hunk (<ret> to see more)? "
 msgstr "на какой блок перейти (нажмите <ввод> чтобы увидеть еще)? "
@@ -580,41 +593,41 @@
 
 #, c-format
 msgid "Invalid number: '%s'"
-msgstr ""
+msgstr "Недопустимый номер: «%s»"
 
 #, c-format
 msgid "Sorry, only %d hunk available."
 msgid_plural "Sorry, only %d hunks available."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Простите, но только %d блок изменений доступнен."
+msgstr[1] "Простите, но только %d блока изменений доступно."
+msgstr[2] "Простите, но только %d блоков изменений доступно."
+msgstr[3] "Простите, но только %d блока изменений доступно."
 
 msgid "No other hunks to search"
-msgstr ""
+msgstr "Нет других блоков для поиска"
 
 msgid "search for regex? "
 msgstr "искать с помощью регулярного выражения? "
 
 #, c-format
 msgid "Malformed search regexp %s: %s"
-msgstr ""
+msgstr "Регулярное выражение для поиска в неправильном формате %s: %s"
 
 msgid "No hunk matches the given pattern"
-msgstr ""
+msgstr "Не найдены блоки, которые соответствуют указанному шаблону"
 
 msgid "Sorry, cannot split this hunk"
 msgstr ""
 
 #, c-format
 msgid "Split into %d hunks."
-msgstr ""
+msgstr "Разбито на %d блока изменений."
 
 msgid "Sorry, cannot edit this hunk"
 msgstr ""
 
 msgid "'git apply' failed"
-msgstr ""
+msgstr "сбой при выполнении «git apply»"
 
 #, c-format
 msgid ""
@@ -666,6 +679,16 @@
 msgid "Exiting because of unfinished merge."
 msgstr "Выход из-за незавершенного слияния."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Быстрая перемотка невозможна, отменяем."
 
@@ -724,19 +747,35 @@
 "advice.detachedHead в значение false\n"
 "\n"
 
+#, c-format
+msgid ""
+"The following paths have been moved outside the\n"
+"sparse-checkout definition but are not sparse due to local\n"
+"modifications.\n"
+msgstr ""
+
+msgid ""
+"To correct the sparsity of these paths, do the following:\n"
+"* Use \"git add --sparse <paths>\" to update the index\n"
+"* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
+msgstr ""
+
 msgid "cmdline ends with \\"
 msgstr "командная строка заканчивается символом \\"
 
 msgid "unclosed quote"
 msgstr "пропущена закрывающая кавычка"
 
+msgid "too many arguments"
+msgstr ""
+
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "неопознанная опция для пробелов «%s»"
+msgstr "неопознанный параметр для пробелов «%s»"
 
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "неопознанная опция для игнорирования пробелов «%s»"
+msgstr "неопознанный параметр для игнорирования пробелов «%s»"
 
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
@@ -1101,6 +1140,10 @@
 msgstr "не удалось открыть %s"
 
 #, c-format
+msgid "cannot unlink '%s'"
+msgstr ""
+
+#, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Блок №%d применен без ошибок."
 
@@ -1256,6 +1299,10 @@
 msgstr "неподдерживаемый режим доступа к файлу: 0%o (SHA1: %s)"
 
 #, c-format
+msgid "deflate error (%d)"
+msgstr "ошибка сжатия (%d)"
+
+#, c-format
 msgid "unable to start '%s' filter"
 msgstr "не удалось запустить фильтр «%s»"
 
@@ -1275,10 +1322,6 @@
 msgstr "путь слишком длинный (%d символов, SHA1: %s): %s"
 
 #, c-format
-msgid "deflate error (%d)"
-msgstr "ошибка сжатия (%d)"
-
-#, c-format
 msgid "timestamp too large for this system: %<PRIuMAX>"
 msgstr "отметка времени слишком большая для этой системы: %<PRIuMAX>"
 
@@ -1298,6 +1341,10 @@
 msgstr "не удалось прочитать «%s»"
 
 #, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr ""
+
+#, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "спецификатор пути «%s» не соответствует ни одному файлу"
 
@@ -1313,9 +1360,6 @@
 msgid "not a tree object: %s"
 msgstr "недействительный объект дерева: %s"
 
-msgid "current working directory is untracked"
-msgstr "текущий рабочий каталог не отслеживается"
-
 #, c-format
 msgid "File not found: %s"
 msgstr ""
@@ -1366,6 +1410,12 @@
 msgid "report archived files on stderr"
 msgstr "отчет об архивированных файлах в stderr"
 
+msgid "time"
+msgstr "время"
+
+msgid "set modification time of archive entries"
+msgstr ""
+
 msgid "set compression level"
 msgstr ""
 
@@ -1406,6 +1456,13 @@
 msgid "%.*s is not a valid attribute name"
 msgstr "%.*s не является допустимым именем атрибута"
 
+msgid "unable to add additional attribute"
+msgstr ""
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr ""
+
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "%s не разрешено: %s:%d"
@@ -1419,6 +1476,21 @@
 "«восклицательный знак»."
 
 #, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr ""
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr ""
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr ""
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr ""
+
+#, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "Плохое содержимое файла «%s»: %s"
 
@@ -1514,7 +1586,6 @@
 
 #. TRANSLATORS: the last %s will be replaced with "(roughly %d
 #. steps)" translation.
-#.
 #, c-format
 msgid "Bisecting: %d revision left to test after this %s\n"
 msgid_plural "Bisecting: %d revisions left to test after this %s\n"
@@ -1526,9 +1597,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents и --reverse не очень сочетаются."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "нельзя использовать --contents с указанием финального имени объекта"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "при --reverse и --first-parent вместе нужно указывать конкретный последний "
@@ -1597,20 +1665,17 @@
 #. 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
 #. around.
-#.
 #, c-format
 msgid "  %s\n"
 msgstr ""
 
 #. TRANSLATORS: The second argument is a \n-delimited list of
 #. duplicate refspecs, composed above.
-#.
 #, c-format
 msgid ""
 "There are multiple remotes whose fetch refspecs map to the remote\n"
@@ -1680,8 +1745,8 @@
 
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
 
 #, c-format
@@ -1692,10 +1757,6 @@
 msgid "'%s' is already checked out at '%s'"
 msgstr "«%s» уже находится на «%s»"
 
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD рабочего каталога %s не обновлён"
-
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<опции>] [--] <спецификатор-пути>..."
 
@@ -1717,6 +1778,11 @@
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Непроиндексированные изменения после обновления индекса:"
 
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+
 msgid "Could not read the index"
 msgstr "Не удалось прочитать индекс"
 
@@ -2006,7 +2072,6 @@
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#.
 #, c-format
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
 msgstr ""
@@ -2103,6 +2168,9 @@
 msgid "run interactively"
 msgstr "запустить в интерактивном режиме"
 
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr ""
+
 msgid "historical option -- no-op"
 msgstr "историческая опция — ничего не делает"
 
@@ -2245,31 +2313,24 @@
 msgid "git archive: expected a flush"
 msgstr "git archive: ожидался сброс буфера"
 
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr ""
-
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
-msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<термин> --term-{old,"
-"good}=<термин>] [--no-checkout] [--first-parent] [<плохая-редация> [<хорошая-"
-"редация>...]] [--] [<пути>...]"
-
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
+"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
+msgid "git bisect (good|bad) [<rev>...]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-replay <filename>"
+msgid "git bisect skip [(<rev>|<range>)...]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
+msgid "git bisect reset [<commit>]"
 msgstr ""
 
-msgid "git bisect--helper --bisect-run <cmd>..."
+msgid "git bisect replay <logfile>"
+msgstr ""
+
+msgid "git bisect run <cmd>..."
 msgstr ""
 
 #, c-format
@@ -2348,7 +2409,6 @@
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
-#.
 msgid "Are you sure [Y/n]? "
 msgstr "Вы уверены [Y - да/n - нет]? "
 
@@ -2406,9 +2466,6 @@
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr ""
 
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "нельзя выполнить двоичный поиск на дереве после cg-seek"
-
 msgid "bad HEAD - strange symbolic ref"
 msgstr ""
 
@@ -2422,7 +2479,6 @@
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
-#.
 msgid "Do you want me to do it for you [Y/n]? "
 msgstr "Вы уверены, что хотите, чтобы я сделал это [Y] - да/[n] - нет? "
 
@@ -2460,7 +2516,7 @@
 msgstr "сбой при выполнении двоичного поиска: не передана команда."
 
 #, c-format
-msgid "unable to verify '%s' on good revision"
+msgid "unable to verify %s on good revision"
 msgstr ""
 
 #, c-format
@@ -2468,7 +2524,7 @@
 msgstr ""
 
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
 msgstr ""
 
 #, c-format
@@ -2478,74 +2534,48 @@
 msgid "bisect run cannot continue any more"
 msgstr "bisect run больше не может продолжать"
 
-#, c-format
 msgid "bisect run success"
 msgstr "bisect run выполнен успешно"
 
-#, c-format
 msgid "bisect found first bad commit"
 msgstr ""
 
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
 msgstr ""
 
-msgid "reset the bisection state"
+#, c-format
+msgid "'%s' requires either no argument or a commit"
 msgstr ""
 
-msgid "check whether bad or good terms exist"
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
 msgstr ""
 
-msgid "print out the bisect terms"
-msgstr ""
-
-msgid "start the bisect session"
-msgstr ""
-
-msgid "find the next bisection commit"
-msgstr ""
-
-msgid "mark the state of ref (or refs)"
-msgstr ""
-
-msgid "list the bisection steps so far"
-msgstr ""
-
-msgid "replay the bisection process from the given file"
-msgstr ""
-
-msgid "skip some commits for checkout"
-msgstr ""
-
-msgid "visualize the bisection"
-msgstr ""
-
-msgid "use <cmd>... to automatically bisect"
-msgstr ""
-
-msgid "no log for BISECT_WRITE"
-msgstr ""
-
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr ""
-
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr ""
-
-msgid "--bisect-next requires 0 arguments"
-msgstr ""
-
-msgid "--bisect-log requires 0 arguments"
+#, c-format
+msgid "'%s' requires 0 arguments"
 msgstr ""
 
 msgid "no logfile given"
 msgstr ""
 
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr ""
+
+msgid "need a command"
+msgstr ""
+
+#, c-format
+msgid "unknown command: '%s'"
+msgstr ""
+
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<опции>] [<опции-редакции>] [<редакция>] [--] <файл>"
 
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr ""
+
 msgid "<rev-opts> are documented in git-rev-list(1)"
 msgstr "<опции-rev-list> документированы в git-rev-list(1)"
 
@@ -2659,7 +2689,6 @@
 #. among various forms of relative timestamps, but
 #. your language may need more or fewer display
 #. columns.
-#.
 msgid "4 years, 11 months ago"
 msgstr "4 года и 11 месяцев назад"
 
@@ -2734,9 +2763,6 @@
 msgid "cannot use -a with -d"
 msgstr "нельзя использовать одновременно ключи -a и -d"
 
-msgid "Couldn't look up commit object for HEAD"
-msgstr "Не удалось найти объект коммита для HEAD"
-
 #, c-format
 msgid "Cannot delete branch '%s' checked out at '%s'"
 msgstr "Нельзя удалить ветку «%s» т.к. она активна на «%s»"
@@ -2746,6 +2772,12 @@
 msgstr "внешняя отслеживаемая ветка «%s» не найдена."
 
 #, c-format
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+
+#, c-format
 msgid "branch '%s' not found."
 msgstr "ветка «%s» не найдена."
 
@@ -2775,20 +2807,22 @@
 msgid "Branch %s is being bisected at %s"
 msgstr "Производится двоичный поиск в ветке %s на %s"
 
-msgid "cannot copy the current branch while not on any."
-msgstr ""
-"невозможно скопировать текущую ветку, если вы не находитесь ни на одной из "
-"веток."
-
-msgid "cannot rename the current branch while not on any."
-msgstr ""
-"невозможно переименовать текущую ветку, если вы не находитесь ни на одной из "
-"них."
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD рабочего каталога %s не обновлён"
 
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr "Недействительное имя ветки: «%s»"
 
+#, c-format
+msgid "No commit on branch '%s' yet."
+msgstr "Еще нет коммита на ветке «%s»."
+
+#, c-format
+msgid "No branch named '%s'."
+msgstr "Нет ветки с именем «%s»."
+
 msgid "Branch rename failed"
 msgstr "Сбой переименования ветки"
 
@@ -2877,6 +2911,9 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "переместить/переименовать ветку, даже если целевое имя уже существует"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr ""
+
 msgid "copy a branch and its reflog"
 msgstr "скопировать ветку и её журнал ссылок"
 
@@ -2945,13 +2982,15 @@
 msgid "cannot edit description of more than one branch"
 msgstr "нельзя изменить описание более одной ветки за раз"
 
-#, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Еще нет коммита на ветке «%s»."
+msgid "cannot copy the current branch while not on any."
+msgstr ""
+"невозможно скопировать текущую ветку, если вы не находитесь ни на одной из "
+"веток."
 
-#, c-format
-msgid "No branch named '%s'."
-msgstr "Нет ветки с именем «%s»."
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+"невозможно переименовать текущую ветку, если вы не находитесь ни на одной из "
+"них."
 
 msgid "too many branches for a copy operation"
 msgstr "слишком много веток для операции копирования"
@@ -3017,7 +3056,9 @@
 msgid "not run from a git repository - no hooks to show\n"
 msgstr ""
 
-msgid "git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"
+msgid ""
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
 msgstr ""
 
 msgid ""
@@ -3038,16 +3079,27 @@
 "You can delete any lines you don't wish to share.\n"
 msgstr ""
 
-msgid "specify a destination for the bugreport file"
+msgid "mode"
+msgstr "режим"
+
+msgid ""
+"create an additional zip archive of detailed diagnostics (default 'stats')"
 msgstr ""
 
-msgid "specify a strftime format suffix for the filename"
+msgid "specify a destination for the bugreport file(s)"
+msgstr ""
+
+msgid "specify a strftime format suffix for the filename(s)"
 msgstr ""
 
 #, c-format
 msgid "could not create leading directories for '%s'"
 msgstr ""
 
+#, c-format
+msgid "unable to create diagnostics archive %s"
+msgstr ""
+
 msgid "System Info"
 msgstr ""
 
@@ -3062,16 +3114,21 @@
 msgid "Created new report at '%s'.\n"
 msgstr ""
 
-msgid "git bundle create [<options>] <file> <git-rev-list args>"
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
 
-msgid "git bundle verify [<options>] <file>"
+msgid "git bundle verify [-q | --quiet] <file>"
 msgstr ""
 
 msgid "git bundle list-heads <file> [<refname>...]"
 msgstr ""
 
-msgid "git bundle unbundle <file> [<refname>...]"
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr ""
+
+msgid "need a <file> argument"
 msgstr ""
 
 msgid "do not show progress meter"
@@ -3080,11 +3137,11 @@
 msgid "show progress meter"
 msgstr "показать прогресс выполнения"
 
-msgid "show progress meter during object writing phase"
-msgstr "показать прогресс выполнения во время записи объектов"
+msgid "historical; same as --progress"
+msgstr ""
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "похоже на --all-progress при включенном прогрессе выполнения"
+msgid "historical; does nothing"
+msgstr ""
 
 msgid "specify bundle format version"
 msgstr ""
@@ -3106,10 +3163,6 @@
 msgstr ""
 
 #, c-format
-msgid "Unknown subcommand: %s"
-msgstr "Неизвестная подкоманда: %s"
-
-#, c-format
 msgid "cannot read object %s '%s'"
 msgstr "невозможно прочитать объект %s «%s»"
 
@@ -3131,10 +3184,6 @@
 msgid "%s takes no arguments"
 msgstr ""
 
-#, c-format
-msgid "unknown command: '%s'"
-msgstr ""
-
 msgid "only one batch option may be specified"
 msgstr ""
 
@@ -3151,7 +3200,7 @@
 "git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
 "objects]\n"
 "             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters]"
+"             [--textconv | --filters] [-z]"
 msgstr ""
 
 msgid ""
@@ -3180,6 +3229,9 @@
 msgid "allow -s and -t to work with broken/corrupt objects"
 msgstr "разрешить -s и -t работать с повреждёнными объектами"
 
+msgid "use mail map file"
+msgstr ""
+
 msgid "Batch objects requested on stdin (or --batch-all-objects)"
 msgstr ""
 
@@ -3189,6 +3241,9 @@
 msgid "like --batch, but don't emit <contents>"
 msgstr ""
 
+msgid "stdin is NUL-terminated"
+msgstr ""
+
 msgid "read commands from stdin"
 msgstr ""
 
@@ -3250,18 +3305,18 @@
 msgid "<object> required with '-%c'"
 msgstr ""
 
-msgid "too many arguments"
-msgstr ""
-
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
 msgstr ""
 
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <атрибут>...] [--] <путь>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
 
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <атрибут>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
 
 msgid "report all attributes set on file"
 msgstr "вывести все атрибуты установленные для файла"
@@ -3275,6 +3330,12 @@
 msgid "terminate input and output records by a NUL character"
 msgstr "окончание ввода и вывода записей по НУЛЕВОМУ символу"
 
+msgid "<tree-ish>"
+msgstr ""
+
+msgid "which tree-ish to check attributes at"
+msgstr ""
+
 msgid "suppress progress reporting"
 msgstr "не выводить прогресс выполнения"
 
@@ -3787,9 +3848,9 @@
 msgstr ""
 
 msgid ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <шаблон>] [-x | -X] [--] <пути>..."
 
 #, c-format
 msgid "Removing %s\n"
@@ -3853,7 +3914,7 @@
 "*          - выбрать все элементы\n"
 "           - (пусто) завершить выделение\n"
 
-#, c-format, perl-format
+#, c-format
 msgid "Huh (%s)?\n"
 msgstr "Хм (%s)?\n"
 
@@ -4006,9 +4067,6 @@
 msgid "create a shallow clone of that depth"
 msgstr "сделать частичный клон указанной глубины"
 
-msgid "time"
-msgstr "время"
-
 msgid "create a shallow clone since a specific time"
 msgstr "сделать частичный клон до определенного времени"
 
@@ -4062,6 +4120,12 @@
 msgid "initialize sparse-checkout file to include only files at root"
 msgstr ""
 
+msgid "uri"
+msgstr ""
+
+msgid "a URI for downloading bundles before fetching from origin remote"
+msgstr ""
+
 #, c-format
 msgid "info: Could not add alternate for '%s': %s\n"
 msgstr "информация: Не удалось добавить альтернативу для «%s»: %s\n"
@@ -4075,10 +4139,18 @@
 msgstr "%s уже существует и не является каталогом"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr ""
+
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr ""
 
 #, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr ""
+
+#, c-format
 msgid "failed to unlink '%s'"
 msgstr "сбой отсоединения «%s»"
 
@@ -4124,10 +4196,8 @@
 msgid "failed to initialize sparse-checkout"
 msgstr ""
 
-msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
 msgstr ""
-"внешний HEAD ссылается на несуществующую ссылку, нельзя переключиться на "
-"такое состояние.\n"
 
 msgid "unable to checkout working tree"
 msgstr "не удалось переключиться на версию в рабочем каталоге"
@@ -4147,8 +4217,9 @@
 msgid "You must specify a repository to clone."
 msgstr "Вы должны указать репозиторий для клонирования."
 
-#, c-format
-msgid "options '%s' and '%s %s' cannot be used together"
+msgid ""
+"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+"exclude"
 msgstr ""
 
 #, c-format
@@ -4227,6 +4298,16 @@
 msgid "cannot clone from filtered bundle"
 msgstr ""
 
+msgid "failed to initialize the repo, skipping bundle URI"
+msgstr ""
+
+#, c-format
+msgid "failed to fetch objects from bundle URI '%s'"
+msgstr ""
+
+msgid "failed to fetch advertised bundles"
+msgstr ""
+
 msgid "remote transport reported error"
 msgstr ""
 
@@ -4262,13 +4343,16 @@
 msgstr "параметр --command должен быть первым"
 
 msgid ""
-"git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
 msgstr ""
 
 msgid ""
-"git commit-graph write [--object-dir <objdir>] [--append] [--"
-"split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] [--changed-"
-"paths] [--[no-]max-new-filters <n>] [--[no-]progress] <split options>"
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split options>"
 msgstr ""
 
 msgid "dir"
@@ -4337,13 +4421,12 @@
 msgid "Collecting commits from input"
 msgstr ""
 
-#, c-format
-msgid "unrecognized subcommand: %s"
+msgid "git commit-tree <tree> [(-p <parent>)...]"
 msgstr ""
 
 msgid ""
-"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] [(-F "
-"<file>)...] <tree>"
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
 msgstr ""
 
 #, c-format
@@ -4386,11 +4469,20 @@
 msgid "git commit-tree: failed to read"
 msgstr "git commit-tree: сбой при чтении"
 
-msgid "git commit [<options>] [--] <pathspec>..."
-msgstr "git commit [<опции>] [--] <спецификатор-пути>..."
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
 
-msgid "git status [<options>] [--] <pathspec>..."
-msgstr "git status [<опции>] [--] <спецификатор-пути>..."
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr ""
 
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
@@ -4672,9 +4764,6 @@
 msgid "terminate entries with NUL"
 msgstr "завершать записи НУЛЕВЫМ байтом"
 
-msgid "mode"
-msgstr "режим"
-
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr ""
 "показать неотслеживаемые файлы, опциональные режимы: all (все), normal (как "
@@ -4747,7 +4836,6 @@
 
 #. TRANSLATORS: Leave "[(amend|reword):]" as-is,
 #. and only translate <commit>.
-#.
 msgid "[(amend|reword):]commit"
 msgstr ""
 
@@ -5136,11 +5224,16 @@
 msgid "unable to get credential storage lock in %d ms"
 msgstr ""
 
-msgid "git describe [<options>] [<commit-ish>...]"
-msgstr "git describe [<опции>] [<указатель-коммита>...]"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
 
-msgid "git describe [<options>] --dirty"
-msgstr "git describe [<опции>] --dirty"
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+
+msgid "git describe <blob>"
+msgstr ""
 
 msgid "head"
 msgstr "указатель на ветку"
@@ -5262,6 +5355,20 @@
 msgid "option '%s' and commit-ishes cannot be used together"
 msgstr ""
 
+msgid ""
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
+msgstr ""
+
+msgid "specify a destination for the diagnostics archive"
+msgstr ""
+
+msgid "specify a strftime format suffix for the filename"
+msgstr ""
+
+msgid "specify the content of the diagnostic archive"
+msgstr ""
+
 msgid "--merge-base only works with two commits"
 msgstr ""
 
@@ -5269,6 +5376,9 @@
 msgid "'%s': not a regular file or symlink"
 msgstr "«%s»: не является обычным файлом или символьной ссылкой"
 
+msgid "no merge given, only parents."
+msgstr ""
+
 #, c-format
 msgid "invalid option: %s"
 msgstr "неправильный параметр: %s"
@@ -5380,25 +5490,6 @@
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "не передана <команда> для --extcmd=<команда>"
 
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr ""
-
-msgid "default for git_env_*(...) to fall back on"
-msgstr ""
-
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr ""
-
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr ""
-
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not `"
-"%s`"
-msgstr ""
-
 msgid "git fast-export [<rev-list-opts>]"
 msgstr ""
 
@@ -5507,6 +5598,161 @@
 msgid "fetch.parallel cannot be negative"
 msgstr ""
 
+msgid "couldn't find remote ref HEAD"
+msgstr ""
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Из %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "объект %s не найден"
+
+msgid "[up to date]"
+msgstr "[актуально]"
+
+msgid "[rejected]"
+msgstr "[отклонено]"
+
+msgid "can't fetch into checked-out branch"
+msgstr ""
+
+msgid "[tag update]"
+msgstr "[обновление метки]"
+
+msgid "unable to update local ref"
+msgstr "не удалось обновить локальную ссылку"
+
+msgid "would clobber existing tag"
+msgstr ""
+
+msgid "[new tag]"
+msgstr "[новая метка]"
+
+msgid "[new branch]"
+msgstr "[новая ветка]"
+
+msgid "[new ref]"
+msgstr "[новая ссылка]"
+
+msgid "forced update"
+msgstr "принудительное обновление"
+
+msgid "non-fast-forward"
+msgstr "без быстрой перемотки"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "не удалось открыть «%s»"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s не отправил все необходимые объекты\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr ""
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"не удалось обновить некоторые локальные ссылки; попробуйте запустить «git "
+"remote prune %s», чтобы почистить старые, конфликтующие ветки"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s будет висящей веткой)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s стала висящей веткой)"
+
+msgid "[deleted]"
+msgstr "[удалено]"
+
+msgid "(none)"
+msgstr "(нет)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr ""
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr ""
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr ""
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr ""
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr ""
+
+msgid "not setting upstream for a remote tag"
+msgstr ""
+
+msgid "unknown branch type"
+msgstr ""
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Извлечение из %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr ""
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr ""
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+
+msgid "you need to specify a tag name"
+msgstr ""
+
 msgid "fetch from all remotes"
 msgstr "извлечь со всех внешних репозиториев"
 
@@ -5611,164 +5857,6 @@
 msgid "accept refspecs from stdin"
 msgstr ""
 
-msgid "couldn't find remote ref HEAD"
-msgstr ""
-
-#, c-format
-msgid "object %s not found"
-msgstr "объект %s не найден"
-
-msgid "[up to date]"
-msgstr "[актуально]"
-
-msgid "[rejected]"
-msgstr "[отклонено]"
-
-msgid "can't fetch in current branch"
-msgstr "нельзя извлечь текущую ветку"
-
-msgid "checked out in another worktree"
-msgstr ""
-
-msgid "[tag update]"
-msgstr "[обновление метки]"
-
-msgid "unable to update local ref"
-msgstr "не удалось обновить локальную ссылку"
-
-msgid "would clobber existing tag"
-msgstr ""
-
-msgid "[new tag]"
-msgstr "[новая метка]"
-
-msgid "[new branch]"
-msgstr "[новая ветка]"
-
-msgid "[new ref]"
-msgstr "[новая ссылка]"
-
-msgid "forced update"
-msgstr "принудительное обновление"
-
-msgid "non-fast-forward"
-msgstr "без быстрой перемотки"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "не удалось открыть «%s»"
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s не отправил все необходимые объекты\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr ""
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "Из %.*s\n"
-
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"не удалось обновить некоторые локальные ссылки; попробуйте запустить «git "
-"remote prune %s», чтобы почистить старые, конфликтующие ветки"
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s будет висящей веткой)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s стала висящей веткой)"
-
-msgid "[deleted]"
-msgstr "[удалено]"
-
-msgid "(none)"
-msgstr "(нет)"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr ""
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr ""
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr ""
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr ""
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr ""
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr ""
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr ""
-
-msgid "not setting upstream for a remote tag"
-msgstr ""
-
-msgid "unknown branch type"
-msgstr ""
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "Извлечение из %s\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr ""
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr ""
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-
-msgid "you need to specify a tag name"
-msgstr ""
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr ""
 
@@ -5778,6 +5866,10 @@
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow не имеет смысла на полном репозитории"
 
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr ""
+
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all не принимает имя репозитория как аргумент"
 
@@ -5878,7 +5970,13 @@
 msgid "print only refs which don't contain the commit"
 msgstr "вывод только ссылок, которые не содержат коммит"
 
-msgid "git for-each-repo --config=<config> <command-args>"
+msgid "read reference patterns from stdin"
+msgstr ""
+
+msgid "unknown arguments supplied with --stdin"
+msgstr ""
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr ""
 
 msgid "config"
@@ -5890,6 +5988,10 @@
 msgid "missing --config=<config>"
 msgstr ""
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr ""
+
 msgid "unknown"
 msgstr ""
 
@@ -6034,18 +6136,35 @@
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr ""
 
-msgid "Checking cache tree"
+#, c-format
+msgid "Checking cache tree of %s"
 msgstr ""
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
 msgstr ""
 
 msgid "non-tree in cache-tree"
 msgstr ""
 
-msgid "git fsck [<options>] [<object>...]"
-msgstr "git fsck [<опции>] [<объект>...]"
+#, c-format
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr ""
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr ""
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr ""
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
 
 msgid "show unreachable objects"
 msgstr "показать недоступные объекты"
@@ -6100,12 +6219,6 @@
 msgid "git fsmonitor--daemon run [<options>]"
 msgstr ""
 
-msgid "git fsmonitor--daemon stop"
-msgstr ""
-
-msgid "git fsmonitor--daemon status"
-msgstr ""
-
 #, c-format
 msgid "value of '%s' out of range: %d"
 msgstr ""
@@ -6342,7 +6455,19 @@
 msgid "use at most one of --auto and --schedule=<frequency>"
 msgstr ""
 
-msgid "failed to run 'git config'"
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr ""
+
+msgid "return success even if repository was not registered"
+msgstr ""
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr ""
+
+#, c-format
+msgid "repository '%s' is not registered"
 msgstr ""
 
 #, c-format
@@ -6369,10 +6494,13 @@
 msgid "failed to run 'crontab -l'; your system might not support 'cron'"
 msgstr ""
 
-msgid "failed to run 'crontab'; your system might not support 'cron'"
+msgid "failed to create crontab temporary file"
 msgstr ""
 
-msgid "failed to open stdin of 'crontab'"
+msgid "failed to open temporary file"
+msgstr ""
+
+msgid "failed to run 'crontab'; your system might not support 'cron'"
 msgstr ""
 
 msgid "'crontab' died"
@@ -6421,10 +6549,6 @@
 msgid "git maintenance <subcommand> [<options>]"
 msgstr ""
 
-#, c-format
-msgid "invalid subcommand: %s"
-msgstr ""
-
 msgid "git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"
 msgstr "git grep [<опции>] [-e] <шаблон> [<редакция>...] [[--] <путь>...]"
 
@@ -6440,7 +6564,6 @@
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
-#.
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "нет поддержки потоков, игнорирование %s"
@@ -6595,6 +6718,9 @@
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr "разрешить вызов grep(1) (игнорируется в этой сборке)"
 
+msgid "maximum number of results per file"
+msgstr ""
+
 msgid "no pattern given"
 msgstr ""
 
@@ -6630,11 +6756,12 @@
 msgstr ""
 
 msgid ""
-"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] "
-"[--] <file>..."
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
 msgstr ""
-"git hash-object [-t <тип>] [-w] [--path=<файл> | --no-filters] [--stdin] "
-"[--] <файл>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr ""
 
 msgid "object type"
 msgstr "тип объекта"
@@ -6683,10 +6810,16 @@
 msgid "print list of useful guides"
 msgstr "вывести список полезных руководств"
 
+msgid "print list of user-facing repository, command and file interfaces"
+msgstr ""
+
+msgid "print list of file formats, protocols and other developer interfaces"
+msgstr ""
+
 msgid "print all configuration variable names"
 msgstr ""
 
-msgid "git help [[-i|--info] [-m|--man] [-w|--web]] [<command>]"
+msgid "git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"
 msgstr ""
 
 #, c-format
@@ -6756,12 +6889,17 @@
 msgid "'git help config' for more information"
 msgstr ""
 
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
 msgstr ""
 
 msgid "silently ignore missing requested <hook-name>"
 msgstr ""
 
+msgid "file to read into hooks' stdin"
+msgstr ""
+
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "несоответствие типа объекта на %s"
@@ -7065,11 +7203,11 @@
 msgstr "Инициализирован пустой репозиторий Git в %s%s\n"
 
 msgid ""
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
-"shared[=<permissions>]] [<directory>]"
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<каталог-шаблонов>] [--"
-"shared[=<права-доступа>]] [<каталог>]"
 
 msgid "permissions"
 msgstr "права-доступа"
@@ -7111,11 +7249,10 @@
 msgstr ""
 
 msgid ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<token>[(=|:)<value>])...] [<file>...]"
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
 msgstr ""
-"git interpret-trailers [--in-place] [--trim-empty] [(--trailer "
-"<ключ>[(=|:)<значение>])...] [<файл>...]"
 
 msgid "edit files in place"
 msgstr "редактировать файлы на месте"
@@ -7172,7 +7309,7 @@
 msgid "show source"
 msgstr "показать источник"
 
-msgid "use mail map file"
+msgid "clear all previously-defined decoration filters"
 msgstr ""
 
 msgid "only decorate refs that match <pattern>"
@@ -7422,6 +7559,9 @@
 msgid "percentage by which creation is weighted"
 msgstr ""
 
+msgid "show in-body From: even if identical to the e-mail header"
+msgstr ""
+
 #, c-format
 msgid "invalid ident line: %s"
 msgstr "неправильная строка идентификации: %s"
@@ -7482,6 +7622,18 @@
 "Не удалось найти отслеживаемую внешнюю ветку, укажите <вышестоящую-ветку> "
 "вручную.\n"
 
+#, c-format
+msgid "bad ls-files format: element '%s' does not start with '('"
+msgstr ""
+
+#, c-format
+msgid "bad ls-files format: element '%s' does not end in ')'"
+msgstr ""
+
+#, c-format
+msgid "bad ls-files format: %%%.*s"
+msgstr ""
+
 msgid "git ls-files [<options>] [<file>...]"
 msgstr "git ls-files [<опции>] [<файл>...]"
 
@@ -7569,9 +7721,14 @@
 msgstr ""
 
 msgid ""
+"--format cannot be used with -s, -o, -k, -t, --resolve-undo, --deduplicate, "
+"--eol"
+msgstr ""
+
+msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
-"              [-q | --quiet] [--exit-code] [--get-url]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 
 msgid "do not print remote URL"
@@ -7704,12 +7861,12 @@
 msgid "git merge-base [-a | --all] --octopus <commit>..."
 msgstr "git merge-base [-a | --all] --octopus <коммит>..."
 
-msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <коммит>..."
-
 msgid "git merge-base --is-ancestor <commit> <commit>"
 msgstr "git merge-base --is-ancestor <коммит> <коммит>"
 
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <коммит>..."
+
 msgid "git merge-base --fork-point <ref> [<commit>]"
 msgstr "git merge-base --fork-point <ссылка> [<коммит>]"
 
@@ -7789,6 +7946,56 @@
 msgid "Merging %s with %s\n"
 msgstr "Слияние %s и %s\n"
 
+msgid "not something we can merge"
+msgstr "не является тем, что можно слить"
+
+msgid "refusing to merge unrelated histories"
+msgstr "отказ слияния несвязанных историй изменений"
+
+msgid "failure to merge"
+msgstr ""
+
+msgid "git merge-tree [--write-tree] [<options>] <branch1> <branch2>"
+msgstr ""
+
+msgid "git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"
+msgstr ""
+
+msgid "do a real merge instead of a trivial merge"
+msgstr ""
+
+msgid "do a trivial merge only"
+msgstr ""
+
+msgid "also show informational/conflict messages"
+msgstr ""
+
+msgid "list filenames without modes/oids/stages"
+msgstr ""
+
+msgid "allow merging unrelated histories"
+msgstr "разрешить слияние несвязанных историй изменений"
+
+msgid "perform multiple merges, one per line of input"
+msgstr ""
+
+msgid "specify a merge-base for the merge"
+msgstr ""
+
+msgid "--trivial-merge is incompatible with all other options"
+msgstr ""
+
+msgid "--merge-base is incompatible with --stdin"
+msgstr ""
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "неправильная введенная строка: «%s»."
+
+#, c-format
+msgid "merging cannot continue; got unclean result of %d"
+msgstr ""
+
 msgid "git merge [<options>] [<commit>...]"
 msgstr "git merge [<опции>] [<коммит>...]"
 
@@ -7870,9 +8077,6 @@
 msgid "continue the current in-progress merge"
 msgstr "продолжить выполнение происходящего слияния"
 
-msgid "allow merging unrelated histories"
-msgstr "разрешить слияние несвязанных историй изменений"
-
 msgid "bypass pre-merge-commit and commit-msg hooks"
 msgstr ""
 
@@ -7994,9 +8198,6 @@
 msgid "not something we can merge in %s: %s"
 msgstr "не является тем, что можно слить в %s: %s"
 
-msgid "not something we can merge"
-msgstr "не является тем, что можно слить"
-
 msgid "--abort expects no arguments"
 msgstr "опция --abort не принимает аргументы"
 
@@ -8046,14 +8247,17 @@
 msgid "Can merge only exactly one commit into empty head"
 msgstr "Можно только один коммит в пустую ветку."
 
-msgid "refusing to merge unrelated histories"
-msgstr "отказ слияния несвязанных историй изменений"
-
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "Обновление %s..%s\n"
 
 #, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"  %s"
+msgstr ""
+
+#, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr "Попытка тривиального слияния в индексе...\n"
 
@@ -8088,6 +8292,10 @@
 "выполнением коммита\n"
 
 #, c-format
+msgid "When finished, apply stashed changes with `git stash pop`\n"
+msgstr ""
+
+#, c-format
 msgid "warning: tag input does not pass fsck: %s"
 msgstr ""
 
@@ -8201,6 +8409,9 @@
 msgid "bad source"
 msgstr "плохой источник"
 
+msgid "destination exists"
+msgstr "целевой путь уже существует"
+
 msgid "can not move directory into itself"
 msgstr "нельзя переместить каталог в самого себя"
 
@@ -8216,9 +8427,6 @@
 msgid "conflicted"
 msgstr ""
 
-msgid "destination exists"
-msgstr "целевой путь уже существует"
-
 #, c-format
 msgid "overwriting '%s'"
 msgstr "перезапись «%s»"
@@ -8232,6 +8440,9 @@
 msgid "destination directory does not exist"
 msgstr "целевой каталог не существует"
 
+msgid "destination exists in the index"
+msgstr ""
+
 #, c-format
 msgid "%s, source=%s, destination=%s"
 msgstr "%s, откуда=%s, куда=%s"
@@ -8399,16 +8610,11 @@
 msgstr "не удалось прочитать данные заметки из недвоичного объекта «%s»."
 
 #, c-format
-msgid "malformed input line: '%s'."
-msgstr "неправильная введенная строка: «%s»."
-
-#, c-format
 msgid "failed to copy notes from '%s' to '%s'"
 msgstr "не удалось скопировать заметку из «%s» в «%s»"
 
 #. TRANSLATORS: the first %s will be replaced by a git
 #. notes command: 'add', 'merge', 'remove', etc.
-#.
 #, c-format
 msgid "refusing to %s notes in %s (outside of refs/notes/)"
 msgstr "отказ в перезаписи %s заметок в %s (за пределами refs/notes/)"
@@ -8590,20 +8796,15 @@
 msgstr "использовать заметку из <ссылка-на-заметку>"
 
 #, c-format
-msgid "unknown subcommand: %s"
-msgstr "неизвестная подкоманда: %s"
+msgid "unknown subcommand: `%s'"
+msgstr ""
+
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr ""
 
 msgid ""
-"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
 msgstr ""
-"git pack-objects --stdout [<опции>...] [< <список-ссылок> | < <список-"
-"объектов>]"
-
-msgid ""
-"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
-msgstr ""
-"git pack-objects [<опции>...] <имя-базы> [< <список-ссылок> | < <список-"
-"объектов>]"
 
 #, c-format
 msgid ""
@@ -8782,6 +8983,12 @@
 msgid "bad index version '%s'"
 msgstr "плохая версия индекса «%s»"
 
+msgid "show progress meter during object writing phase"
+msgstr "показать прогресс выполнения во время записи объектов"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "похоже на --all-progress при включенном прогрессе выполнения"
+
 msgid "<version>[,<offset>]"
 msgstr ""
 
@@ -8967,8 +9174,11 @@
 "to <git@vger.kernel.org>.  Thanks.\n"
 msgstr ""
 
-msgid "git pack-refs [<options>]"
-msgstr "git pack-refs [<опции>]"
+msgid "refusing to run without --i-still-use-this"
+msgstr ""
+
+msgid "git pack-refs [--all] [--no-prune]"
+msgstr ""
 
 msgid "pack everything"
 msgstr "паковать всё"
@@ -8976,6 +9186,18 @@
 msgid "prune loose refs (default)"
 msgstr "почистить слабые ссылки (по умолчанию)"
 
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr ""
+
+msgid "use the unstable patch-id algorithm"
+msgstr ""
+
+msgid "use the stable patch-id algorithm"
+msgstr ""
+
+msgid "don't strip whitespace from the patch"
+msgstr ""
+
 msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
 msgstr ""
 "git prune [-n] [-v] [--progress] [--expire <время>] [--] [<редакция>...]"
@@ -9114,8 +9336,8 @@
 msgid "pull with rebase"
 msgstr "получение с перемещением"
 
-msgid "please commit or stash them."
-msgstr "сделайте коммит или спрячьте их."
+msgid "Please commit or stash them."
+msgstr "Сделайте коммит или спрячьте их."
 
 #, c-format
 msgid ""
@@ -9175,9 +9397,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"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
 "in 'git help config'.\n"
 msgstr ""
 
@@ -9321,6 +9542,11 @@
 msgid "failed to push some refs to '%s'"
 msgstr "не удалось отправить некоторые ссылки в «%s»"
 
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+
 #, c-format
 msgid "invalid value for '%s'"
 msgstr ""
@@ -9328,8 +9554,8 @@
 msgid "repository"
 msgstr "репозиторий"
 
-msgid "push all refs"
-msgstr "отправить все ссылки"
+msgid "push all branches"
+msgstr ""
 
 msgid "mirror all refs"
 msgstr "сделать зеркало всех ссылок"
@@ -9337,8 +9563,8 @@
 msgid "delete refs"
 msgstr "удалить ссылки"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "отправить метки (нельзя использовать вместе с --all или --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
 
 msgid "force updates"
 msgstr "принудительное обновление"
@@ -9441,19 +9667,25 @@
 msgstr ""
 
 #, c-format
+msgid "not a revision: '%s'"
+msgstr ""
+
+#, c-format
 msgid "not a commit range: '%s'"
 msgstr ""
 
-msgid "single arg format must be symmetric range"
+#, c-format
+msgid "not a symmetric range: '%s'"
 msgstr ""
 
 msgid "need two commit ranges"
 msgstr ""
 
 msgid ""
-"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) "
-"[-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-"
-"ish1> [<tree-ish2> [<tree-ish3>]])"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
 
 msgid "write resulting index to <file>"
@@ -9542,7 +9774,7 @@
 msgstr ""
 
 #, c-format
-msgid "could not get 'onto': '%s'"
+msgid "invalid onto: '%s'"
 msgstr ""
 
 #, c-format
@@ -9582,13 +9814,26 @@
 msgstr ""
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr ""
+
+#, c-format
 msgid "could not switch to %s"
 msgstr ""
 
+msgid "apply options and merge options cannot be used together"
+msgstr ""
+
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
+msgstr ""
+
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
 msgstr ""
 
 #, c-format
@@ -9693,6 +9938,9 @@
 msgid "move commits that begin with squash!/fixup! under -i"
 msgstr ""
 
+msgid "update branches that point to commits that are being rebased"
+msgstr ""
+
 msgid "add exec lines after each commit of the editable list"
 msgstr ""
 
@@ -9779,14 +10027,22 @@
 msgid "switch `C' expects a numerical value"
 msgstr ""
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr ""
-
 msgid "--strategy requires --merge or --interactive"
 msgstr ""
 
-msgid "apply options and merge options cannot be used together"
+msgid ""
+"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
+"autosquash"
+msgstr ""
+
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
 msgstr ""
 
 #, c-format
@@ -9811,7 +10067,7 @@
 msgid "No such ref: %s"
 msgstr "Нет такой ссылки: %s"
 
-msgid "Could not resolve HEAD to a revision"
+msgid "Could not resolve HEAD to a commit"
 msgstr ""
 
 #, c-format
@@ -9826,9 +10082,6 @@
 msgid "Does not point to a valid commit '%s'"
 msgstr ""
 
-msgid "Please commit or stash them."
-msgstr "Сделайте коммит или спрячьте их."
-
 msgid "HEAD is up to date."
 msgstr "HEAD уже в актуальном состоянии."
 
@@ -10203,6 +10456,9 @@
 msgid " tracked"
 msgstr " отслеживается"
 
+msgid " skipped"
+msgstr ""
+
 msgid " stale (use 'git remote prune' to remove)"
 msgstr " недействительна (используйте «git remote prune», чтобы удалить)"
 
@@ -10287,7 +10543,6 @@
 #. TRANSLATORS: the colon ':' should align
 #. with the one in " Fetch URL: %s"
 #. translation.
-#.
 #, c-format
 msgid "  Push  URL: %s"
 msgstr "    URL для отправки: %s"
@@ -10472,6 +10727,10 @@
 msgid "could not close refs snapshot tempfile"
 msgstr ""
 
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr ""
+
 msgid "pack everything in a single pack"
 msgstr "упаковать всё в один пакет"
 
@@ -10484,7 +10743,7 @@
 msgid "approxidate"
 msgstr "примерная-дата"
 
-msgid "with -C, expire objects older than this"
+msgid "with --cruft, expire objects older than this"
 msgstr ""
 
 msgid "remove redundant packs, and run git-prune-packed"
@@ -10544,6 +10803,9 @@
 msgid "write a multi-pack index of the resulting packs"
 msgstr ""
 
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr ""
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "нельзя удалять пакеты в precious-objects репозитории"
 
@@ -10555,7 +10817,11 @@
 msgstr ""
 
 #, c-format
-msgid "missing required file: %s"
+msgid "renaming pack to '%s' failed"
+msgstr ""
+
+#, c-format
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
 msgstr ""
 
 #, c-format
@@ -10743,8 +11009,9 @@
 msgid "only one pattern can be given with -l"
 msgstr ""
 
-msgid "git rerere [clear | forget <path>... | status | remaining | diff | gc]"
-msgstr "git rerere [clear | forget <путь>... | status | remaining | diff | gc]"
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
 
 msgid "register clean resolutions in index"
 msgstr "записать чистые разрешения конфликтов в индекс"
@@ -10866,6 +11133,10 @@
 msgid "unable to get disk usage of %s"
 msgstr ""
 
+#, c-format
+msgid "invalid value for '%s': '%s', the only allowed format is '%s'"
+msgstr ""
+
 msgid "rev-list does not support display of notes"
 msgstr "rev-list не поддерживает отображение заметок"
 
@@ -10891,6 +11162,9 @@
 msgid "no usage string given before the `--' separator"
 msgstr ""
 
+msgid "missing opt-spec before option flags"
+msgstr ""
+
 msgid "Needed a single revision"
 msgstr ""
 
@@ -10938,6 +11212,15 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr ""
 
+msgid "--exclude-hidden cannot be used together with --branches"
+msgstr ""
+
+msgid "--exclude-hidden cannot be used together with --tags"
+msgstr ""
+
+msgid "--exclude-hidden cannot be used together with --remotes"
+msgstr ""
+
 msgid "this operation must be run in a work tree"
 msgstr ""
 
@@ -10945,17 +11228,21 @@
 msgid "unknown mode for --show-object-format: %s"
 msgstr ""
 
-msgid "git revert [<options>] <commit-ish>..."
-msgstr "git revert [<опции>] <указатель-коммита>..."
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
 
-msgid "git revert <subcommand>"
-msgstr "git revert <подкоманда>"
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr ""
 
-msgid "git cherry-pick [<options>] <commit-ish>..."
-msgstr "git cherry-pick [<опции>] <указатель-коммита>..."
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
 
-msgid "git cherry-pick <subcommand>"
-msgstr "git cherry-pick <подкоманда>"
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr ""
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
@@ -11017,8 +11304,11 @@
 msgid "cherry-pick failed"
 msgstr "сбой при копировании коммита"
 
-msgid "git rm [<options>] [--] <file>..."
-msgstr "git rm [<опции>] [--] <файл>..."
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
 
 msgid ""
 "the following file has staged content different from both the\n"
@@ -11101,12 +11391,16 @@
 "git send-pack [--mirror] [--dry-run] [--force]\n"
 "              [--receive-pack=<git-receive-pack>]\n"
 "              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
 "              [<host>:]<directory> (--all | <ref>...)"
 msgstr ""
 
 msgid "remote name"
 msgstr "имя внешнего репозитория"
 
+msgid "push all refs"
+msgstr "отправить все ссылки"
+
 msgid "use stateless RPC protocol"
 msgstr "протокол без сохранения состояния для RPC"
 
@@ -11125,7 +11419,8 @@
 msgid "using multiple --group options with stdin is not supported"
 msgstr ""
 
-msgid "using --group=trailer with stdin is not supported"
+#, c-format
+msgid "using %s with stdin is not supported"
 msgstr ""
 
 #, c-format
@@ -11163,7 +11458,8 @@
 "git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
 "                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
 "                [--more=<n> | --list | --independent | --merge-base]\n"
-"                [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
 msgstr ""
 
 msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
@@ -11270,11 +11566,10 @@
 msgstr ""
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"
+"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --"
-"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<шаблон>...]"
 
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<шаблон>]"
@@ -11305,7 +11600,9 @@
 msgstr ""
 "вывести ссылки со стандартного ввода, которых нет в локальном репозитории"
 
-msgid "git sparse-checkout (init|list|set|add|reapply|disable) <options>"
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
 
 msgid "this worktree is not sparse"
@@ -11413,57 +11710,59 @@
 msgid "error while refreshing working directory"
 msgstr ""
 
-msgid "git stash list [<options>]"
-msgstr "git stash list [<опции>]"
-
-msgid "git stash show [<options>] [<stash>]"
-msgstr "git stash show [<опциии>] [<спрятанные-изменения>]"
-
-msgid "git stash drop [-q|--quiet] [<stash>]"
-msgstr "git stash drop [-q|--quiet] [<спрятанные-изменения>]"
-
-msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
 msgstr ""
-"git stash ( pop | apply ) [--index] [-q|--quiet] [<спрятанные-изменения>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr ""
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr ""
+
+msgid "git stash list [<log-options>]"
+msgstr ""
+
+msgid ""
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
+msgstr ""
+
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr ""
+
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr ""
+
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr ""
 
 msgid "git stash branch <branchname> [<stash>]"
 msgstr "git stash branch <имя-ветки> [<спрятанные-изменения>]"
 
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr ""
+
 msgid ""
-"git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\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>...]]"
 msgstr ""
 
 msgid ""
-"git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--"
-"quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [<message>]"
+"git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<message>]"
 msgstr ""
 
-msgid "git stash pop [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash pop [--index] [-q|--quiet] [<спрятанные-изменения>]"
-
-msgid "git stash apply [--index] [-q|--quiet] [<stash>]"
-msgstr "git stash apply [--index] [-q|--quiet] [<спрятанные-изменения>]"
-
-msgid "git stash store [-m|--message <message>] [-q|--quiet] <commit>"
-msgstr "git stash store [-m|--message <сообщение>] [-q|--quiet] <коммит>"
-
-msgid ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-"          [--] [<pathspec>...]]"
-msgstr ""
-"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"          [-u|--include-untracked] [-a|--all] [-m|--message <сообщение>]\n"
-"          [--] [<спецификатор-пути>...]]"
-
-msgid ""
-"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-"               [-u|--include-untracked] [-a|--all] [<message>]"
+msgid "git stash create [<message>]"
 msgstr ""
 
 #, c-format
@@ -11637,17 +11936,15 @@
 msgstr "Ожидалось полное имя ссылки, а получено %s"
 
 #, c-format
+msgid "could not get a repository handle for submodule '%s'"
+msgstr ""
+
+#, c-format
 msgid ""
 "could not look up configuration '%s'. Assuming this repository is its own "
 "authoritative upstream."
 msgstr ""
 
-msgid "alternative anchor for relative paths"
-msgstr "альтернативный символ для относительных путей"
-
-msgid "git submodule--helper list [--prefix=<path>] [<path>...]"
-msgstr "git submodule--helper list [--prefix=<путь>] [<путь>...]"
-
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
 msgstr "URL для подмодуля по пути «%s» не найден в .gitmodules"
@@ -11675,7 +11972,7 @@
 msgid "recurse into nested submodules"
 msgstr "проходить вглубь вложенных подмодулей"
 
-msgid "git submodule--helper foreach [--quiet] [--recursive] [--] <command>"
+msgid "git submodule foreach [--quiet] [--recursive] [--] <command>"
 msgstr ""
 
 #, c-format
@@ -11697,7 +11994,7 @@
 msgid "suppress output for initializing a submodule"
 msgstr ""
 
-msgid "git submodule--helper init [<options>] [<path>]"
+msgid "git submodule init [<options>] [<path>]"
 msgstr ""
 
 #, c-format
@@ -11723,9 +12020,6 @@
 msgid "git submodule status [--quiet] [--cached] [--recursive] [<path>...]"
 msgstr "git submodule status [--quiet] [--cached] [--recursive] [<путь>...]"
 
-msgid "git submodule--helper name <path>"
-msgstr "git submodule--helper name <путь>"
-
 #, c-format
 msgid "* %s %s(blob)->%s(submodule)"
 msgstr ""
@@ -11758,7 +12052,7 @@
 msgid "limit the summary size"
 msgstr ""
 
-msgid "git submodule--helper summary [<options>] [<commit>] [--] [<path>]"
+msgid "git submodule summary [<options>] [<commit>] [--] [<path>]"
 msgstr ""
 
 msgid "could not fetch a revision for HEAD"
@@ -11773,18 +12067,14 @@
 msgstr ""
 
 #, c-format
-msgid "failed to get the default remote for submodule '%s'"
-msgstr ""
-
-#, c-format
 msgid "failed to update remote for submodule '%s'"
 msgstr ""
 
 msgid "suppress output of synchronizing submodule url"
 msgstr ""
 
-msgid "git submodule--helper sync [--quiet] [--recursive] [<path>]"
-msgstr "git submodule--helper sync [--quiet] [--recursive] [<путь>]"
+msgid "git submodule sync [--quiet] [--recursive] [<path>]"
+msgstr ""
 
 #, c-format
 msgid ""
@@ -11838,6 +12128,10 @@
 msgstr ""
 
 #, c-format
+msgid "could not get a repository handle for gitdir '%s'"
+msgstr ""
+
+#, c-format
 msgid "submodule '%s' cannot add alternate: %s"
 msgstr "подмодулю «%s» не удалось добавить альтернативу: %s"
 
@@ -11866,6 +12160,9 @@
 msgid "could not get submodule directory for '%s'"
 msgstr "не удалось получить каталог для подмодуля «%s»"
 
+msgid "alternative anchor for relative paths"
+msgstr "альтернативный символ для относительных путей"
+
 msgid "where the new submodule will be cloned to"
 msgstr "куда должен быть склонирован новый подмодуль"
 
@@ -11891,10 +12188,6 @@
 msgstr ""
 
 #, c-format
-msgid "Invalid update mode '%s' for submodule path '%s'"
-msgstr ""
-
-#, c-format
 msgid "Invalid update mode '%s' configured for submodule path '%s'"
 msgstr ""
 
@@ -11964,6 +12257,10 @@
 msgstr ""
 
 #, c-format
+msgid "could not initialize submodule at path '%s'"
+msgstr ""
+
+#, c-format
 msgid ""
 "Submodule (%s) branch configured to inherit branch from superproject, but "
 "the superproject is not on any branch"
@@ -11972,10 +12269,6 @@
 "проекта, но он не находится ни на одной ветке"
 
 #, c-format
-msgid "could not get a repository handle for submodule '%s'"
-msgstr ""
-
-#, c-format
 msgid "Unable to find current revision in submodule path '%s'"
 msgstr ""
 
@@ -12006,14 +12299,14 @@
 msgid "don't fetch new objects from the remote site"
 msgstr ""
 
-msgid "path into the working tree"
-msgstr "путь в рабочем каталоге"
+msgid "use the 'checkout' update strategy (default)"
+msgstr ""
 
-msgid "path into the working tree, across nested submodule boundaries"
-msgstr "путь в рабочем каталоге, в пределах границ подмодуля"
+msgid "use the 'merge' update strategy"
+msgstr ""
 
-msgid "rebase, merge, checkout or none"
-msgstr "rebase, merge, checkout или none"
+msgid "use the 'rebase' update strategy"
+msgstr ""
 
 msgid "create a shallow clone truncated to the specified number of revisions"
 msgstr ""
@@ -12029,6 +12322,9 @@
 msgid "don't print cloning progress"
 msgstr "вы выводить прогресс клонирования"
 
+msgid "disallow cloning into non-empty directory, implies --init"
+msgstr ""
+
 msgid ""
 "git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
 "[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
@@ -12036,34 +12332,13 @@
 "[--] [<path>...]"
 msgstr ""
 
-msgid "bad value for update parameter"
-msgstr "плохое значение для параметра update"
-
-msgid "recurse into submodules"
-msgstr "рекурсивно по подмодулям"
-
-msgid "git submodule--helper absorb-git-dirs [<options>] [<path>...]"
-msgstr "git submodule--helper absorb-git-dirs [<опции>] [<путь>...]"
-
-msgid "check if it is safe to write to the .gitmodules file"
-msgstr ""
-
-msgid "unset the config in the .gitmodules file"
-msgstr ""
-
-msgid "git submodule--helper config <name> [<value>]"
-msgstr "git submodule--helper config <имя> [<значение>]"
-
-msgid "git submodule--helper config --unset <name>"
-msgstr "git submodule--helper config --unset <имя>"
-
-msgid "please make sure that the .gitmodules file is in the working tree"
+msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr ""
 
 msgid "suppress output for setting url of a submodule"
 msgstr ""
 
-msgid "git submodule--helper set-url [--quiet] <path> <newurl>"
+msgid "git submodule set-url [--quiet] <path> <newurl>"
 msgstr ""
 
 msgid "set the default tracking branch to master"
@@ -12072,11 +12347,10 @@
 msgid "set the default tracking branch"
 msgstr ""
 
-msgid "git submodule--helper set-branch [-q|--quiet] (-d|--default) <path>"
+msgid "git submodule set-branch [-q|--quiet] (-d|--default) <path>"
 msgstr ""
 
-msgid ""
-"git submodule--helper set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
+msgid "git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
 msgstr ""
 
 msgid "--branch or --default required"
@@ -12130,6 +12404,9 @@
 msgid "unable to checkout submodule '%s'"
 msgstr ""
 
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ""
+
 #, c-format
 msgid "Failed to add submodule '%s'"
 msgstr ""
@@ -12164,7 +12441,7 @@
 "path"
 msgstr ""
 
-msgid "git submodule--helper add [<options>] [--] <repository> [<path>]"
+msgid "git submodule add [<options>] [--] <repository> [<path>]"
 msgstr ""
 
 msgid "Relative path can only be used from the toplevel of the working tree"
@@ -12180,19 +12457,17 @@
 msgid "'%s' is not a valid submodule name"
 msgstr ""
 
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s не поддерживает параметр --super-prefix"
+msgid "git submodule--helper <command>"
+msgstr ""
 
-#, c-format
-msgid "'%s' is not a valid submodule--helper subcommand"
-msgstr "«%s» не является подкомандой submodule--helper"
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr ""
 
-msgid "git symbolic-ref [<options>] <name> [<ref>]"
-msgstr "git symbolic-ref [<опции>] <имя> [<ссылка>]"
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr ""
 
-msgid "git symbolic-ref -d [-q] <name>"
-msgstr "git symbolic-ref -d [-q] <имя>"
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr ""
 
 msgid "suppress error message for non-symbolic (detached) refs"
 msgstr ""
@@ -12204,6 +12479,9 @@
 msgid "shorten ref output"
 msgstr "укороченный вывод ссылок"
 
+msgid "recursively dereference (default)"
+msgstr ""
+
 msgid "reason"
 msgstr "причина"
 
@@ -12211,18 +12489,18 @@
 msgstr "причина обновления"
 
 msgid ""
-"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
-"        <tagname> [<head>]"
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
 msgstr ""
 
 msgid "git tag -d <tagname>..."
 msgstr "git tag -d <имя-метки>..."
 
 msgid ""
-"git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--"
-"points-at <object>]\n"
-"        [--format=<format>] [--merged <commit>] [--no-merged <commit>] "
-"[<pattern>...]"
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
 msgstr ""
 
 msgid "git tag -v [--format=<format>] <tagname>..."
@@ -12363,6 +12641,16 @@
 msgid "pack exceeds maximum allowed size"
 msgstr "размер пакета превышает максимальный допустимый"
 
+msgid "failed to write object in stream"
+msgstr ""
+
+#, c-format
+msgid "inflate returned (%d)"
+msgstr ""
+
+msgid "invalid blob object from stream"
+msgstr ""
+
 msgid "Unpacking objects"
 msgstr "Распаковка объектов"
 
@@ -12592,8 +12880,10 @@
 msgid "update the info files from scratch"
 msgstr "обновить информацию о серверах с нуля"
 
-msgid "git upload-pack [<options>] <dir>"
-msgstr "git upload-pack [<опции>] <каталог>"
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
 
 msgid "quit after a single request/response exchange"
 msgstr "выход после обмена одним запросом/ответом"
@@ -12607,8 +12897,8 @@
 msgid "interrupt transfer after <n> seconds of inactivity"
 msgstr "прервать передачу после <кол> секунд простоя"
 
-msgid "git verify-commit [-v | --verbose] <commit>..."
-msgstr "git verify-commit [-v | --verbose] <коммит>..."
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr ""
 
 msgid "print commit contents"
 msgstr "вывести содержимое коммита"
@@ -12616,8 +12906,8 @@
 msgid "print raw gpg status output"
 msgstr "выводить сырой вывод статуса от gpg"
 
-msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
-msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <пакет>..."
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
 
 msgid "verbose"
 msgstr "быть многословнее"
@@ -12625,35 +12915,37 @@
 msgid "show statistics only"
 msgstr "вывести только статистику"
 
-msgid "git verify-tag [-v | --verbose] [--format=<format>] <tag>..."
-msgstr "git verify-tag [-v | --verbose] [--format=<формат>] <метка>..."
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr ""
 
 msgid "print tag contents"
 msgstr "вывести содержимое метки"
 
-msgid "git worktree add [<options>] <path> [<commit-ish>]"
-msgstr "git worktree add [<опции>] <путь> [<указатель-коммита>]"
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [-b <new-branch>] <path> [<commit-ish>]"
+msgstr ""
 
-msgid "git worktree list [<options>]"
-msgstr "git worktree list [<опции>]"
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr ""
 
-msgid "git worktree lock [<options>] <path>"
-msgstr "git worktree lock [<опции>] <путь>"
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr ""
 
 msgid "git worktree move <worktree> <new-path>"
 msgstr "git worktree move <рабочий-каталог> <новый-путь>"
 
-msgid "git worktree prune [<options>]"
-msgstr "git worktree prune [<опции>]"
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr ""
 
-msgid "git worktree remove [<options>] <worktree>"
-msgstr "git worktree remove [<опции>] <рабочий-каталог>"
+msgid "git worktree remove [-f] <worktree>"
+msgstr ""
 
 msgid "git worktree repair [<path>...]"
 msgstr ""
 
-msgid "git worktree unlock <path>"
-msgstr "git worktree unlock <путь>"
+msgid "git worktree unlock <worktree>"
+msgstr ""
 
 #, c-format
 msgid "Removing %s/%s: %s"
@@ -12722,7 +13014,7 @@
 
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
-"переключиться на <ветка> даже если она уже активна в другом рабочесм каталоге"
+"переключиться на <ветка> даже если она уже активна в другом рабочем каталоге"
 
 msgid "create a new branch"
 msgstr "создать новую ветку"
@@ -12866,6 +13158,62 @@
 msgid "only useful for debugging"
 msgstr "используется только при отладке"
 
+msgid "core.fsyncMethod = batch is unsupported on this platform"
+msgstr ""
+
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr ""
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr ""
+
+msgid "failed to create temporary file"
+msgstr ""
+
+msgid "insufficient capabilities"
+msgstr ""
+
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr ""
+
+msgid "failed to store maximum creation token"
+msgstr ""
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr ""
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr ""
+
+#, c-format
+msgid "failed to download bundle from URI '%s'"
+msgstr ""
+
+#, c-format
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr ""
+
+#, c-format
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr ""
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr ""
+
+msgid "bundle-uri: got an empty line"
+msgstr ""
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr ""
+
+msgid "bundle-uri: line has empty key or value"
+msgstr ""
+
 #, c-format
 msgid "unrecognized bundle hash algorithm: %s"
 msgstr ""
@@ -12888,6 +13236,11 @@
 msgid "need a repository to verify a bundle"
 msgstr ""
 
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -13072,6 +13425,9 @@
 msgid "Give an object a human readable name based on an available ref"
 msgstr "Присвоение объекту удобочитаемое имя на основе доступной ссылки"
 
+msgid "Generate a zip archive of diagnostic information"
+msgstr ""
+
 msgid "Show changes between commits, commit and working tree, etc"
 msgstr "Вывод разницы между коммитами, коммитом и рабочим каталогом и т.д."
 
@@ -13205,8 +13561,8 @@
 msgstr ""
 "Стандартная программа-помощник для использования совместно с git-merge-index"
 
-msgid "Show three-way merge without touching index"
-msgstr "Вывод трёхходового слияние без затрагивания индекса"
+msgid "Perform merge without touching index or working tree"
+msgstr ""
 
 msgid "Run merge conflict resolution tools to resolve merge conflicts"
 msgstr "Запуск инструментов разрешения конфликтов слияния"
@@ -13405,6 +13761,9 @@
 msgid "Check the GPG signature of tags"
 msgstr "Проверка подписи GPG меток"
 
+msgid "Display version information about Git"
+msgstr ""
+
 msgid "Show logs with difference each commit introduces"
 msgstr "Вывод журнала с изменениями, которые вводил каждый из коммитов"
 
@@ -13438,6 +13797,24 @@
 msgid "Frequently asked questions about using Git"
 msgstr ""
 
+msgid "The bundle file format"
+msgstr ""
+
+msgid "Chunk-based file formats"
+msgstr ""
+
+msgid "Git commit-graph format"
+msgstr ""
+
+msgid "Git index format"
+msgstr ""
+
+msgid "Git pack format"
+msgstr ""
+
+msgid "Git cryptographic signature formats"
+msgstr ""
+
 msgid "A Git Glossary"
 msgstr "Глоссарий Git"
 
@@ -13459,6 +13836,21 @@
 msgid "Git namespaces"
 msgstr "Пространства имён Git"
 
+msgid "Protocol v0 and v1 capabilities"
+msgstr ""
+
+msgid "Things common to various protocols"
+msgstr ""
+
+msgid "Git HTTP-based protocols"
+msgstr ""
+
+msgid "How packs are transferred over-the-wire"
+msgstr ""
+
+msgid "Git Wire Protocol, Version 2"
+msgstr ""
+
 msgid "Helper programs to interact with remote repositories"
 msgstr ""
 
@@ -13483,6 +13875,9 @@
 msgid "An overview of recommended workflows with Git"
 msgstr "Обзор рекомендуемых последовательностей выполняемых действий с Git"
 
+msgid "A tool for managing large Git repositories"
+msgstr ""
+
 msgid "commit-graph file is too small"
 msgstr "файл commit-graph слишком маленький"
 
@@ -13724,6 +14119,14 @@
 msgstr ""
 
 #, c-format
+msgid "could not determine free disk size for '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not get info for '%s'"
+msgstr ""
+
+#, c-format
 msgid "[GLE %ld] health thread could not open '%ls'"
 msgstr ""
 
@@ -13747,6 +14150,10 @@
 msgid "health thread wait failed [GLE %ld]"
 msgstr ""
 
+#, c-format
+msgid "Invalid path: %s"
+msgstr ""
+
 msgid "Unable to create FSEventStream."
 msgstr ""
 
@@ -13778,6 +14185,30 @@
 msgstr ""
 
 #, c-format
+msgid "opendir('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr ""
+
+#, c-format
+msgid "[GLE %ld] unable to open for read '%ls'"
+msgstr ""
+
+#, c-format
+msgid "[GLE %ld] unable to get protocol information for '%ls'"
+msgstr ""
+
+#, c-format
 msgid "failed to copy SID (%ld)"
 msgstr ""
 
@@ -14047,7 +14478,7 @@
 msgid "bad zlib compression level %d"
 msgstr "неправильный уровень сжатия zlib %d"
 
-msgid "core.commentChar should only be one character"
+msgid "core.commentChar should only be one ASCII character"
 msgstr ""
 
 #, c-format
@@ -14159,6 +14590,10 @@
 msgstr ""
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+
+#, c-format
 msgid "missing value for '%s'"
 msgstr ""
 
@@ -14213,15 +14648,22 @@
 msgstr ""
 
 #, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr ""
+
+msgid "expected flush after bundle-uri listing"
+msgstr ""
+
+msgid "expected response end packet after ref listing"
+msgstr ""
+
+#, c-format
 msgid "invalid ls-refs response: %s"
 msgstr ""
 
 msgid "expected flush after ref listing"
 msgstr ""
 
-msgid "expected response end packet after ref listing"
-msgstr ""
-
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr ""
@@ -14306,175 +14748,6 @@
 msgstr "сбой закрытия стандартного ввода у rev-list"
 
 #, c-format
-msgid "'%s' does not exist"
-msgstr "«%s» не существует"
-
-msgid "need a working directory"
-msgstr ""
-
-msgid "could not find enlistment root"
-msgstr ""
-
-#, c-format
-msgid "could not switch to '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not configure %s=%s"
-msgstr ""
-
-msgid "could not configure log.excludeDecoration"
-msgstr ""
-
-msgid "Scalar enlistments require a worktree"
-msgstr ""
-
-#, c-format
-msgid "could not open directory '%s'"
-msgstr "не удалось открыть каталог «%s»"
-
-#, c-format
-msgid "skipping '%s', which is neither file nor directory"
-msgstr ""
-
-#, c-format
-msgid "could not determine free disk size for '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not get info for '%s'"
-msgstr ""
-
-#, c-format
-msgid "remote HEAD is not a branch: '%.*s'"
-msgstr ""
-
-msgid "failed to get default branch name from remote; using local default"
-msgstr ""
-
-msgid "failed to get default branch name"
-msgstr ""
-
-msgid "failed to unregister repository"
-msgstr ""
-
-msgid "failed to delete enlistment directory"
-msgstr ""
-
-msgid "branch to checkout after clone"
-msgstr ""
-
-msgid "when cloning, create full working directory"
-msgstr ""
-
-msgid "only download metadata for the branch that will be checked out"
-msgstr ""
-
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr ""
-
-#, c-format
-msgid "cannot deduce worktree name from '%s'"
-msgstr ""
-
-#, c-format
-msgid "directory '%s' exists already"
-msgstr ""
-
-#, c-format
-msgid "failed to get default branch for '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not configure remote in '%s'"
-msgstr ""
-
-#, c-format
-msgid "could not configure '%s'"
-msgstr ""
-
-msgid "partial clone failed; attempting full clone"
-msgstr ""
-
-msgid "could not configure for full clone"
-msgstr ""
-
-msgid "scalar diagnose [<enlistment>]"
-msgstr ""
-
-#, c-format
-msgid "could not create directory for '%s'"
-msgstr ""
-
-msgid "could not duplicate stdout"
-msgstr ""
-
-msgid "failed to write archive"
-msgstr ""
-
-msgid "`scalar list` does not take arguments"
-msgstr ""
-
-msgid "scalar register [<enlistment>]"
-msgstr ""
-
-msgid "reconfigure all registered enlistments"
-msgstr ""
-
-msgid "scalar reconfigure [--all | <enlistment>]"
-msgstr ""
-
-msgid "--all or <enlistment>, but not both"
-msgstr ""
-
-#, c-format
-msgid "git repository gone in '%s'"
-msgstr ""
-
-msgid ""
-"scalar run <task> [<enlistment>]\n"
-"Tasks:\n"
-msgstr ""
-
-#, c-format
-msgid "no such task: '%s'"
-msgstr ""
-
-msgid "scalar unregister [<enlistment>]"
-msgstr ""
-
-msgid "scalar delete <enlistment>"
-msgstr ""
-
-msgid "refusing to delete current working directory"
-msgstr ""
-
-msgid "include Git version"
-msgstr ""
-
-msgid "include Git's build options"
-msgstr ""
-
-msgid "scalar verbose [-v | --verbose] [--build-options]"
-msgstr ""
-
-msgid "-C requires a <directory>"
-msgstr ""
-
-#, c-format
-msgid "could not change to '%s'"
-msgstr ""
-
-msgid "-c requires a <key>=<value> argument"
-msgstr ""
-
-msgid ""
-"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
-"\n"
-"Commands:\n"
-msgstr ""
-
-#, c-format
 msgid "illegal crlf_action %d"
 msgstr ""
 
@@ -14689,6 +14962,32 @@
 msgid "Marked %d islands, done.\n"
 msgstr ""
 
+#, c-format
+msgid "invalid --%s value '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not archive missing directory '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not open directory '%s'"
+msgstr "не удалось открыть каталог «%s»"
+
+#, c-format
+msgid "skipping '%s', which is neither file nor directory"
+msgstr ""
+
+msgid "could not duplicate stdout"
+msgstr ""
+
+#, c-format
+msgid "could not add directory '%s' to archiver"
+msgstr ""
+
+msgid "failed to write archive"
+msgstr ""
+
 msgid "--merge-base does not work with ranges"
 msgstr ""
 
@@ -14951,6 +15250,9 @@
 msgid "do not show any source or destination prefix"
 msgstr ""
 
+msgid "use default prefixes a/ and b/"
+msgstr ""
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr ""
 
@@ -15238,6 +15540,14 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "подсказка: Ожидание, пока вы закроете редактор с файлом...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "не удалось записать в «%s»"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr ""
+
 msgid "Filtering content"
 msgstr "Фильтруется содержимое"
 
@@ -15269,6 +15579,9 @@
 msgid "unable to write to remote"
 msgstr ""
 
+msgid "Server supports filter"
+msgstr "Сервер поддерживает фильтрацию"
+
 #, c-format
 msgid "invalid shallow line: %s"
 msgstr "неправильная строка частичного получения: %s"
@@ -15379,9 +15692,6 @@
 msgid "Server does not support shallow requests"
 msgstr "Сервер не поддерживает частичные запросы"
 
-msgid "Server supports filter"
-msgstr "Сервер поддерживает фильтрацию"
-
 msgid "unable to write request to remote"
 msgstr ""
 
@@ -15403,14 +15713,12 @@
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
-#.
 #, c-format
 msgid "expected packfile to be sent after '%s'"
 msgstr ""
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
-#.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
 msgstr ""
@@ -15481,7 +15789,8 @@
 
 #, c-format
 msgid ""
-"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
 msgstr ""
 
 msgid ""
@@ -15490,8 +15799,7 @@
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 
 msgid ""
@@ -15519,10 +15827,6 @@
 msgstr ""
 
 #, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr ""
-
-#, c-format
 msgid "-c expects a configuration string\n"
 msgstr ""
 
@@ -15531,6 +15835,10 @@
 msgstr ""
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr ""
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr ""
 
@@ -15626,8 +15934,11 @@
 msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
 msgstr ""
 
-msgid "gpg failed to sign the data"
-msgstr "gpg не удалось подписать данные"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
 
 msgid "user.signingKey needs to be set for ssh signing"
 msgstr ""
@@ -15706,6 +16017,12 @@
 msgid "Low-level Commands / Internal Helpers"
 msgstr "Низкоуровневые команды / Внутренние вспомогательные"
 
+msgid "User-facing repository, command and file interfaces"
+msgstr ""
+
+msgid "Developer-facing file formats, protocols and other interfaces"
+msgstr ""
+
 #, c-format
 msgid "available git commands in '%s'"
 msgstr "доступные команды git в «%s»"
@@ -15719,6 +16036,12 @@
 msgid "The Git concept guides are:"
 msgstr ""
 
+msgid "User-facing repository, command and file interfaces:"
+msgstr ""
+
+msgid "File formats, protocols and other developer interfaces:"
+msgstr ""
+
 msgid "External commands"
 msgstr ""
 
@@ -15778,8 +16101,8 @@
 "\n"
 "Самые похожие команды:"
 
-msgid "git version [<options>]"
-msgstr "git version [<options>]"
+msgid "git version [--build-options]"
+msgstr ""
 
 #, c-format
 msgid "%s: %s - %s"
@@ -15815,10 +16138,6 @@
 "ignoredHook false»."
 
 #, c-format
-msgid "Couldn't start hook '%s'\n"
-msgstr ""
-
-#, c-format
 msgid "argument to --packfile must be a valid hash (got '%s')"
 msgstr ""
 
@@ -16002,16 +16321,14 @@
 msgstr ""
 
 #, c-format
-msgid ""
-"Your local changes to the following files would be overwritten by merge:\n"
-"  %s"
-msgstr ""
-
-#, c-format
 msgid "Failed to merge submodule %s (not checked out)"
 msgstr "Не удалось слить подмодуль %s (состояние не забрано)"
 
 #, c-format
+msgid "Failed to merge submodule %s (no merge base)"
+msgstr ""
+
+#, c-format
 msgid "Failed to merge submodule %s (commits not present)"
 msgstr "Не удалось слить подмодуль %s (нет коммитов)"
 
@@ -16029,27 +16346,11 @@
 
 #, c-format
 msgid ""
-"Failed to merge submodule %s, but a possible merge resolution exists:\n"
-"%s\n"
+"Failed to merge submodule %s, but a possible merge resolution exists: %s"
 msgstr ""
 
 #, 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 ""
-"Если оно верное, то просто добавьте его в индекс, например так:\n"
-"\n"
-"  git update-index --cacheinfo 160000 %s \"%s\"\n"
-"\n"
-"тем самым принимая это предположение.\n"
-
-#, c-format
-msgid ""
 "Failed to merge submodule %s, but multiple possible merges exist:\n"
 "%s"
 msgstr ""
@@ -16174,15 +16475,33 @@
 "of %s left in tree."
 msgstr ""
 
+#. TRANSLATORS: This is a line of advice to resolve a merge
+#. conflict in a submodule. The first argument is the submodule
+#. name, and the second argument is the abbreviated id of the
+#. commit that needs to be merged.  For example:
+#.  - go to submodule (mysubmodule), and either merge commit abc1234"
 #, c-format
 msgid ""
-"Note: %s not up to date and in way of checking out conflicted version; old "
-"copy renamed to %s"
+" - go to submodule (%s), and either merge commit %s\n"
+"   or update to an existing commit which has merged those changes\n"
+msgstr ""
+
+#, c-format
+msgid ""
+"Recursive merging with submodules currently only supports trivial cases.\n"
+"Please manually handle the merging of each conflicted submodule.\n"
+"This can be accomplished with the following steps:\n"
+"%s - come back to superproject and run:\n"
+"\n"
+"      git add %s\n"
+"\n"
+"   to record the above merge or update\n"
+" - resolve any other conflicts in the superproject\n"
+" - commit the resulting index in the superproject\n"
 msgstr ""
 
 #. TRANSLATORS: The %s arguments are: 1) tree hash of a merge
 #. base, and 2-3) the trees for the two trees we're merging.
-#.
 #, c-format
 msgid "collecting merge info failed for trees %s, %s, %s"
 msgstr ""
@@ -16249,6 +16568,21 @@
 msgstr "Найдено возможное разрешение слиятия для подмодуля:\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 ""
+"Если оно верное, то просто добавьте его в индекс, например так:\n"
+"\n"
+"  git update-index --cacheinfo 160000 %s \"%s\"\n"
+"\n"
+"тем самым принимая это предположение.\n"
+
+#, c-format
 msgid "Failed to merge submodule %s (multiple merges found)"
 msgstr "Не удалось слить подмодуль %s (найдено несколько слияний)"
 
@@ -16316,8 +16650,8 @@
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
 "КОНФЛИКТ (переименование/переименование): Переименование «%s»→«%s» в ветке "
 "«%s» и переименование «%s»→«%s» в ветке «%s»%s"
@@ -16612,7 +16946,6 @@
 #. TRANSLATORS: The first %s is the name of
 #. the environment variable, the second %s is
 #. its value.
-#.
 #, c-format
 msgid "Bad %s value: '%s'"
 msgstr "Неправильное значение переменной %s: «%s»"
@@ -16629,10 +16962,6 @@
 msgid "%s: ignoring alternate object stores, nesting too deep"
 msgstr ""
 
-#, c-format
-msgid "unable to normalize object directory: %s"
-msgstr ""
-
 msgid "unable to fdopen alternates lockfile"
 msgstr ""
 
@@ -16692,6 +17021,10 @@
 msgstr ""
 
 #, c-format
+msgid "unable to open loose object %s"
+msgstr ""
+
+#, c-format
 msgid "unable to parse %s header"
 msgstr ""
 
@@ -16707,7 +17040,7 @@
 msgstr ""
 
 #, c-format
-msgid "failed to read object %s"
+msgid "loose object %s (stored in %s) is corrupt"
 msgstr ""
 
 #, c-format
@@ -16715,10 +17048,6 @@
 msgstr ""
 
 #, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr ""
-
-#, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr ""
 
@@ -16730,9 +17059,6 @@
 msgid "unable to set permission to '%s'"
 msgstr ""
 
-msgid "file write error"
-msgstr ""
-
 msgid "error when closing loose object file"
 msgstr ""
 
@@ -16759,13 +17085,30 @@
 msgstr ""
 
 #, c-format
+msgid "write stream object %ld != %<PRIuMAX>"
+msgstr ""
+
+#, c-format
+msgid "unable to stream deflate new object (%d)"
+msgstr ""
+
+#, c-format
+msgid "deflateEnd on stream object failed (%d)"
+msgstr ""
+
+#, c-format
+msgid "unable to create directory %s"
+msgstr ""
+
+#, c-format
 msgid "cannot read object for %s"
 msgstr ""
 
-msgid "corrupt commit"
+#, c-format
+msgid "object fails fsck: %s"
 msgstr ""
 
-msgid "corrupt tag"
+msgid "refusing to create malformed object"
 msgstr ""
 
 #, c-format
@@ -16815,7 +17158,6 @@
 #. TRANSLATORS: This is a line of ambiguous object
 #. output shown when we cannot look up or parse the
 #. object in question. E.g. "deadbeef [bad object]".
-#.
 #, c-format
 msgid "%s [bad object]"
 msgstr ""
@@ -16824,7 +17166,6 @@
 #. object output. E.g.:
 #. *
 #.    "deadbeef commit 2021-01-01 - Some Commit Message"
-#.
 #, c-format
 msgid "%s commit %s - %s"
 msgstr ""
@@ -16839,7 +17180,6 @@
 #. *
 #. The third argument is the "tag" string
 #. from object.c.
-#.
 #, c-format
 msgid "%s tag %s - %s"
 msgstr ""
@@ -16849,21 +17189,18 @@
 #. the tag itself. E.g.:
 #. *
 #.    "deadbeef [bad tag, could not parse it]"
-#.
 #, c-format
 msgid "%s [bad tag, could not parse it]"
 msgstr ""
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef tree".
-#.
 #, c-format
 msgid "%s tree"
 msgstr ""
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef blob".
-#.
 #, c-format
 msgid "%s blob"
 msgstr ""
@@ -16875,7 +17212,6 @@
 #. TRANSLATORS: The argument is the list of ambiguous
 #. objects composed in show_ambiguous_object(). See
 #. its "TRANSLATORS" comments for details.
-#.
 #, c-format
 msgid ""
 "The candidates are:\n"
@@ -16967,6 +17303,52 @@
 msgid "hash mismatch %s"
 msgstr "несоответствие хэш-кода %s"
 
+msgid "trying to write commit not in index"
+msgstr ""
+
+msgid "failed to load bitmap index (corrupted?)"
+msgstr ""
+
+msgid "corrupted bitmap index (too small)"
+msgstr ""
+
+msgid "corrupted bitmap index file (wrong header)"
+msgstr ""
+
+#, c-format
+msgid "unsupported version '%d' for bitmap index file"
+msgstr ""
+
+msgid "corrupted bitmap index file (too short to fit hash cache)"
+msgstr ""
+
+msgid "corrupted bitmap index file (too short to fit lookup table)"
+msgstr ""
+
+#, c-format
+msgid "duplicate entry in bitmap index: '%s'"
+msgstr ""
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for entry %d"
+msgstr ""
+
+#, c-format
+msgid "corrupt ewah bitmap: commit index %u out of range"
+msgstr ""
+
+msgid "corrupted bitmap pack index"
+msgstr ""
+
+msgid "invalid XOR offset in bitmap pack index"
+msgstr ""
+
+msgid "cannot fstat bitmap file"
+msgstr ""
+
+msgid "checksum doesn't match in MIDX and bitmap"
+msgstr ""
+
 msgid "multi-pack bitmap is missing required reverse index"
 msgstr ""
 
@@ -16978,8 +17360,59 @@
 msgid "preferred pack (%s) is invalid"
 msgstr ""
 
+msgid "corrupt bitmap lookup table: triplet position out of index"
+msgstr ""
+
+msgid "corrupt bitmap lookup table: xor chain exceeds entry count"
+msgstr ""
+
 #, c-format
-msgid "could not find %s in pack %s at offset %<PRIuMAX>"
+msgid "corrupt bitmap lookup table: commit index %u out of range"
+msgstr ""
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
+msgstr ""
+
+#, c-format
+msgid "object '%s' not found in type bitmaps"
+msgstr ""
+
+#, c-format
+msgid "object '%s' does not have a unique type"
+msgstr ""
+
+#, c-format
+msgid "object '%s': real type '%s', expected: '%s'"
+msgstr ""
+
+#, c-format
+msgid "object not in bitmap: '%s'"
+msgstr ""
+
+msgid "failed to load bitmap indexes"
+msgstr ""
+
+msgid "you must specify exactly one commit to test"
+msgstr ""
+
+#, c-format
+msgid "commit '%s' doesn't have an indexed bitmap"
+msgstr ""
+
+msgid "mismatch in bitmap results"
+msgstr ""
+
+#, c-format
+msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
+msgstr ""
+
+#, c-format
+msgid "unable to get disk usage of '%s'"
+msgstr ""
+
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
 msgstr ""
 
 #, c-format
@@ -17022,6 +17455,13 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr ""
 
+msgid "invalid checksum"
+msgstr ""
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+
 msgid "cannot both write and verify reverse index"
 msgstr ""
 
@@ -17104,6 +17544,9 @@
 msgid "alias of --%s"
 msgstr ""
 
+msgid "need a subcommand"
+msgstr ""
+
 #, c-format
 msgid "unknown option `%s'"
 msgstr ""
@@ -17125,7 +17568,6 @@
 
 #. TRANSLATORS: the colon here should align with the
 #. one in "usage: %s" translation.
-#.
 #, c-format
 msgid "   or: %s"
 msgstr "          или: %s"
@@ -17148,7 +17590,6 @@
 #. function. The "%s" is a line in the (hopefully already
 #. translated) N_() usage string, which contained embedded
 #. newlines before we split it up.
-#.
 #, c-format
 msgid "%*s%s"
 msgstr ""
@@ -17175,6 +17616,9 @@
 msgid "use <n> digits to display object names"
 msgstr ""
 
+msgid "prefixed path to initial superproject"
+msgstr ""
+
 msgid "how to strip spaces and #comments from message"
 msgstr "как удалять пробелы и #комментарии из сообщения коммита"
 
@@ -17322,6 +17766,10 @@
 msgid "promisor remote name cannot begin with '/': %s"
 msgstr ""
 
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr ""
+
 msgid "object-info: expected flush after arguments"
 msgstr ""
 
@@ -17537,9 +17985,12 @@
 "l, label <label> = label current HEAD with a name\n"
 "t, reset <label> = reset HEAD to a label\n"
 "m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]\n"
-".       create a merge commit using the original merge commit's\n"
-".       message (or the oneline, if no original merge commit was\n"
-".       specified); use -c <commit> to reword the commit message\n"
+"        create a merge commit using the original merge commit's\n"
+"        message (or the oneline, if no original merge commit was\n"
+"        specified); use -c <commit> to reword the commit message\n"
+"u, update-ref <ref> = track a placeholder for the <ref> to be updated\n"
+"                      to this position in the new commits. The <ref> is\n"
+"                      updated at the end of the rebase\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n"
 msgstr ""
@@ -17637,6 +18088,14 @@
 msgstr "впереди %d, позади %d"
 
 #, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr ""
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr ""
+
+#, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "ожидаемый формат: %%(color:<color>)"
 
@@ -17653,22 +18112,6 @@
 msgstr "Ожидается целочисленное значение refname:rstrip=%s"
 
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "неопознанный аргумент %%(%s): %s"
-
-#, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr ""
-
-#, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr ""
-
-#, c-format
-msgid "%%(body) does not take arguments"
-msgstr "параметр %%(body) не принимает аргументы"
-
-#, c-format
 msgid "expected %%(trailers:key=<value>)"
 msgstr ""
 
@@ -17685,10 +18128,6 @@
 msgstr ""
 
 #, c-format
-msgid "unrecognized email option: %s"
-msgstr ""
-
-#, c-format
 msgid "expected format: %%(align:<width>,<position>)"
 msgstr "ожидаемый формат: %%(align:<width>,<position>)"
 
@@ -17701,11 +18140,15 @@
 msgstr "неопознанная ширина:%s"
 
 #, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "неопознанный аргумент %%(%s): %s"
+
+#, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "ожидается положительная ширина с указанием частицы %%(align)"
 
 #, c-format
-msgid "%%(rest) does not take arguments"
+msgid "expected format: %%(ahead-behind:<committish>)"
 msgstr ""
 
 #, c-format
@@ -17997,6 +18440,13 @@
 msgid "http transport does not support %s"
 msgstr ""
 
+msgid "protocol error: expected '<url> <path>', missing space"
+msgstr ""
+
+#, c-format
+msgid "failed to download file at URL '%s'"
+msgstr ""
+
 msgid "git-http-push failed"
 msgstr ""
 
@@ -18063,7 +18513,6 @@
 #. TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
 #. <remote> <src>:<dst>" push, and "being pushed ('%s')" is
 #. the <src>.
-#.
 #, c-format
 msgid ""
 "The destination you provided is not a full refname (i.e.,\n"
@@ -18306,10 +18755,6 @@
 msgstr ""
 
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr ""
-
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr ""
 
@@ -18332,6 +18777,21 @@
 msgid "failed to find tree of %s"
 msgstr "не удалось найти дерево для %s"
 
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr ""
+
+msgid "--exclude-hidden= passed more than once"
+msgstr ""
+
+#, c-format
+msgid "resolve-undo records `%s` which is missing"
+msgstr ""
+
+#, c-format
+msgid "could not get commit for ancestry-path argument %s"
+msgstr ""
+
 msgid "--unpacked=<packfile> no longer supported"
 msgstr ""
 
@@ -18352,6 +18812,175 @@
 msgid "cannot create async thread: %s"
 msgstr ""
 
+#, c-format
+msgid "'%s' does not exist"
+msgstr "«%s» не существует"
+
+#, c-format
+msgid "could not switch to '%s'"
+msgstr ""
+
+msgid "need a working directory"
+msgstr ""
+
+msgid "Scalar enlistments require a worktree"
+msgstr ""
+
+#, c-format
+msgid "could not configure %s=%s"
+msgstr ""
+
+msgid "could not configure log.excludeDecoration"
+msgstr ""
+
+msgid "could not add enlistment"
+msgstr ""
+
+msgid "could not set recommended config"
+msgstr ""
+
+msgid "could not turn on maintenance"
+msgstr ""
+
+msgid "could not start the FSMonitor daemon"
+msgstr ""
+
+msgid "could not turn off maintenance"
+msgstr ""
+
+msgid "could not remove enlistment"
+msgstr ""
+
+#, c-format
+msgid "remote HEAD is not a branch: '%.*s'"
+msgstr ""
+
+msgid "failed to get default branch name from remote; using local default"
+msgstr ""
+
+msgid "failed to get default branch name"
+msgstr ""
+
+msgid "failed to unregister repository"
+msgstr ""
+
+msgid "failed to stop the FSMonitor daemon"
+msgstr ""
+
+msgid "failed to delete enlistment directory"
+msgstr ""
+
+msgid "branch to checkout after clone"
+msgstr ""
+
+msgid "when cloning, create full working directory"
+msgstr ""
+
+msgid "only download metadata for the branch that will be checked out"
+msgstr ""
+
+msgid "scalar clone [<options>] [--] <repo> [<dir>]"
+msgstr ""
+
+#, c-format
+msgid "cannot deduce worktree name from '%s'"
+msgstr ""
+
+#, c-format
+msgid "directory '%s' exists already"
+msgstr ""
+
+#, c-format
+msgid "failed to get default branch for '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not configure remote in '%s'"
+msgstr ""
+
+#, c-format
+msgid "could not configure '%s'"
+msgstr ""
+
+msgid "partial clone failed; attempting full clone"
+msgstr ""
+
+msgid "could not configure for full clone"
+msgstr ""
+
+msgid "scalar diagnose [<enlistment>]"
+msgstr ""
+
+msgid "`scalar list` does not take arguments"
+msgstr ""
+
+msgid "scalar register [<enlistment>]"
+msgstr ""
+
+msgid "reconfigure all registered enlistments"
+msgstr ""
+
+msgid "scalar reconfigure [--all | <enlistment>]"
+msgstr ""
+
+msgid "--all or <enlistment>, but not both"
+msgstr ""
+
+#, c-format
+msgid "could not remove stale scalar.repo '%s'"
+msgstr ""
+
+#, c-format
+msgid "removing stale scalar.repo '%s'"
+msgstr ""
+
+#, c-format
+msgid "git repository gone in '%s'"
+msgstr ""
+
+msgid ""
+"scalar run <task> [<enlistment>]\n"
+"Tasks:\n"
+msgstr ""
+
+#, c-format
+msgid "no such task: '%s'"
+msgstr ""
+
+msgid "scalar unregister [<enlistment>]"
+msgstr ""
+
+msgid "scalar delete <enlistment>"
+msgstr ""
+
+msgid "refusing to delete current working directory"
+msgstr ""
+
+msgid "include Git version"
+msgstr ""
+
+msgid "include Git's build options"
+msgstr ""
+
+msgid "scalar verbose [-v | --verbose] [--build-options]"
+msgstr ""
+
+msgid "-C requires a <directory>"
+msgstr ""
+
+#, c-format
+msgid "could not change to '%s'"
+msgstr ""
+
+msgid "-c requires a <key>=<value> argument"
+msgstr ""
+
+msgid ""
+"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
+"\n"
+"Commands:\n"
+msgstr ""
+
 msgid "unexpected flush packet while reading remote unpack status"
 msgstr "неожиданный пустой пакет при чтении статуса внешней распаковки"
 
@@ -18442,10 +19071,6 @@
 msgstr "не удалось заблокировать «%s»"
 
 #, c-format
-msgid "could not write to '%s'"
-msgstr "не удалось записать в «%s»"
-
-#, c-format
 msgid "could not write eol to '%s'"
 msgstr "не удалось записать eol в «%s»"
 
@@ -18460,13 +19085,8 @@
 msgid "commit your changes or stash them to proceed."
 msgstr "для продолжения закоммитьте ваши изменения или спрячьте их."
 
-#, c-format
-msgid "%s: fast-forward"
-msgstr "%s: быстрая перемотка"
-
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
-#.
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s: Не удалось записать файл индекса"
@@ -18726,6 +19346,22 @@
 msgstr "git %s: сбой обновления индекса"
 
 #, c-format
+msgid "'%s' is not a valid label"
+msgstr ""
+
+#, c-format
+msgid "'%s' is not a valid refname"
+msgstr ""
+
+#, c-format
+msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
+msgstr ""
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr ""
+
+#, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "параметр %s не принимает аргументы: «%s»"
 
@@ -18795,11 +19431,8 @@
 msgid "could not create sequencer directory '%s'"
 msgstr "не удалось создать каталог для указателя следования коммитов «%s»"
 
-msgid "could not lock HEAD"
-msgstr "не удалось заблокировать HEAD"
-
 msgid "no cherry-pick or revert in progress"
-msgstr "копирование или обращение изменений коммита уже выполняются"
+msgstr "копирование или обращение изменений коммита сейчас не выполняются"
 
 msgid "cannot resolve HEAD"
 msgstr "не удалось определить HEAD"
@@ -18885,39 +19518,33 @@
 "  git rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "и были сделаны изменения в индексе и/или в рабочем каталоге\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr ""
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
-"успешное выполнение: %s\n"
-"но остались изменения в индексе и/или в рабочем каталоге\n"
-"Сделайте коммит или спрячьте ваши изменения, а затем выполните\n"
-"\n"
-"  git rebase --continue\n"
-"\n"
 
 #, c-format
 msgid "illegal label name: '%.*s'"
 msgstr ""
 
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "не удалось распознать «%s»"
+
 msgid "writing fake root commit"
 msgstr "запись поддельного корневого коммита"
 
 msgid "writing squash-onto"
 msgstr "запить уплотнить-над"
 
-#, c-format
-msgid "could not resolve '%s'"
-msgstr "не удалось распознать «%s»"
-
 msgid "cannot merge without a current revision"
 msgstr "нельзя слить без текущей редакции"
 
@@ -18943,6 +19570,23 @@
 msgid "merge: Unable to write new index file"
 msgstr "слияние: Не удалось записать файл индекса"
 
+#, c-format
+msgid ""
+"another 'rebase' process appears to be running; '%s.lock' already exists"
+msgstr ""
+
+#, c-format
+msgid ""
+"Updated the following refs with %s:\n"
+"%s"
+msgstr ""
+
+#, c-format
+msgid ""
+"Failed to update the following refs with %s:\n"
+"%s"
+msgstr ""
+
 msgid "Cannot autostash"
 msgstr "Не удалось автоматически спрятать изменения"
 
@@ -19100,6 +19744,10 @@
 msgstr "сценарий уже был перестроен."
 
 #, c-format
+msgid "update-refs file at '%s' is invalid"
+msgstr ""
+
+#, c-format
 msgid "'%s' is outside repository at '%s'"
 msgstr ""
 
@@ -19225,13 +19873,17 @@
 
 #, c-format
 msgid ""
-"unsafe repository ('%s' is owned by someone else)\n"
-"To add an exception for this directory, call:\n"
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
 "\n"
 "\tgit config --global --add safe.directory %s"
 msgstr ""
 
 #, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+
+#, c-format
 msgid ""
 "problem with core.sharedRepository filemode value (0%.3o).\n"
 "The owner of files must always have read and write permissions."
@@ -19299,15 +19951,11 @@
 msgstr[3] "%u байта/с"
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr ""
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "игнорирую подозрительный подмодуль с именем: %s"
 
 msgid "negative values not allowed for submodule.fetchJobs"
-msgstr "нельзя использовать отприцательные значения для submodule.fetchJobs"
+msgstr "нельзя использовать отрицательные значения для submodule.fetchJobs"
 
 #, c-format
 msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
@@ -19472,6 +20120,25 @@
 msgid "failed to lstat '%s'"
 msgstr ""
 
+msgid "no remote configured to get bundle URIs from"
+msgstr ""
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr ""
+
+msgid "could not get the bundle-uri list"
+msgstr ""
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr ""
+
+msgid "clear the cache tree before each iteration"
+msgstr ""
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+
 msgid "unhandled options"
 msgstr ""
 
@@ -19806,6 +20473,12 @@
 msgid "failed to push all needed submodules"
 msgstr ""
 
+msgid "bundle-uri operation not supported by protocol"
+msgstr ""
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr ""
+
 msgid "too-short tree object"
 msgstr "слишком  короткий объект дерева"
 
@@ -20077,6 +20750,18 @@
 msgid "invalid '..' path segment"
 msgstr "неправильная часть пути «..»"
 
+msgid "usage: "
+msgstr ""
+
+msgid "fatal: "
+msgstr ""
+
+msgid "error: "
+msgstr ""
+
+msgid "warning: "
+msgstr ""
+
 msgid "Fetching objects"
 msgstr ""
 
@@ -20530,14 +21215,16 @@
 
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"%.2f сек занял вывод списка неотслеживаемых файлов.\n"
-"«status -uno» возможно ускорит это, но будьте внимательны\n"
-"и не забудьте добавить новые файлы вручную\n"
-"(смотрите «git help status» для подробностей)."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr ""
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr ""
 
 #, c-format
 msgid "Untracked files not listed%s"
@@ -20687,296 +21374,6 @@
 msgid "Unable to determine absolute path of git directory"
 msgstr "Не удалось определить абсолютный путь к каталогу git"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "тронут %d путь\n"
-msgstr[1] "тронуты %d пути\n"
-msgstr[2] "тронуты %d путей\n"
-msgstr[3] "тронуты %d пути\n"
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"индексирования."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"прятанья."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"убирания из индекса."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"применения."
-
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr ""
-"Если патч применяется без ошибок, то изменённый блок будет сразу помечен для "
-"отмены изменений."
-
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "не удалось открыть файл редактирования блока изменений для записи: %s"
-
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"Чтобы удалить «%s» строки, сделайте их ' ' строками (контекст).\n"
-"Чтобы удалить «%s» строки, удалите их.\n"
-"Строки, начинающиеся с %s будут удалены.\n"
-
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "не удалось открыть файл редактирования блока изменений для чтения: %s"
-
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - индексировать этот блок\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все оставшиеся блоки\n"
-"a - индексировать этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - спрятать этот блок\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - спрятать этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - убрать из индекса этот блок\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - убрать из индекса этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - принять этот блок в индекс\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - принять этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - отбросить этот блок из рабочего дерева\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - отбросить этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - отбросить этот блок из индекса и рабочего дерева\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - отбросить этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - принять этот блок в индекс и рабочее дерево\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - принять этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - принять этот блок в рабочее дерево\n"
-"n - пропустить этот блок\n"
-"q - выход; пропустить этот и все последующие блоки\n"
-"a - принять этот и остальные блоки файла\n"
-"d - пропустить этот и остальные блоки файла"
-
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"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"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - выбрать блок изменений на который нужно перейти\n"
-"/ - поиск блока изменений с помощью регулярного выражения\n"
-"j - не принимать решение по этому блоку, перейти на следующий нерешенный\n"
-"J - не принимать решение по этому блоку, перейти на следующий\n"
-"k - не принимать решение по этому блоку, перейти на предыдущий нерешенный\n"
-"K - не принимать решение по этому блоку, перейти на предыдущий\n"
-"s - разделить текущий блок на блоки меньшего размера\n"
-"e - вручную отредактировать текущий блок\n"
-"? - вывести справку\n"
-
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "Выбранные блоки не применяются без ошибок к индексу!\n"
-
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "игнорирую не слитое: %s\n"
-
-#, perl-format
-msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
-msgstr ""
-
-#, perl-format
-msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
-msgstr ""
-
-#, perl-format
-msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
-msgstr ""
-
-#, perl-format
-msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
-msgstr "Принять этот блок в рабочее дерево [y,n,q,a,d%s,?]? "
-
-msgid "No other hunks to goto\n"
-msgstr ""
-
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "Неверный номер: «%s»\n"
-
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "Простите, но только %d блок изменений доступен.\n"
-msgstr[1] "Простите, но только %d блока изменений доступно.\n"
-msgstr[2] "Простите, но только %d блоков изменений доступно.\n"
-msgstr[3] "Простите, но только %d блока изменений доступно.\n"
-
-msgid "No other hunks to search\n"
-msgstr ""
-
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "Регулярное выражение для поиска в неверном формате %s: %s\n"
-
-msgid "No hunk matches the given pattern\n"
-msgstr "Не найдены блоки, которые соответствуют указанному шаблону\n"
-
-msgid "No previous hunk\n"
-msgstr "Нет предыдущего блока\n"
-
-msgid "No next hunk\n"
-msgstr "Не следующего блока\n"
-
-msgid "Sorry, cannot split this hunk\n"
-msgstr ""
-
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "Разбито на %d блок изменений.\n"
-msgstr[1] "Разбито на %d блока изменений.\n"
-msgstr[2] "Разбито на %d блоков изменений.\n"
-msgstr[3] "Разбито на %d блока изменений.\n"
-
-msgid "Sorry, cannot edit this hunk\n"
-msgstr ""
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - показать пути с изменениями\n"
-"update        - добавить состояние рабочей копии в индекс\n"
-"revert        - вернуть проиндексированный набор изменений к HEAD-версии\n"
-"patch         - выбрать и выборочно обновить блоки\n"
-"diff          - просмотреть различия между HEAD и индексом\n"
-"add untracked - добавить содержимое неотслеживаемых файлов в индекс\n"
-
-msgid "missing --"
-msgstr "отсутствует --"
-
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "неизвестный режим для --patch: %s"
-
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "недопустимый аргумент %s, ожидается --"
-
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "локальный часовой пояс отличается от GMT на не минутный интервал\n"
 
@@ -21255,13 +21652,17 @@
 msgstr "(%s) Не удалось выполнить «%s»"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Добавление %s: %s из: «%s»\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr ""
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
 msgstr "(%s) не удалось закрыть поток к «%s»"
 
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Добавление %s: %s из: «%s»\n"
+
 msgid "cannot send message as 7bit"
 msgstr "не удалось отправить сообщение в 7 битной кодировке"
 
diff --git a/po/sv.po b/po/sv.po
index 0ba8585..786c2f7 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,22 +1,22 @@
 # Swedish translations for Git.
-# Copyright (C) 2010-2023 Peter Krefting <peter@softwolves.pp.se>
+# Copyright (C) 2010-2024 Peter Krefting <peter@softwolves.pp.se>
 # This file is distributed under the same license as the Git package.
-# Peter Krefting <peter@softwolves.pp.se>, 2010-2023.
+# Peter Krefting <peter@softwolves.pp.se>, 2010-2024.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: git 2.40.0\n"
+"Project-Id-Version: git 2.44.0\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-02 09:34+0100\n"
-"PO-Revision-Date: 2023-03-02 09:35+0100\n"
+"POT-Creation-Date: 2024-02-16 07:58+0100\n"
+"PO-Revision-Date: 2024-02-16 07:59+0100\n"
 "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
-"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\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"
-"X-Generator: Gtranslator 3.38.0\n"
+"X-Generator: Gtranslator 42.0\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -39,7 +39,7 @@
 
 #, c-format
 msgid "could not stage '%s'"
-msgstr "kunde inte köa \"%s\""
+msgstr "kunde inte köa ”%s”"
 
 msgid "could not write index"
 msgstr "kunde inte skriva indexet"
@@ -56,7 +56,7 @@
 
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
-msgstr "make_cache_entry misslyckades för sökvägen \"%s\""
+msgstr "make_cache_entry misslyckades för sökvägen ”%s”"
 
 msgid "Revert"
 msgstr "Återställ"
@@ -228,7 +228,7 @@
 "stashing."
 msgstr ""
 "Om patchen kan appliceras rent kommer det redigerade stycket att läggas till "
-"i \"stash\" omedelbart."
+"i ”stash” omedelbart."
 
 msgid ""
 "y - stash this hunk\n"
@@ -237,11 +237,11 @@
 "a - stash this hunk and all later hunks in the file\n"
 "d - do not stash this hunk or any of the later hunks in the file\n"
 msgstr ""
-"y - \"stash\":a stycket\n"
-"n - \"stash\":a inte stycket\n"
-"q - avsluta; \"stash\":a inte stycket eller något av de följande\n"
-"a - \"stash\":a stycket och alla följande i filen\n"
-"d - \"stash\":a inte stycket eller något av de följande i filen\n"
+"y - ”stash”:a stycket\n"
+"n - ”stash”:a inte stycket\n"
+"q - avsluta; ”stash”:a inte stycket eller något av de följande\n"
+"a - ”stash”:a stycket och alla följande i filen\n"
+"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,?]? "
@@ -440,7 +440,7 @@
 
 #, c-format
 msgid "could not parse hunk header '%.*s'"
-msgstr "kunde inte tolka styckehuvudet \"%.*s\""
+msgstr "kunde inte tolka styckehuvudet ”%.*s”"
 
 msgid "could not parse diff"
 msgstr "kunde inte tolka diff"
@@ -450,7 +450,7 @@
 
 #, c-format
 msgid "failed to run '%s'"
-msgstr "misslyckades att köra \"%s\""
+msgstr "misslyckades att köra ”%s”"
 
 msgid "mismatched output from interactive.diffFilter"
 msgstr "omaka utdata från interactive.diffFilter"
@@ -493,8 +493,8 @@
 "Lines starting with %c will be removed.\n"
 msgstr ""
 "---\n"
-"Ta bort \"%c\" rader genom att göra dem \" \"-rader (sammanhang).\n"
-"Ta bort \"%c\" rader genom att radera dem.\n"
+"Ta bort ”%c” rader genom att göra dem ” ”-rader (sammanhang).\n"
+"Ta bort ”%c” rader genom att radera dem.\n"
 "Rader som börjar med %c kommer att tas bort.\n"
 
 msgid ""
@@ -510,7 +510,7 @@
 msgstr "kunde inte tolka styckehuvud"
 
 msgid "'git apply --cached' failed"
-msgstr "\"git apply --cached\" misslyckades"
+msgstr "”git apply --cached” misslyckades"
 
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
@@ -521,8 +521,8 @@
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr ""
-"Ditt redigerade stycke kan inte appliceras. Redigera igen (\"nej\" kastar!) "
-"[y/n]? "
+"Ditt redigerade stycke kan inte appliceras. Redigera igen (”nej” kastar!) [y/"
+"n]? "
 
 msgid "The selected hunks do not apply to the index!"
 msgstr "Markerade stycken kan inte appliceras på indexet!"
@@ -571,7 +571,7 @@
 
 #, c-format
 msgid "Invalid number: '%s'"
-msgstr "Ogiltigt siffervärde: \"%s\""
+msgstr "Ogiltigt siffervärde: ”%s”"
 
 #, c-format
 msgid "Sorry, only %d hunk available."
@@ -603,7 +603,7 @@
 msgstr "Beklagar, kan inte redigera stycket"
 
 msgid "'git apply' failed"
-msgstr "\"git apply\" misslyckades"
+msgstr "”git apply” misslyckades"
 
 #, c-format
 msgid ""
@@ -611,7 +611,7 @@
 "Disable this message with \"git config advice.%s false\""
 msgstr ""
 "\n"
-"Slå av meddelandet med \"git config advice.%s false\""
+"Slå av meddelandet med ”git config advice.%s false”"
 
 #, c-format
 msgid "%shint: %.*s%s\n"
@@ -619,13 +619,11 @@
 
 msgid "Cherry-picking is not possible because you have unmerged files."
 msgstr ""
-"Du kan inte utföra en cherry-pick eftersom du har filer som inte slagits "
+"Du kan inte utföra en ”cherry-pick” eftersom du har filer som inte slagits "
 "samman."
 
 msgid "Committing is not possible because you have unmerged files."
-msgstr ""
-"Du kan inte utföra en incheckning eftersom du har filer som inte slagits "
-"samman."
+msgstr "Du kan inte checka in eftersom du har filer som inte slagits samman."
 
 msgid "Merging is not possible because you have unmerged files."
 msgstr ""
@@ -634,23 +632,19 @@
 
 msgid "Pulling is not possible because you have unmerged files."
 msgstr ""
-"Du kan inte utföra en \"pull\" eftersom du har filer som inte slagits samman."
+"Du kan inte utföra en ”pull” eftersom du har filer som inte slagits samman."
 
 msgid "Reverting is not possible because you have unmerged files."
-msgstr ""
-"Du kan inte utföra en \"revert\" eftersom du har filer som inte slagits "
-"samman."
+msgstr "Du kan inte återställa eftersom du har filer som inte slagits samman."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr ""
-"Du kan inte utföra en \"%s\" eftersom du har filer som inte slagits samman."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Du kan inte ombasera eftersom du har filer som inte slagits samman."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
 "as appropriate to mark resolution and make a commit."
 msgstr ""
-"Rätta dem i din arbetskatalog och använd sedan \"git add/rm <fil>\"\n"
+"Rätta dem i din arbetskatalog och använd sedan ”git add/rm <fil>”\n"
 "som lämpligt för att ange lösning och checka in."
 
 msgid "Exiting because of an unresolved conflict."
@@ -665,6 +659,23 @@
 msgid "Exiting because of unfinished merge."
 msgstr "Avslutar på grund av ofullbordad sammanslagning."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Divergerande grenar kan inte snabbspolas, du måste antingen använda:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"eller:\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "Kan inte snabbspola, avbryter."
 
@@ -675,7 +686,7 @@
 "updated in the index:\n"
 msgstr ""
 "Följande sökvägar och/eller sökvägsangivelser motsvarar sökvägar\n"
-"utanför din \"sparse-checkout\"-definition, så de kommer inte\n"
+"utanför din ”sparse-checkout”-definition, så de kommer inte\n"
 "uppdateras i indexet:\n"
 
 msgid ""
@@ -708,9 +719,9 @@
 "false\n"
 "\n"
 msgstr ""
-"Observera: checkar ut \"%s\".\n"
+"Observera: checkar ut ”%s”.\n"
 "\n"
-"Du har nu ett \"frånkopplat HEAD\". Du kan se dig omkring, experimentera\n"
+"Du har nu ett ”frånkopplat HEAD”. Du kan se dig omkring, experimentera\n"
 "med ändringar och checka in dem, och du kan kasta incheckningar du gör\n"
 "i det här läget utan att det påverkar grenar genom att växla tillbaka\n"
 "till en gren.\n"
@@ -736,7 +747,7 @@
 "modifications.\n"
 msgstr ""
 "Följande sökvägar har flyttats ut från din\n"
-"\"sparse-checkout\"-definition, men är inte glesa på grund av\n"
+"”sparse-checkout”-definition, men är inte glesa på grund av\n"
 "lokala ändringar.\n"
 
 msgid ""
@@ -745,8 +756,8 @@
 "* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
 msgstr ""
 "För att korrigera glesheten för dessa sökvägar, gör följande:\n"
-"* Använd \"git add --sparse <sökväg>\" för att uppdatera indexet\n"
-"* Använd \"git sparse-checkout reapply\" för att tillämpa gleshetsreglerna"
+"* Använd ”git add --sparse <sökväg>” för att uppdatera indexet\n"
+"* Använd ”git sparse-checkout reapply” för att tillämpa gleshetsreglerna"
 
 msgid "cmdline ends with \\"
 msgstr "kommandorad avslutas med \\"
@@ -759,19 +770,25 @@
 
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "okänt alternativ för whitespace: \"%s\""
+msgstr "okänt alternativ för whitespace: ”%s”"
 
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "okänt alternativ för ignore-whitespace: \"%s\""
+msgstr "okänt alternativ för ignore-whitespace: ”%s”"
 
 #, c-format
 msgid "options '%s' and '%s' cannot be used together"
-msgstr "flaggorna \"%s\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s” och ”%s” kan inte användas samtidigt"
 
 #, c-format
 msgid "'%s' outside a repository"
-msgstr "\"%s\" utanför arkiv"
+msgstr "”%s” utanför arkiv"
+
+msgid "failed to read patch"
+msgstr "misslyckades läsa patchen"
+
+msgid "patch too large"
+msgstr "patchen är för stor"
 
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
@@ -880,7 +897,7 @@
 
 #, c-format
 msgid "invalid start of line: '%c'"
-msgstr "felaktig inledning på rad: \"%c\""
+msgstr "felaktig inledning på rad: ”%c”"
 
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
@@ -902,41 +919,38 @@
 
 #, c-format
 msgid "missing binary patch data for '%s'"
-msgstr "saknar binära patchdata för \"%s\""
+msgstr "saknar binära patchdata för ”%s”"
 
 #, c-format
 msgid "cannot reverse-apply a binary patch without the reverse hunk to '%s'"
 msgstr ""
-"kan inte applicera en binärpatch baklänges utan den omvända patchen för \"%s"
-"\""
+"kan inte applicera en binärpatch baklänges utan den omvända patchen för ”%s”"
 
 #, c-format
 msgid "cannot apply binary patch to '%s' without full index line"
-msgstr ""
-"kan inte applicera binärpatch på \"%s\" utan den fullständiga indexraden"
+msgstr "kan inte applicera binärpatch på ”%s” utan den fullständiga indexraden"
 
 #, c-format
 msgid ""
 "the patch applies to '%s' (%s), which does not match the current contents."
 msgstr ""
-"patchen appliceras på \"%s\" (%s), som inte motsvarar det nuvarande "
-"innehållet."
+"patchen appliceras på ”%s” (%s), som inte motsvarar det nuvarande innehållet."
 
 #, c-format
 msgid "the patch applies to an empty '%s' but it is not empty"
-msgstr "patchen appliceras på en tom \"%s\", men den är inte tom"
+msgstr "patchen appliceras på en tom ”%s”, men den är inte tom"
 
 #, c-format
 msgid "the necessary postimage %s for '%s' cannot be read"
-msgstr "nödvändig efterbild %s för \"%s\" kan inte läsas"
+msgstr "nödvändig efterbild %s för ”%s” kan inte läsas"
 
 #, c-format
 msgid "binary patch does not apply to '%s'"
-msgstr "binärpatchen kan inte tillämpas på \"%s\""
+msgstr "binärpatchen kan inte tillämpas på ”%s”"
 
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
-msgstr "binärpatchen på \"%s\" ger felaktigt resultat (förväntade %s, fick %s)"
+msgstr "binärpatchen på ”%s” ger felaktigt resultat (förväntade %s, fick %s)"
 
 #, c-format
 msgid "patch failed: %s:%ld"
@@ -952,7 +966,7 @@
 
 #, c-format
 msgid "reading from '%s' beyond a symbolic link"
-msgstr "läser från \"%s\" som är på andra sidan av en symbolisk länk"
+msgstr "läser från ”%s” som är på andra sidan av en symbolisk länk"
 
 #, c-format
 msgid "path %s has been renamed/deleted"
@@ -975,7 +989,7 @@
 
 #, c-format
 msgid "cannot read the current contents of '%s'"
-msgstr "kunde inte läsa aktuellt innehåll i \"%s\""
+msgstr "kunde inte läsa aktuellt innehåll i ”%s”"
 
 #, c-format
 msgid "Failed to perform three-way merge...\n"
@@ -983,11 +997,11 @@
 
 #, c-format
 msgid "Applied patch to '%s' with conflicts.\n"
-msgstr "Applicerade patchen på \"%s\" med konflikter.\n"
+msgstr "Applicerade patchen på ”%s” med konflikter.\n"
 
 #, c-format
 msgid "Applied patch to '%s' cleanly.\n"
-msgstr "Tillämpade patchen på  \"%s\" rent.\n"
+msgstr "Tillämpade patchen på  ”%s” rent.\n"
 
 #, c-format
 msgid "Falling back to direct application...\n"
@@ -1006,7 +1020,7 @@
 
 #, c-format
 msgid "invalid path '%s'"
-msgstr "ogiltig sökväg \"%s\""
+msgstr "ogiltig sökväg ”%s”"
 
 #, c-format
 msgid "%s: already exists in index"
@@ -1026,7 +1040,7 @@
 
 #, c-format
 msgid "affected file '%s' is beyond a symbolic link"
-msgstr "den berörda filen \"%s\" är på andra sidan av en symbolisk länk"
+msgstr "den berörda filen ”%s” är på andra sidan av en symbolisk länk"
 
 #, c-format
 msgid "%s: patch does not apply"
@@ -1066,7 +1080,7 @@
 
 #, c-format
 msgid "unable to stat newly created file '%s'"
-msgstr "kan inte ta status på nyligen skapade filen \"%s\""
+msgstr "kan inte ta status på nyligen skapade filen ”%s”"
 
 #, c-format
 msgid "unable to create backing store for newly created file %s"
@@ -1078,15 +1092,15 @@
 
 #, c-format
 msgid "failed to write to '%s'"
-msgstr "misslyckades skriva till \"%s\""
+msgstr "misslyckades skriva till ”%s”"
 
 #, c-format
 msgid "closing file '%s'"
-msgstr "stänger filen \"%s\""
+msgstr "stänger filen ”%s”"
 
 #, c-format
 msgid "unable to write file '%s' mode %o"
-msgstr "kan inte skriva filen \"%s\" läge %o"
+msgstr "kan inte skriva filen ”%s” läge %o"
 
 #, c-format
 msgid "Applied patch %s cleanly."
@@ -1110,6 +1124,10 @@
 msgstr "kan inte öppna %s"
 
 #, c-format
+msgid "cannot unlink '%s'"
+msgstr "kan inte ta bort länken ”%s”"
+
+#, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Stycke %d tillämpades rent."
 
@@ -1119,17 +1137,17 @@
 
 #, c-format
 msgid "Skipped patch '%s'."
-msgstr "Ignorerar patch \"%s\"."
+msgstr "Ignorerar patch ”%s”."
 
 msgid "No valid patches in input (allow with \"--allow-empty\")"
-msgstr "Inga giltiga patchar i indata (tillåt med \"--allow-empty\")"
+msgstr "Inga giltiga patchar i indata (tillåt med ”--allow-empty”)"
 
 msgid "unable to read index file"
 msgstr "kan inte läsa indexfilen"
 
 #, c-format
 msgid "can't open patch '%s': %s"
-msgstr "kan inte öppna patchen \"%s\": %s"
+msgstr "kan inte öppna patchen ”%s”: %s"
 
 #, c-format
 msgid "squelched %d whitespace error"
@@ -1183,7 +1201,7 @@
 msgstr "se till att patchen kan tillämpas på aktuellt index"
 
 msgid "mark new files with `git add --intent-to-add`"
-msgstr "markera nya filer med \"git add --intent-to-add\""
+msgstr "markera nya filer med ”git add --intent-to-add”"
 
 msgid "apply a patch without touching the working tree"
 msgstr "tillämpa en patch utan att röra arbetskatalogen"
@@ -1258,14 +1276,14 @@
 
 #, c-format
 msgid "unable to start '%s' filter"
-msgstr "kunde inte starta filtret \"%s\""
+msgstr "kunde inte starta filtret ”%s”"
 
 msgid "unable to redirect descriptor"
 msgstr "kan inte omdirigera handtag"
 
 #, c-format
 msgid "'%s' filter reported error"
-msgstr "filtret \"%s\" rapporterade fel"
+msgstr "filtret ”%s” rapporterade fel"
 
 #, c-format
 msgid "path is not valid UTF-8: %s"
@@ -1293,11 +1311,15 @@
 
 #, c-format
 msgid "cannot read '%s'"
-msgstr "kunde inte läsa \"%s\""
+msgstr "kunde inte läsa ”%s”"
+
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "sökvägsangivelsen ”%s” motsvarar filer utanför aktuell katalog"
 
 #, c-format
 msgid "pathspec '%s' did not match any files"
-msgstr "sökvägsangivelsen \"%s\" motsvarade inte några filer"
+msgstr "sökvägsangivelsen ”%s” motsvarade inte några filer"
 
 #, c-format
 msgid "no such ref: %.*s"
@@ -1311,9 +1333,6 @@
 msgid "not a tree object: %s"
 msgstr "inte ett trädobjekt: %s"
 
-msgid "current working directory is untracked"
-msgstr "aktuell arbetskatalog är inte spårad"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Hittar inte filen: %s"
@@ -1324,15 +1343,15 @@
 
 #, c-format
 msgid "unclosed quote: '%s'"
-msgstr "citat ej stängt: \"%s\""
+msgstr "citat ej stängt: ”%s”"
 
 #, c-format
 msgid "missing colon: '%s'"
-msgstr "kolon saknas: \"%s\""
+msgstr "kolon saknas: ”%s”"
 
 #, c-format
 msgid "empty file name: '%s'"
-msgstr "tomt filnamn: \"%s\""
+msgstr "tomt filnamn: ”%s”"
 
 msgid "fmt"
 msgstr "fmt"
@@ -1393,18 +1412,22 @@
 
 #, c-format
 msgid "the option '%s' requires '%s'"
-msgstr "flaggan \"%s\" kräver \"%s\""
+msgstr "flaggan ”%s” kräver ”%s”"
 
 msgid "Unexpected option --output"
 msgstr "Oväntad flagga --output"
 
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "falsk konfigureringsparameter: ”%s”"
+
+#, c-format
 msgid "Unknown archive format '%s'"
-msgstr "Okänt arkivformat \"%s\""
+msgstr "Okänt arkivformat ”%s”"
 
 #, c-format
 msgid "Argument not supported for format '%s': -%d"
-msgstr "Argumentet stöd inte för formatet \"%s\": -%d"
+msgstr "Argumentet stöd inte för formatet ”%s”: -%d"
 
 #, c-format
 msgid "%.*s is not a valid attribute name"
@@ -1430,23 +1453,34 @@
 
 #, c-format
 msgid "cannot fstat gitattributes file '%s'"
-msgstr "kan inte utföra fstat på gitattributes-filen \"%s\""
+msgstr "kan inte utföra fstat på gitattributes-filen ”%s”"
 
 #, c-format
 msgid "ignoring overly large gitattributes file '%s'"
-msgstr "ignorerar allt för stor gitattributes-fil \"%s\""
+msgstr "ignorerar allt för stor gitattributes-fil ”%s”"
 
 #, c-format
 msgid "ignoring overly large gitattributes blob '%s'"
-msgstr "ignorerar allt för stor gitattributes-objekt \"%s\""
+msgstr "ignorerar allt för stor gitattributes-objekt ”%s”"
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "felaktig --attr-source eller GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "kan inte ta status på ”%s”"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "kunde inte läsa %s"
 
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
-msgstr "Felaktigt citerat innehåll i filen \"%s\": %s"
+msgstr "Felaktigt citerat innehåll i filen ”%s”: %s"
 
 #, c-format
 msgid "We cannot bisect more!\n"
-msgstr "Det finns inte mer att göra \"bisect\" på!\n"
+msgstr "Det finns inte mer att göra ”bisect” på!\n"
 
 #, c-format
 msgid "Not a valid commit name %s"
@@ -1474,7 +1508,7 @@
 "This means the first '%s' commit is between %s and [%s].\n"
 msgstr ""
 "Sammanslagningsbasen %s är %s.\n"
-"Det betyder att den första \"%s\" incheckningen är mellan %s och [%s].\n"
+"Det betyder att den första ”%s” incheckningen är mellan %s och [%s].\n"
 
 #, c-format
 msgid ""
@@ -1507,11 +1541,11 @@
 
 #, c-format
 msgid "could not create file '%s'"
-msgstr "kunde inte skapa filen \"%s\""
+msgstr "kunde inte skapa filen ”%s”"
 
 #, c-format
 msgid "could not read file '%s'"
-msgstr "kunde inte läsa filen \"%s\""
+msgstr "kunde inte läsa filen ”%s”"
 
 msgid "reading bisect refs failed"
 msgstr "misslyckades läsa bisect-referenser"
@@ -1546,9 +1580,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents och --reverse fungerar inte så bra tillsammans."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "kan inte använda --contents med namn på slutgiltigt incheckningsobjekt"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse och --first-parent tillsammans kräver att du anger senaste "
@@ -1584,15 +1615,15 @@
 
 #, c-format
 msgid "branch '%s' set up to track '%s' by rebasing."
-msgstr "grenen \"%s\" inställd på att spåra \"%s\" genom ombasering."
+msgstr "grenen ”%s” inställd på att spåra ”%s” genom ombasering."
 
 #, c-format
 msgid "branch '%s' set up to track '%s'."
-msgstr "grenen \"%s\" inställd på att spåra \"%s\"."
+msgstr "grenen ”%s” inställd på att spåra ”%s”."
 
 #, c-format
 msgid "branch '%s' set up to track:"
-msgstr "grenen \"%s\" inställd på att spåra:"
+msgstr "grenen ”%s” inställd på att spåra:"
 
 msgid "unable to write upstream branch configuration"
 msgstr "kan inte skriva inställningar för uppströmsgren"
@@ -1608,17 +1639,17 @@
 
 #, c-format
 msgid "asked to inherit tracking from '%s', but no remote is set"
-msgstr "bad om att ärva spårning från \"%s\", men ingen fjärr är vald"
+msgstr "bad om att ärva spårning från ”%s”, men ingen fjärr är vald"
 
 #, c-format
 msgid "asked to inherit tracking from '%s', but no merge configuration is set"
 msgstr ""
-"bad om att ärva spårning från \"%s\", men ingen sammanslagningsinställning "
-"är vald"
+"bad om att ärva spårning från ”%s”, men ingen sammanslagningsinställning är "
+"vald"
 
 #, c-format
 msgid "not tracking: ambiguous information for ref '%s'"
-msgstr "spårar inte: tvetydig information för referensen \"%s\""
+msgstr "spårar inte: tvetydig information för referensen ”%s”"
 
 #. #-#-#-#-#  branch.c.po  #-#-#-#-#
 #. TRANSLATORS: This is a line listing a remote with duplicate
@@ -1650,7 +1681,7 @@
 "tracking namespaces."
 msgstr ""
 "Flera fjärrars hämtnings-referensspecifikationer motsvarar fjärr-\n"
-"spårningsreferensen \"%s\":\n"
+"spårningsreferensen ”%s”:\n"
 "%s\n"
 "Detta är vanligtvis ett fel i konfigurationen.\n"
 "\n"
@@ -1660,25 +1691,26 @@
 
 #, c-format
 msgid "'%s' is not a valid branch name"
-msgstr "\"%s\" är inte ett giltigt grennamn"
+msgstr "”%s” är inte ett giltigt grennamn"
 
 #, c-format
 msgid "a branch named '%s' already exists"
-msgstr "det finns redan en gren som heter \"%s\""
+msgstr "det finns redan en gren som heter ”%s”"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
 msgstr ""
-"kan inte tvinga uppdatering av grenen \"%s\" som är utcheckad på \"%s\""
+"kan inte tvinga uppdatering av grenen ”%s” som används av arbetskatalogen på "
+"”%s”"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
 msgstr ""
-"kan inte ställa in spårningsinformation; startpunkten \"%s\" är inte en gren"
+"kan inte ställa in spårningsinformation; startpunkten ”%s” är inte en gren"
 
 #, c-format
 msgid "the requested upstream branch '%s' does not exist"
-msgstr "den efterfrågade uppströmsgrenen \"%s\" finns inte"
+msgstr "den efterfrågade uppströmsgrenen ”%s” finns inte"
 
 msgid ""
 "\n"
@@ -1692,66 +1724,51 @@
 msgstr ""
 "\n"
 "Om du har tänkt basera ditt arbete på en uppströmsgren\n"
-"som redan finns på fjärren kan du behöva köra \"git fetch\"\n"
+"som redan finns på fjärren kan du behöva köra ”git fetch”\n"
 "för att hämta den.\n"
 "\n"
 "Om du har tänkt sända in en ny lokal gren som ska\n"
-"spåra dess fjärrmotsvarighet kan du använda \"git push -u\"\n"
+"spåra dess fjärrmotsvarighet kan du använda ”git push -u”\n"
 "för att ställa in uppströmskonfigurationen när du sänder in."
 
 #, c-format
 msgid "not a valid object name: '%s'"
-msgstr "objektnamnet är inte giltigt: \"%s\""
+msgstr "objektnamnet är inte giltigt: ”%s”"
 
 #, c-format
 msgid "ambiguous object name: '%s'"
-msgstr "objektnamnet är tvetydigt: \"%s\""
+msgstr "objektnamnet är tvetydigt: ”%s”"
 
 #, c-format
 msgid "not a valid branch point: '%s'"
-msgstr "avgreningspunkten är inte giltig: \"%s\""
+msgstr "avgreningspunkten är inte giltig: ”%s”"
 
 #, c-format
 msgid "submodule '%s': unable to find submodule"
-msgstr "undermodulen \"%s\": kan inte hitta undermodulen"
+msgstr "undermodulen ”%s”: kan inte hitta undermodulen"
 
 #, c-format
 msgid ""
 "You may try updating the submodules using 'git checkout --no-recurse-"
 "submodules %s && git submodule update --init'"
 msgstr ""
-"Du kan försöka uppdatera undermodulerna med \"git checkout --no-recurse-"
-"submodules %s && git submodule update --init\""
+"Du kan försöka uppdatera undermodulerna med ”git checkout --no-recurse-"
+"submodules %s && git submodule update --init”"
 
 #, c-format
 msgid "submodule '%s': cannot create branch '%s'"
-msgstr "undermodulen \"%s\": kan inte skapa grenen \"%s\""
+msgstr "undermodulen ”%s”: kan inte skapa grenen ”%s”"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "\"%s\" är redan utcheckad på \"%s\""
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "HEAD i arbetskatalogen %s har inte uppdaterats"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "”%s” används redan av arbetskatalogen ”%s”"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<flaggor>] [--] <sökväg>..."
 
 #, c-format
 msgid "cannot chmod %cx '%s'"
-msgstr "kan inte utföra chmod %cx \"%s\""
-
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "diff-status %c förväntades inte"
-
-msgid "updating files failed"
-msgstr "misslyckades uppdatera filer"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "ta bort \"%s\"\n"
+msgstr "kan inte utföra chmod %cx ”%s”"
 
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Oköade ändringar efter att ha uppdaterat indexet:"
@@ -1761,27 +1778,24 @@
 "See its entry in 'git help config' for details."
 msgstr ""
 "inställningen add.interactive.useBuiltin har tagits bort!\n"
-"Se dess post i \"git help config\" för detaljer."
+"Se dess post i ”git help config” för detaljer."
 
-msgid "Could not read the index"
-msgstr "Kunde inte läsa indexet"
-
-msgid "Could not write patch"
-msgstr "Kunde inte skriva patch"
+msgid "could not read the index"
+msgstr "kunde inte läsa indexet"
 
 msgid "editing patch failed"
 msgstr "redigering av patch misslyckades"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "Kunde inte ta status på \"%s\""
+msgid "could not stat '%s'"
+msgstr "kunde inte ta status på ”%s”"
 
-msgid "Empty patch. Aborted."
-msgstr "Tom patch. Avbryter."
+msgid "empty patch. aborted"
+msgstr "tom patch. avbryter"
 
 #, c-format
-msgid "Could not apply '%s'"
-msgstr "Kunde inte tillämpa \"%s\""
+msgid "could not apply '%s'"
+msgstr "kunde inte tillämpa ”%s”"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr "Följande sökvägar ignoreras av en av dina .gitignore-filer:\n"
@@ -1829,7 +1843,7 @@
 msgstr "se om - även saknade - filer ignoreras i testkörning"
 
 msgid "allow updating entries outside of the sparse-checkout cone"
-msgstr "tillåt uppdatera poster utanför området angivet i \"sparse-checkout\""
+msgstr "tillåt uppdatera poster utanför området angivet i ”sparse-checkout”"
 
 msgid "override the executable bit of the listed files"
 msgstr "överstyr exekveringsbiten för angivna filer"
@@ -1865,7 +1879,7 @@
 "\n"
 "\tgit rm --cached %s\n"
 "\n"
-"Se \"git help submodule\" för ytterligare information."
+"Se ”git help submodule” för ytterligare information."
 
 #, c-format
 msgid "adding embedded git repository: %s"
@@ -1878,18 +1892,18 @@
 msgstr ""
 "Använd -f om du verkligen vill lägga till dem.\n"
 "Slå av detta meddelande med\n"
-"\"git config advice.addIgnoredFile false\""
+"”git config advice.addIgnoredFile false”"
 
 msgid "adding files failed"
 msgstr "misslyckades lägga till filer"
 
 #, c-format
 msgid "--chmod param '%s' must be either -x or +x"
-msgstr "\"--chmod\"-parametern \"%s\" måste antingen vara -x eller +x"
+msgstr "”--chmod”-parametern ”%s” måste antingen vara -x eller +x"
 
 #, c-format
 msgid "'%s' and pathspec arguments cannot be used together"
-msgstr "\"%s\" kan inte användas tillsammans med sökvägsangivelser"
+msgstr "”%s” kan inte användas tillsammans med sökvägsangivelser"
 
 #, c-format
 msgid "Nothing specified, nothing added.\n"
@@ -1900,24 +1914,27 @@
 "Turn this message off by running\n"
 "\"git config advice.addEmptyPathspec false\""
 msgstr ""
-"Tänkte du kanske säga \"git add .\"?\n"
+"Tänkte du kanske säga ”git add .”?\n"
 "Slå av detta meddelande genom att köra\n"
-"\"git config advice.addEmptyPathspec false\""
+"”git config advice.addEmptyPathspec false”"
 
 msgid "index file corrupt"
 msgstr "indexfilen trasig"
 
+msgid "unable to write new index file"
+msgstr "kunde inte skriva ny indexfil"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
-msgstr "felaktig funktion \"%s\" för \"%s\""
+msgstr "felaktig funktion ”%s” för ”%s”"
 
 #, c-format
 msgid "invalid value for '%s': '%s'"
-msgstr "felaktigt värde för \"%s\": \"%s\""
+msgstr "felaktigt värde för ”%s”: ”%s”"
 
 #, c-format
 msgid "could not read '%s'"
-msgstr "kunde inte läsa \"%s\""
+msgstr "kunde inte läsa ”%s”"
 
 msgid "could not parse author script"
 msgstr "kunde inte tolka författarskript"
@@ -1928,30 +1945,30 @@
 
 #, c-format
 msgid "'%s' was deleted by the applypatch-msg hook"
-msgstr "\"%s\" togs bort av kroken applypatch-msg"
+msgstr "”%s” togs bort av kroken applypatch-msg"
 
 #, c-format
 msgid "Malformed input line: '%s'."
-msgstr "Felaktig indatarad: \"%s\"."
+msgstr "Felaktig indatarad: ”%s”."
 
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
-msgstr "Misslyckades kopiera anteckningar från \"%s\" till \"%s\""
+msgstr "Misslyckades kopiera anteckningar från ”%s” till ”%s”"
 
 msgid "fseek failed"
-msgstr "\"fseek\" misslyckades"
+msgstr "”fseek” misslyckades"
 
 #, c-format
 msgid "could not open '%s' for reading"
-msgstr "kunde inte öppna \"%s\" för läsning"
+msgstr "kunde inte öppna ”%s” för läsning"
 
 #, c-format
 msgid "could not open '%s' for writing"
-msgstr "kunde inte öppna \"%s\" för skrivning"
+msgstr "kunde inte öppna ”%s” för skrivning"
 
 #, c-format
 msgid "could not parse patch '%s'"
-msgstr "kunde inte tolka patchen \"%s\""
+msgstr "kunde inte tolka patchen ”%s”"
 
 msgid "Only one StGIT patch series can be applied at once"
 msgstr "Endast en StGIT-patchserie kan tillämpas åt gången"
@@ -1960,7 +1977,7 @@
 msgstr "ogiltig tidsstämpel"
 
 msgid "invalid Date line"
-msgstr "ogiltig \"Date\"-rad"
+msgstr "ogiltig ”Date”-rad"
 
 msgid "invalid timezone offset"
 msgstr "ogiltig tidszons-offset"
@@ -1970,29 +1987,29 @@
 
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr "misslyckades skapa katalogen \"%s\""
+msgstr "misslyckades skapa katalogen ”%s”"
 
 msgid "Failed to split patches."
 msgstr "Misslyckades dela patchar."
 
 #, c-format
 msgid "When you have resolved this problem, run \"%s --continue\"."
-msgstr "När du har löst problemet, kör \"%s --continue\"."
+msgstr "När du har löst problemet, kör ”%s --continue”."
 
 #, c-format
 msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
-msgstr "Om du hellre vill hoppa över patchen, kör \"%s --skip\" i stället."
+msgstr "Om du hellre vill hoppa över patchen, kör ”%s --skip” i stället."
 
 #, c-format
 msgid "To record the empty patch as an empty commit, run \"%s --allow-empty\"."
 msgstr ""
-"För att registrera den tomma patchen som en tom incheckning, kör \"%s --"
-"allow-empty\"."
+"För att registrera den tomma patchen som en tom incheckning, kör ”%s --allow-"
+"empty”."
 
 #, c-format
 msgid "To restore the original branch and stop patching, run \"%s --abort\"."
 msgstr ""
-"För att återgå till ursprunglig gren och sluta patcha, kör \"%s --abort\"."
+"För att återgå till ursprunglig gren och sluta patcha, kör ”%s --abort”."
 
 msgid "Patch sent with format=flowed; space at the end of lines might be lost."
 msgstr ""
@@ -2000,7 +2017,7 @@
 
 #, c-format
 msgid "missing author line in commit %s"
-msgstr "saknad \"author\"-rad i incheckningen %s"
+msgstr "saknad ”author”-rad i incheckningen %s"
 
 #, c-format
 msgid "invalid ident line: %.*s"
@@ -2087,8 +2104,7 @@
 
 msgid "Use 'git am --show-current-patch=diff' to see the failed patch"
 msgstr ""
-"Använd \"git am --show-current-patch=diff\" för att se patchen som "
-"misslyckades"
+"Använd ”git am --show-current-patch=diff” för att se patchen som misslyckades"
 
 msgid "No changes - recorded it as an empty commit."
 msgstr "Inga ändringar - sparat som en tom incheckning."
@@ -2098,7 +2114,7 @@
 "If there is nothing left to stage, chances are that something else\n"
 "already introduced the same changes; you might want to skip this patch."
 msgstr ""
-"Inga ändringar - glömde du att använda \"git add\"?\n"
+"Inga ändringar - glömde du att använda ”git add”?\n"
 "Om det inte är något kvar att köa kan det hända att något annat redan\n"
 "introducerat samma ändringar; kanske du bör hoppa över patchen."
 
@@ -2109,16 +2125,13 @@
 "You might run `git rm` on a file to accept \"deleted by them\" for it."
 msgstr ""
 "Du har fortfarande ej sammanslagna sökvägar i indexet.\n"
-"Du bör köra \"git add\" på filer med lösta konflikter för att ange dem som "
+"Du bör köra ”git add” på filer med lösta konflikter för att ange dem som "
 "lösta.\n"
-"Du kan köra \"git rm\" för att godta \"borttagen av dem\" för den."
-
-msgid "unable to write new index file"
-msgstr "kunde inte skriva ny indexfil"
+"Du kan köra ”git rm” för att godta ”borttagen av dem” för den."
 
 #, c-format
 msgid "Could not parse object '%s'."
-msgstr "Kan inte tolka objektet \"%s\"."
+msgstr "Kan inte tolka objektet ”%s”."
 
 msgid "failed to clean index"
 msgstr "misslyckades städa upp indexet"
@@ -2127,16 +2140,12 @@
 "You seem to have moved HEAD since the last 'am' failure.\n"
 "Not rewinding to ORIG_HEAD"
 msgstr ""
-"Du verkar ha flyttat HEAD sedan \"am\" sist misslyckades.\n"
+"Du verkar ha flyttat HEAD sedan ”am” sist misslyckades.\n"
 "Återställer inte till ORIG_HEAD"
 
 #, c-format
 msgid "failed to read '%s'"
-msgstr "misslyckades läsa \"%s\""
-
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "flaggorna \"%s=%s\" och \"%s=%s\" kan inte användas samtidigt"
+msgstr "misslyckades läsa ”%s”"
 
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<flaggor>] [(<mbox> | <Maildir>)...]"
@@ -2160,7 +2169,7 @@
 msgstr "var tyst"
 
 msgid "add a Signed-off-by trailer to the commit message"
-msgstr "lägg till \"Signed-off-by\"-släprad i incheckningsmeddelandet"
+msgstr "lägg till ”Signed-off-by”-släprad i incheckningsmeddelandet"
 
 msgid "recode into utf8 (default)"
 msgstr "koda om till utf8 (standard)"
@@ -2177,9 +2186,6 @@
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "sänd flaggan --keep-cr till git-mailsplit för mbox-formatet"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "sänd inte flaggan --keep-cr till git-mailsplit oberoende av am.keepcr"
-
 msgid "strip everything before a scissors line"
 msgstr "ta bort allting före en saxlinje"
 
@@ -2260,7 +2266,7 @@
 "Use \"git am --abort\" to remove it."
 msgstr ""
 "Kvarbliven katalog %s hittades.\n"
-"Använd \"git am --abort\" för att ta bort den."
+"Använd ”git am --abort” för att ta bort den."
 
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "Lösningsoperation pågår inte, vi återupptar inte."
@@ -2291,10 +2297,10 @@
 msgstr "git archive: förväntade en tömning (flush)"
 
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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-"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
 "checkout] [--first-parent] [<dålig> [<bra>...]] [--]    [<sökvägar>...]"
 
 msgid "git bisect (good|bad) [<rev>...]"
@@ -2309,32 +2315,32 @@
 msgid "git bisect replay <logfile>"
 msgstr "git bisect replay <loggfil>"
 
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <kommando>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <kommando> [<argument>]..."
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
-msgstr "kan inte kopiera filen \"%s\" i läget \"%s\""
+msgstr "kan inte kopiera filen ”%s” i läget ”%s”"
 
 #, c-format
 msgid "could not write to file '%s'"
-msgstr "kunde inte skriva till filen \"%s\""
+msgstr "kunde inte skriva till filen ”%s”"
 
 #, c-format
 msgid "cannot open file '%s' for reading"
-msgstr "kan inte öppna filen \"%s\" för läsning"
+msgstr "kan inte öppna filen ”%s” för läsning"
 
 #, c-format
 msgid "'%s' is not a valid term"
-msgstr "\"%s\" är inte en giltig term"
+msgstr "”%s” är inte en giltig term"
 
 #, c-format
 msgid "can't use the builtin command '%s' as a term"
-msgstr "kan inte använda det inbyggda kommandot \"%s\" som term"
+msgstr "kan inte använda det inbyggda kommandot ”%s” som term"
 
 #, c-format
 msgid "can't change the meaning of the term '%s'"
-msgstr "kan inte ändra betydelsen av termen \"%s\""
+msgstr "kan inte ändra betydelsen av termen ”%s”"
 
 msgid "please use two different terms"
 msgstr "termerna måste vara olika"
@@ -2345,14 +2351,14 @@
 
 #, c-format
 msgid "'%s' is not a valid commit"
-msgstr "\"%s\" är inte en giltig incheckning"
+msgstr "”%s” är inte en giltig incheckning"
 
 #, c-format
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr ""
-"Kunde inte checka ut original-HEAD \"%s\". Försök \"git bisect reset "
-"<incheckning>\"."
+"Kunde inte checka ut original-HEAD ”%s”. Försök ”git bisect reset "
+"<incheckning>”."
 
 #, c-format
 msgid "Bad bisect_write argument: %s"
@@ -2360,15 +2366,15 @@
 
 #, c-format
 msgid "couldn't get the oid of the rev '%s'"
-msgstr "kan inte läsa oid för referensen \"%s\""
+msgstr "kan inte läsa oid för referensen ”%s”"
 
 #, c-format
 msgid "couldn't open the file '%s'"
-msgstr "kunde inte öppna filen \"%s\""
+msgstr "kunde inte öppna filen ”%s”"
 
 #, 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 ""
@@ -2376,7 +2382,7 @@
 "You can use \"git bisect %s\" and \"git bisect %s\" for that."
 msgstr ""
 "Du måste 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 ""
@@ -2384,9 +2390,9 @@
 "You then need to give me at least one %s and %s revision.\n"
 "You can use \"git bisect %s\" and \"git bisect %s\" for that."
 msgstr ""
-"Du måste starta med \"git bisect start\".\n"
+"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"
@@ -2427,7 +2433,7 @@
 "invalid argument %s for 'git bisect terms'.\n"
 "Supported options are: --term-good|--term-old and --term-bad|--term-new."
 msgstr ""
-"ogiltigt argument %s för \"git bisect terms\".\n"
+"ogiltigt argument %s för ”git bisect terms”.\n"
 "Flaggor som stöds är: --term-good|--term-old och --term-bad|--term-new."
 
 msgid "revision walk setup failed\n"
@@ -2435,10 +2441,10 @@
 
 #, c-format
 msgid "could not open '%s' for appending"
-msgstr "kunde inte öppna \"%s\" för tillägg"
+msgstr "kunde inte öppna ”%s” för tillägg"
 
 msgid "'' is not a valid term"
-msgstr "\"\" är inte en giltig term"
+msgstr "”” är inte en giltig term"
 
 #, c-format
 msgid "unrecognized option: '%s'"
@@ -2446,25 +2452,24 @@
 
 #, c-format
 msgid "'%s' does not appear to be a valid revision"
-msgstr "\"%s\" verkar inte vara en giltig revision"
+msgstr "”%s” verkar inte vara en giltig revision"
 
 msgid "bad HEAD - I need a HEAD"
 msgstr "felaktigt HEAD - Jag behöver ett HEAD"
 
 #, c-format
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
-msgstr ""
-"misslyckades checka ut \"%s\". Försök \"git bisect reset <giltig_gren>\"."
+msgstr "misslyckades checka ut ”%s”. Försök ”git bisect reset <giltig_gren>”."
 
 msgid "bad HEAD - strange symbolic ref"
 msgstr "felaktigt HEAD - konstig symbolisk referens"
 
 #, c-format
 msgid "invalid ref: '%s'"
-msgstr "ogiltig referens: \"%s\""
+msgstr "ogiltig referens: ”%s”"
 
 msgid "You need to start by \"git bisect start\"\n"
-msgstr "Du måste starta med \"git bisect start\"\n"
+msgstr "Du måste starta med ”git bisect start”\n"
 
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
@@ -2474,11 +2479,11 @@
 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."
-msgstr "\"git bisect %s\" kan bara ta ett argument."
+msgstr "”git bisect %s” kan bara ta ett argument."
 
 #, c-format
 msgid "Bad rev input: %s"
@@ -2493,11 +2498,11 @@
 
 #, c-format
 msgid "'%s'?? what are you talking about?"
-msgstr "\"%s\"?? vad menar du?"
+msgstr "”%s”?? vad menar du?"
 
 #, c-format
 msgid "cannot read file '%s' for replaying"
-msgstr "kan inte läsa filen \"%s\" för återuppspelning"
+msgstr "kan inte läsa filen ”%s” för återuppspelning"
 
 #, c-format
 msgid "running %s\n"
@@ -2516,18 +2521,17 @@
 
 #, c-format
 msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
-msgstr ""
-"\"bisect\"-körningen misslyckades: felkod %d från %s är < 0 eller >= 128"
+msgstr "”bisect”-körningen misslyckades: felkod %d från %s är < 0 eller >= 128"
 
 #, c-format
 msgid "cannot open file '%s' for writing"
-msgstr "kan inte öppna \"%s\" för skrivning"
+msgstr "kan inte öppna ”%s” för skrivning"
 
 msgid "bisect run cannot continue any more"
-msgstr "\"bisect\"-körningen kan inte fortsätta längre"
+msgstr "”bisect”-körningen kan inte fortsätta längre"
 
 msgid "bisect run success"
-msgstr "\"bisect\"-körningen lyckades"
+msgstr "”bisect”-körningen lyckades"
 
 msgid "bisect found first bad commit"
 msgstr "bisect hittade första trasiga incheckning"
@@ -2535,34 +2539,33 @@
 #, c-format
 msgid "bisect run failed: 'git bisect %s' exited with error code %d"
 msgstr ""
-"\"bisect\"-körningen misslyckades: \"git bisect %s\" avslutades med felkoden "
-"%d"
+"”bisect”-körningen misslyckades: ”git bisect %s” avslutades med felkoden %d"
 
 #, c-format
 msgid "'%s' requires either no argument or a commit"
-msgstr "\"%s\" kräver antingen inget argument eller en incheckning"
+msgstr "”%s” kräver antingen inget argument eller en incheckning"
 
 #, c-format
 msgid "'%s' requires 0 or 1 argument"
-msgstr "\"%s\" kräver noll eller ett argument"
+msgstr "”%s” kräver noll eller ett argument"
 
 #, c-format
 msgid "'%s' requires 0 arguments"
-msgstr "\"%s\" kräver noll argument"
+msgstr "”%s” kräver noll argument"
 
 msgid "no logfile given"
 msgstr "ingen loggfil angiven"
 
 #, c-format
 msgid "'%s' failed: no command provided."
-msgstr "\"%s\" misslyckades: inget kommando gavs."
+msgstr "”%s” misslyckades: inget kommando gavs."
 
 msgid "need a command"
 msgstr "behöver ett kommando"
 
 #, c-format
 msgid "unknown command: '%s'"
-msgstr "okänt kommando: \"%s\""
+msgstr "okänt kommando: ”%s”"
 
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 msgstr "git blame [<flaggor>] [<rev-flaggor>] [<rev>] [--] <fil>"
@@ -2591,7 +2594,7 @@
 msgstr "visa inte objektnamn för gränsincheckningar (Standard: av)"
 
 msgid "do not treat root commits as boundaries (Default: off)"
-msgstr "vehandla inte rotincheckningar som gränser (Standard: av)"
+msgstr "behandla inte rotincheckningar som gränser (Standard: av)"
 
 msgid "show work cost statistics"
 msgstr "visa statistik över arbetskostnad"
@@ -2725,48 +2728,56 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"tar bort grenen \"%s\" som har slagits ihop med\n"
-"         \"%s\", men ännu inte slagits ihop med HEAD."
+"tar bort grenen ”%s” som har slagits ihop med\n"
+"         ”%s”, men ännu inte slagits ihop med HEAD"
 
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"tar inte bort grenen \"%s\" som inte har slagits ihop med\n"
-"         \"%s\", trots att den har slagits ihop med HEAD."
+"tar inte bort grenen ”%s” som inte har slagits ihop med\n"
+"         ”%s”, trots att den har slagits ihop med HEAD"
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "Kunde inte slå upp incheckningsobjekt för \"%s\""
+msgid "couldn't look up commit object for '%s'"
+msgstr "kunde inte slå upp incheckningsobjekt för ”%s”"
 
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"Grenen \"%s\" har inte slagits samman i sin helhet.\n"
-"Om du är säker på att du vill ta bort den, kör \"git branch -D %s\"."
+msgid "the branch '%s' is not fully merged"
+msgstr "grenen ”%s” har inte slagits samman i sin helhet"
 
-msgid "Update of config-file failed"
-msgstr "Misslyckades uppdatera konfigurationsfil"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Om du är säker på att du vill ta bort den, kör ”git branch -D %s”"
+
+msgid "update of config-file failed"
+msgstr "misslyckades uppdatera konfigurationsfil"
 
 msgid "cannot use -a with -d"
 msgstr "kan inte ange -a med -d"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "Kan inte ta bort grenen \"%s\" som är utcheckad på \"%s\""
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr "kan inte ta bort grenen ”%s” som används av arbetskatalogen på ”%s”"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "fjärrspårande grenen \"%s\" hittades inte."
+msgid "remote-tracking branch '%s' not found"
+msgstr "fjärrspårande grenen ”%s” hittades inte"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "grenen \"%s\" hittades inte."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"grenen ”%s” hittades inte.\n"
+"Glömde du --remote?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "grenen ”%s” hittades inte"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2787,48 +2798,52 @@
 msgstr "HEAD (%s) pekar utenför refs/heads/"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "Grenen %s ombaseras på %s"
+msgid "branch %s is being rebased at %s"
+msgstr "grenen %s ombaseras på %s"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "Grenen %s är i en \"bisect\" på %s"
+msgid "branch %s is being bisected at %s"
+msgstr "grenen %s är i en ”bisect” på %s"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Felaktigt namn på gren: \"%s\""
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD i arbetskatalogen %s har inte uppdaterats"
 
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "Inga incheckningar på grenen \"%s\" ännu."
+msgid "invalid branch name: '%s'"
+msgstr "gelaktigt namn på gren: ”%s”"
 
 #, c-format
-msgid "No branch named '%s'."
-msgstr "Ingen gren vid namnet \"%s\"."
-
-msgid "Branch rename failed"
-msgstr "Misslyckades byta namn på gren"
-
-msgid "Branch copy failed"
-msgstr "Misslyckades kopiera gren"
+msgid "no commit on branch '%s' yet"
+msgstr "inga incheckningar på grenen ”%s” ännu"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Skapade kopia av felaktigt namngiven gren \"%s\""
+msgid "no branch named '%s'"
+msgstr "ingen gren vid namnet ”%s”"
+
+msgid "branch rename failed"
+msgstr "misslyckades byta namn på gren"
+
+msgid "branch copy failed"
+msgstr "misslyckades kopiera gren"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Bytte bort namn på en felaktigt namngiven gren \"%s\""
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "skapade kopia av felaktigt namngiven gren ”%s”"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Grenen namnbytt till %s, men HEAD har inte uppdaterats!"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "bytte bort namn på en felaktigt namngiven gren ”%s”"
 
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Grenen namnbytt, men misslyckades uppdatera konfigurationsfilen"
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "grenen namnbytt till %s, men HEAD har inte uppdaterats"
 
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Grenen kopierades, men misslyckades uppdatera konfigurationsfilen"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "grenen namnbytt, men misslyckades uppdatera konfigurationsfilen"
+
+msgid "branch is copied, but update of config-file failed"
+msgstr "grenen kopierades, men misslyckades uppdatera konfigurationsfilen"
 
 #, c-format
 msgid ""
@@ -2838,7 +2853,7 @@
 msgstr ""
 "Redigera beskrivningen för grenen\n"
 "  %s\n"
-"Rader som inleds med \"%c\" ignoreras.\n"
+"Rader som inleds med ”%c” ignoreras.\n"
 
 msgid "Generic options"
 msgstr "Allmänna flaggor"
@@ -2894,6 +2909,9 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "flytta/ta bort en gren, även om målet finns"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "skriv inte ut ett nyradstecken efter tomma formaterade referenser"
+
 msgid "copy a branch and its reflog"
 msgstr "kopiera en gren och dess reflogg"
 
@@ -2939,8 +2957,8 @@
 msgid "format to use for the output"
 msgstr "format att använda för utdata"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Misslyckades slå upp HEAD som giltig referens."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "misslyckades slå upp HEAD som giltig referens"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD hittades inte under refs/heads!"
@@ -2958,18 +2976,17 @@
 msgid "branch name required"
 msgstr "grennamn krävs"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Kan inte beskriva frånkopplad HEAD"
+msgid "cannot give description to detached HEAD"
+msgstr "kan inte beskriva frånkopplad HEAD"
 
 msgid "cannot edit description of more than one branch"
 msgstr "kan inte redigera beskrivning för mer än en gren"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "kunde inte kopiera aktuell gren när du inte befinner dig på någon."
+msgid "cannot copy the current branch while not on any"
+msgstr "kunde inte kopiera aktuell gren när du inte befinner dig på någon"
 
-msgid "cannot rename the current branch while not on any."
-msgstr ""
-"kunde inte byta namn på aktuell gren när du inte befinner dig på någon."
+msgid "cannot rename the current branch while not on any"
+msgstr "kunde inte byta namn på aktuell gren när du inte befinner dig på någon"
 
 msgid "too many branches for a copy operation"
 msgstr "för många grenar för kopiering"
@@ -2982,49 +2999,48 @@
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
-"kunde inte sätta uppström för HEAD till %s när det inte pekar mot någon gren."
+"kunde inte sätta uppström för HEAD till %s när det inte pekar mot någon gren"
 
 #, c-format
 msgid "no such branch '%s'"
-msgstr "okänd gren \"%s\""
+msgstr "okänd gren ”%s”"
 
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "grenen \"%s\" finns inte"
+msgstr "grenen ”%s” finns inte"
 
 msgid "too many arguments to unset upstream"
 msgstr "för många flaggor för att ta bort uppström"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr ""
-"kunde inte ta bort uppström för HEAD när det inte pekar mot någon gren."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "kunde inte ta bort uppström för HEAD när det inte pekar mot någon gren"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "Grenen \"%s\" har ingen uppströmsinformation"
+msgid "branch '%s' has no upstream information"
+msgstr "grenen ”%s” har ingen uppströmsinformation"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"Flaggorna -a och -r på \"git branch\" tar inte ett namn på gren.\n"
+"flaggorna -a och -r på ”git branch” tar inte ett namn på gren.\n"
 "Menade du att använda: -a|-r --list <mönster>?"
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
-"Flaggan --set-upstream rekommenderas ej och kommer tas bort. Använd --track "
-"eller --set-upstream-to istället."
+"flaggan ”--set-upstream” rekommenderas ej och kommer tas bort. Använd ”--"
+"track” eller ”--set-upstream-to” istället"
 
 msgid "git version:\n"
 msgstr "git version:\n"
 
 #, c-format
 msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() misslyckades med felet \"%s\" (%d)\n"
+msgstr "uname() misslyckades med felet ”%s” (%d)\n"
 
 msgid "compiler info: "
 msgstr "kompilatorinfo:"
@@ -3083,8 +3099,7 @@
 msgid ""
 "create an additional zip archive of detailed diagnostics (default 'stats')"
 msgstr ""
-"skapa ett ytterligare zip-arkiv med detaljerad diagnostik (förval är \"stats"
-"\")"
+"skapa ett ytterligare zip-arkiv med detaljerad diagnostik (förval är ”stats”)"
 
 msgid "specify a destination for the bugreport file(s)"
 msgstr "ange mål för buggrapporteringsfilen/-rna"
@@ -3093,8 +3108,12 @@
 msgstr "ange filändelse i strftime-format"
 
 #, c-format
+msgid "unknown argument `%s'"
+msgstr "okänt argument ”%s”"
+
+#, c-format
 msgid "could not create leading directories for '%s'"
-msgstr "kunde inte skapa inledande kataloger för \"%s\""
+msgstr "kunde inte skapa inledande kataloger för ”%s”"
 
 #, c-format
 msgid "unable to create diagnostics archive %s"
@@ -3112,15 +3131,13 @@
 
 #, 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 | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <fil> <git-rev-list-flaggor>"
 
 msgid "git bundle verify [-q | --quiet] <file>"
@@ -3141,11 +3158,11 @@
 msgid "show progress meter"
 msgstr "visa förloppsindikator"
 
-msgid "show progress meter during object writing phase"
-msgstr "visa förloppsindikator under objektskrivningsfasen"
+msgid "historical; same as --progress"
+msgstr "historisk; samma som --progress"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "som --all-progress när förloppsindikatorn visas"
+msgid "historical; does nothing"
+msgstr "historisk flagga; gör ingenting"
 
 msgid "specify bundle format version"
 msgstr "ange formatversion för bunten."
@@ -3168,17 +3185,17 @@
 
 #, c-format
 msgid "cannot read object %s '%s'"
-msgstr "kan inte läsa objektet %s: \"%s\""
+msgstr "kan inte läsa objektet %s: ”%s”"
 
 msgid "flush is only for --buffer mode"
-msgstr "flush är endast till för --buffer-läge"
+msgstr "flush är endast till för ”--buffer”-läge"
 
 msgid "empty command in input"
 msgstr "tomt kommando i indata"
 
 #, c-format
 msgid "whitespace before command: '%s'"
-msgstr "blanksteg före kommando: \"%s\""
+msgstr "blanksteg före kommando: ”%s”"
 
 #, c-format
 msgid "%s requires arguments"
@@ -3201,17 +3218,6 @@
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objekt>"
 
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-
-msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
@@ -3219,6 +3225,17 @@
 "             [<revision>:<sökväg|träd-igt> | --path=<sökväg|träd-igt> "
 "<revision>]"
 
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
 msgid "Check object existence or emit object contents"
 msgstr "Kontrollera om objektet finns eller mata ut objektets innehåll"
 
@@ -3232,7 +3249,7 @@
 msgstr "Skriv ut [trasiga] objektattribut"
 
 msgid "show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"
-msgstr "visa objekttyp (en av: \"blob\", \"tree\", \"commit\", \"tag\", ...)"
+msgstr "visa objekttyp (en av: ”blob”, ”tree”, ”commit”, ”tag”, ...)"
 
 msgid "show object size"
 msgstr "visa objektstorlek"
@@ -3255,6 +3272,9 @@
 msgid "stdin is NUL-terminated"
 msgstr "standard in är NUL-terminerad"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "standard in och standard ut är NUL-terminerade"
+
 msgid "read commands from stdin"
 msgstr "läs kommandon från standard in"
 
@@ -3301,22 +3321,22 @@
 
 #, c-format
 msgid "'%s' requires a batch mode"
-msgstr "\"%s\" behöver ett buntläge"
+msgstr "”%s” behöver ett buntläge"
 
 #, c-format
 msgid "'-%c' is incompatible with batch mode"
-msgstr "\"-%c\" är inkompatibel med buntläge"
+msgstr "”-%c” är inkompatibel med buntläge"
 
 msgid "batch modes take no arguments"
 msgstr "buntlägen inte några argument"
 
 #, c-format
 msgid "<rev> required with '%s'"
-msgstr "<rev> krävs med \"%s\""
+msgstr "<rev> krävs med ”%s”"
 
 #, c-format
 msgid "<object> required with '-%c'"
-msgstr "<objekt> krävs med \"-%c\""
+msgstr "<objekt> krävs med ”-%c”"
 
 #, c-format
 msgid "only two arguments allowed in <type> <object> mode, not %d"
@@ -3405,7 +3425,7 @@
 msgstr "git checkout-index [<flaggor>] [--] [<fil>...]"
 
 msgid "stage should be between 1 and 3 or all"
-msgstr "etapp måste vara mellan 1 och 3 eller \"all\""
+msgstr "etapp måste vara mellan 1 och 3 eller ”all”"
 
 msgid "check out all files in the index"
 msgstr "checka ut alla filer i indexet"
@@ -3448,27 +3468,27 @@
 
 #, c-format
 msgid "path '%s' does not have our version"
-msgstr "sökvägen \"%s\" har inte vår version"
+msgstr "sökvägen ”%s” har inte vår version"
 
 #, c-format
 msgid "path '%s' does not have their version"
-msgstr "sökvägen \"%s\" har inte deras version"
+msgstr "sökvägen ”%s” har inte deras version"
 
 #, c-format
 msgid "path '%s' does not have all necessary versions"
-msgstr "sökvägen \"%s\" innehåller inte alla nödvändiga versioner"
+msgstr "sökvägen ”%s” innehåller inte alla nödvändiga versioner"
 
 #, c-format
 msgid "path '%s' does not have necessary versions"
-msgstr "sökvägen \"%s\" innehåller inte nödvändiga versioner"
+msgstr "sökvägen ”%s” innehåller inte nödvändiga versioner"
 
 #, c-format
 msgid "path '%s': cannot merge"
-msgstr "sökväg \"%s\": kan inte slå ihop"
+msgstr "sökväg ”%s”: kan inte slå ihop"
 
 #, c-format
 msgid "Unable to add merge result for '%s'"
-msgstr "Kunde inte lägga till sammanslagningsresultat för \"%s\""
+msgstr "Kunde inte lägga till sammanslagningsresultat för ”%s”"
 
 #, c-format
 msgid "Recreated %d merge conflict"
@@ -3490,27 +3510,31 @@
 
 #, c-format
 msgid "'%s' cannot be used with updating paths"
-msgstr "\"%s\" kan inte användas vid uppdatering av sökvägar"
+msgstr "”%s” kan inte användas vid uppdatering av sökvägar"
 
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
-msgstr "Kan inte uppdatera sökvägar och växla till grenen \"%s\" samtidigt."
+msgstr "Kan inte uppdatera sökvägar och växla till grenen ”%s” samtidigt."
 
 #, c-format
 msgid "neither '%s' or '%s' is specified"
-msgstr "varken \"%s\" eller \"%s\" har angivits"
+msgstr "varken ”%s” eller ”%s” har angivits"
 
 #, c-format
 msgid "'%s' must be used when '%s' is not specified"
-msgstr "\"%s\" måste användas när \"%s\" inte anges"
+msgstr "”%s” måste användas när ”%s” inte anges"
 
 #, c-format
 msgid "'%s' or '%s' cannot be used with %s"
-msgstr "\"%s\" eller \"%s\" kan inte användas med %s"
+msgstr "”%s” eller ”%s” kan inte användas med %s"
+
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "”%s”, ”%s” eller ”%s” kan inte användas när en katalog checkas ut"
 
 #, c-format
 msgid "path '%s' is unmerged"
-msgstr "sökvägen \"%s\" har inte slagits ihop"
+msgstr "sökvägen ”%s” har inte slagits ihop"
 
 msgid "you need to resolve your current index first"
 msgstr "du måste lösa ditt befintliga index först"
@@ -3525,7 +3549,7 @@
 
 #, c-format
 msgid "Can not do reflog for '%s': %s\n"
-msgstr "Kan inte skapa referenslogg för \"%s\": %s\n"
+msgstr "Kan inte skapa referenslogg för ”%s”: %s\n"
 
 msgid "HEAD is now at"
 msgstr "HEAD är nu på"
@@ -3535,23 +3559,23 @@
 
 #, c-format
 msgid "Reset branch '%s'\n"
-msgstr "Återställ gren \"%s\"\n"
+msgstr "Återställ gren ”%s”\n"
 
 #, c-format
 msgid "Already on '%s'\n"
-msgstr "Redan på \"%s\"\n"
+msgstr "Redan på ”%s”\n"
 
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
-msgstr "Växlade till och nollställde grenen \"%s\"\n"
+msgstr "Växlade till och nollställde grenen ”%s”\n"
 
 #, c-format
 msgid "Switched to a new branch '%s'\n"
-msgstr "Växlade till en ny gren \"%s\"\n"
+msgstr "Växlade till en ny gren ”%s”\n"
 
 #, c-format
 msgid "Switched to branch '%s'\n"
-msgstr "Växlade till grenen \"%s\"\n"
+msgstr "Växlade till grenen ”%s”\n"
 
 #, c-format
 msgid " ... and %d more.\n"
@@ -3619,7 +3643,7 @@
 "'%s' could be both a local file and a tracking branch.\n"
 "Please use -- (and optionally --no-guess) to disambiguate"
 msgstr ""
-"\"%s\" kan vara både en lokal fil och en spårande gren.\n"
+"”%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"
 
 msgid ""
@@ -3632,18 +3656,18 @@
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"Om du menade checka ut en spårad fjärrgren på t.ex \"origin\", kan du\n"
+"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"
 "\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"
+"föredra en fjärr, t.ex fjärren ”origin” kan du ställa in\n"
 "checkout.defaultRemote=origin i din konfiguration."
 
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
-msgstr "\"%s\" motsvarar flera (%d) spårade fjärrgrenar"
+msgstr "”%s” motsvarar flera (%d) spårade fjärrgrenar"
 
 msgid "only one reference expected"
 msgstr "endast en referens förväntades"
@@ -3662,19 +3686,19 @@
 
 #, c-format
 msgid "a branch is expected, got tag '%s'"
-msgstr "förväntade gren, fick taggen \"%s\""
+msgstr "förväntade gren, fick taggen ”%s”"
 
 #, c-format
 msgid "a branch is expected, got remote branch '%s'"
-msgstr "förväntade gren, fick fjärrgrenen \"%s\""
+msgstr "förväntade gren, fick fjärrgrenen ”%s”"
 
 #, c-format
 msgid "a branch is expected, got '%s'"
-msgstr "förväntade gren, fick \"%s\""
+msgstr "förväntade gren, fick ”%s”"
 
 #, c-format
 msgid "a branch is expected, got commit '%s'"
-msgstr "förväntade gren, fick incheckningen \"%s\""
+msgstr "förväntade gren, fick incheckningen ”%s”"
 
 msgid ""
 "If you want to detach HEAD at the commit, try again with the --detach option."
@@ -3687,57 +3711,57 @@
 "Consider \"git merge --quit\" or \"git worktree add\"."
 msgstr ""
 "kan inte växla gren vid sammanslagning\n"
-"Överväg \"git merge --quit\" eller \"git worktree add\"."
+"Överväg ”git merge --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch in the middle of an am session\n"
 "Consider \"git am --quit\" or \"git worktree add\"."
 msgstr ""
-"kan inte växla gren mitt i en \"am\"-körning\n"
-"Överväg \"git am --quit\" eller \"git worktree add\"."
+"kan inte växla gren mitt i en ”am”-körning\n"
+"Överväg ”git am --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch while rebasing\n"
 "Consider \"git rebase --quit\" or \"git worktree add\"."
 msgstr ""
 "kan inte växla gren vid ombasering\n"
-"Överväg \"git rebase --quit\" eller \"git worktree add\"."
+"Överväg ”git rebase --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch while cherry-picking\n"
 "Consider \"git cherry-pick --quit\" or \"git worktree add\"."
 msgstr ""
-"kan inte växla gren i en \"cherry-pick\"\n"
-"Överväg \"git cherry-pick --quit\" eller \"git worktree add\"."
+"kan inte växla gren i en ”cherry-pick”\n"
+"Överväg ”git cherry-pick --quit” eller ”git worktree add”."
 
 msgid ""
 "cannot switch branch while reverting\n"
 "Consider \"git revert --quit\" or \"git worktree add\"."
 msgstr ""
-"kan inte växla gren i en \"revert\"\n"
-"Överväg \"git revert --quit\" eller \"git worktree add\"."
+"kan inte växla gren i en ”revert”\n"
+"Överväg ”git revert --quit” eller ”git worktree add”."
 
 msgid "you are switching branch while bisecting"
-msgstr "då växlar grenar medan du gör en \"bisect\""
+msgstr "då växlar grenar medan du gör en ”bisect”"
 
 msgid "paths cannot be used with switching branches"
 msgstr "sökvägar kan inte användas vid byte av gren"
 
 #, c-format
 msgid "'%s' cannot be used with switching branches"
-msgstr "\"%s\" kan inte användas vid byte av gren"
+msgstr "”%s” kan inte användas vid byte av gren"
 
 #, c-format
 msgid "'%s' cannot be used with '%s'"
-msgstr "\"%s\" kan inte användas med \"%s\""
+msgstr "”%s” kan inte användas med ”%s”"
 
 #, c-format
 msgid "'%s' cannot take <start-point>"
-msgstr "\"%s\" kan inte ta <startpunkt>"
+msgstr "”%s” kan inte ta <startpunkt>"
 
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
-msgstr "Kan inte växla gren till icke-incheckningen \"%s\""
+msgstr "Kan inte växla gren till icke-incheckningen ”%s”"
 
 msgid "missing branch or commit argument"
 msgstr "saknar gren- eller incheckingsargument"
@@ -3760,8 +3784,8 @@
 msgid "new-branch"
 msgstr "ny-gren"
 
-msgid "new unparented branch"
-msgstr "ny gren utan förälder"
+msgid "new unborn branch"
+msgstr "ny ofödd gren"
 
 msgid "update ignored files (default)"
 msgstr "uppdatera ignorerade filer (standard)"
@@ -3781,7 +3805,7 @@
 
 #, 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"
@@ -3799,12 +3823,11 @@
 
 #, c-format
 msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
-msgstr ""
-"\"%s\" är inte en incheckning och grenen \"%s\" kan inte skapas från den"
+msgstr "”%s” är inte en incheckning och grenen ”%s” kan inte skapas från den"
 
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
-msgstr "git checkout: --detach tar inte en sökväg som argument \"%s\""
+msgstr "git checkout: --detach tar inte en sökväg som argument ”%s”"
 
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
@@ -3829,7 +3852,7 @@
 msgstr "skapa reflogg för ny gren"
 
 msgid "second guess 'git checkout <no-such-branch>' (default)"
-msgstr "förutspå \"git checkout <gren-saknas>\" (förval)"
+msgstr "förutspå ”git checkout <gren-saknas>” (förval)"
 
 msgid "use overlay mode (default)"
 msgstr "använd överläggsläge (standard)"
@@ -3841,7 +3864,7 @@
 msgstr "skapa/nollställ och växla till en gren"
 
 msgid "second guess 'git switch <no-such-branch>'"
-msgstr "förutspå \"git checkout <gren-saknas>\""
+msgstr "förutspå ”git checkout <gren-saknas>”"
 
 msgid "throw away local modifications"
 msgstr "kasta bort lokala ändringar"
@@ -3889,7 +3912,7 @@
 
 #, c-format
 msgid "could not lstat %s\n"
-msgstr "kunde inte ta status (\"lstat\") på %s\n"
+msgstr "kunde inte ta status (”lstat”) på %s\n"
 
 msgid "Refusing to remove current working directory\n"
 msgstr "Vägrar ta bort aktuell arbetskatalog\n"
@@ -3961,7 +3984,7 @@
 "clean               - börja städa\n"
 "filter by pattern   - uteslut poster från borttagning\n"
 "select by numbers   - markera poster som ska tas bort med siffror\n"
-"ask each            - bekräfta varje borttagning (som \"rm -i\")\n"
+"ask each            - bekräfta varje borttagning (som ”rm -i”)\n"
 "quit                - sluta städa\n"
 "help                - denna skärm\n"
 "?                   - hjälp för kommandoval"
@@ -4012,9 +4035,6 @@
 "clean.requireForce har standardvärdet true och varken -i, -n eller -f "
 "angavs; vägrar städa"
 
-msgid "-x and -X cannot be used together"
-msgstr "-x och -X kan inte användas samtidigt"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<flaggor>] [--] <arkiv> [<kat>]"
 
@@ -4025,10 +4045,10 @@
 msgstr "skapa inte någon utcheckning"
 
 msgid "create a bare repository"
-msgstr "skapa ett naket (\"bare\") arkiv"
+msgstr "skapa ett naket (”bare”) arkiv"
 
 msgid "create a mirror repository (implies bare)"
-msgstr "skapa ett spegelarkiv (implicerar \"bare\")"
+msgstr "skapa ett spegelarkiv (implicerar ”bare”)"
 
 msgid "to clone from a local repository"
 msgstr "för att klona från ett lokalt arkiv"
@@ -4064,7 +4084,7 @@
 msgstr "namn"
 
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "använd <namn> istället för \"origin\" för att spåra uppströms"
+msgstr "använd <namn> istället för ”origin” för att spåra uppströms"
 
 msgid "checkout <branch> instead of the remote's HEAD"
 msgstr "checka ut <gren> istället för fjärrens HEAD"
@@ -4102,6 +4122,9 @@
 msgid "separate git dir from working tree"
 msgstr "separera gitkatalogen från arbetskatalogen"
 
+msgid "specify the reference format to use"
+msgstr "använd referensformatet som ska användas"
+
 msgid "key=value"
 msgstr "nyckel=värde"
 
@@ -4114,12 +4137,6 @@
 msgid "option to transmit"
 msgstr "flagga att sända"
 
-msgid "use IPv4 addresses only"
-msgstr "använd endast IPv4-adresser"
-
-msgid "use IPv6 addresses only"
-msgstr "använd endast IPv6-adresser"
-
 msgid "apply partial clone filters to submodules"
 msgstr "tillämpa delvisa klonfilter på undermoduler"
 
@@ -4137,39 +4154,43 @@
 
 #, c-format
 msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: Kan inte skapa suppleant för \"%s\": %s\n"
+msgstr "info: Kan inte skapa suppleant för ”%s”: %s\n"
 
 #, c-format
 msgid "failed to stat '%s'"
-msgstr "misslyckades ta status på \"%s\""
+msgstr "misslyckades ta status på ”%s”"
 
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr "%s finns och är ingen katalog"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "”%s” är en symbolisk länk, vägrar klona med --local"
+
+#, c-format
 msgid "failed to start iterator over '%s'"
-msgstr "misslyckades starta iterator över \"%s\""
+msgstr "misslyckades starta iterator över ”%s”"
 
 #, c-format
 msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "symbolisk länk \"%s\" finns redan, vägrar klona med --local"
+msgstr "symbolisk länk ”%s” finns redan, vägrar klona med --local"
 
 #, c-format
 msgid "failed to unlink '%s'"
-msgstr "misslyckades ta bort länken \"%s\""
+msgstr "misslyckades ta bort länken ”%s”"
 
 #, c-format
 msgid "failed to create link '%s'"
-msgstr "misslyckades skapa länken \"%s\""
+msgstr "misslyckades skapa länken ”%s”"
 
 #, c-format
 msgid "failed to copy file to '%s'"
-msgstr "misslyckades kopiera filen till \"%s\""
+msgstr "misslyckades kopiera filen till ”%s”"
 
 #, c-format
 msgid "failed to iterate over '%s'"
-msgstr "misslyckades iterera över \"%s\""
+msgstr "misslyckades iterera över ”%s”"
 
 #, c-format
 msgid "done.\n"
@@ -4181,8 +4202,8 @@
 "and retry with 'git restore --source=HEAD :/'\n"
 msgstr ""
 "Klonen lyckades, men utcheckningen misslyckades.\n"
-"Du kan inspektera det som checkades ut med \"git status\"\n"
-"och försöka med \"git restore --source=HEAD :/\"\n"
+"Du kan inspektera det som checkades ut med ”git status”\n"
+"och försöka med ”git restore --source=HEAD :/”\n"
 
 #, c-format
 msgid "Could not find remote branch %s to clone."
@@ -4211,7 +4232,7 @@
 msgstr "kan inte packa om för att städa upp"
 
 msgid "cannot unlink temporary alternates file"
-msgstr "kunde inte ta bort temporär \"alternates\"-fil"
+msgstr "kunde inte ta bort temporär ”alternates”-fil"
 
 msgid "Too many arguments."
 msgstr "För många argument."
@@ -4219,16 +4240,13 @@
 msgid "You must specify a repository to clone."
 msgstr "Du måste ange ett arkiv att klona."
 
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri är inkompatibelt med --depth, --shallow-since och --shallow-"
-"exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "okänt format för lagring av referenser ”%s”"
 
 #, c-format
 msgid "repository '%s' does not exist"
-msgstr "arkivet \"%s\" finns inte"
+msgstr "arkivet ”%s” finns inte"
 
 #, c-format
 msgid "depth %s is not a positive number"
@@ -4236,31 +4254,31 @@
 
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
-msgstr "destinationssökvägen \"%s\" finns redan och är inte en tom katalog."
+msgstr "destinationssökvägen ”%s” finns redan och är inte en tom katalog."
 
 #, c-format
 msgid "repository path '%s' already exists and is not an empty directory."
-msgstr "arkivsökvägen \"%s\" finns redan och är inte en tom katalog."
+msgstr "arkivsökvägen ”%s” finns redan och är inte en tom katalog."
 
 #, c-format
 msgid "working tree '%s' already exists."
-msgstr "arbetsträdet \"%s\" finns redan."
+msgstr "arbetsträdet ”%s” finns redan."
 
 #, c-format
 msgid "could not create leading directories of '%s'"
-msgstr "kunde inte skapa inledande kataloger för \"%s\""
+msgstr "kunde inte skapa inledande kataloger för ”%s”"
 
 #, c-format
 msgid "could not create work tree dir '%s'"
-msgstr "kunde inte skapa arbetskatalogen \"%s\""
+msgstr "kunde inte skapa arbetskatalogen ”%s”"
 
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
-msgstr "Klonar till ett naket arkiv \"%s\"...\n"
+msgstr "Klonar till ett naket arkiv ”%s”...\n"
 
 #, c-format
 msgid "Cloning into '%s'...\n"
-msgstr "Klonar till \"%s\"...\n"
+msgstr "Klonar till ”%s”...\n"
 
 msgid ""
 "clone --recursive is not compatible with both --reference and --reference-if-"
@@ -4270,7 +4288,7 @@
 
 #, c-format
 msgid "'%s' is not a valid remote name"
-msgstr "\"%s\" är inte ett giltigt namn på fjärrarkiv"
+msgstr "”%s” är inte ett giltigt namn på fjärrarkiv"
 
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr "--depth ignoreras i lokala kloningar; använd file:// istället."
@@ -4302,7 +4320,7 @@
 
 #, c-format
 msgid "failed to fetch objects from bundle URI '%s'"
-msgstr "misslyckades hämta objekt från bunt-URI \"%s\""
+msgstr "misslyckades hämta objekt från bunt-URI ”%s”"
 
 msgid "failed to fetch advertised bundles"
 msgstr "misslyckades hämta annonserade buntar"
@@ -4352,7 +4370,7 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <kat>] [--append]\n"
 "                       [--split[=<strategi>]] [--reachable | --stdin-packs | "
@@ -4372,7 +4390,11 @@
 
 #, c-format
 msgid "Could not open commit-graph '%s'"
-msgstr "Kunde inte öppna incheckningsgrafen \"%s\""
+msgstr "Kunde inte öppna incheckningsgrafen ”%s”"
+
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "kunde inte öppna incheckningsgrafen ”%s”"
 
 #, c-format
 msgid "unrecognized --split argument, %s"
@@ -4388,7 +4410,7 @@
 
 #, c-format
 msgid "option `%s' expects a numerical value"
-msgstr "flaggan \"%s\" antar ett numeriskt värde"
+msgstr "flaggan ”%s” antar ett numeriskt värde"
 
 msgid "start walk at all refs"
 msgstr "starta traversering vid alla referenser"
@@ -4448,11 +4470,11 @@
 
 #, c-format
 msgid "git commit-tree: failed to read '%s'"
-msgstr "git commit-tree: misslyckades läsa \"%s\""
+msgstr "git commit-tree: misslyckades läsa ”%s”"
 
 #, c-format
 msgid "git commit-tree: failed to close '%s'"
-msgstr "git commit-tree: misslyckades stänga \"%s\""
+msgstr "git commit-tree: misslyckades stänga ”%s”"
 
 msgid "parent"
 msgstr "förälder"
@@ -4510,7 +4532,7 @@
 msgstr ""
 "Du bad om att utöka den senaste incheckningen, men om du gör det\n"
 "blir den tom. Du kan köra kommandot på nytt med --allow-empty, eller\n"
-"så kan du ta bort incheckningen helt med \"git reset HEAD^\".\n"
+"så kan du ta bort incheckningen helt med ”git reset HEAD^”.\n"
 
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
@@ -4519,17 +4541,17 @@
 "    git commit --allow-empty\n"
 "\n"
 msgstr ""
-"Den tidigare \"cherry-pick\":en är nu tom, kanske på grund av en löst\n"
+"Den tidigare ”cherry-pick”:en är nu tom, kanske på grund av en löst\n"
 "konflikt. Om du vill checka in den ändå använder du:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
 
 msgid "Otherwise, please use 'git rebase --skip'\n"
-msgstr "Använd annars \"git rebase --skip\"\n"
+msgstr "Använd annars ”git rebase --skip”\n"
 
 msgid "Otherwise, please use 'git cherry-pick --skip'\n"
-msgstr "Använd annars \"git cherry-pick --skip\"\n"
+msgstr "Använd annars ”git cherry-pick --skip”\n"
 
 msgid ""
 "and then use:\n"
@@ -4546,12 +4568,15 @@
 "\n"
 "    git cherry-pick --continue\n"
 "\n"
-"för att fortsätta \"cherry-pick\" med resterande incheckningar.\n"
+"för att fortsätta ”cherry-pick” med resterande incheckningar.\n"
 "Om du vill hoppa över den här incheckningen, använd:\n"
 "\n"
 "    git cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "misslyckades uppdatera filer"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "misslyckades packa upp HEAD:s trädobjekt"
 
@@ -4570,9 +4595,6 @@
 msgid "Failed to update main cache tree"
 msgstr "Misslyckades uppdatera huvud-cacheträdet"
 
-msgid "unable to write new_index file"
-msgstr "kunde inte skriva filen new_index"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "kan inte utföra en delvis incheckning under en sammanslagning."
 
@@ -4590,14 +4612,14 @@
 
 #, c-format
 msgid "commit '%s' lacks author header"
-msgstr "incheckningen \"%s\" saknar författarhuvud"
+msgstr "incheckningen ”%s” saknar författarhuvud"
 
 #, c-format
 msgid "commit '%s' has malformed author line"
-msgstr "incheckningen \"%s\" har felformaterat författarhuvud"
+msgstr "incheckningen ”%s” har felformaterat författarhuvud"
 
 msgid "malformed --author parameter"
-msgstr "felformad \"--author\"-flagga"
+msgstr "felformad ”--author”-flagga"
 
 #, c-format
 msgid "invalid date format: %s"
@@ -4611,8 +4633,8 @@
 "i det befintliga incheckningsmeddelandet"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "kunde inte slå upp incheckningen %s"
+msgid "could not lookup commit '%s'"
+msgstr "kunde inte slå upp incheckningen ”%s”"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -4623,11 +4645,11 @@
 
 #, c-format
 msgid "could not read log file '%s'"
-msgstr "kunde inte läsa loggfilen \"%s\""
+msgstr "kunde inte läsa loggfilen ”%s”"
 
 #, c-format
 msgid "options '%s' and '%s:%s' cannot be used together"
-msgstr "flaggorna \"%s\" och \"%s:%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s” och ”%s:%s” kan inte användas samtidigt"
 
 msgid "could not read SQUASH_MSG"
 msgstr "kunde inte läsa SQUASH_MSG"
@@ -4637,7 +4659,7 @@
 
 #, c-format
 msgid "could not open '%s'"
-msgstr "kunde inte öppna \"%s\""
+msgstr "kunde inte öppna ”%s”"
 
 msgid "could not write commit template"
 msgstr "kunde inte skriva incheckningsmall"
@@ -4648,7 +4670,7 @@
 "with '%c' will be ignored.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer ignoreras.\n"
+"med ”%c” kommer ignoreras.\n"
 
 #, c-format
 msgid ""
@@ -4656,8 +4678,7 @@
 "with '%c' will be ignored, and an empty message aborts the commit.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer ignoreras, och ett tomt meddelande avbryter "
-"incheckningen.\n"
+"med ”%c” kommer ignoreras, och ett tomt meddelande avbryter incheckningen.\n"
 
 #, c-format
 msgid ""
@@ -4665,7 +4686,7 @@
 "with '%c' will be kept; you may remove them yourself if you want to.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer behållas; du kan själv ta bort dem om du vill.\n"
+"med ”%c” kommer behållas; du kan själv ta bort dem om du vill.\n"
 
 #, c-format
 msgid ""
@@ -4674,7 +4695,7 @@
 "An empty message aborts the commit.\n"
 msgstr ""
 "Ange incheckningsmeddelandet för dina ändringar. Rader som inleds\n"
-"med \"%c\" kommer behållas; du kan själv ta bort dem om du vill.\n"
+"med ”%c” kommer behållas; du kan själv ta bort dem om du vill.\n"
 "Ett tomt meddelande avbryter incheckningen.\n"
 
 msgid ""
@@ -4735,11 +4756,11 @@
 
 #, c-format
 msgid "Invalid ignored mode '%s'"
-msgstr "Ogiltigt ignorerat läge \"%s\""
+msgstr "Ogiltigt ignorerat läge ”%s”"
 
 #, c-format
 msgid "Invalid untracked files mode '%s'"
-msgstr "Ogiltigt läge för ospårade filer: \"%s\""
+msgstr "Ogiltigt läge för ospårade filer: ”%s”"
 
 msgid "You are in the middle of a merge -- cannot reword."
 msgstr "Du är i mitten av en sammanslagning -- kan inte omformulera."
@@ -4750,11 +4771,11 @@
 #, c-format
 msgid "reword option of '%s' and path '%s' cannot be used together"
 msgstr ""
-"reword-flaggan till \"%s\" och sökvägen \"%s\" kan inte användas tillsammans"
+"reword-flaggan till ”%s” och sökvägen ”%s” kan inte användas tillsammans"
 
 #, c-format
 msgid "reword option of '%s' and '%s' cannot be used together"
-msgstr "reword-flaggan till \"%s\" och \"%s\" kan inte användas tillsammans"
+msgstr "reword-flaggan till ”%s” och ”%s” kan inte användas tillsammans"
 
 msgid "You have nothing to amend."
 msgstr "Du har inget att utöka."
@@ -4777,7 +4798,7 @@
 
 #, c-format
 msgid "paths '%s ...' with -a does not make sense"
-msgstr "sökvägarna \"%s ...\" med -a ger ingen mening"
+msgstr "sökvägarna ”%s ...” med -a ger ingen mening"
 
 msgid "show status concisely"
 msgstr "visa koncis status"
@@ -4970,13 +4991,13 @@
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"arkivet har uppdaterats, men kunde inte skriva filen\n"
-"new_index. Kontrollera att disken inte är full och\n"
+"arkivet har uppdaterats, men kunde inte skriva ny\n"
+"indexfil. Kontrollera att disken inte är full och\n"
 "att kvoten inte har överskridits, och kör sedan\n"
-"\"git restore --staged :/\" för att återställa."
+"”git restore --staged :/” för att återställa."
 
 msgid "git config [<options>]"
 msgstr "git config [<flaggor>]"
@@ -5049,7 +5070,7 @@
 msgstr "visa alla"
 
 msgid "use string equality when comparing values to 'value-pattern'"
-msgstr "använd stränglikhet vid när värden jämförs med \"värde-mönster\""
+msgstr "använd stränglikhet vid när värden jämförs med ”värde-mönster”"
 
 msgid "open an editor"
 msgstr "öppna textredigeringsprogram"
@@ -5070,7 +5091,7 @@
 msgstr "värdet har givits denna typ"
 
 msgid "value is \"true\" or \"false\""
-msgstr "värdet är \"true\" eller \"false\""
+msgstr "värdet är ”true” eller ”false”"
 
 msgid "value is decimal number"
 msgstr "värdet är ett decimalt tal"
@@ -5135,7 +5156,7 @@
 
 #, c-format
 msgid "cannot parse color '%s'"
-msgstr "kan inte tolka färgen \"%s\""
+msgstr "kan inte tolka färgen ”%s”"
 
 msgid "unable to parse default color value"
 msgstr "kan inte tolka standardfärgvärde"
@@ -5185,7 +5206,7 @@
 msgstr ""
 "--worktree kan inte användas med flera arbetskataloger om inte\n"
 "konfigurationsutöknignen worktreeConfig har aktiverats. Läsa stycket\n"
-"\"KONFIGURATIONSFIL\" i \"git help worktree\" för detaljer"
+"”KONFIGURATIONSFIL” i ”git help worktree” för detaljer"
 
 msgid "--get-color and variable type are incoherent"
 msgstr "--get-color och variabeltyp stämmer inte överens"
@@ -5206,11 +5227,11 @@
 msgstr "--default gäller bara för --get"
 
 msgid "--fixed-value only applies with 'value-pattern'"
-msgstr "--fixed-value gäller endast med \"värde-mönster\""
+msgstr "--fixed-value gäller endast med ”värde-mönster”"
 
 #, c-format
 msgid "unable to read config file '%s'"
-msgstr "kan inte konfigurationsfil \"%s\""
+msgstr "kan inte läsa konfigurationsfilen ”%s”"
 
 msgid "error processing config file(s)"
 msgstr "fel vid hantering av konfigurationsfil(er)"
@@ -5223,7 +5244,7 @@
 
 #, c-format
 msgid "cannot create configuration file %s"
-msgstr "kan inte skapa konfigurationsfilen \"%s\""
+msgstr "kan inte skapa konfigurationsfilen ”%s”"
 
 #, c-format
 msgid ""
@@ -5256,11 +5277,10 @@
 msgstr "skriv felsökningsmeddelanden på standard fel"
 
 msgid "credential-cache--daemon unavailable; no unix socket support"
-msgstr ""
-"\"credential-cache--daemon\" ej tillgänglig; stöd för unix-uttag saknas"
+msgstr "”credential-cache--daemon” ej tillgänglig; stöd för unix-uttag saknas"
 
 msgid "credential-cache unavailable; no unix socket support"
-msgstr "\"credential-cache\" ej tillgänglig; stöd för unix-uttag saknas"
+msgstr "”credential-cache” ej tillgänglig; stöd för unix-uttag saknas"
 
 #, c-format
 msgid "unable to get credential storage lock in %d ms"
@@ -5295,11 +5315,11 @@
 
 #, c-format
 msgid "tag '%s' is externally known as '%s'"
-msgstr "taggen \"%s\" är utanför känd som \"%s\""
+msgstr "taggen ”%s” är utanför känd som ”%s”"
 
 #, c-format
 msgid "no tag exactly matches '%s'"
-msgstr "ingen tagg motsvarar \"%s\" exakt"
+msgstr "ingen tagg motsvarar ”%s” exakt"
 
 #, c-format
 msgid "No exact match on refs or tags, searching to describe\n"
@@ -5315,7 +5335,7 @@
 "No annotated tags can describe '%s'.\n"
 "However, there were unannotated tags: try --tags."
 msgstr ""
-"Inga annoterade taggar kan beskriva \"%s\".\n"
+"Inga annoterade taggar kan beskriva ”%s”.\n"
 "Det finns dock oannoterade taggar: testa --tags."
 
 #, c-format
@@ -5323,7 +5343,7 @@
 "No tags can describe '%s'.\n"
 "Try --always, or create some tags."
 msgstr ""
-"Inga taggar kan beskriva \"%s\".\n"
+"Inga taggar kan beskriva ”%s”.\n"
 "Testa --always, eller skapa några taggar."
 
 #, c-format
@@ -5387,17 +5407,17 @@
 msgstr "märke"
 
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
-msgstr "lägg till <märke> på lortigt arbetsträd (standard: \"-dirty\")"
+msgstr "lägg till <märke> på lortigt arbetsträd (standard: ”-dirty”)"
 
 msgid "append <mark> on broken working tree (default: \"-broken\")"
-msgstr "lägg till <märke> på trasigt arbetsträd (standard: \"-broken\")"
+msgstr "lägg till <märke> på trasigt arbetsträd (standard: ”-broken”)"
 
 msgid "No names found, cannot describe anything."
 msgstr "Inga namn hittades, kan inte beskriva något."
 
 #, c-format
 msgid "option '%s' and commit-ishes cannot be used together"
-msgstr "flaggorna \"%s\" och incheckning-igter kan inte användas samtidigt"
+msgstr "flaggorna ”%s” och incheckning-igter kan inte användas samtidigt"
 
 msgid ""
 "git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
@@ -5421,7 +5441,7 @@
 
 #, c-format
 msgid "'%s': not a regular file or symlink"
-msgstr "\"%s\": inte en normal fil eller symbolisk länk"
+msgstr "”%s”: inte en normal fil eller symbolisk länk"
 
 msgid "no merge given, only parents."
 msgstr "ingen sammanslagning angiven, endast föräldrar."
@@ -5439,15 +5459,15 @@
 
 #, c-format
 msgid "invalid object '%s' given."
-msgstr "objektet \"%s\" som angavs är felaktigt."
+msgstr "objektet ”%s” som angavs är felaktigt."
 
 #, c-format
 msgid "more than two blobs given: '%s'"
-msgstr "mer än två blobbar angavs: \"%s\""
+msgstr "mer än två blobbar angavs: ”%s”"
 
 #, c-format
 msgid "unhandled object '%s' given."
-msgstr "ej hanterat objekt \"%s\" angavs."
+msgstr "ej hanterat objekt ”%s” angavs."
 
 #, c-format
 msgid "%s...%s: multiple merge bases, using %s"
@@ -5473,23 +5493,23 @@
 "combined diff formats ('-c' and '--cc') are not supported in\n"
 "directory diff mode ('-d' and '--dir-diff')."
 msgstr ""
-"kombinerade diff-format (\"-c\" och \"--cc\") stöds inte i\n"
-"katalogdiffläge (\"-d\" och \"--dir-diff\")."
+"kombinerade diff-format (”-c” och ”--cc”) stöds inte i\n"
+"katalogdiffläge (”-d” och ”--dir-diff”)."
 
 #, c-format
 msgid "both files modified: '%s' and '%s'."
-msgstr "bägge filerna ändrade: \"%s\" och \"%s\"."
+msgstr "bägge filerna ändrade: ”%s” och ”%s”."
 
 msgid "working tree file has been left."
 msgstr "filen i arbetskatalogen lämnades kvar."
 
 #, c-format
 msgid "could not copy '%s' to '%s'"
-msgstr "kunde inte kopiera in \"%s\" till \"%s\""
+msgstr "kunde inte kopiera in ”%s” till ”%s”"
 
 #, c-format
 msgid "temporary files exist in '%s'."
-msgstr "temporära filer finns i \"%s\"."
+msgstr "temporära filer finns i ”%s”."
 
 msgid "you may want to cleanup or recover these."
 msgstr "du kanske vill städa eller rädda dem."
@@ -5499,7 +5519,7 @@
 msgstr "misslyckades: %d"
 
 msgid "use `diff.guitool` instead of `diff.tool`"
-msgstr "använd \"diff.guitool\" istället för \"diff.tool\""
+msgstr "använd ”diff.guitool” istället för ”diff.tool”"
 
 msgid "perform a full-directory diff"
 msgstr "utför diff för hela katalogen"
@@ -5517,20 +5537,20 @@
 msgstr "använd angivet diff-verktyg"
 
 msgid "print a list of diff tools that may be used with `--tool`"
-msgstr "visa en lista över diff-verktyg som kan användas med \"--tool\""
+msgstr "visa en lista över diff-verktyg som kan användas med ”--tool”"
 
 msgid ""
 "make 'git-difftool' exit when an invoked diff tool returns a non-zero exit "
 "code"
 msgstr ""
-"låt \"git-difftool\" avsluta när ett anropat diff-verktyg ger returvärde "
-"skilt från noll"
+"låt ”git-difftool” avsluta när ett anropat diff-verktyg ger returvärde skilt "
+"från noll"
 
 msgid "specify a custom command for viewing diffs"
 msgstr "ange eget kommando för att visa diffar"
 
 msgid "passed to `diff`"
-msgstr "sändes till \"diff\""
+msgstr "sändes till ”diff”"
 
 msgid "difftool requires worktree or --no-index"
 msgstr "difftool kräver en arbetskatalog eller --no-index"
@@ -5609,26 +5629,26 @@
 
 #, c-format
 msgid "Missing from marks for submodule '%s'"
-msgstr "Saknar från-märken för undermodulen \"%s\""
+msgstr "Saknar från-märken för undermodulen ”%s”"
 
 #, c-format
 msgid "Missing to marks for submodule '%s'"
-msgstr "Saknar till-märken för undermodulen \"%s\""
+msgstr "Saknar till-märken för undermodulen ”%s”"
 
 #, c-format
 msgid "Expected 'mark' command, got %s"
-msgstr "Förväntade \"mark\"-kommando, fick %s"
+msgstr "Förväntade ”mark”-kommando, fick %s"
 
 #, c-format
 msgid "Expected 'to' command, got %s"
-msgstr "Förväntade \"to\"-kommando, fick %s"
+msgstr "Förväntade ”to”-kommando, fick %s"
 
 msgid "Expected format name:filename for submodule rewrite option"
 msgstr "Förvändae formatet namn:filnamn för undermodul-omskrivningsflaggan"
 
 #, c-format
 msgid "feature '%s' forbidden in input without --allow-unsafe-features"
-msgstr "funktionen \"%s\" förbjuden i indata utan --allow-unsafe-features"
+msgstr "funktionen ”%s” förbjuden i indata utan --allow-unsafe-features"
 
 #, c-format
 msgid "Lockfile created but not reported: %s"
@@ -5649,6 +5669,174 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel kan inte vara negativt"
 
+msgid "couldn't find remote ref HEAD"
+msgstr "kunde inte hitta fjärr-referensen HEAD"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Från %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "objektet %s hittades inte"
+
+msgid "[up to date]"
+msgstr "[àjour]"
+
+msgid "[rejected]"
+msgstr "[refuserad]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "kan inte hämta i utcheckad gren"
+
+msgid "[tag update]"
+msgstr "[uppdaterad tagg]"
+
+msgid "unable to update local ref"
+msgstr "kunde inte uppdatera lokal ref"
+
+msgid "would clobber existing tag"
+msgstr "skulle skriva över befintlig tagg"
+
+msgid "[new tag]"
+msgstr "[ny tagg]"
+
+msgid "[new branch]"
+msgstr "[ny gren]"
+
+msgid "[new ref]"
+msgstr "[ny ref]"
+
+msgid "forced update"
+msgstr "tvingad uppdatering"
+
+msgid "non-fast-forward"
+msgstr "ej snabbspolad"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "kan inte öppna ”%s”"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"fetch visar normalt vilka grenar som tvångsuppdaterats, men testet har "
+"slagits\n"
+"av; för att slå på igen, använd flaggan ”--show-forced-updates” eller kör\n"
+"”git config fetch.showForcedUpdates true”"
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"det tog %.2f sekunder att se efter tvångsuppdateringar; Du kan använda\n"
+"”--no-show-forced-updates” eller köra ”git config fetch.showForcedUpdates\n"
+"false” för att undvika testet\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s sände inte alla nödvändiga objekt\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr "avvisade %s då grunda rötter inte tillåts uppdateras"
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"vissa lokala referenser kunde inte uppdateras; testa att köra\n"
+" ”git remote prune %s” för att ta bort gamla grenar som står i konflikt"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s kommer bli dinglande)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s har blivit dinglande)"
+
+msgid "[deleted]"
+msgstr "[borttagen]"
+
+msgid "(none)"
+msgstr "(ingen)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "vägrar hämta till grenen ”%s” som är utcheckad på ”%s”"
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "flaggan ”%s” med värdet ”%s” är inte giltigt för %s"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "flaggan ”%s” ignoreras för %s\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s är inte ett giltigt objekt"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "objektet %s finns inte"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "flera grenar upptäcktes, inkompatibelt med --set-upstream"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"kunde inte sätta uppström för HEAD till ”%s” från ”%s” när det inte pekar "
+"mot någon gren."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "ställer inte in uppströmsgren för en fjärrspårande gren på fjärren"
+
+msgid "not setting upstream for a remote tag"
+msgstr "ställer inte in uppström för en fjärrtag"
+
+msgid "unknown branch type"
+msgstr "okänd grentyp"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"hittade ingen källgren;\n"
+"du måste ange exakt en gren med flaggan --set-upstream"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Hämtar %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "kunde inte hämta %s"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "kunde inte hämta ”%s” (felkod: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"inget fjärrarkiv angavs; ange antingen en URL eller namnet på ett\n"
+"fjärrarkiv som nya incheckningar ska hämtas från"
+
+msgid "you need to specify a tag name"
+msgstr "du måste ange namnet på en tagg"
+
 msgid "fetch from all remotes"
 msgstr "hämta från alla fjärrar"
 
@@ -5735,7 +5923,7 @@
 msgstr "referenskarta"
 
 msgid "specify fetch refmap"
-msgstr "ange referenskarta för \"fetch\""
+msgstr "ange referenskarta för ”fetch”"
 
 msgid "report that we have only objects reachable from this object"
 msgstr "rapportera att vi bara har objekt nåbara från detta objektet"
@@ -5744,7 +5932,7 @@
 msgstr "hämta inte paketfil; skriv istället förfäder till förhandlingstips"
 
 msgid "run 'maintenance --auto' after fetching"
-msgstr "kör \"maintenance --auto\" efter hämtning"
+msgstr "kör ”maintenance --auto” efter hämtning"
 
 msgid "check for forced-updates on all updated branches"
 msgstr "se efter tvingade uppdateringar i alla uppdaterade grenar"
@@ -5755,175 +5943,6 @@
 msgid "accept refspecs from stdin"
 msgstr "ta emot referenser från standard in"
 
-msgid "couldn't find remote ref HEAD"
-msgstr "kunde inte hitta fjärr-referensen HEAD"
-
-#, c-format
-msgid "object %s not found"
-msgstr "objektet %s hittades inte"
-
-msgid "[up to date]"
-msgstr "[àjour]"
-
-msgid "[rejected]"
-msgstr "[refuserad]"
-
-msgid "can't fetch into checked-out branch"
-msgstr "kan inte hämta i utcheckad gren"
-
-msgid "[tag update]"
-msgstr "[uppdaterad tagg]"
-
-msgid "unable to update local ref"
-msgstr "kunde inte uppdatera lokal ref"
-
-msgid "would clobber existing tag"
-msgstr "skulle skriva över befintlig tagg"
-
-msgid "[new tag]"
-msgstr "[ny tagg]"
-
-msgid "[new branch]"
-msgstr "[ny gren]"
-
-msgid "[new ref]"
-msgstr "[ny ref]"
-
-msgid "forced update"
-msgstr "tvingad uppdatering"
-
-msgid "non-fast-forward"
-msgstr "ej snabbspolad"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "kan inte öppna \"%s\""
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"fetch visar normalt vilka grenar som tvångsuppdaterats, men testet har "
-"slagits\n"
-"av; för att slå på igen, använd flaggan \"--show-forced-updates\" eller kör\n"
-"\"git config fetch.showForcedUpdates true\""
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"det tog %.2f sekunder att se efter tvångsuppdateringar; Du kan använda\n"
-"\"--no-show-forced-updates\" eller köra \"git config fetch."
-"showForcedUpdates\n"
-"false\" för att undvika testet\n"
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s sände inte alla nödvändiga objekt\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr "avvisade %s då grunda rötter inte tillåts uppdateras"
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "Från %.*s\n"
-
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"vissa lokala referenser kunde inte uppdateras; testa att köra\n"
-" \"git remote prune %s\" för att ta bort gamla grenar som står i konflikt"
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s kommer bli dinglande)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s har blivit dinglande)"
-
-msgid "[deleted]"
-msgstr "[borttagen]"
-
-msgid "(none)"
-msgstr "(ingen)"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "vägrar hämta till grenen \"%s\" som är utcheckad på \"%s\""
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "flaggan \"%s\" med värdet \"%s\" är inte giltigt för %s"
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "flaggan \"%s\" ignoreras för %s\n"
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s är inte ett giltigt objekt"
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr "objektet %s finns inte"
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "flera grenar upptäcktes, inkompatibelt med --set-upstream"
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"kunde inte sätta uppström för HEAD till \"%s\" från \"%s\" när det inte "
-"pekar mot någon gren."
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "ställer inte in uppströmsgren för en fjärrspårande gren på fjärren"
-
-msgid "not setting upstream for a remote tag"
-msgstr "ställer inte in uppström för en fjärrtag"
-
-msgid "unknown branch type"
-msgstr "okänd grentyp"
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"hittade ingen källgren;\n"
-"du måste ange exakt en gren med flaggan --set-upstream"
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "Hämtar %s\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr "kunde inte hämta %s"
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "kunde inte hämta \"%s\" (felkod: %d)\n"
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"inget fjärrarkiv angavs; ange antingen en URL eller namnet på ett\n"
-"fjärrarkiv som nya incheckningar ska hämtas från"
-
-msgid "you need to specify a tag name"
-msgstr "du måste ange namnet på en tagg"
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only behöver en eller flera --negotiation-tip=*"
 
@@ -5935,7 +5954,7 @@
 
 #, c-format
 msgid "failed to fetch bundles from '%s'"
-msgstr "misslyckades hämta buntar från \"%s\""
+msgstr "misslyckades hämta buntar från ”%s”"
 
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all tar inte namnet på ett arkiv som argument"
@@ -6039,6 +6058,12 @@
 msgid "print only refs which don't contain the commit"
 msgstr "visa endast referenser som inte innehåller incheckningen"
 
+msgid "read reference patterns from stdin"
+msgstr "läs referensmönster från standard in"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "okända argument angavs tillsammans med --stdin"
+
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<konfig> [--] <argument>"
 
@@ -6051,6 +6076,10 @@
 msgid "missing --config=<config>"
 msgstr "saknar --config=<konfig>"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "fick felaktig konfiguration --config=%s"
+
 msgid "unknown"
 msgstr "okänd"
 
@@ -6101,11 +6130,11 @@
 
 #, c-format
 msgid "could not write '%s'"
-msgstr "kunde inte skriva \"%s\""
+msgstr "kunde inte skriva ”%s”"
 
 #, c-format
 msgid "could not finish '%s'"
-msgstr "kunde inte avsluta \"%s\""
+msgstr "kunde inte avsluta ”%s”"
 
 #, c-format
 msgid "Checking %s"
@@ -6142,7 +6171,7 @@
 
 #, c-format
 msgid "Checking reflog %s->%s"
-msgstr "Kontrollerar reflog %s->%s"
+msgstr "Kontrollerar reflog %s→%s"
 
 #, c-format
 msgid "%s: invalid sha1 pointer %s"
@@ -6165,7 +6194,7 @@
 
 #, c-format
 msgid "%s: object is of unknown type '%s': %s"
-msgstr "%s: objektet har okänd typ \"%s\": %s"
+msgstr "%s: objektet har okänd typ ”%s”: %s"
 
 #, c-format
 msgid "%s: object could not be parsed: %s"
@@ -6201,19 +6230,28 @@
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "obs: %s pekar på en ofödd gren (%s)"
 
-msgid "Checking cache tree"
-msgstr "Kontrollerar cacheträd"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Kontrollerar cacheträd för %s"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: ogiltig sha1-pekare i cacheträd"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: ogiltig sha1-pekare i cacheträd för %s"
 
 msgid "non-tree in cache-tree"
 msgstr "icke-träd i cacheträd"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: ogiltig sha1-pekare i resolve-undo"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: ogiltig sha1-pekare i resolve-undo för %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "kunde inte läsa rev-index för paketfil ”%s”"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "ogiltigt rev-index för paketet ”%s”"
 
 msgid ""
 "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
@@ -6273,7 +6311,7 @@
 
 #, c-format
 msgid "invalid parameter: expected sha1, got '%s'"
-msgstr "ogiltig parameter: förväntade sha1, fick \"%s\""
+msgstr "ogiltig parameter: förväntade sha1, fick ”%s”"
 
 msgid "git fsmonitor--daemon start [<options>]"
 msgstr "git fsmonitor--daemon start [<flaggor>]"
@@ -6283,23 +6321,23 @@
 
 #, c-format
 msgid "value of '%s' out of range: %d"
-msgstr "värdet för \"%s\" utanför intervallet: %d"
+msgstr "värdet för ”%s” utanför intervallet: %d"
 
 #, c-format
 msgid "value of '%s' not bool or int: %d"
-msgstr "värdet för \"%s\" är inte bool eller int: %d"
+msgstr "värdet för ”%s” är inte bool eller int: %d"
 
 #, c-format
 msgid "fsmonitor-daemon is watching '%s'\n"
-msgstr "fsmonitor-daemon bevakar \"%s\"\n"
+msgstr "fsmonitor-daemon bevakar ”%s”\n"
 
 #, c-format
 msgid "fsmonitor-daemon is not watching '%s'\n"
-msgstr "fsmonitor-daemon bevakar inte \"%s\"\n"
+msgstr "fsmonitor-daemon bevakar inte ”%s”\n"
 
 #, c-format
 msgid "could not create fsmonitor cookie '%s'"
-msgstr "kunde inte skapa fsmonitor-kaka \"%s\""
+msgstr "kunde inte skapa fsmonitor-kaka ”%s”"
 
 #, c-format
 msgid "fsmonitor: cookie_result '%d' != SEEN"
@@ -6307,7 +6345,7 @@
 
 #, c-format
 msgid "could not start IPC thread pool on '%s'"
-msgstr "kunde inte starta IPC-trådpol på \"%s\""
+msgstr "kunde inte starta IPC-trådpol på ”%s”"
 
 msgid "could not start fsmonitor listener thread"
 msgstr "kunde inte starta fsmonitor-lyssnartråd"
@@ -6323,19 +6361,19 @@
 
 #, c-format
 msgid "could not cd home '%s'"
-msgstr "kunde inte byta katalog hem \"%s\""
+msgstr "kunde inte byta katalog hem ”%s”"
 
 #, c-format
 msgid "fsmonitor--daemon is already running '%s'"
-msgstr "fsmonitor--daemon körs redan på \"%s\""
+msgstr "fsmonitor--daemon körs redan på ”%s”"
 
 #, c-format
 msgid "running fsmonitor-daemon in '%s'\n"
-msgstr "kör fsmonitor-daemon i \"%s\"\n"
+msgstr "kör fsmonitor-daemon i ”%s”\n"
 
 #, c-format
 msgid "starting fsmonitor-daemon in '%s'\n"
-msgstr "startar fsmonitor-daemon i \"%s\"\n"
+msgstr "startar fsmonitor-daemon i ”%s”\n"
 
 msgid "daemon failed to start"
 msgstr "serverprocessen kunde inte startas"
@@ -6357,11 +6395,11 @@
 
 #, c-format
 msgid "invalid 'ipc-threads' value (%d)"
-msgstr "ogiltigt värde för \"ipc-threads\" (%d)"
+msgstr "ogiltigt värde för ”ipc-threads” (%d)"
 
 #, c-format
 msgid "Unhandled subcommand '%s'"
-msgstr "Ej hanterat underkommando \"%s\""
+msgstr "Ej hanterat underkommando ”%s”"
 
 msgid "fsmonitor--daemon not supported on this platform"
 msgstr "fsmonitor--daemon stöds inte på denna plattform"
@@ -6375,11 +6413,11 @@
 
 #, c-format
 msgid "failed to parse '%s' value '%s'"
-msgstr "misslyckades tolka \"%s\" värde \"%s\""
+msgstr "misslyckades tolka ”%s” värde ”%s”"
 
 #, c-format
 msgid "cannot stat '%s'"
-msgstr "kan inte ta status på \"%s\""
+msgstr "kan inte ta status på ”%s”"
 
 #, c-format
 msgid ""
@@ -6401,6 +6439,9 @@
 msgid "pack unreferenced objects separately"
 msgstr "packa ej refererade objekt separat"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "med --cruft, begränsa storleken på nya onödiga paket"
+
 msgid "be more thorough (increased runtime)"
 msgstr "var mer grundlig (ökar körtiden)"
 
@@ -6431,20 +6472,19 @@
 
 #, c-format
 msgid "See \"git help gc\" for manual housekeeping.\n"
-msgstr "Se \"git help gc\" för manuell hushållning.\n"
+msgstr "Se ”git help gc” för manuell hushållning.\n"
 
 #, c-format
 msgid ""
 "gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)"
 msgstr ""
-"gc körs redan på maskinen \"%s\" pid %<PRIuMAX> (använd --force om så inte "
-"är fallet)"
+"gc körs redan på maskinen ”%s” pid %<PRIuMAX> (använd --force om så inte är "
+"fallet)"
 
 msgid ""
 "There are too many unreachable loose objects; run 'git prune' to remove them."
 msgstr ""
-"Det finns för många onåbara lösa objekt; kör \"git prune\" för att ta bort "
-"dem."
+"Det finns för många onåbara lösa objekt; kör ”git prune” för att ta bort dem."
 
 msgid ""
 "git maintenance run [--auto] [--[no-]quiet] [--task=<task>] [--schedule]"
@@ -6465,41 +6505,41 @@
 msgstr "kunde inte förhämta fjärrar"
 
 msgid "failed to start 'git pack-objects' process"
-msgstr "kunde inte starta \"git pack-objects\"-process"
+msgstr "kunde inte starta ”git pack-objects”-process"
 
 msgid "failed to finish 'git pack-objects' process"
-msgstr "kunde inte avsluta \"git pack-objects\"-process"
+msgstr "kunde inte avsluta ”git pack-objects”-process"
 
 msgid "failed to write multi-pack-index"
 msgstr "kunde inte skriva multi-pack-index"
 
 msgid "'git multi-pack-index expire' failed"
-msgstr "\"git multi-pack-index expire\" misslyckades"
+msgstr "”git multi-pack-index expire” misslyckades"
 
 msgid "'git multi-pack-index repack' failed"
-msgstr "\"git multi-pack-index repack\" misslyckades"
+msgstr "”git multi-pack-index repack” misslyckades"
 
 msgid ""
 "skipping incremental-repack task because core.multiPackIndex is disabled"
 msgstr ""
-"hoppar över \"incremental-repack\"-uppgift eftersom core.multiPackIndex är "
+"hoppar över ”incremental-repack”-uppgift eftersom core.multiPackIndex är "
 "inaktiverat"
 
 #, c-format
 msgid "lock file '%s' exists, skipping maintenance"
-msgstr "låsfilen \"%s\" finns, hoppar över underhåll"
+msgstr "låsfilen ”%s” finns, hoppar över underhåll"
 
 #, c-format
 msgid "task '%s' failed"
-msgstr "uppgiften \"%s\" misslyckades"
+msgstr "uppgiften ”%s” misslyckades"
 
 #, c-format
 msgid "'%s' is not a valid task"
-msgstr "\"%s\" är inte en giltig uppgift"
+msgstr "”%s” är inte en giltig uppgift"
 
 #, c-format
 msgid "task '%s' cannot be selected multiple times"
-msgstr "uppgiften \"%s\" kan inte väljas flera gånger"
+msgstr "uppgiften ”%s” kan inte väljas flera gånger"
 
 msgid "run tasks based on the state of the repository"
 msgstr "kör uppgifter baserad på arkivets tillstånd"
@@ -6524,29 +6564,29 @@
 
 #, c-format
 msgid "unable to add '%s' value of '%s'"
-msgstr "kan inte lägga till \"%s\"-värdet för \"%s\""
+msgstr "kan inte lägga till ”%s”-värdet för ”%s”"
 
 msgid "return success even if repository was not registered"
 msgstr "returnera framgång även om arkivet inte var registrerat"
 
 #, c-format
 msgid "unable to unset '%s' value of '%s'"
-msgstr "kan inte ta bort \"%s\"-värdet för \"%s\""
+msgstr "kan inte ta bort ”%s”-värdet för ”%s”"
 
 #, c-format
 msgid "repository '%s' is not registered"
-msgstr "arkivet \"%s\" har inte registrerats"
+msgstr "arkivet ”%s” har inte registrerats"
 
 #, c-format
 msgid "failed to expand path '%s'"
-msgstr "misslyckades expandera sökvägen \"%s\""
+msgstr "misslyckades expandera sökvägen ”%s”"
 
 msgid "failed to start launchctl"
 msgstr "misslyckades starta launchctl"
 
 #, c-format
 msgid "failed to create directories for '%s'"
-msgstr "misslyckades skapa kataloger för \"%s\""
+msgstr "misslyckades skapa kataloger för ”%s”"
 
 #, c-format
 msgid "failed to bootstrap service %s"
@@ -6559,8 +6599,7 @@
 msgstr "misslyckades starta schtasks"
 
 msgid "failed to run 'crontab -l'; your system might not support 'cron'"
-msgstr ""
-"misslyckades köra \"crontab -l\"; ditt system kanske inte stöder \"cron\""
+msgstr "misslyckades köra ”crontab -l”; ditt system kanske inte stöder ”cron”"
 
 msgid "failed to create crontab temporary file"
 msgstr "misslyckades skapa temporär crontab-fil"
@@ -6569,10 +6608,18 @@
 msgstr "misslyckades öppna temporär fil"
 
 msgid "failed to run 'crontab'; your system might not support 'cron'"
-msgstr "misslyckades köra \"crontab\"; ditt system kanske inte stöder \"cron\""
+msgstr "misslyckades köra ”crontab”; ditt system kanske inte stöder ”cron”"
 
 msgid "'crontab' died"
-msgstr "\"crontab\" dog"
+msgstr "”crontab” dog"
+
+#, c-format
+msgid "failed to delete '%s'"
+msgstr "misslyckades ta bort ”%s”"
+
+#, c-format
+msgid "failed to flush '%s'"
+msgstr "misslyckades spola ”%s”"
 
 msgid "failed to start systemctl"
 msgstr "misslyckades starta systemctl"
@@ -6581,16 +6628,8 @@
 msgstr "misslyckades att köra systemctl"
 
 #, c-format
-msgid "failed to delete '%s'"
-msgstr "misslyckades ta bort \"%s\""
-
-#, c-format
-msgid "failed to flush '%s'"
-msgstr "misslyckades spola \"%s\""
-
-#, c-format
 msgid "unrecognized --scheduler argument '%s'"
-msgstr "okänt argument för --scheduler, \"%s\""
+msgstr "okänt argument för --scheduler, ”%s”"
 
 msgid "neither systemd timers nor crontab are available"
 msgstr "varken systemd-timer eller crontab är tillgänglig"
@@ -6609,7 +6648,10 @@
 msgstr "schemaläggare"
 
 msgid "scheduler to trigger git maintenance run"
-msgstr "schemaläggare som utlöser \"git maintenance\"-körning"
+msgstr "schemaläggare som utlöser ”git maintenance”-körning"
+
+msgid "failed to set up maintenance schedule"
+msgstr "misslyckades uppdatera underhållsschema"
 
 msgid "failed to add repo to global config"
 msgstr "misslyckades lägga till arkiv till global konfiguration"
@@ -6642,12 +6684,16 @@
 msgstr "kunde inte läsa träd (%s)"
 
 #, c-format
+msgid "unable to read tree %s"
+msgstr "kunde inte läsa trädet %s"
+
+#, c-format
 msgid "unable to grep from object of type %s"
-msgstr "kunde inte \"grep\" från objekt av typen %s"
+msgstr "kunde inte ”grep” från objekt av typen %s"
 
 #, c-format
 msgid "switch `%c' expects a numerical value"
-msgstr "flaggan \"%c\" antar ett numeriskt värde"
+msgstr "flaggan ”%c” antar ett numeriskt värde"
 
 msgid "search in index instead of in the work tree"
 msgstr "sök i indexet istället för i arbetskatalogen"
@@ -6659,7 +6705,7 @@
 msgstr "sök i både spårade och ospårade filer"
 
 msgid "ignore files specified via '.gitignore'"
-msgstr "ignorera filer angivna i \".gitignore\""
+msgstr "ignorera filer angivna i ”.gitignore”"
 
 msgid "recursively search in each submodule"
 msgstr "sök varje undermodul rekursivt"
@@ -6685,8 +6731,8 @@
 msgid "search in subdirectories (default)"
 msgstr "sök i underkataloger (standard)"
 
-msgid "descend at most <depth> levels"
-msgstr "gå som mest ned <djup> nivåer"
+msgid "descend at most <n> levels"
+msgstr "gå som mest ned <n> nivåer"
 
 msgid "use extended POSIX regular expressions"
 msgstr "använd utökade POSIX-reguljära uttryck"
@@ -6893,7 +6939,7 @@
 
 #, c-format
 msgid "unrecognized help format '%s'"
-msgstr "okänt hjälpformat: \"%s\""
+msgstr "okänt hjälpformat: ”%s”"
 
 msgid "Failed to start emacsclient."
 msgstr "Misslyckades starta emacsclient."
@@ -6903,31 +6949,31 @@
 
 #, c-format
 msgid "emacsclient version '%d' too old (< 22)."
-msgstr "emacsclient version \"%d\" för gammal (< 22)."
+msgstr "emacsclient version ”%d” för gammal (< 22)."
 
 #, c-format
 msgid "failed to exec '%s'"
-msgstr "exec misslyckades för \"%s\""
+msgstr "exec misslyckades för ”%s”"
 
 #, c-format
 msgid ""
 "'%s': path for unsupported man viewer.\n"
 "Please consider using 'man.<tool>.cmd' instead."
 msgstr ""
-"\"%s\": sökväg för man-visare som ej stöds.\n"
-"Använd \"man.<verktyg>.cmd\" istället."
+"”%s”: sökväg för man-visare som ej stöds.\n"
+"Använd ”man.<verktyg>.cmd” istället."
 
 #, c-format
 msgid ""
 "'%s': cmd for supported man viewer.\n"
 "Please consider using 'man.<tool>.path' instead."
 msgstr ""
-"\"%s\": kommando för man-visare som stöds.\n"
-"Använd \"man.<verktyg>.path\" istället."
+"”%s”: kommando för man-visare som stöds.\n"
+"Använd ”man.<verktyg>.path” istället."
 
 #, c-format
 msgid "'%s': unknown man viewer."
-msgstr "\"%s\": okänd man-visare."
+msgstr "”%s”: okänd man-visare."
 
 msgid "no man viewer handled the request"
 msgstr "ingen man-visare hanterade förfrågan"
@@ -6937,7 +6983,7 @@
 
 #, c-format
 msgid "'%s' is aliased to '%s'"
-msgstr "\"%s\" är ett alias för \"%s\""
+msgstr "”%s” är ett alias för ”%s”"
 
 #, c-format
 msgid "bad alias.%s string: %s"
@@ -6945,20 +6991,19 @@
 
 #, c-format
 msgid "the '%s' option doesn't take any non-option arguments"
-msgstr "flaggan \"%s\" tar inte några argument som inte är flaggor"
+msgstr "flaggan ”%s” tar inte några argument som inte är flaggor"
 
 msgid ""
 "the '--no-[external-commands|aliases]' options can only be used with '--all'"
 msgstr ""
-"flaggorna '--no-[external-commands|aliases]' kan endast användas med \"--all"
-"\""
+"flaggorna '--no-[external-commands|aliases]' kan endast användas med ”--all”"
 
 #, c-format
 msgid "usage: %s%s"
 msgstr "användning: %s%s"
 
 msgid "'git help config' for more information"
-msgstr "\"git help config\" för mer information"
+msgstr "”git help config” för mer information"
 
 msgid ""
 "git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
@@ -7033,7 +7078,7 @@
 msgstr "okänd objekttyp %d"
 
 msgid "cannot pread pack file"
-msgstr "kan inte utföra \"pread\" på paketfil"
+msgstr "kan inte utföra ”pread” på paketfil"
 
 #, c-format
 msgid "premature end of pack file, %<PRIuMAX> byte missing"
@@ -7049,10 +7094,6 @@
 msgstr "SHA1-KOLLISION UPPTÄCKT VID %s !"
 
 #, c-format
-msgid "unable to read %s"
-msgstr "kunde inte läsa %s"
-
-#, c-format
 msgid "cannot read existing object info %s"
 msgstr "kan inte läsa information om befintligt objekt %s"
 
@@ -7084,7 +7125,7 @@
 msgstr "paketet är trasigt (SHA1 stämmer inte)"
 
 msgid "cannot fstat packfile"
-msgstr "kan inte utföra \"fstat\" på paketfil"
+msgstr "kan inte utföra ”fstat” på paketfil"
 
 msgid "pack has junk at the end"
 msgstr "paket har skräp i slutet"
@@ -7120,7 +7161,7 @@
 
 #, c-format
 msgid "unable to deflate appended object (%d)"
-msgstr "kunde inte utföra \"deflate\" på tillagt objekt (%d)"
+msgstr "kunde inte utföra ”deflate” på tillagt objekt (%d)"
 
 #, c-format
 msgid "local object %s is corrupt"
@@ -7128,19 +7169,19 @@
 
 #, c-format
 msgid "packfile name '%s' does not end with '.%s'"
-msgstr "paketfilnamnet \"%s\" slutar inte med \".%s\""
+msgstr "paketfilnamnet ”%s” slutar inte med ”.%s”"
 
 #, c-format
 msgid "cannot write %s file '%s'"
-msgstr "kan inte ta skriva %s-fil \"%s\""
+msgstr "kan inte ta skriva %s-fil ”%s”"
 
 #, c-format
 msgid "cannot close written %s file '%s'"
-msgstr "kan inte stänga skriven %s-fil \"%s\""
+msgstr "kan inte stänga skriven %s-fil ”%s”"
 
 #, c-format
 msgid "unable to rename temporary '*.%s' file to '%s'"
-msgstr "kunde inte byta namn på temporär \"*.%s\"-fil till \"%s\""
+msgstr "kunde inte byta namn på temporär ”*.%s”-fil till ”%s”"
 
 msgid "error while closing pack file"
 msgstr "fel vid stängning av paketfil"
@@ -7151,11 +7192,11 @@
 
 #, c-format
 msgid "Cannot open existing pack file '%s'"
-msgstr "Kan inte öppna befintlig paketfil \"%s\""
+msgstr "Kan inte öppna befintlig paketfil ”%s”"
 
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
-msgstr "Kan inte öppna befintlig paket-idx-fil för \"%s\""
+msgstr "Kan inte öppna befintlig paket-idx-fil för ”%s”"
 
 #, c-format
 msgid "non delta: %d object"
@@ -7178,7 +7219,7 @@
 
 #, c-format
 msgid "unknown hash algorithm '%s'"
-msgstr "okänd hashningsalgoritm \"%s\""
+msgstr "okänd hashningsalgoritm ”%s”"
 
 msgid "--stdin requires a git repository"
 msgstr "--stdin kräver ett git-arkiv"
@@ -7189,85 +7230,16 @@
 msgid "fsck error in pack objects"
 msgstr "fsck-fel i packat objekt"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "kan inte ta status på mallen \"%s\""
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "kan inte öppna katalogen (opendir) \"%s\""
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "kan inte läsa länk (readlink) \"%s\""
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "kan inte skapa symbolisk länk \"%s\" \"%s\""
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "kan inte kopiera \"%s\" till \"%s\""
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "ignorerar mallen %s"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "mallarna hittades inte i %s"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "kopierade inte mallar från \"%s\": %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "ogiltigt namn på första gren: \"%s\""
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "kan inte hantera filtyp %d"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "kan inte flytta %s till %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "försöker initiera arkivet på nytt med annan hash"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s finns redan"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: ignorerade --initial-branch=%s"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "Ominitierade befintligt delat Git-arkiv i %s%s\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "Ominitierade befintligt Git-arkiv i %s%s\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "Initierade tomt delat Git-arkiv i %s%s\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "Initierade tomt Git-arkiv i %s%s\n"
-
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<mallkatalog>]\n"
 "         [--separate-git-dir <git-kat>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <grennamn> | --initial-branch=<grennamn>]\n"
 "         [--shared[=<behörigheter>]] [<katalog>]"
 
@@ -7304,18 +7276,19 @@
 
 #, c-format
 msgid "Cannot access work tree '%s'"
-msgstr "Kan inte komma åt arbetskatalogen \"%s\""
+msgstr "Kan inte komma åt arbetskatalogen ”%s”"
 
 msgid "--separate-git-dir incompatible with bare repository"
 msgstr "--separate-git-dir är inkompatibelt med naket arkiv"
 
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <symbol>[(=|:)<värde>])...]\n"
+"                       [(--trailer (<nyckel>|<nyckelAlias>)"
+"[(=|:)<värde>])...]\n"
 "                       [--parse] [<fil>...]"
 
 msgid "edit files in place"
@@ -7324,6 +7297,9 @@
 msgid "trim empty trailers"
 msgstr "ta bort tomma släprader"
 
+msgid "placement"
+msgstr "placering"
+
 msgid "where to place the new trailer"
 msgstr "var nya släprader ska placeras"
 
@@ -7336,17 +7312,17 @@
 msgid "output only the trailers"
 msgstr "visa endast släprader"
 
-msgid "do not apply config rules"
-msgstr "använd inte regler från konfigurationen"
+msgid "do not apply trailer.* configuration variables"
+msgstr "tillämpa inte konfigurationsvariablerna trailer.*"
 
-msgid "join whitespace-continued values"
-msgstr "slå ihop värden avdelade med blanksteg"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "omformatera flerradiga släpradsvärden som enradsvärden"
 
-msgid "set parsing options"
-msgstr "välj tolkningsalternativ"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "alias för --only-trailers --only-input --unfold"
 
-msgid "do not treat --- specially"
-msgstr "tolka inte --- speciellt"
+msgid "do not treat \"---\" as the end of input"
+msgstr "tolka inte ”---” som slut på indata"
 
 msgid "trailer(s) to add"
 msgstr "släprad(er) att lägga till"
@@ -7435,6 +7411,10 @@
 msgid "not a range"
 msgstr "inte ett intervall"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "kan inte läsa grenbeskrivningsfilen ”%s”"
+
 msgid "cover letter needs email format"
 msgstr "omslagsbrevet behöver e-postformat"
 
@@ -7457,7 +7437,7 @@
 
 #, c-format
 msgid "failed to resolve '%s' as a valid ref"
-msgstr "misslyckades slå upp \"%s\" som en giltig referens"
+msgstr "misslyckades slå upp ”%s” som en giltig referens"
 
 msgid "could not find exact merge base"
 msgstr "kunde inte hitta exakt sammanslagningsbas"
@@ -7488,7 +7468,7 @@
 
 #, c-format
 msgid "using '%s' as range-diff origin of current series"
-msgstr "använd \"%s\" som intervalldiff-ursprung för aktuell serie"
+msgstr "använd ”%s” som intervalldiff-ursprung för aktuell serie"
 
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "använd [PATCH n/m] även för en ensam patch"
@@ -7509,7 +7489,7 @@
 msgstr "sfx"
 
 msgid "use <sfx> instead of '.patch'"
-msgstr "använd <sfx> istället för \".patch\""
+msgstr "använd <sfx> istället för ”.patch”"
 
 msgid "start numbering patches at <n> instead of 1"
 msgstr "börja numrera patchar på <n> istället för 1"
@@ -7532,6 +7512,9 @@
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "skapa delar av omslagsbrevet baserat på grenbeskrivelsen"
 
+msgid "use branch description from file"
+msgstr "använd grenbeskrivningar från fil"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "använd [<prefix>] istället för [PATCH]"
 
@@ -7566,10 +7549,10 @@
 msgstr "epost"
 
 msgid "add To: header"
-msgstr "lägg till mottagarhuvud (\"To:\")"
+msgstr "lägg till mottagarhuvud (”To:”)"
 
 msgid "add Cc: header"
-msgstr "lägg till kopiehuvud (\"Cc:\")"
+msgstr "lägg till kopiehuvud (”Cc:”)"
 
 msgid "ident"
 msgstr "ident"
@@ -7646,7 +7629,7 @@
 
 #, c-format
 msgid "could not create directory '%s'"
-msgstr "kunde inte skapa katalogen \"%s\""
+msgstr "kunde inte skapa katalogen ”%s”"
 
 msgid "--interdiff requires --cover-letter or single patch"
 msgstr "--interdiff kräver --cover-letter eller ensam patch"
@@ -7670,7 +7653,7 @@
 
 #, c-format
 msgid "unable to read signature file '%s'"
-msgstr "kunde inte läsa signaturfil \"%s\""
+msgstr "kunde inte läsa signaturfil ”%s”"
 
 msgid "Generating patches"
 msgstr "Skapar patchar"
@@ -7687,12 +7670,16 @@
 msgstr "Kunde inte hitta en spårad fjärrgren, ange <uppström> manuellt.\n"
 
 #, c-format
+msgid "could not get object info about '%s'"
+msgstr "kunde inte hämta objektinfo om ”%s”"
+
+#, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
-msgstr "felaktigt ls-files-format: elementet \"%s\" börjar inte med \"(\""
+msgstr "felaktigt ls-files-format: elementet ”%s” börjar inte med ”(”"
 
 #, c-format
 msgid "bad ls-files format: element '%s' does not end in ')'"
-msgstr "felaktigt ls-files-format: elementet \"%s\" slutar inte med \")\""
+msgstr "felaktigt ls-files-format: elementet ”%s” slutar inte med ”)”"
 
 #, c-format
 msgid "bad ls-files format: %%%.*s"
@@ -7708,10 +7695,10 @@
 msgstr "identifiera filstatus med taggar"
 
 msgid "use lowercase letters for 'assume unchanged' files"
-msgstr "använd små bokstäver för \"anta oförändrade\"-filer"
+msgstr "använd små bokstäver för ”anta oförändrade”-filer"
 
 msgid "use lowercase letters for 'fsmonitor clean' files"
-msgstr "använd små bokstäver för \"fsmonitor clean\"-filer"
+msgstr "använd små bokstäver för ”fsmonitor clean”-filer"
 
 msgid "show cached files in the output (default)"
 msgstr "visa cachade filer i utdata (standard)"
@@ -7735,7 +7722,7 @@
 msgstr "visa filer i filsystemet som behöver tas bort"
 
 msgid "show 'other' directories' names only"
-msgstr "visa endast namn för \"andra\" kataloger"
+msgstr "visa endast namn för ”andra” kataloger"
 
 msgid "show line endings of files"
 msgstr "visa radslut i filer"
@@ -7747,7 +7734,7 @@
 msgstr "visa ej sammanslagna filer i utdata"
 
 msgid "show resolve-undo information"
-msgstr "visa \"resolve-undo\"-information"
+msgstr "visa ”resolve-undo”-information"
 
 msgid "skip files matching pattern"
 msgstr "hoppa över filer som motsvarar mönster"
@@ -7829,16 +7816,12 @@
 msgstr "git ls-tree [<flaggor>] <träd-igt> [<sökväg>...]"
 
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "kunde inte hämta objektinfo om \"%s\""
-
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
-msgstr "felaktigt ls-tree-format: elementet \"%s\" börjar inte med \"(\""
+msgstr "felaktigt ls-tree-format: elementet ”%s” börjar inte med ”(”"
 
 #, c-format
 msgid "bad ls-tree format: element '%s' does not end in ')'"
-msgstr "felaktigt ls-tree-format: elementet \"%s\" slutar inte med \")\""
+msgstr "felaktigt ls-tree-format: elementet ”%s” slutar inte med ”)”"
 
 #, c-format
 msgid "bad ls-tree format: %%%.*s"
@@ -7882,7 +7865,7 @@
 msgstr "behåll ärenderad"
 
 msgid "keep non patch brackets in subject"
-msgstr "behåll hakparanterser som inte är \"patch\" i ärenderaden"
+msgstr "behåll hakparanterser som inte är ”patch” i ärenderaden"
 
 msgid "copy Message-ID to the end of commit message"
 msgstr "kopiera Message-ID till slutet av incheckningsmeddelandet"
@@ -7916,7 +7899,7 @@
 
 #, c-format
 msgid "empty mbox: '%s'"
-msgstr "tom mbox: \"%s\""
+msgstr "tom mbox: ”%s”"
 
 msgid "git merge-base [-a | --all] <commit> <commit>..."
 msgstr "git merge-base [-a | --all] <incheckning> <incheckning>..."
@@ -7955,9 +7938,18 @@
 "git merge-file [<alternativ>] [-L <namn1> [-L <orig> [-L <namn2>]]] <fil1> "
 "<origfil> <fil2>"
 
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"flaggan diff-algorithm godtar ”myers”, ”minimal”, ”patience” och ”histogram”"
+
 msgid "send results to standard output"
 msgstr "sänd resultat till standard ut"
 
+msgid "use object IDs instead of filenames"
+msgstr "använd objekt-ID istället för filnamn"
+
 msgid "use a diff3 based merge"
 msgstr "använd diff3-baserad sammanslagning"
 
@@ -7973,6 +7965,12 @@
 msgid "for conflicts, use a union version"
 msgstr "för konflikter, använd en förenad version"
 
+msgid "<algorithm>"
+msgstr "<algoritm>"
+
+msgid "choose a diff algorithm"
+msgstr "välj en diff-algoritm"
+
 msgid "for conflicts, use this marker size"
 msgstr "för konflikter, använd denna markörstorlek"
 
@@ -7983,12 +7981,19 @@
 msgstr "sätt etiketter för fil1/origfil/fil2"
 
 #, c-format
+msgid "object '%s' does not exist"
+msgstr "objektet ”%s” finns inte"
+
+msgid "Could not write object file"
+msgstr "Kunde inte skriva objektfilen"
+
+#, c-format
 msgid "unknown option %s"
 msgstr "okänd flagga %s"
 
 #, c-format
 msgid "could not parse object '%s'"
-msgstr "kunde inte tolka objektet \"%s\""
+msgstr "kunde inte tolka objektet ”%s”"
 
 #, c-format
 msgid "cannot handle more than %d base. Ignoring %s."
@@ -8001,7 +8006,7 @@
 
 #, c-format
 msgid "could not resolve ref '%s'"
-msgstr "kunde inte bestämma referensen \"%s\""
+msgstr "kunde inte bestämma referensen ”%s”"
 
 #, c-format
 msgid "Merging %s with %s\n"
@@ -8043,15 +8048,22 @@
 msgid "specify a merge-base for the merge"
 msgstr "ange en sammanslagningsbas för sammanslagningen"
 
+msgid "option=value"
+msgstr "alternativ=värde"
+
+msgid "option for selected merge strategy"
+msgstr "alternativ för vald sammanslagningsstrategi"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge är inkompatibelt med andra flaggor"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base är inkompatibel med --stdin"
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "okänd strategiflagga: -X%s"
 
 #, c-format
 msgid "malformed input line: '%s'."
-msgstr "felaktig indatarad: \"%s\"."
+msgstr "felaktig indatarad: ”%s”."
 
 #, c-format
 msgid "merging cannot continue; got unclean result of %d"
@@ -8061,15 +8073,15 @@
 msgstr "git merge [<flaggor>] [<incheckning>...]"
 
 msgid "switch `m' requires a value"
-msgstr "flaggan \"m\" behöver ett värde"
+msgstr "flaggan ”m” behöver ett värde"
 
 #, c-format
 msgid "option `%s' requires a value"
-msgstr "flaggan \"%s\" behöver ett värde"
+msgstr "flaggan ”%s” behöver ett värde"
 
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
-msgstr "Kunde inte hitta sammanslagningsstrategin \"%s\".\n"
+msgstr "Kunde inte hitta sammanslagningsstrategin ”%s”.\n"
 
 #, c-format
 msgid "Available strategies are:"
@@ -8116,12 +8128,6 @@
 msgid "merge strategy to use"
 msgstr "sammanslagningsstrategi att använda"
 
-msgid "option=value"
-msgstr "alternativ=värde"
-
-msgid "option for selected merge strategy"
-msgstr "alternativ för vald sammanslagningsstrategi"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "incheckningsmeddelande för (icke snabbspolande) sammanslagning"
 
@@ -8169,7 +8175,7 @@
 
 #, c-format
 msgid "'%s' does not point to a commit"
-msgstr "\"%s\" verkar inte peka på en incheckning"
+msgstr "”%s” verkar inte peka på en incheckning"
 
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
@@ -8182,22 +8188,17 @@
 msgstr "Hanterar inte något annat än en sammanslagning av två huvuden."
 
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "okänd strategiflagga: -X%s"
-
-#, c-format
 msgid "unable to write %s"
 msgstr "kunde inte skriva %s"
 
 #, c-format
 msgid "Could not read from '%s'"
-msgstr "Kunde inte läsa från \"%s\""
+msgstr "Kunde inte läsa från ”%s”"
 
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
-"Checkar inte in sammanslagningen; använd \"git commit\" för att slutföra "
-"den.\n"
+"Checkar inte in sammanslagningen; använd ”git commit” för att slutföra den.\n"
 
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -8217,7 +8218,7 @@
 "Lines starting with '%c' will be ignored, and an empty message aborts\n"
 "the commit.\n"
 msgstr ""
-"Rader som inleds med \"%c\" kommer ignoreras, och ett tomt meddelande\n"
+"Rader som inleds med ”%c” kommer ignoreras, och ett tomt meddelande\n"
 "avbryter incheckningen.\n"
 
 msgid "Empty commit message."
@@ -8247,11 +8248,11 @@
 
 #, c-format
 msgid "Bad value '%s' in environment '%s'"
-msgstr "Felaktigt värde \"%s\" i miljövariabeln \"%s\""
+msgstr "Felaktigt värde ”%s” i miljövariabeln ”%s”"
 
 #, c-format
 msgid "could not close '%s'"
-msgstr "kunde inte stänga \"%s\""
+msgstr "kunde inte stänga ”%s”"
 
 #, c-format
 msgid "not something we can merge in %s: %s"
@@ -8283,11 +8284,11 @@
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you merge."
 msgstr ""
-"Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns).\n"
+"Du har inte avslutat din ”cherry-pick” (CHERRY_PICK_HEAD finns).\n"
 "Checka in dina ändringar innan du slår ihop."
 
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
-msgstr "Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns)."
+msgstr "Du har inte avslutat din ”cherry-pick” (CHERRY_PICK_HEAD finns)."
 
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr "Ingen incheckning angiven och merge.defaultToUpstream är ej satt."
@@ -8353,7 +8354,7 @@
 
 #, c-format
 msgid "When finished, apply stashed changes with `git stash pop`\n"
-msgstr "När färdig, applicerade sparade ändringar med \"git stash pop\"\n"
+msgstr "När färdig, applicerade sparade ändringar med ”git stash pop”\n"
 
 #, c-format
 msgid "warning: tag input does not pass fsck: %s"
@@ -8369,11 +8370,11 @@
 
 #, c-format
 msgid "could not read tagged object '%s'"
-msgstr "kunde inte läsa det taggade objektet \"%s\""
+msgstr "kunde inte läsa det taggade objektet ”%s”"
 
 #, c-format
 msgid "object '%s' tagged as '%s', but is a '%s' type"
-msgstr "objektet \"%s\" taggat som \"%s\", men är av typen \"%s\""
+msgstr "objektet ”%s” taggat som ”%s”, men är av typen ”%s”"
 
 msgid "could not read from stdin"
 msgstr "kunde inte läsa från standard in"
@@ -8449,7 +8450,7 @@
 
 msgid "Please stage your changes to .gitmodules or stash them to proceed"
 msgstr ""
-"Köa dina ändringar i .gitmodules eller använd \"stash\" för att fortsätta"
+"Köa dina ändringar i .gitmodules eller använd ”stash” för att fortsätta"
 
 #, c-format
 msgid "%.*s is in index"
@@ -8463,11 +8464,11 @@
 
 #, c-format
 msgid "destination '%s' is not a directory"
-msgstr "destinationen \"%s\" är ingen katalog"
+msgstr "destinationen ”%s” är ingen katalog"
 
 #, c-format
 msgid "Checking rename of '%s' to '%s'\n"
-msgstr "Kontrollerar namnbyte av \"%s\" till \"%s\"\n"
+msgstr "Kontrollerar namnbyte av ”%s” till ”%s”\n"
 
 msgid "bad source"
 msgstr "felaktig källa"
@@ -8478,8 +8479,8 @@
 msgid "can not move directory into itself"
 msgstr "kan inte flytta katalog till sig själv"
 
-msgid "cannot move directory over file"
-msgstr "kan inte flytta katalog över fil"
+msgid "destination already exists"
+msgstr "destinationen finns redan"
 
 msgid "source directory is empty"
 msgstr "källkatalogen är tom"
@@ -8492,7 +8493,7 @@
 
 #, c-format
 msgid "overwriting '%s'"
-msgstr "skriver över \"%s\""
+msgstr "skriver över ”%s”"
 
 msgid "Cannot overwrite"
 msgstr "Kan inte skriva över"
@@ -8516,7 +8517,7 @@
 
 #, c-format
 msgid "renaming '%s' failed"
-msgstr "misslyckades byta namn på \"%s\""
+msgstr "misslyckades byta namn på ”%s”"
 
 msgid "git name-rev [<options>] <commit>..."
 msgstr "git name-rev [<flaggor>] <incheckning>..."
@@ -8549,7 +8550,7 @@
 msgstr "annotera text från standard in"
 
 msgid "allow to print `undefined` names (default)"
-msgstr "tillåt att skriva \"odefinierade\" namn (standard)"
+msgstr "tillåt att skriva ”odefinierade” namn (standard)"
 
 msgid "dereference tags in the input (internal use)"
 msgstr "avreferera taggar i indata (används internt)"
@@ -8558,22 +8559,26 @@
 msgstr "git notes [--ref <anteckningsref>] [list [<objekt>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <anteckningsref>] add [-f] [--allow-empty] [-m <medd> | -F "
-"<fil> | (-c | -C) <objekt>] [<objekt>]"
+"git notes [--ref <anteckningsref>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<styckebrytning>] [--[no-]stripspace] [-m <medd> "
+"| -F <fil> | (-c | -C) <objekt>] [<objekt>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <anteckningsref>] copy [-f] <från-objekt> <till-objekt>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <anteckningsref>] append [--allow-empty] [-m <medd> | -F "
-"<fil> | (-c | -C) <objekt>] [<objekt>]"
+"git notes [--ref <anteckningsref>] append [--allow-empty] [--"
+"[no-]separator|--separator=<styckebrytning>] [--[no-]stripspace] [-m <medd> "
+"| -F <fil> | (-c | -C) <objekt>] [<objekt>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <anteckningsref>] edit [--allow-empty] [<objekt>]"
@@ -8637,14 +8642,14 @@
 
 #, c-format
 msgid "unable to start 'show' for object '%s'"
-msgstr "kunde inte starta \"show\" för objektet \"%s\""
+msgstr "kunde inte starta ”show” för objektet ”%s”"
 
 msgid "could not read 'show' output"
-msgstr "kunde inte läsa utdata från \"show\""
+msgstr "kunde inte läsa utdata från ”show”"
 
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
-msgstr "kunde inte avsluta \"show\" för objektet \"%s\""
+msgstr "kunde inte avsluta ”show” för objektet ”%s”"
 
 msgid "please supply the note contents using either -m or -F option"
 msgstr "ange innehåll för anteckningen med antingen -m eller -F"
@@ -8658,30 +8663,30 @@
 
 #, c-format
 msgid "could not open or read '%s'"
-msgstr "kunde inte öppna eller läsa \"%s\""
+msgstr "kunde inte öppna eller läsa ”%s”"
 
 #, c-format
 msgid "failed to resolve '%s' as a valid ref."
-msgstr "kunde inte slå upp \"%s\" som en giltig referens."
+msgstr "kunde inte slå upp ”%s” som en giltig referens."
 
 #, c-format
 msgid "failed to read object '%s'."
-msgstr "kunde inte läsa objektet \"%s\"."
+msgstr "kunde inte läsa objektet ”%s”."
 
 #, c-format
 msgid "cannot read note data from non-blob object '%s'."
-msgstr "kan inte läsa anteckningsdata från icke-blob-objektet \"%s\"."
+msgstr "kan inte läsa anteckningsdata från icke-blob-objektet ”%s”."
 
 #, c-format
 msgid "failed to copy notes from '%s' to '%s'"
-msgstr "misslyckades kopiera anteckningar från \"%s\" till \"%s\""
+msgstr "misslyckades kopiera anteckningar från ”%s” till ”%s”"
 
 #. TRANSLATORS: the first %s will be replaced by a git
 #. notes command: 'add', 'merge', 'remove', etc.
 #.
 #, c-format
 msgid "refusing to %s notes in %s (outside of refs/notes/)"
-msgstr "vägrar utföra \"%s\" på anteckningar i %s (utanför refs/notes/)"
+msgstr "vägrar utföra ”%s” på anteckningar i %s (utanför refs/notes/)"
 
 #, c-format
 msgid "no note found for object %s."
@@ -8705,13 +8710,22 @@
 msgid "replace existing notes"
 msgstr "ersätt befintliga anteckningar"
 
+msgid "<paragraph-break>"
+msgstr "<styckebrytning>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "sätt in <styckebrytning> mellan stycken"
+
+msgid "remove unnecessary whitespace"
+msgstr "ta bort onödiga blanksteg"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
 "existing notes"
 msgstr ""
 "Kan inte lägga till anteckningar. Hittade befintliga anteckningar för "
-"objektet %s. Använd \"-f\" för att skriva över befintliga anteckningar"
+"objektet %s. Använd ”-f” för att skriva över befintliga anteckningar"
 
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
@@ -8736,7 +8750,7 @@
 "existing notes"
 msgstr ""
 "Kan inte kopiera anteckningar. Hittade befintliga anteckningar för objektet "
-"%s. Använd \"-f\" för att skriva över befintliga anteckningar"
+"%s. Använd ”-f” för att skriva över befintliga anteckningar"
 
 #, c-format
 msgid "missing notes on source object %s. Cannot copy."
@@ -8747,8 +8761,8 @@
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
 "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
 msgstr ""
-"Flaggorna -m/-F/-c/-C rekommenderas inte för underkommandot \"edit\".\n"
-"Använd \"git notes add -f -m/-F/-c/-C\" istället.\n"
+"Flaggorna -m/-F/-c/-C rekommenderas inte för underkommandot ”edit”.\n"
+"Använd ”git notes add -f -m/-F/-c/-C” istället.\n"
 
 msgid "failed to delete ref NOTES_MERGE_PARTIAL"
 msgstr "misslyckades ta bort referensen NOTES_MERGE_PARTIAL"
@@ -8757,7 +8771,7 @@
 msgstr "misslyckades ta bort referensen NOTES_MERGE_REF"
 
 msgid "failed to remove 'git notes merge' worktree"
-msgstr "misslyckades ta bort arbetskatalogen för \"git notes merge\""
+msgstr "misslyckades ta bort arbetskatalogen för ”git notes merge”"
 
 msgid "failed to read ref NOTES_MERGE_PARTIAL"
 msgstr "misslyckades läsa references NOTES_MERGE_PARTIAL"
@@ -8830,12 +8844,12 @@
 "abort'.\n"
 msgstr ""
 "Automatisk sammanslagning av anteckningar misslyckades. Rätta konflikter i "
-"%s och checka in resultatet med \"git notes merge --commit\", eller avbryt "
-"sammanslagningen med \"git notes merge --abort\".\n"
+"%s och checka in resultatet med ”git notes merge --commit”, eller avbryt "
+"sammanslagningen med ”git notes merge --abort”.\n"
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
-msgstr "Kunde inte slå upp \"%s\" som en giltig referens."
+msgstr "Kunde inte slå upp ”%s” som en giltig referens."
 
 #, c-format
 msgid "Object %s has no note\n"
@@ -8861,7 +8875,7 @@
 
 #, c-format
 msgid "unknown subcommand: `%s'"
-msgstr "okänt underkommando: \"%s\""
+msgstr "okänt underkommando: ”%s”"
 
 msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
 msgstr "git pack-objects --stdout [<flaggor>] [< <reflista> | < <objektlista>]"
@@ -8912,7 +8926,7 @@
 
 #, c-format
 msgid "failed utime() on %s"
-msgstr "\"utime()\" misslyckades på %s"
+msgstr "”utime()” misslyckades på %s"
 
 msgid "failed to write bitmap index"
 msgstr "misslyckade skriva bitkarteindex"
@@ -8973,6 +8987,10 @@
 msgstr "deltaräknaren är inkonsekvent"
 
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "felaktigt värde för pack.allowPackReuse: ”%s”"
+
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -8992,7 +9010,7 @@
 
 #, c-format
 msgid "could not find pack '%s'"
-msgstr "kunde inte hitta paketet \"%s\""
+msgstr "kunde inte hitta paketet ”%s”"
 
 #, c-format
 msgid "packfile %s cannot be accessed"
@@ -9038,11 +9056,11 @@
 
 #, c-format
 msgid "not a rev '%s'"
-msgstr "inte en referens \"%s\""
+msgstr "inte en referens ”%s”"
 
 #, c-format
 msgid "bad revision '%s'"
-msgstr "felaktig revision \"%s\""
+msgstr "felaktig revision ”%s”"
 
 msgid "unable to add recent objects"
 msgstr "kan inte lägga till nya objekt"
@@ -9053,7 +9071,13 @@
 
 #, c-format
 msgid "bad index version '%s'"
-msgstr "felaktig indexversion \"%s\""
+msgstr "felaktig indexversion ”%s”"
+
+msgid "show progress meter during object writing phase"
+msgstr "visa förloppsindikator under objektskrivningsfasen"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "som --all-progress när förloppsindikatorn visas"
 
 msgid "<version>[,<offset>]"
 msgstr "<version>[,<offset>]"
@@ -9152,7 +9176,7 @@
 msgstr "komprimeringsgrad för paket"
 
 msgid "do not hide commits by grafts"
-msgstr "göm inte incheckningar med ympningar (\"grafts\")"
+msgstr "göm inte incheckningar med ympningar (”grafts”)"
 
 msgid "use a bitmap index if available to speed up counting objects"
 msgstr "använd bitkartindex om tillgängligt för att räkna objekt snabbare"
@@ -9201,9 +9225,6 @@
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin kan inte användas för att bygga ett indexerbart paket"
 
-msgid "cannot use --filter without --stdout"
-msgstr "kan inte använda --filter utan --stdout"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "kan inte använda --filter med --stdin-packs"
 
@@ -9216,19 +9237,16 @@
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "kan inte använda --stdin-packs med --cruft"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "kan inte använda --max-pack-size med --cruft"
-
 msgid "Enumerating objects"
 msgstr "Räknar upp objekt"
 
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Totalt %<PRIu32> (delta %<PRIu32>), återanvände %<PRIu32> (delta %<PRIu32>), "
-"paket-återanvända %<PRIu32>"
+"paket-återanvända %<PRIu32> (från %<PRIuMAX>)"
 
 msgid ""
 "'git pack-redundant' is nominated for removal.\n"
@@ -9237,14 +9255,21 @@
 "and let us know you still use it by sending an e-mail\n"
 "to <git@vger.kernel.org>.  Thanks.\n"
 msgstr ""
-"\"git pack-redundant\" har nominerats för borttagning.\n"
+"”git pack-redundant” har nominerats för borttagning.\n"
 "Om du fortfarande använder kommandot, lägg till flaggan\n"
-"\"--i-still-use-this\" på kommandoraden och berätta för\n"
+"”--i-still-use-this” på kommandoraden och berätta för\n"
 "oss att du fortfarande använder det på e-post till\n"
 "<git@vger.kernel.org>. Tack.\n"
 
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "vägrar köra utan --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <mönster>] [--exclude "
+"<mönster>]"
 
 msgid "pack everything"
 msgstr "packa allt"
@@ -9252,6 +9277,12 @@
 msgid "prune loose refs (default)"
 msgstr "ta bort lösa referenser (standard)"
 
+msgid "references to include"
+msgstr "referenser att ta med"
+
+msgid "references to exclude"
+msgstr "referenser att utesluta"
+
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
 
@@ -9277,7 +9308,7 @@
 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"
+msgstr "kan inte rensa i ett ”precious-objekt”-arkiv"
 
 msgid "git pull [<options>] [<repository> [<refspec>...]]"
 msgstr "git pull [<flaggor>] [<arkiv> [<refspec>...]]"
@@ -9309,6 +9340,12 @@
 msgid "number of submodules pulled in parallel"
 msgstr "antal undermoduler som hämtas parallellt"
 
+msgid "use IPv4 addresses only"
+msgstr "använd endast IPv4-adresser"
+
+msgid "use IPv6 addresses only"
+msgstr "använd endast IPv6-adresser"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9335,7 +9372,7 @@
 "a branch. Because this is not the default configured remote\n"
 "for your current branch, you must specify a branch on the command line."
 msgstr ""
-"Du bad om att hämta från fjärren \"%s\", men angav inte någon\n"
+"Du bad om att hämta från fjärren ”%s”, men angav inte någon\n"
 "gren. Eftersom det inte är den fjärr som är konfigurerad som\n"
 "standard för aktuell gren måste du ange en gren på kommandoraden."
 
@@ -9369,7 +9406,7 @@
 "Your configuration specifies to merge with the ref '%s'\n"
 "from the remote, but no such ref was fetched."
 msgstr ""
-"Dina inställningar anger sammanslagning med referensen \"%s\"\n"
+"Dina inställningar anger sammanslagning med referensen ”%s”\n"
 "från fjärren, men någon sådan referens togs inte emot."
 
 #, c-format
@@ -9396,13 +9433,13 @@
 msgstr ""
 "Du har avvikande grenar och måste ange hur de skall förlikas.\n"
 "Du kan göra detta genom att köra ett av följande kommando innan du\n"
-"gör \"pull\" nästa gång: \n"
+"gör ”pull” nästa gång: \n"
 "\n"
 "  git config pull.rebase false  # sammanslagning\n"
 "  git config pull.rebase true   # ombasering\n"
 "  git config pull.ff only       # endast snabbspolning\n"
 "\n"
-"Du kan ersätta \"git config\" med \"git config --global\" för att välja en\n"
+"Du kan ersätta ”git config” med ”git config --global” för att välja en\n"
 "förvald inställning för alla arkiv. Du kan också ange --rebase, --no-rebase\n"
 "eller --ff-only på kommandoraden för att överstyra det konfigurerade\n"
 "förvalet vid körning.\n"
@@ -9413,8 +9450,8 @@
 msgid "pull with rebase"
 msgstr "pull med ombasering"
 
-msgid "please commit or stash them."
-msgstr "checka in eller använd \"stash\" på dem."
+msgid "Please commit or stash them."
+msgstr "Checka in eller använd ”stash” på dem."
 
 #, c-format
 msgid ""
@@ -9471,8 +9508,8 @@
 "To choose either option permanently, see push.default in 'git help config'.\n"
 msgstr ""
 "\n"
-"För att välja ett av alternativen permanent, se push.default i \"git help "
-"config\".\n"
+"För att välja ett av alternativen permanent, se push.default i ”git help "
+"config”.\n"
 
 msgid ""
 "\n"
@@ -9482,9 +9519,9 @@
 msgstr ""
 "\n"
 "För att undvika att en uppströmsgren automatiskt konfigureras när dess namn\n"
-"inte motsvarar den lokala grenen, se värdet \"simple\" i branch."
+"inte motsvarar den lokala grenen, se värdet ”simple” i branch."
 "autoSetupMerge\n"
-"i \"git help config\".\n"
+"i ”git help config”.\n"
 
 #, c-format
 msgid ""
@@ -9531,7 +9568,7 @@
 msgstr ""
 "\n"
 "För att detta ska ske automatiskt för grenar som saknar en spårande\n"
-"uppströmsgren, se \"push.autoSetupRemote\" i \"git help config\".\n"
+"uppströmsgren, se ”push.autoSetupRemote” i ”git help config”.\n"
 
 #, c-format
 msgid ""
@@ -9555,7 +9592,7 @@
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 "Du angav inga referensspecifikationer att sända, och push.default är "
-"\"nothing\"."
+"”nothing”."
 
 #, c-format
 msgid ""
@@ -9563,44 +9600,44 @@
 "your current branch '%s', without telling me what to push\n"
 "to update which remote branch."
 msgstr ""
-"Du sänder till fjärren \"%s\", som inte är uppströms för den\n"
-"aktuella grenen \"%s\", utan att tala om för mig vad som\n"
+"Du sänder till fjärren ”%s”, som inte är uppströms för den\n"
+"aktuella grenen ”%s”, utan att tala om för mig vad som\n"
 "ska sändas för att uppdatera fjärrgrenen."
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då änden på din befintliga gren är bakom\n"
-"dess fjärrmotsvarighet. Integrera fjärrändringarna (t.ex\n"
-"\"git pull ....\") innan du sänder igen.\n"
-"Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
+"dess fjärrmotsvarighet. Om du vill integrera fjärrändringarna,\n"
+"använd ”git pull” innan du sänder igen.\t\n"
+"Se avsnittet ”Note about fast-forward” i ”git push --help” för detaljer."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då änden på en gren som sänds in är bakom dess\n"
-"fjärrmotsvarighet. Checka ut grenen och integrera fjärrändringarna (t.ex.\n"
-"\"git pull ...\") innan du sänder igen.\n"
-"Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
+"fjärrmotsvarighet. Om du vill integrera fjärrändringarna, använd ”git pull”\n"
+"innan du sänder igen.\n"
+"Se avsnittet ”Note about fast-forward” i ”git push --help” för detaljer."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då fjärren innehåller ändringar som du inte\n"
 "har lokalt. Det beror oftast på att ett annat arkiv har sänt in samma\n"
-"referenser. Det kan vara en idé att först integrera fjärrändringarna\n"
-"(t.ex. \"git pull ...\") innan du sänder igen.\n"
-"Se avsnittet \"Note about fast-forwards\" i \"git push --help\" för detaljer."
+"referenser. Om du vill integrera fjärrändringarna, använd ”git pull”\n"
+"innan du sänder igen.\n"
+"Se avsnittet ”Note about fast-forwards” i ”git push --help” för detaljer."
 
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr "Uppdateringarna avvisades eftersom taggen redan finns på fjärren."
@@ -9613,18 +9650,18 @@
 "Du kan inte uppdatera en fjärr-referens som pekar på ett objekt som\n"
 "inte är en incheckning, eller uppdatera en fjärr-referens så att den\n"
 "pekar på något som inte är en incheckning, utan att använda flaggan\n"
-"\"--force\".\n"
+"”--force”.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Uppdateringar avvisades då änden på den fjärrspårande grenen\n"
-"har uppdaterats sedan senaste utcheckning. Integrera\n"
-"fjärrändringarna lokalt (t.ex \"git pull ....\") innan du\n"
-"tvingar en uppdatering.\n"
+"Uppdateringar avvisades då änden på din befintliga gren är bakom\n"
+"dess fjärrmotsvarighet. Om du vill integrera fjärrändringarna,\n"
+"använd ”git pull” innan du sänder igen.\n"
+"Se avsnittet ”Note about fast-forward” i ”git push --help” för detaljer."
 
 #, c-format
 msgid "Pushing to %s\n"
@@ -9632,7 +9669,7 @@
 
 #, c-format
 msgid "failed to push some refs to '%s'"
-msgstr "misslyckades sända vissa referenser till \"%s\""
+msgstr "misslyckades sända vissa referenser till ”%s”"
 
 msgid ""
 "recursing into submodule with push.recurseSubmodules=only; using on-demand "
@@ -9643,13 +9680,13 @@
 
 #, c-format
 msgid "invalid value for '%s'"
-msgstr "ogiltigt värde för \"%s\""
+msgstr "ogiltigt värde för ”%s”"
 
 msgid "repository"
 msgstr "arkiv"
 
-msgid "push all refs"
-msgstr "sänd alla referenser"
+msgid "push all branches"
+msgstr "sänd alla grenar"
 
 msgid "mirror all refs"
 msgstr "spegla alla referenser"
@@ -9657,8 +9694,9 @@
 msgid "delete refs"
 msgstr "ta bort referenser"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "sänd taggar (kan inte användas med --all eller --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"sänd taggar (kan inte användas med --all eller --branches eller --mirror)"
 
 msgid "force updates"
 msgstr "tvinga uppdateringar"
@@ -9704,7 +9742,7 @@
 
 #, c-format
 msgid "bad repository '%s'"
-msgstr "felaktigt arkiv \"%s\""
+msgstr "felaktigt arkiv ”%s”"
 
 msgid ""
 "No configured push destination.\n"
@@ -9752,7 +9790,7 @@
 msgstr "anteckningar"
 
 msgid "passed to 'git log'"
-msgstr "sänds till \"git log\""
+msgstr "sänds till ”git log”"
 
 msgid "only emit output related to the first range"
 msgstr "visa endast utdata för det första intervallet"
@@ -9762,15 +9800,15 @@
 
 #, c-format
 msgid "not a revision: '%s'"
-msgstr "inte en revision: \"%s\""
+msgstr "inte en revision: ”%s”"
 
 #, c-format
 msgid "not a commit range: '%s'"
-msgstr "inte ett incheckningsintervall: \"%s\""
+msgstr "inte ett incheckningsintervall: ”%s”"
 
 #, c-format
 msgid "not a symmetric range: '%s'"
-msgstr "inte ett symmetriskt intervall: \"%s\""
+msgstr "inte ett symmetriskt intervall: ”%s”"
 
 msgid "need two commit ranges"
 msgstr "behöver två incheckningsintervall"
@@ -9854,7 +9892,7 @@
 
 #, c-format
 msgid "could not read '%s'."
-msgstr "kunde inte läsa \"%s\"."
+msgstr "kunde inte läsa ”%s”."
 
 #, c-format
 msgid "could not create temporary %s"
@@ -9871,23 +9909,23 @@
 
 #, c-format
 msgid "%s requires the merge backend"
-msgstr "%s kräver \"merge\"-bakändan"
+msgstr "%s kräver ”merge”-bakändan"
 
 #, c-format
 msgid "invalid onto: '%s'"
-msgstr "ogiltig \"onto\": \"%s\""
+msgstr "ogiltig ”onto”: ”%s”"
 
 #, c-format
 msgid "invalid orig-head: '%s'"
-msgstr "ogiltig \"orig-head\": \"%s\""
+msgstr "ogiltig ”orig-head”: ”%s”"
 
 #, c-format
 msgid "ignoring invalid allow_rerere_autoupdate: '%s'"
-msgstr "ignorera ogiltigt allow_rerere_autoupdate: \"%s\""
+msgstr "ignorera ogiltigt allow_rerere_autoupdate: ”%s”"
 
 #, c-format
 msgid "could not remove '%s'"
-msgstr "kunde inte ta bort \"%s\""
+msgstr "kunde inte ta bort ”%s”"
 
 msgid ""
 "Resolve all conflicts manually, mark them as resolved with\n"
@@ -9897,10 +9935,10 @@
 "abort\"."
 msgstr ""
 "Lös alla konflikter manuellt, märk dem som lösta med\n"
-"\"git add/rm <filer_i_konflikt>\", kör sedan \"git rebase --continue\".\n"
-"Du kan hoppa över incheckningen istället: kör \"git rebase --skip\".\n"
-"För att avbryta och återgå till där du var före ombaseringen, kör \"git "
-"rebase --abort\"."
+"”git add/rm <filer_i_konflikt>”, kör sedan ”git rebase --continue”.\n"
+"Du kan hoppa över incheckningen istället: kör ”git rebase --skip”.\n"
+"För att avbryta och återgå till där du var före ombaseringen, kör ”git "
+"rebase --abort”."
 
 #, c-format
 msgid ""
@@ -9921,6 +9959,10 @@
 "Därför kan inte git ombasera dessa."
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Okänd rebase-merges-läge: %s"
+
+#, c-format
 msgid "could not switch to %s"
 msgstr "kunde inte växla till %s"
 
@@ -9930,10 +9972,18 @@
 
 #, c-format
 msgid ""
-"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask"
-"\"."
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
+msgstr "okänd tom-typ ”%s”; giltiga värden är ”drop”, ”keep” och ”ask”."
+
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
 msgstr ""
-"okänd tom-typ \"%s\"; giltiga värden är \"drop\", \"keep\" och \"ask\"."
+"--rebase-merges med en tom sträng som argument avråds från och kommer sluta "
+"fungera i en framtida version av Git. Använd istället --rebase-merges utan "
+"argument, vilket har samma effekt."
 
 #, c-format
 msgid ""
@@ -9988,7 +10038,7 @@
 msgstr "visa inte en diffstat för vad som ändrats uppströms"
 
 msgid "add a Signed-off-by trailer to each commit"
-msgstr "lägg \"Signed-off-by:\"-släprad till varje incheckning"
+msgstr "lägg ”Signed-off-by:”-släprad till varje incheckning"
 
 msgid "make committer date match author date"
 msgstr "sätt incheckningsdatum till författardatum"
@@ -10000,7 +10050,7 @@
 msgstr "synonym för --reset-author-date"
 
 msgid "passed to 'git apply'"
-msgstr "sänds till \"git apply\""
+msgstr "sänds till ”git apply”"
 
 msgid "ignore changes in whitespace"
 msgstr "ignorera ändringar i blanksteg"
@@ -10062,7 +10112,7 @@
 msgstr "försök ombasera sammanslagningar istället för att ignorera dem"
 
 msgid "use 'merge-base --fork-point' to refine upstream"
-msgstr "använd \"merge-base --fork-point\" för att förfina uppström"
+msgstr "använd ”merge-base --fork-point” för att förfina uppström"
 
 msgid "use the given merge strategy"
 msgstr "använd angiven sammanslagningsstrategi"
@@ -10077,21 +10127,21 @@
 msgstr "ombasera alla nåbara incheckningar upp till roten/rötterna"
 
 msgid "automatically re-schedule any `exec` that fails"
-msgstr "kör automatiskt alla \"exec\" som misslyckas på nytt"
+msgstr "kör automatiskt alla ”exec” som misslyckas på nytt"
 
 msgid "apply all changes, even those already present upstream"
 msgstr "applicera alla ändringar, även de som redan finns uppströms"
 
 msgid "It looks like 'git am' is in progress. Cannot rebase."
-msgstr "Det verkar som en \"git am\" körs. Kan inte ombasera."
+msgstr "Det verkar som en ”git am” körs. Kan inte ombasera."
 
 msgid ""
 "`rebase --preserve-merges` (-p) is no longer supported.\n"
 "Use `git rebase --abort` to terminate current rebase.\n"
 "Or downgrade to v2.33, or earlier, to complete the rebase."
 msgstr ""
-"\"rebase --preserve-merges\" (-p) stöds ej längre.\n"
-"Använd \"git rebase --abort\" för att avsluta aktuell ombasering.\n"
+"”rebase --preserve-merges” (-p) stöds ej längre.\n"
+"Använd ”git rebase --abort” för att avsluta aktuell ombasering.\n"
 "Eller nedgradera till v2.33 eller tidigare för att slutföra ombaseringen."
 
 msgid ""
@@ -10100,8 +10150,8 @@
 "which is no longer supported; use 'merges' instead"
 msgstr ""
 "--preserve-merges ersattes av --rebase-merges\n"
-"Observera: Din inställning för \"pull.rebase\" kan också vara satt till\n"
-"\"preserve\", som inte längre stöds; använd \"merges\" istället"
+"Observera: Din inställning för ”pull.rebase” kan också vara satt till\n"
+"”preserve”, som inte längre stöds; använd ”merges” istället"
 
 msgid "No rebase in progress?"
 msgstr "Ingen ombasering pågår?"
@@ -10147,27 +10197,20 @@
 "något av värde där.\n"
 
 msgid "switch `C' expects a numerical value"
-msgstr "flaggan \"C\" förväntar ett numeriskt värde"
-
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Okänt läge: %s"
-
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy kräver --merge eller --interactive"
+msgstr "flaggan ”C” förväntar ett numeriskt värde"
 
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"argument för \"apply\" är inkompatibla med rebase.autosquash. Överväg att "
-"lägga till --no-autosquash"
+"argument för ”apply” är inkompatibla med rebase.rebaseMerges. Överväg att "
+"lägga till --no-rebase-merges"
 
 msgid ""
 "apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
 "update-refs"
 msgstr ""
-"argument för \"apply\" är inkompatibla med rebase.updateRefs. Överväg att "
+"argument för ”apply” är inkompatibla med rebase.updateRefs. Överväg att "
 "lägga till --no-update-refs"
 
 #, c-format
@@ -10179,14 +10222,14 @@
 
 #, c-format
 msgid "invalid upstream '%s'"
-msgstr "felaktig uppström \"%s\""
+msgstr "felaktig uppström ”%s”"
 
 msgid "Could not create new root commit"
 msgstr "kunde inte skapa ny rotincheckning"
 
 #, c-format
 msgid "no such branch/commit '%s'"
-msgstr "ingen sådan gren/incheckning: \"%s\""
+msgstr "ingen sådan gren/incheckning: ”%s”"
 
 #, c-format
 msgid "No such ref: %s"
@@ -10197,18 +10240,15 @@
 
 #, c-format
 msgid "'%s': need exactly one merge base with branch"
-msgstr "\"%s\": behöver precis en sammanslagningsbas med gren"
+msgstr "”%s”: behöver precis en sammanslagningsbas med gren"
 
 #, c-format
 msgid "'%s': need exactly one merge base"
-msgstr "\"%s\": behöver precis en sammanslagningsbas"
+msgstr "”%s”: behöver precis en sammanslagningsbas"
 
 #, c-format
 msgid "Does not point to a valid commit '%s'"
-msgstr "Pekar inte på en giltig incheckning: \"%s\""
-
-msgid "Please commit or stash them."
-msgstr "Checka in eller använd \"stash\" på dem."
+msgstr "Pekar inte på en giltig incheckning: ”%s”"
 
 msgid "HEAD is up to date."
 msgstr "HEAD är à jour."
@@ -10267,17 +10307,17 @@
 msgstr ""
 "Normalt tillåts inte uppdatering av aktuell gren i ett icke-naket\n"
 "arkiv, då index och arbetskatalog inte kommer stämma med det du\n"
-"sände och \"git reset --hard\" krävs för att få arbetskatalogen och\n"
+"sände och ”git reset --hard” krävs för att få arbetskatalogen och\n"
 "HEAD att stämma överens.\n"
 "\n"
-"Du kan ställa in variabeln \"receive.denyCurrentBranch\" till\n"
-"\"ignore\" eller \"warn\" i fjärrarkivet för att tillåta sändning till\n"
+"Du kan ställa in variabeln ”receive.denyCurrentBranch” till\n"
+"”ignore” eller ”warn” i fjärrarkivet för att tillåta sändning till\n"
 "dess aktuella gren; detta rekommenderas dock inte såvida du inte\n"
 "sett till att dess arbetskatalog uppdateras till det tu sände in\n"
 "på annat sätt.\n"
 "\n"
 "För att undvika detta meddelande och fortfarande behålla det\n"
-"normala beteendet, sätt \"receive.denyCurrentBranch\" till \"refuse\"."
+"normala beteendet, sätt ”receive.denyCurrentBranch” till ”refuse”."
 
 msgid ""
 "By default, deleting the current branch is denied, because the next\n"
@@ -10290,14 +10330,14 @@
 "To squelch this message, you can set it to 'refuse'."
 msgstr ""
 "Normalt tillåts inte radering av aktuell gren, eftersom nästa\n"
-"\"git clone\" inte kommer innebära att några filer checkas ut,\n"
+"”git clone” inte kommer innebära att några filer checkas ut,\n"
 "vilket är förvirrande.\n"
 "\n"
-"Du kan ställa in variabeln \"receive.denyDeleteCurrent\" till\n"
-"\"warn\" eller \"ignore\" i fjärrarkivet för att tillåta borttagning\n"
+"Du kan ställa in variabeln ”receive.denyDeleteCurrent” till\n"
+"”warn” eller ”ignore” i fjärrarkivet för att tillåta borttagning\n"
 "av aktuell gren, med eller utan varningsmeddelande.\n"
 "\n"
-"För att undvika detta meddelande kan du sätta det till \"refuse\"."
+"För att undvika detta meddelande kan du sätta det till ”refuse”."
 
 msgid "quiet"
 msgstr "tyst"
@@ -10331,7 +10371,7 @@
 
 #, c-format
 msgid "invalid timestamp '%s' given to '--%s'"
-msgstr "ogiltig tidsstämpel \"%s\" given i \"--%s\""
+msgstr "ogiltig tidsstämpel ”%s” given i ”--%s”"
 
 msgid "do not actually prune any entries"
 msgstr "rensa faktiskt inte några poster"
@@ -10464,11 +10504,12 @@
 msgid "fetch the remote branches"
 msgstr "hämta fjärrgrenarna"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "importera alla taggar och associerade objekt vid hämtning"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "eller hämta inte några taggar alls (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"importera alla taggar och associerade objekt vid hämtning\n"
+"eller hämta inte några taggar alls (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "gren(ar) att spåra"
@@ -10491,7 +10532,7 @@
 
 #, c-format
 msgid "Could not setup master '%s'"
-msgstr "Kunde inte skapa master \"%s\""
+msgstr "Kunde inte skapa master ”%s”"
 
 #, c-format
 msgid "more than one %s"
@@ -10499,7 +10540,7 @@
 
 #, c-format
 msgid "unhandled branch.%s.rebase=%s; assuming 'true'"
-msgstr "ohanterad branch.%s.rebase=%s; antar \"true\""
+msgstr "ohanterad branch.%s.rebase=%s; antar ”true”"
 
 #, c-format
 msgid "Could not get fetch map for refspec %s"
@@ -10513,11 +10554,11 @@
 
 #, c-format
 msgid "could not set '%s'"
-msgstr "kunde inte ställa in \"%s\""
+msgstr "kunde inte ställa in ”%s”"
 
 #, c-format
 msgid "could not unset '%s'"
-msgstr "kunde inte ta bort inställning för \"%s\""
+msgstr "kunde inte ta bort inställning för ”%s”"
 
 #, c-format
 msgid ""
@@ -10527,15 +10568,15 @@
 msgstr ""
 "Konfigurationen för %s för remote.pushDefault i:\n"
 "\t%s:%d\n"
-"anger nu den icke-existerande fjärren \"%s\""
+"anger nu den icke-existerande fjärren ”%s”"
 
 #, c-format
 msgid "No such remote: '%s'"
-msgstr "Ingen sådan fjärr: \"%s\""
+msgstr "Ingen sådan fjärr: ”%s”"
 
 #, c-format
 msgid "Could not rename config section '%s' to '%s'"
-msgstr "Kunde inte byta namn på konfigurationssektionen \"%s\" till \"%s\""
+msgstr "Kunde inte byta namn på konfigurationssektionen ”%s” till ”%s”"
 
 #, c-format
 msgid ""
@@ -10552,11 +10593,11 @@
 
 #, c-format
 msgid "deleting '%s' failed"
-msgstr "misslyckades ta bort \"%s\""
+msgstr "misslyckades ta bort ”%s”"
 
 #, c-format
 msgid "creating '%s' failed"
-msgstr "misslyckades skapa \"%s\""
+msgstr "misslyckades skapa ”%s”"
 
 msgid ""
 "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
@@ -10573,7 +10614,7 @@
 
 #, c-format
 msgid "Could not remove config section '%s'"
-msgstr "Kunde inte ta bort konfigurationssektionen \"%s\""
+msgstr "Kunde inte ta bort konfigurationssektionen ”%s”"
 
 #, c-format
 msgid " new (next fetch will store in remotes/%s)"
@@ -10586,7 +10627,7 @@
 msgstr " överhoppad"
 
 msgid " stale (use 'git remote prune' to remove)"
-msgstr " förlegad (använd \"git remote prune\" för att ta bort)"
+msgstr " förlegad (använd ”git remote prune” för att ta bort)"
 
 msgid " ???"
 msgstr " ???"
@@ -10698,17 +10739,17 @@
 
 msgid "  Local branch configured for 'git pull':"
 msgid_plural "  Local branches configured for 'git pull':"
-msgstr[0] "  Lokal gren konfigurerad för \"git pull\":"
-msgstr[1] "  Lokala grenar konfigurerade för \"git pull\":"
+msgstr[0] "  Lokal gren konfigurerad för ”git pull”:"
+msgstr[1] "  Lokala grenar konfigurerade för ”git pull”:"
 
 msgid "  Local refs will be mirrored by 'git push'"
-msgstr "  Lokala referenser speglas av \"git push\""
+msgstr "  Lokala referenser speglas av ”git push”"
 
 #, c-format
 msgid "  Local ref configured for 'git push'%s:"
 msgid_plural "  Local refs configured for 'git push'%s:"
-msgstr[0] "  Lokal referens konfigurerad för \"git push\"%s:"
-msgstr[1] "  Lokala referenser konfigurerade för \"git push\"%s:"
+msgstr[0] "  Lokal referens konfigurerad för ”git push”%s:"
+msgstr[1] "  Lokala referenser konfigurerade för ”git push”%s:"
 
 msgid "set refs/remotes/<name>/HEAD according to remote"
 msgstr "sätt refs/remotes/<namn>/HEAD enligt fjärren"
@@ -10763,7 +10804,7 @@
 
 #, c-format
 msgid "No such remote '%s'"
-msgstr "Ingen sådan fjärr \"%s\""
+msgstr "Ingen sådan fjärr ”%s”"
 
 msgid "add branch"
 msgstr "lägg till gren"
@@ -10779,7 +10820,7 @@
 
 #, c-format
 msgid "no URLs configured for remote '%s'"
-msgstr "ingen URL:er angivna för fjärren \"%s\""
+msgstr "ingen URL:er angivna för fjärren ”%s”"
 
 msgid "manipulate push URLs"
 msgstr "manipulera URL:ar för sändning"
@@ -10851,6 +10892,10 @@
 msgid "could not remove stale bitmap: %s"
 msgstr "kunde inte ta bort gammal bitkarta: %s"
 
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "paketprefixet %s börjar inte med objkat %s"
+
 msgid "pack everything in a single pack"
 msgstr "packa allt i ett enda paket"
 
@@ -10863,8 +10908,8 @@
 msgid "approxidate"
 msgstr "cirkadatum"
 
-msgid "with -C, expire objects older than this"
-msgstr "med -C, låt tid gå ut för objekt äldre än detta"
+msgid "with --cruft, expire objects older than this"
+msgstr "med --cruft, låt tid gå ut för objekt äldre än detta"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "ta bort överflödiga paket, och kör git-prune-packed"
@@ -10926,27 +10971,30 @@
 msgid "pack prefix to store a pack containing pruned objects"
 msgstr "paketprefix att lagra ett paket som innehåller bortrensade objekt"
 
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "paketprefix att lagra ett paket som innehåller utfiltrerade objekt"
+
 msgid "cannot delete packs in a precious-objects repo"
-msgstr "kan inte ta bort paket i ett \"precious-objects\"-arkiv"
+msgstr "kan inte ta bort paket i ett ”precious-objects”-arkiv"
+
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "flaggan ”%s” kan inte användas med ”%s”"
 
 msgid "Nothing new to pack."
 msgstr "Inget nytt att packa."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "paketprefixet %s börjar inte med objkat %s"
-
-#, c-format
 msgid "renaming pack to '%s' failed"
-msgstr "misslyckades byta namn på paket till \"%s\""
+msgstr "misslyckades byta namn på paket till ”%s”"
 
 #, c-format
 msgid "pack-objects did not write a '%s' file for pack %s-%s"
-msgstr "pack-objects skrev inte en \"%s\"-fil för paketet %s-%s"
+msgstr "pack-objects skrev inte en ”%s”-fil för paketet %s-%s"
 
 #, c-format
 msgid "could not unlink: %s"
-msgstr "kunde inte ta bort: \"%s\""
+msgstr "kunde inte ta bort: ”%s”"
 
 msgid "git replace [-f] <object> <replacement>"
 msgstr "git replace [-f] <objekt> <ersättning>"
@@ -10968,24 +11016,24 @@
 "invalid replace format '%s'\n"
 "valid formats are 'short', 'medium' and 'long'"
 msgstr ""
-"ogiltigt ersättningsformat \"%s\"\n"
-"giltiga format är \"short\", \"medium\" och \"long\""
+"ogiltigt ersättningsformat ”%s”\n"
+"giltiga format är ”short”, ”medium” och ”long”"
 
 #, c-format
 msgid "replace ref '%s' not found"
-msgstr "ersättningsreferensen \"%s\" hittades inte"
+msgstr "ersättningsreferensen ”%s” hittades inte"
 
 #, c-format
 msgid "Deleted replace ref '%s'"
-msgstr "Tog bort ersättningsreferensen \"%s\""
+msgstr "Tog bort ersättningsreferensen ”%s”"
 
 #, c-format
 msgid "'%s' is not a valid ref name"
-msgstr "\"%s\" är inte ett giltigt referensnamn"
+msgstr "”%s” är inte ett giltigt referensnamn"
 
 #, c-format
 msgid "replace ref '%s' already exists"
-msgstr "ersättningsreferensen \"%s\" finns redan"
+msgstr "ersättningsreferensen ”%s” finns redan"
 
 #, c-format
 msgid ""
@@ -10994,8 +11042,8 @@
 "while '%s' points to a replacement object of type '%s'."
 msgstr ""
 "Objekt måste vara av samma typ.\n"
-"\"%s\" pekar på ett ersatt objekt med typen \"%s\"\n"
-"medan \"%s\" pekar på ett ersättningsobjekt av typen \"%s\"."
+"”%s” pekar på ett ersatt objekt med typen ”%s”\n"
+"medan ”%s” pekar på ett ersättningsobjekt av typen ”%s”."
 
 #, c-format
 msgid "unable to open %s for writing"
@@ -11022,7 +11070,7 @@
 
 #, c-format
 msgid "unable to fstat %s"
-msgstr "kan inte utföra \"fstat\" på %s"
+msgstr "kan inte utföra ”fstat” på %s"
 
 msgid "unable to write object to database"
 msgstr "kan inte skriva objektet till databasen"
@@ -11036,7 +11084,7 @@
 
 #, c-format
 msgid "new object is the same as the old one: '%s'"
-msgstr "nytt objekt är samma som det gamla: \"%s\""
+msgstr "nytt objekt är samma som det gamla: ”%s”"
 
 #, c-format
 msgid "could not parse %s as a commit"
@@ -11044,38 +11092,38 @@
 
 #, c-format
 msgid "bad mergetag in commit '%s'"
-msgstr "felaktig sammanslagningstagg i incheckningen \"%s\""
+msgstr "felaktig sammanslagningstagg i incheckningen ”%s”"
 
 #, c-format
 msgid "malformed mergetag in commit '%s'"
-msgstr "felformad sammanslagningstagg i incheckningen \"%s\""
+msgstr "felformad sammanslagningstagg i incheckningen ”%s”"
 
 #, c-format
 msgid ""
 "original commit '%s' contains mergetag '%s' that is discarded; use --edit "
 "instead of --graft"
 msgstr ""
-"den ursprungliga incheckningen \"%s\" innehåller sammanslagningstaggen \"%s"
-"\" som har förkastats; använd --edit istället för --graft"
+"den ursprungliga incheckningen ”%s” innehåller sammanslagningstaggen ”%s” "
+"som har förkastats; använd --edit istället för --graft"
 
 #, c-format
 msgid "the original commit '%s' has a gpg signature"
-msgstr "den ursprungliga incheckningen \"%s\" har en gpg-signatur"
+msgstr "den ursprungliga incheckningen ”%s” har en gpg-signatur"
 
 msgid "the signature will be removed in the replacement commit!"
 msgstr "signaturen kommer att tas bort i ersättningsincheckningen!"
 
 #, c-format
 msgid "could not write replacement commit for: '%s'"
-msgstr "kunde inte skriva ersättningsincheckning för: \"%s\""
+msgstr "kunde inte skriva ersättningsincheckning för: ”%s”"
 
 #, c-format
 msgid "graft for '%s' unnecessary"
-msgstr "ympning för \"%s\" behövs inte"
+msgstr "ympning för ”%s” behövs inte"
 
 #, c-format
 msgid "new commit is the same as the old one: '%s'"
-msgstr "ny incheckning är samma som den gamla: \"%s\""
+msgstr "ny incheckning är samma som den gamla: ”%s”"
 
 #, c-format
 msgid ""
@@ -11136,6 +11184,76 @@
 msgid "only one pattern can be given with -l"
 msgstr "endast ett mönster kan anges med -l"
 
+msgid "need some commits to replay"
+msgstr "behöver några incheckningar för omspelning"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto och --advance kan inte kombineras"
+
+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"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"kan inte avgöra om den underförstådda processen är --advance eller --onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"kan inte flytta målet framåt när det finns flera källgrenar eftersom "
+"ordningen inte kan fastställas"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "kan inte avgöra den underförstådda basen för --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTELLT!) git replay ([--contained] --onto <nybas> | --advance "
+"<gren>) <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"
+
+msgid "advance all branches contained in revision-range"
+msgstr "flytta alla grenar som finns i revisionsintervallet framåt"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "flaggan --onto eller --advance måste anges"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"några flaggor för revisionstraversering kommer överstyras eftersom ”%s”-"
+"biten i ”struct rev_info” kommer att tvingas"
+
+msgid "error preparing revisions"
+msgstr "fel när revisioner skulle förberedas"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "kan ännu inte spela om hela vägen ned till rotincheckningen!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "kan ännu inte spela om sammanslagningsincheckningar!"
+
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
@@ -11145,11 +11263,11 @@
 msgstr "registrera rena lösningar i indexet"
 
 msgid "'git rerere forget' without paths is deprecated"
-msgstr "\"git rerere forget\" utan sökvägar är föråldrat"
+msgstr "”git rerere forget” utan sökvägar är föråldrat"
 
 #, c-format
 msgid "unable to generate diff for '%s'"
-msgstr "misslyckades skapa diff för \"%s\""
+msgstr "misslyckades skapa diff för ”%s”"
 
 msgid ""
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
@@ -11223,15 +11341,15 @@
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
-msgstr "Kunde inte slå upp \"%s\" som en giltig revision."
+msgstr "Kunde inte slå upp ”%s” som en giltig revision."
 
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
-msgstr "Kunde inte slå upp \"%s\" som ett giltigt träd."
+msgstr "Kunde inte slå upp ”%s” som ett giltigt träd."
 
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr ""
-"--mixed rekommenderas inte med sökvägar; använd \"git reset -- <sökvägar>\"."
+"--mixed rekommenderas inte med sökvägar; använd ”git reset -- <sökvägar>”."
 
 #, c-format
 msgid "Cannot do %s reset with paths."
@@ -11250,11 +11368,11 @@
 "'--no-refresh' to avoid this."
 msgstr ""
 "Det tog %.2f sekunder att uppdatera indexet efter återställning.\n"
-"Du kan använda \"--no-refresh\" för undvika detta."
+"Du kan använda ”--no-refresh” för undvika detta."
 
 #, c-format
 msgid "Could not reset index file to revision '%s'."
-msgstr "Kunde inte återställa indexfilen till versionen \"%s\"."
+msgstr "Kunde inte återställa indexfilen till versionen ”%s”."
 
 msgid "Could not write new index file."
 msgstr "Kunde inte skriva ny indexfil."
@@ -11265,21 +11383,20 @@
 
 #, c-format
 msgid "invalid value for '%s': '%s', the only allowed format is '%s'"
-msgstr ""
-"felaktigt värde för \"%s\": \"%s\", det enda tillåtna formatet är \"%s\""
+msgstr "felaktigt värde för ”%s”: ”%s”, det enda tillåtna formatet är ”%s”"
 
 msgid "rev-list does not support display of notes"
 msgstr "rev-list stöder inte visning av anteckningar"
 
 #, 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>...]"
 
 msgid "keep the `--` passed as an arg"
-msgstr "behåll \"--\" sänt som argument"
+msgstr "behåll ”--” sänt som argument"
 
 msgid "stop parsing after the first non-option argument"
 msgstr "sluta tolka efter första argument som inte är flagga"
@@ -11291,7 +11408,7 @@
 msgstr "för tidigt slut på indata"
 
 msgid "no usage string given before the `--' separator"
-msgstr "ingen användningssträng angavs före \"--\"-avdelaren"
+msgstr "ingen användningssträng angavs före ”--”-avdelaren"
 
 msgid "missing opt-spec before option flags"
 msgstr "saknar flagg-spec före alternativflaggor"
@@ -11310,7 +11427,7 @@
 "     eller: git rev-parse --sq-quote [<argument>...]\n"
 "     eller: git rev-parse [<flaggor>] [<argument>...]\n"
 "\n"
-"Kör \"git rev-parse --parseopt -h\" för mer information om den första "
+"Kör ”git rev-parse --parseopt -h” för mer information om den första "
 "varianten."
 
 msgid "--resolve-git-dir requires an argument"
@@ -11318,7 +11435,7 @@
 
 #, c-format
 msgid "not a gitdir '%s'"
-msgstr "inte en gitkatalog \"%s\""
+msgstr "inte en gitkatalog ”%s”"
 
 msgid "--git-path requires an argument"
 msgstr "--git-path kräver ett argument"
@@ -11343,18 +11460,12 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "okänt läge för --abbrev-ref: %s"
 
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden kan endast användas tillsammans med --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden kan  kan inte användas tillsammans med --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden kan  kan inte användas tillsammans med --remotes"
-
 msgid "this operation must be run in a work tree"
 msgstr "funktionen måste köras i en arbetskatalog"
 
+msgid "Could not read the index"
+msgstr "Kunde inte läsa indexet"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "okänt läge för --show-object-format: %s"
@@ -11381,7 +11492,7 @@
 
 #, c-format
 msgid "option `%s' expects a number greater than zero"
-msgstr "flaggan \"%s\" antar ett numeriskt värde större än noll"
+msgstr "flaggan ”%s” antar ett numeriskt värde större än noll"
 
 #, c-format
 msgid "%s: %s cannot be used with %s"
@@ -11430,13 +11541,13 @@
 msgstr "behåll redundanta, tomma incheckningar"
 
 msgid "use the 'reference' format to refer to commits"
-msgstr "använd \"referens\"-format för att referera till incheckningar"
+msgstr "använd ”referens”-format för att referera till incheckningar"
 
 msgid "revert failed"
-msgstr "\"revert\" misslyckades"
+msgstr "”revert” misslyckades"
 
 msgid "cherry-pick failed"
-msgstr "\"cherry-pick\" misslyckades"
+msgstr "”cherry-pick” misslyckades"
 
 msgid ""
 "git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
@@ -11502,11 +11613,11 @@
 
 msgid "please stage your changes to .gitmodules or stash them to proceed"
 msgstr ""
-"löa dina ändringar i .gitmodules eller använd \"stash\" för att fortsätta"
+"löa dina ändringar i .gitmodules eller använd ”stash” för att fortsätta"
 
 #, c-format
 msgid "not removing '%s' recursively without -r"
-msgstr "tar inte bort \"%s\" rekursivt utan -r"
+msgstr "tar inte bort ”%s” rekursivt utan -r"
 
 #, c-format
 msgid "git rm: unable to remove %s"
@@ -11528,6 +11639,9 @@
 msgid "remote name"
 msgstr "fjärrnamn"
 
+msgid "push all refs"
+msgstr "sänd alla referenser"
+
 msgid "use stateless RPC protocol"
 msgstr "använd tillståndslöst RPC-protokoll"
 
@@ -11544,7 +11658,7 @@
 msgstr "git log --pretty=short | git shortlog [<flaggor>]"
 
 msgid "using multiple --group options with stdin is not supported"
-msgstr "mer än en \"--group\"-flagga stöds inte med standard in"
+msgstr "mer än en ”--group”-flagga stöds inte med standard in"
 
 #, c-format
 msgid "using %s with stdin is not supported"
@@ -11614,7 +11728,7 @@
 msgstr "visa fjärrspårande grenar"
 
 msgid "color '*!+-' corresponding to the branch"
-msgstr "färga \"*!+-\" enligt grenen"
+msgstr "färga ”*!+-” enligt grenen"
 
 msgid "show <n> more commits after the common ancestor"
 msgstr "visa <n> ytterligare incheckningar efter gemensam anfader"
@@ -11679,7 +11793,7 @@
 
 #, c-format
 msgid "'%s' is not a valid ref."
-msgstr "\"%s\" är inte en giltig referens."
+msgstr "”%s” är inte en giltig referens."
 
 #, c-format
 msgid "cannot find commit %s (%s)"
@@ -11692,23 +11806,44 @@
 msgstr "okänd hashningsalgoritm"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<mönster>...]"
 
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<mönster>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <ref>"
+
+msgid "reference does not exist"
+msgstr "referensen existerar inte"
+
+msgid "failed to look up reference"
+msgstr "misslyckades slå upp referensen"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "visa endast taggar (kan kombineras med huvuden)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "visa endast huvuden (kan kombineras med taggar)"
 
+msgid "check for reference existence without resolving"
+msgstr "kontrollerar att referensen existerar utan att slå upp dem"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr "striktare referenskontroll, kräver exakt referenssökväg"
 
@@ -11728,9 +11863,11 @@
 msgstr "visa referenser från standard in som inte finns i lokalt arkiv"
 
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) <flaggor>"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) <flaggor>"
 
 msgid "this worktree is not sparse"
 msgstr "arbetskatalogen är inte gren"
@@ -11744,15 +11881,15 @@
 "directory '%s' contains untracked files, but is not in the sparse-checkout "
 "cone"
 msgstr ""
-"katalogen \"%s\" innehåller ospårade filer, men är inte i området som ages i "
-"\"sparse-checkout\""
+"katalogen ”%s” innehåller ospårade filer, men är inte i området som ages i "
+"”sparse-checkout”"
 
 #, c-format
 msgid "failed to remove directory '%s'"
-msgstr "misslyckades ta bort katalogen \"%s\""
+msgstr "misslyckades ta bort katalogen ”%s”"
 
 msgid "failed to create directory for sparse-checkout file"
-msgstr "misslyckades skapa katalog för \"sparse-checkout\"-filen"
+msgstr "misslyckades skapa katalog för ”sparse-checkout”-filen"
 
 msgid "failed to initialize worktree config"
 msgstr "misslyckades initiera arbetskataloginställning"
@@ -11772,15 +11909,15 @@
 
 #, c-format
 msgid "failed to open '%s'"
-msgstr "misslyckades öppna \"%s\""
+msgstr "misslyckades öppna ”%s”"
 
 #, c-format
 msgid "could not normalize path %s"
-msgstr "kunde inte normalisera sökvägen \"%s\""
+msgstr "kunde inte normalisera sökvägen ”%s”"
 
 #, c-format
 msgid "unable to unquote C-style string '%s'"
-msgstr "kan inte ta bort citering av C-sträng \"%s\""
+msgstr "kan inte ta bort citering av C-sträng ”%s”"
 
 msgid "unable to load existing sparse-checkout patterns"
 msgstr "kunde inte läsa in existerande mönster för gles utcheckning"
@@ -11798,8 +11935,8 @@
 "specify directories rather than patterns.  If your directory starts with a "
 "'!', pass --skip-checks"
 msgstr ""
-"ange kataloger istället för mönster. Om din katalog börjar med ett \"!\", "
-"sänd med --skip-checks"
+"ange kataloger istället för mönster. Om din katalog börjar med ett ”!”, sänd "
+"med --skip-checks"
 
 msgid ""
 "specify directories rather than patterns.  If your directory really has any "
@@ -11813,7 +11950,7 @@
 "'%s' is not a directory; to treat it as a directory anyway, rerun with --"
 "skip-checks"
 msgstr ""
-"\"%s\" är inte en katalog: för att ändå behandla det som en katalog, kör på "
+"”%s” är inte en katalog: för att ändå behandla det som en katalog, kör på "
 "nytt med --skip-checks"
 
 #, c-format
@@ -11821,7 +11958,7 @@
 "pass a leading slash before paths such as '%s' if you want a single file "
 "(see NON-CONE PROBLEMS in the git-sparse-checkout manual)."
 msgstr ""
-"sänd med ett inledande snedstreck före sökvägar som \"%s\" om du vill ha en "
+"sänd med ett inledande snedstreck före sökvägar som ”%s” om du vill ha en "
 "enstaka file (se NON-CONE PROBLEMS i manualen git-sparse-checkout)."
 
 msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
@@ -11853,14 +11990,30 @@
 msgid "error while refreshing working directory"
 msgstr "fel vid uppdatering av arbetskatalog"
 
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks] [--[no-]cone] [--rules-"
+"file <fil>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "avsluta in- och utdatafiler med NUL-tecken"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr "om används med --rules-file tolka mönster som kon-lägemönster"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "använd mönster i <fil> istället för de nuvarande."
+
 msgid "git stash list [<log-options>]"
-msgstr "git stash list [<\"log\"-flaggor>]"
+msgstr "git stash list [<”log”-flaggor>]"
 
 msgid ""
 "git stash show [-u | --include-untracked | --only-untracked] [<diff-"
 "options>] [<stash>]"
 msgstr ""
-"git stash show [-u | --include-untracked | --only-untracked] [<\"diff\"-"
+"git stash show [-u | --include-untracked | --only-untracked] [<”diff”-"
 "flaggor>] [<stash>]"
 
 msgid "git stash drop [-q | --quiet] [<stash>]"
@@ -11908,21 +12061,21 @@
 
 #, c-format
 msgid "'%s' is not a stash-like commit"
-msgstr "\"%s\" är inte en \"stash\"-liknande incheckning"
+msgstr "”%s” är inte en ”stash”-liknande incheckning"
 
 #, c-format
 msgid "Too many revisions specified:%s"
 msgstr "För många revisioner angivna:%s"
 
 msgid "No stash entries found."
-msgstr "Inga \"stash\"-poster hittades."
+msgstr "Inga ”stash”-poster hittades."
 
 #, c-format
 msgid "%s is not a valid reference"
 msgstr "%s är inte en giltigt referens"
 
 msgid "git stash clear with arguments is unimplemented"
-msgstr "\"git stash clear\" med argument har inte implementerats"
+msgstr "”git stash clear” med argument har inte implementerats"
 
 #, c-format
 msgid ""
@@ -11931,11 +12084,11 @@
 "         to make room.\n"
 msgstr ""
 "VARNING: En ospårad fil är i vägen för en spårad fil! Byter namn\n"
-"            %s -> %s\n"
+"            %s → %s\n"
 "         för att lämna plats.\n"
 
 msgid "cannot apply a stash in the middle of a merge"
-msgstr "kan inte tillämpa en \"stash\" mitt i en sammanslagning"
+msgstr "kan inte tillämpa en ”stash” mitt i en sammanslagning"
 
 #, c-format
 msgid "could not generate diff %s^!."
@@ -11952,7 +12105,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"
@@ -11966,11 +12119,11 @@
 
 #, c-format
 msgid "%s: Could not drop stash entry"
-msgstr "%s: Kunde inte kasta \"stash\"-post"
+msgstr "%s: Kunde inte kasta ”stash”-post"
 
 #, c-format
 msgid "'%s' is not a stash reference"
-msgstr "\"%s\" är inte en \"stash\"-referens"
+msgstr "”%s” är inte en ”stash”-referens"
 
 msgid "The stash entry is kept in case you need it again."
 msgstr "Stash-posten behålls ifall du behöver den igen."
@@ -11985,20 +12138,20 @@
 msgstr "misslyckades packa upp träd"
 
 msgid "include untracked files in the stash"
-msgstr "ta med ospårade filer i \"stash\""
+msgstr "ta med ospårade filer i ”stash”"
 
 msgid "only show untracked files in the stash"
-msgstr "visa bara ospårade filer i \"stash\""
+msgstr "visa bara ospårade filer i ”stash”"
 
 #, c-format
 msgid "Cannot update %s with %s"
 msgstr "Kan inte uppdatera %s med %s"
 
 msgid "stash message"
-msgstr "\"stash\"-meddelande"
+msgstr "”stash”-meddelande"
 
 msgid "\"git stash store\" requires one <commit> argument"
-msgstr "\"git stash store\" kräver ett <incheckning>-argument"
+msgstr "”git stash store” kräver ett <incheckning>-argument"
 
 msgid "No staged changes"
 msgstr "Inga köade ändringar"
@@ -12032,13 +12185,13 @@
 "Kan inte använda --staged och --include-untracked eller --all samtidigt"
 
 msgid "Did you forget to 'git add'?"
-msgstr "Glömde du använda \"git add\"?"
+msgstr "Glömde du använda ”git add”?"
 
 msgid "No local changes to save"
 msgstr "Inga lokala ändringar att spara"
 
 msgid "Cannot initialize stash"
-msgstr "Kan inte initiera \"stash\""
+msgstr "Kan inte initiera ”stash”"
 
 msgid "Cannot save the current status"
 msgstr "Kan inte spara aktuell status"
@@ -12057,13 +12210,13 @@
 msgstr "stash:a endast köade ändringar"
 
 msgid "stash in patch mode"
-msgstr "\"stash\" i \"patch\"-läge"
+msgstr "”stash” i ”patch”-läge"
 
 msgid "quiet mode"
 msgstr "tyst läge"
 
 msgid "include untracked files in stash"
-msgstr "ta med ospårade filer i \"stash\""
+msgstr "ta med ospårade filer i ”stash”"
 
 msgid "include ignore files"
 msgstr "ta med ignorerade filer"
@@ -12080,23 +12233,23 @@
 
 #, c-format
 msgid "could not get a repository handle for submodule '%s'"
-msgstr "kunde inte få tag i arkivhandtag för undermodulen \"%s\""
+msgstr "kunde inte få tag i arkivhandtag för undermodulen ”%s”"
 
 #, c-format
 msgid ""
 "could not look up configuration '%s'. Assuming this repository is its own "
 "authoritative upstream."
 msgstr ""
-"kunde inte slå upp konfigurationen \"%s\". Antar att arkivet är sin eget "
+"kunde inte slå upp konfigurationen ”%s”. Antar att arkivet är sin eget "
 "officiella uppström."
 
 #, c-format
 msgid "No url found for submodule path '%s' in .gitmodules"
-msgstr "Hittade ingen url för undermodulsökvägen \"%s\" i .gitmodules"
+msgstr "Hittade ingen url för undermodulsökvägen ”%s” i .gitmodules"
 
 #, c-format
 msgid "Entering '%s'\n"
-msgstr "Går in i \"%s\"\n"
+msgstr "Går in i ”%s”\n"
 
 #, c-format
 msgid ""
@@ -12127,19 +12280,19 @@
 
 #, c-format
 msgid "Failed to register url for submodule path '%s'"
-msgstr "Misslyckades registrera url för undermodulsökväg \"%s\""
+msgstr "Misslyckades registrera url för undermodulsökväg ”%s”"
 
 #, c-format
 msgid "Submodule '%s' (%s) registered for path '%s'\n"
-msgstr "Undermodulen \"%s\" (%s) registrerad för sökvägen \"%s\"\n"
+msgstr "Undermodulen ”%s” (%s) registrerad för sökvägen ”%s”\n"
 
 #, c-format
 msgid "warning: command update mode suggested for submodule '%s'\n"
-msgstr "varning: kommandouppdateringsläge föreslogs för undermodulen \"%s\"\n"
+msgstr "varning: kommandouppdateringsläge föreslogs för undermodulen ”%s”\n"
 
 #, c-format
 msgid "Failed to register update mode for submodule path '%s'"
-msgstr "Misslyckades registrera uppdateringsläge för undermodulsökväg \"%s\""
+msgstr "Misslyckades registrera uppdateringsläge för undermodulsökväg ”%s”"
 
 msgid "suppress output for initializing a submodule"
 msgstr "dölj utdata från initiering av undermodul"
@@ -12149,15 +12302,15 @@
 
 #, c-format
 msgid "no submodule mapping found in .gitmodules for path '%s'"
-msgstr "hittade ingen undermodulmappning i .gitmodules för sökvägen \"%s\""
+msgstr "hittade ingen undermodulmappning i .gitmodules för sökvägen ”%s”"
 
 #, c-format
 msgid "could not resolve HEAD ref inside the submodule '%s'"
-msgstr "kunde inte bestämma HEAD:s incheckning i undermodulen \"%s\""
+msgstr "kunde inte bestämma HEAD:s incheckning i undermodulen ”%s”"
 
 #, c-format
 msgid "failed to recurse into submodule '%s'"
-msgstr "misslyckades rekursera in i undermodulen \"%s\""
+msgstr "misslyckades rekursera in i undermodulen ”%s”"
 
 msgid "suppress submodule status output"
 msgstr "hindra statusutskrift för undermodul"
@@ -12174,11 +12327,11 @@
 
 #, c-format
 msgid "* %s %s(blob)->%s(submodule)"
-msgstr "* %s %s(blob)->%s(submodule)"
+msgstr "* %s %s(blob)→%s(submodule)"
 
 #, c-format
 msgid "* %s %s(submodule)->%s(blob)"
-msgstr "* %s %s(submodule)->%s(blob)"
+msgstr "* %s %s(submodule)→%s(blob)"
 
 #, c-format
 msgid "%s"
@@ -12186,7 +12339,7 @@
 
 #, c-format
 msgid "couldn't hash object from '%s'"
-msgstr "kunde inte hasha objekt från \"%s\""
+msgstr "kunde inte hasha objekt från ”%s”"
 
 #, c-format
 msgid "unexpected mode %o\n"
@@ -12200,7 +12353,7 @@
 
 msgid "skip submodules with 'ignore_config' value set to 'all'"
 msgstr ""
-"hoppa över undermoduler där värdet för \"ignore_config\" är satt till \"all\""
+"hoppa över undermoduler där värdet för ”ignore_config” är satt till ”all”"
 
 msgid "limit the summary size"
 msgstr "begränsa översiktsstorleken"
@@ -12213,15 +12366,15 @@
 
 #, c-format
 msgid "Synchronizing submodule url for '%s'\n"
-msgstr "Synkroniserar undermodul-url för \"%s\"\n"
+msgstr "Synkroniserar undermodul-url för ”%s”\n"
 
 #, c-format
 msgid "failed to register url for submodule path '%s'"
-msgstr "misslyckades registrera url för undermodulsökväg \"%s\""
+msgstr "misslyckades registrera url för undermodulsökväg ”%s”"
 
 #, c-format
 msgid "failed to update remote for submodule '%s'"
-msgstr "misslyckades uppdatera fjärr för undermodulsökväg \"%s\""
+msgstr "misslyckades uppdatera fjärr för undermodulsökväg ”%s”"
 
 msgid "suppress output of synchronizing submodule url"
 msgstr "dölj utdata från synkronisering av undermodul-url"
@@ -12234,7 +12387,7 @@
 "Submodule work tree '%s' contains a .git directory. This will be replaced "
 "with a .git file by using absorbgitdirs."
 msgstr ""
-"Undermodulsarbetskatalogen \"%s\" innehåller en .git-katalog. Denna kommer "
+"Undermodulsarbetskatalogen ”%s” innehåller en .git-katalog. Denna kommer "
 "ersättas med en .git-fil med absorbgitdirs."
 
 #, c-format
@@ -12242,16 +12395,15 @@
 "Submodule work tree '%s' contains local modifications; use '-f' to discard "
 "them"
 msgstr ""
-"Undermodulens arbetskatalog \"%s\" har lokala ändringar; \"-f\" kastar bort "
-"dem"
+"Undermodulens arbetskatalog ”%s” har lokala ändringar; ”-f” kastar bort dem"
 
 #, c-format
 msgid "Cleared directory '%s'\n"
-msgstr "Rensade katalogen \"%s\"\n"
+msgstr "Rensade katalogen ”%s”\n"
 
 #, c-format
 msgid "Could not remove submodule work tree '%s'\n"
-msgstr "Kunde inte ta bort undermodulens arbetskatalog \"%s\"\n"
+msgstr "Kunde inte ta bort undermodulens arbetskatalog ”%s”\n"
 
 #, c-format
 msgid "could not create empty submodule directory %s"
@@ -12259,7 +12411,7 @@
 
 #, c-format
 msgid "Submodule '%s' (%s) unregistered for path '%s'\n"
-msgstr "Undermodulen \"%s\" (%s) registrerad för sökvägen \"%s\"\n"
+msgstr "Undermodulen ”%s” (%s) registrerad för sökvägen ”%s”\n"
 
 msgid "remove submodule working trees even if they contain local changes"
 msgstr ""
@@ -12274,7 +12426,7 @@
 "git submodule deinit [--quiet] [-f | --force] [--all | [--] [<sökväg>...]]"
 
 msgid "Use '--all' if you really want to deinitialize all submodules"
-msgstr "Använd \"--all\" om du verkligen vill avinitiera alla undermoduler"
+msgstr "Använd ”--all” om du verkligen vill avinitiera alla undermoduler"
 
 msgid ""
 "An alternate computed from a superproject's alternate is invalid.\n"
@@ -12284,40 +12436,40 @@
 msgstr ""
 "En suppleant beräknad från huvudprojektets suppleant är ogiltig.\n"
 "För att i så fall låta Git klona utan ett suppleant, sätt\n"
-"submodule.alternateErrorStrategy till \"info\" eller, likvärdigt, klona\n"
-"med \"--reference-if-able\" istället för \"--reference\"."
+"submodule.alternateErrorStrategy till ”info” eller, likvärdigt, klona\n"
+"med ”--reference-if-able” istället för ”--reference”."
 
 #, c-format
 msgid "could not get a repository handle for gitdir '%s'"
-msgstr "kunde inte få tag i arkivhandtag för gitkatalogen \"%s\""
+msgstr "kunde inte få tag i arkivhandtag för gitkatalogen ”%s”"
 
 #, c-format
 msgid "submodule '%s' cannot add alternate: %s"
-msgstr "undermodulen \"%s\" kan inte lägga till suppleant: %s"
+msgstr "undermodulen ”%s” kan inte lägga till suppleant: %s"
 
 #, c-format
 msgid "Value '%s' for submodule.alternateErrorStrategy is not recognized"
-msgstr "Värdet \"%s\" i submodule.alternateErrorStrategy förstås inte"
+msgstr "Värdet ”%s” i submodule.alternateErrorStrategy förstås inte"
 
 #, c-format
 msgid "Value '%s' for submodule.alternateLocation is not recognized"
-msgstr "Värdet \"%s\" i submodule.alternateLocation förstås inte"
+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"
+msgstr "vägrar skapa/använda ”%s” i en annan undermoduls gitkatalog"
 
 #, c-format
 msgid "clone of '%s' into submodule path '%s' failed"
-msgstr "misslyckades klona \"%s\" till undermodulsökvägen \"%s\""
+msgstr "misslyckades klona ”%s” till undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "directory not empty: '%s'"
-msgstr "katalogen inte tom: \"%s\""
+msgstr "katalogen inte tom: ”%s”"
 
 #, c-format
 msgid "could not get submodule directory for '%s'"
-msgstr "kunde inte få tag i undermodulkatalog för \"%s\""
+msgstr "kunde inte få tag i undermodulkatalog för ”%s”"
 
 msgid "alternative anchor for relative paths"
 msgstr "alternativa ankare för relativa sökvägar"
@@ -12351,15 +12503,14 @@
 
 #, c-format
 msgid "Invalid update mode '%s' configured for submodule path '%s'"
-msgstr ""
-"Ogiltigt uppdateringsläge \"%s\" konfigurerat för undermodulsökväg \"%s\""
+msgstr "Ogiltigt uppdateringsläge ”%s” konfigurerat för undermodulsökväg ”%s”"
 
 #, c-format
 msgid "Submodule path '%s' not initialized"
-msgstr "Undermodulsökvägen \"%s\" har inte initierats"
+msgstr "Undermodulsökvägen ”%s” har inte initierats"
 
 msgid "Maybe you want to use 'update --init'?"
-msgstr "Kanske menade du att använda \"update --init\"?"
+msgstr "Kanske menade du att använda ”update --init”?"
 
 #, c-format
 msgid "Skipping unmerged submodule %s"
@@ -12367,63 +12518,67 @@
 
 #, c-format
 msgid "Skipping submodule '%s'"
-msgstr "Hoppar över undermodulen \"%s\""
+msgstr "Hoppar över undermodulen ”%s”"
+
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "kan inte klona undermodulen ”%s” utan en URL"
 
 #, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
-msgstr "Misslyckades klona \"%s\". Nytt försök planlagt"
+msgstr "Misslyckades klona ”%s”. Nytt försök planlagt"
 
 #, c-format
 msgid "Failed to clone '%s' a second time, aborting"
-msgstr "Misslyckades klona \"%s\" för andra gången, avbryter"
+msgstr "Misslyckades klona ”%s” för andra gången, avbryter"
 
 #, c-format
 msgid "Unable to checkout '%s' in submodule path '%s'"
-msgstr "Kan inte checka ut \"%s\" i undermodulsökvägen \"%s\""
+msgstr "Kan inte checka ut ”%s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Unable to rebase '%s' in submodule path '%s'"
-msgstr "Kan inte ombasera \"%s\" i undermodulsökvägen \"%s\""
+msgstr "Kan inte ombasera ”%s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Unable to merge '%s' in submodule path '%s'"
-msgstr "Kan inte slå ihop \"%s\" i undermodulsökvägen \"%s\""
+msgstr "Kan inte slå ihop ”%s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Execution of '%s %s' failed in submodule path '%s'"
-msgstr "Misslyckades köra \"%s %s\" i undermodulsökvägen \"%s\""
+msgstr "Misslyckades köra ”%s %s” i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Submodule path '%s': checked out '%s'\n"
-msgstr "Undermodulsökvägen \"%s\": checkade ut \"%s\"\n"
+msgstr "Undermodulsökvägen ”%s”: checkade ut ”%s”\n"
 
 #, c-format
 msgid "Submodule path '%s': rebased into '%s'\n"
-msgstr "Undermodulsökvägen \"%s\": ombaserade in i \"%s\"\n"
+msgstr "Undermodulsökvägen ”%s”: ombaserade in i ”%s”\n"
 
 #, c-format
 msgid "Submodule path '%s': merged in '%s'\n"
-msgstr "Undermodulsökvägen \"%s\": sammanslagen i \"%s\"\n"
+msgstr "Undermodulsökvägen ”%s”: sammanslagen i ”%s”\n"
 
 #, c-format
 msgid "Submodule path '%s': '%s %s'\n"
-msgstr "Undermodulsökvägen \"%s\": \"%s %s\"\n"
+msgstr "Undermodulsökvägen ”%s”: ”%s %s”\n"
 
 #, c-format
 msgid "Unable to fetch in submodule path '%s'; trying to directly fetch %s:"
-msgstr "Kan inte hämta i undermodulsökväg \"%s\"; försökte hämta %s direkt:"
+msgstr "Kan inte hämta i undermodulsökväg ”%s”; försökte hämta %s direkt:"
 
 #, c-format
 msgid ""
 "Fetched in submodule path '%s', but it did not contain %s. Direct fetching "
 "of that commit failed."
 msgstr ""
-"Hämtade i undermodulssökvägen \"%s\", men den innehöll inte %s. Direkt "
+"Hämtade i undermodulssökvägen ”%s”, men den innehöll inte %s. Direkt "
 "hämtning av incheckningen misslyckades."
 
 #, c-format
 msgid "could not initialize submodule at path '%s'"
-msgstr "kunde inte initiera undermodul i sökvägen \"%s\""
+msgstr "kunde inte initiera undermodul i sökvägen ”%s”"
 
 #, c-format
 msgid ""
@@ -12435,19 +12590,19 @@
 
 #, c-format
 msgid "Unable to find current revision in submodule path '%s'"
-msgstr "Kan inte hitta aktuell revision i undermodulsökvägen \"%s\""
+msgstr "Kan inte hitta aktuell revision i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Unable to fetch in submodule path '%s'"
-msgstr "Kan inte hämta i undermodulsökväg \"%s\""
+msgstr "Kan inte hämta i undermodulsökväg ”%s”"
 
 #, c-format
 msgid "Unable to find %s revision in submodule path '%s'"
-msgstr "Kan inte hitta %s revision i undermodulsökvägen \"%s\""
+msgstr "Kan inte hitta %s revision i undermodulsökvägen ”%s”"
 
 #, c-format
 msgid "Failed to recurse into submodule path '%s'"
-msgstr "Misslyckades rekursera in i undermodulsökväg \"%s\""
+msgstr "Misslyckades rekursera in i undermodulsökväg ”%s”"
 
 msgid "force checkout updates"
 msgstr "tvinga utcheckningsuppdateringar"
@@ -12465,13 +12620,13 @@
 msgstr "hämta inte nya objekt från fjärrplatsen"
 
 msgid "use the 'checkout' update strategy (default)"
-msgstr "använd uppdateringsstrategin \"checkout\" (utcheckning; förval)"
+msgstr "använd uppdateringsstrategin ”checkout” (utcheckning; förval)"
 
 msgid "use the 'merge' update strategy"
-msgstr "använd uppdateringsstrategin \"merge\" (sammanslagning)"
+msgstr "använd uppdateringsstrategin ”merge” (sammanslagning)"
 
 msgid "use the 'rebase' update strategy"
-msgstr "använd uppdateringsstrategin \"rebase\" (ombasering)"
+msgstr "använd uppdateringsstrategin ”rebase” (ombasering)"
 
 msgid "create a shallow clone truncated to the specified number of revisions"
 msgstr "skapa en grund klon trunkerad till angivet antal revisioner"
@@ -12499,6 +12654,9 @@
 "[no-]recommend-shallow] [--reference <arkiv>] [--recursive] [--[no-]single-"
 "branch] [--] [<sökväg>...]"
 
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Misslyckades slå upp HEAD som giltig referens."
+
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<flaggor>] [<sökväg>...]"
 
@@ -12541,19 +12699,19 @@
 
 #, c-format
 msgid "creating branch '%s'"
-msgstr "skapar grenen \"%s\""
+msgstr "skapar grenen ”%s”"
 
 #, c-format
 msgid "Adding existing repo at '%s' to the index\n"
-msgstr "Lägger till befintligt arkiv i \"%s\" i indexet\n"
+msgstr "Lägger till befintligt arkiv i ”%s” i indexet\n"
 
 #, c-format
 msgid "'%s' already exists and is not a valid git repo"
-msgstr "\"%s\" finns redan och är inte ett giltigt git-arkiv"
+msgstr "”%s” finns redan och är inte ett giltigt git-arkiv"
 
 #, c-format
 msgid "A git directory for '%s' is found locally with remote(s):\n"
-msgstr "En git-katalog för \"%s\" hittades lokalt med fjärr(ar):\n"
+msgstr "En git-katalog för ”%s” hittades lokalt med fjärr(ar):\n"
 
 #, c-format
 msgid ""
@@ -12567,41 +12725,41 @@
 "Om du vill återanvända den lokala git-katalogen istället för att klona på "
 "nytt från\n"
 "  %s\n"
-"kan du använda flaggan \"--force\". Om den lokala git-katalogen inte är "
+"kan du använda flaggan ”--force”. Om den lokala git-katalogen inte är "
 "korrekt\n"
 "arkiv eller om du är osäker på vad det här betyder, välj ett annat namn med\n"
-"flaggan \"--name\"."
+"flaggan ”--name”."
 
 #, 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'"
-msgstr "Kan inte checka ut undermodulen \"%s\""
+msgstr "Kan inte checka ut undermodulen ”%s”"
 
 msgid "please make sure that the .gitmodules file is in the working tree"
 msgstr "se till att .gitmodules finns i arbetskatalogen"
 
 #, c-format
 msgid "Failed to add submodule '%s'"
-msgstr "Misslyckades lägga till undermodulen \"%s\""
+msgstr "Misslyckades lägga till undermodulen ”%s”"
 
 #, c-format
 msgid "Failed to register submodule '%s'"
-msgstr "Misslyckades registrera undermodulen \"%s\""
+msgstr "Misslyckades registrera undermodulen ”%s”"
 
 #, c-format
 msgid "'%s' already exists in the index"
-msgstr "\"%s\" finns redan i indexet"
+msgstr "”%s” finns redan i indexet"
 
 #, c-format
 msgid "'%s' already exists in the index and is not a submodule"
-msgstr "\"%s\" finns redan i indexet och är inte en undermodul"
+msgstr "”%s” finns redan i indexet och är inte en undermodul"
 
 #, c-format
 msgid "'%s' does not have a commit checked out"
-msgstr "\"%s\" har inte någon utcheckad incheckning"
+msgstr "”%s” har inte någon utcheckad incheckning"
 
 msgid "branch of repository to add as submodule"
 msgstr "gren från arkivet att lägga till som undermodul"
@@ -12627,11 +12785,11 @@
 
 #, c-format
 msgid "repo URL: '%s' must be absolute or begin with ./|../"
-msgstr "arkiv-URL: \"%s\" måste vara absolut eller börja med ./|../"
+msgstr "arkiv-URL: ”%s” måste vara absolut eller börja med ./|../"
 
 #, c-format
 msgid "'%s' is not a valid submodule name"
-msgstr "\"%s\" är inte ett giltigt namn på undermodul"
+msgstr "”%s” är inte ett giltigt namn på undermodul"
 
 msgid "git submodule--helper <command>"
 msgstr "git submodule--helper <kommando>"
@@ -12691,11 +12849,11 @@
 
 #, c-format
 msgid "tag '%s' not found."
-msgstr "taggen \"%s\" hittades inte."
+msgstr "taggen ”%s” hittades inte."
 
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
-msgstr "Tog bort tagg \"%s\" (var %s)\n"
+msgstr "Tog bort tagg ”%s” (var %s)\n"
 
 #, c-format
 msgid ""
@@ -12707,7 +12865,7 @@
 "\n"
 "Skriv ett meddelande för taggen:\n"
 "  %s\n"
-"Rader som inleds med \"%c\" ignoreras.\n"
+"Rader som inleds med ”%c” ignoreras.\n"
 
 #, c-format
 msgid ""
@@ -12720,7 +12878,7 @@
 "\n"
 "Skriv ett meddelande för taggen:\n"
 "  %s\n"
-"Rader som inleds med \"%c\" kommer behållas; du kan själv ta bort dem om\n"
+"Rader som inleds med ”%c” kommer behållas; du kan själv ta bort dem om\n"
 "du vill.\n"
 
 msgid "unable to sign the tag"
@@ -12807,15 +12965,15 @@
 
 #, c-format
 msgid "the '%s' option is only allowed in list mode"
-msgstr "flaggan \"%s\" är endast tillåten i listläge"
+msgstr "flaggan ”%s” är endast tillåten i listläge"
 
 #, c-format
 msgid "'%s' is not a valid tag name."
-msgstr "\"%s\" är inte ett giltigt taggnamn."
+msgstr "”%s” är inte ett giltigt taggnamn."
 
 #, c-format
 msgid "tag '%s' already exists"
-msgstr "taggen \"%s\" finns redan"
+msgstr "taggen ”%s” finns redan"
 
 #, c-format
 msgid "Invalid cleanup mode %s"
@@ -12823,7 +12981,7 @@
 
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
-msgstr "Uppdaterad tagg \"%s\" (var %s)\n"
+msgstr "Uppdaterad tagg ”%s” (var %s)\n"
 
 msgid "pack exceeds maximum allowed size"
 msgstr "paket är större än tillåten maximal storlek"
@@ -12855,7 +13013,7 @@
 
 #, c-format
 msgid "Testing mtime in '%s' "
-msgstr "Testar mtime i \"%s\" "
+msgstr "Testar mtime i ”%s” "
 
 msgid "directory stat info does not change after adding a new file"
 msgstr "stat-informationen för en katalog ändras inte när nya filer läggs till"
@@ -12915,19 +13073,19 @@
 msgstr "lägg till angiven post i indexet"
 
 msgid "mark files as \"not changing\""
-msgstr "markera filer som \"ändras inte\""
+msgstr "markera filer som ”ändras inte”"
 
 msgid "clear assumed-unchanged bit"
-msgstr "rensa \"assume-unchanged\"-biten"
+msgstr "rensa ”assume-unchanged”-biten"
 
 msgid "mark files as \"index-only\""
-msgstr "markera filer som \"endast index\""
+msgstr "markera filer som ”endast index”"
 
 msgid "clear skip-worktree bit"
-msgstr "töm \"skip-worktree\"-biten"
+msgstr "töm ”skip-worktree”-biten"
 
 msgid "do not touch index-only entries"
-msgstr "rör inte \"endast index\"-poster"
+msgstr "rör inte ”endast index”-poster"
 
 msgid "add to index only; do not add content to object database"
 msgstr "lägg endast till indexet; lägg inte till innehållet i objektdatabasen"
@@ -12962,6 +13120,9 @@
 msgid "write index in this format"
 msgstr "skriv index i detta format"
 
+msgid "report on-disk index format version"
+msgstr "rapportera formatversion för indexfilen på disk"
+
 msgid "enable or disable split index"
 msgstr "aktivera eller inaktivera delat index"
 
@@ -12981,10 +13142,18 @@
 msgstr "aktivera eller inaktivera filsystemsövervakning"
 
 msgid "mark files as fsmonitor valid"
-msgstr "markera filer som \"fsmonitor valid\""
+msgstr "markera filer som ”fsmonitor valid”"
 
 msgid "clear fsmonitor valid bit"
-msgstr "töm \"fsmonitor valid\"-bit"
+msgstr "töm ”fsmonitor valid”-bit"
+
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: vad %d, sattes till %d"
 
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
@@ -13019,7 +13188,7 @@
 
 #, c-format
 msgid "Untracked cache enabled for '%s'"
-msgstr "Ospårad cache är aktiverad för \"%s\""
+msgstr "Ospårad cache är aktiverad för ”%s”"
 
 msgid "core.fsmonitor is unset; set it if you really want to enable fsmonitor"
 msgstr "core.fsmonitor inte satt; sätt om du verkligen vill aktivera fsmonitor"
@@ -13105,10 +13274,11 @@
 
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <sträng>]]\n"
-"                 [-b <ny-gren>] <sökväg> [<incheckning-igt>]"
+"                 [--orphan] [(-b | -B) <ny-gren>] <sökväg> [<incheckning-"
+"igt>]"
 
 msgid "git worktree list [-v | --porcelain [-z]]"
 msgstr "git worktree list [-v | --porcelain [-z]]"
@@ -13131,6 +13301,37 @@
 msgid "git worktree unlock <worktree>"
 msgstr "git worktree unlock <arbetskatalog>"
 
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Ingen möjlig källgren, använder ”--orphan”"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Om meningen var att skapa en arbetskatalog från en ny ofödd\n"
+"gren (gren utan incheckningar) för det här arkivet kan du göra\n"
+"det med flaggan --orphan:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Om meningen var att skapa en arbetskatalog från en ny ofödd\n"
+"gren (gren utan incheckningar) för det här arkivet kan du göra\n"
+"det med flaggan --orphan:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "Tar bort %s/%s: %s"
@@ -13143,68 +13344,92 @@
 
 #, c-format
 msgid "'%s' already exists"
-msgstr "\"%s\" finns redan"
+msgstr "”%s” finns redan"
 
 #, c-format
 msgid "unusable worktree destination '%s'"
-msgstr "oanvändbar mål för arbetskatalog \"%s\""
+msgstr "oanvändbar mål för arbetskatalog ”%s”"
 
 #, c-format
 msgid ""
 "'%s' is a missing but locked worktree;\n"
 "use '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"
 msgstr ""
-"\"%s\" är en saknad men låst arbetskatalog;\n"
-"använd \"%s -f -f\" för att överstyra, eller \"unlock\" och \"prune\" eller "
-"\"remove\" för att rensa"
+"”%s” är en saknad men låst arbetskatalog;\n"
+"använd ”%s -f -f” för att överstyra, eller ”unlock” och ”prune” eller "
+"”remove” för att rensa"
 
 #, c-format
 msgid ""
 "'%s' is a missing but already registered worktree;\n"
 "use '%s -f' to override, or 'prune' or 'remove' to clear"
 msgstr ""
-"\"%s\" är en saknad men redan registrerad arbetskatalog;\n"
-"använd \"%s -f\" för att överstyra, eller \"prune\" eller \"remove\" för att "
-"rensa"
+"”%s” är en saknad men redan registrerad arbetskatalog;\n"
+"använd ”%s -f” för att överstyra, eller ”prune” eller ”remove” för att rensa"
 
 #, c-format
 msgid "failed to copy '%s' to '%s'; sparse-checkout may not work correctly"
 msgstr ""
-"misslyckades kopiera \"%s\" till \"%s\"; sparse-checkout kanske inte kommer "
-"att fungera korrekt"
+"misslyckades kopiera ”%s” till ”%s”; sparse-checkout kanske inte kommer att "
+"fungera korrekt"
 
 #, c-format
 msgid "failed to copy worktree config from '%s' to '%s'"
-msgstr ""
-"misslyckades kopiera arbetskatalogkonfiguration från \"%s\" till \"%s\""
+msgstr "misslyckades kopiera arbetskatalogkonfiguration från ”%s” till ”%s”"
 
 #, c-format
 msgid "failed to unset '%s' in '%s'"
-msgstr "misslyckades slå av \"%s\" i \"%s\""
+msgstr "misslyckades slå av ”%s” i ”%s”"
 
 #, c-format
 msgid "could not create directory of '%s'"
-msgstr "kunde inte skapa katalogen \"%s\""
+msgstr "kunde inte skapa katalogen ”%s”"
 
 msgid "initializing"
 msgstr "initierar"
 
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "kunde inte hitta den skapade arbetskatalogen ”%s”"
+
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
-msgstr "Förbereder arbetskatalog (ny gren \"%s\")"
+msgstr "Förbereder arbetskatalog (ny gren ”%s”)"
 
 #, c-format
 msgid "Preparing worktree (resetting branch '%s'; was at %s)"
-msgstr "Förbereder arbetskatalog (återställer gren \"%s\"; var på %s)"
+msgstr "Förbereder arbetskatalog (återställer gren ”%s”; var på %s)"
 
 #, c-format
 msgid "Preparing worktree (checking out '%s')"
-msgstr "Förbereder arbetskatalog (checkar ut \"%s\")"
+msgstr "Förbereder arbetskatalog (checkar ut ”%s”)"
+
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "onåbar: felaktig referens: %s"
 
 #, c-format
 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 ""
+"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 ""
+"Ingen lokal eller fjärr-referens finns trots att åtminstone en fjärr\n"
+"finns, avslutar; använd ”add -f” för att överstyra eller hämta från en fjärr "
+"först"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "checka ut <gren> även om den redan är utcheckad i en annan arbetskatalog"
@@ -13215,6 +13440,9 @@
 msgid "create or reset a branch"
 msgstr "skapa eller återställ en gren"
 
+msgid "create unborn branch"
+msgstr "skapa en ofödd gren"
+
 msgid "populate the new working tree"
 msgstr "befolka den nya arbetskatalogen"
 
@@ -13232,7 +13460,11 @@
 
 #, c-format
 msgid "options '%s', '%s', and '%s' cannot be used together"
-msgstr "flaggorna \"%s\", \"%s\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s”, ”%s” och ”%s” kan inte användas samtidigt"
+
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "flaggorna ”%s” och incheckning-igt kan inte användas samtidigt"
 
 msgid "added with --lock"
 msgstr "lagt till med --lock"
@@ -13244,30 +13476,29 @@
 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>"
+msgstr "lägg till ”prunable”-annoteringar till arbetskataloger äldre än <tid>"
 
 msgid "terminate records with a NUL character"
 msgstr "avsluta poster med NUL-tecken"
 
 #, c-format
 msgid "'%s' is not a working tree"
-msgstr "\"%s\" är inte en arbetskatalog"
+msgstr "”%s” är inte en arbetskatalog"
 
 msgid "The main working tree cannot be locked or unlocked"
 msgstr "Huvudarbetskatalogen kan inte låsas eller låsas upp"
 
 #, c-format
 msgid "'%s' is already locked, reason: %s"
-msgstr "\"%s\" är redan låst, orsak: %s"
+msgstr "”%s” är redan låst, orsak: %s"
 
 #, c-format
 msgid "'%s' is already locked"
-msgstr "\"%s\" är redan låst"
+msgstr "”%s” är redan låst"
 
 #, c-format
 msgid "'%s' is not locked"
-msgstr "\"%s\" är inte låst"
+msgstr "”%s” är inte låst"
 
 msgid "working trees containing submodules cannot be moved or removed"
 msgstr "arbetskataloger med undermoduler kan inte flyttas eller tas bort"
@@ -13277,11 +13508,11 @@
 
 #, c-format
 msgid "'%s' is a main working tree"
-msgstr "\"%s\" är inte en huvudarbetskatalog"
+msgstr "”%s” är inte en huvudarbetskatalog"
 
 #, c-format
 msgid "could not figure out destination name from '%s'"
-msgstr "kunde inte lista ut målnamn från \"%s\""
+msgstr "kunde inte lista ut målnamn från ”%s”"
 
 #, c-format
 msgid ""
@@ -13289,14 +13520,14 @@
 "use 'move -f -f' to override or unlock first"
 msgstr ""
 "kan inte flytta en låst arbetskatalog, orsak till lås: %s\n"
-"använd \"move -f -f\" för att överstyra, eller lås upp först"
+"använd ”move -f -f” för att överstyra, eller lås upp först"
 
 msgid ""
 "cannot move a locked working tree;\n"
 "use 'move -f -f' to override or unlock first"
 msgstr ""
 "kan inte flytta en låst arbetskatalog;\n"
-"använd \"move -f -f\" för att överstyra, eller lås upp först"
+"använd ”move -f -f” för att överstyra, eller lås upp först"
 
 #, c-format
 msgid "validation failed, cannot move working tree: %s"
@@ -13304,21 +13535,21 @@
 
 #, c-format
 msgid "failed to move '%s' to '%s'"
-msgstr "misslyckades flytta \"%s\" till \"%s\""
+msgstr "misslyckades flytta ”%s” till ”%s”"
 
 #, c-format
 msgid "failed to run 'git status' on '%s'"
-msgstr "misslyckades köra \"git status\" på \"%s\""
+msgstr "misslyckades köra ”git status” på ”%s”"
 
 #, c-format
 msgid "'%s' contains modified or untracked files, use --force to delete it"
 msgstr ""
-"\"%s\" innehåller ändrade eller ospårade filer, använd --force för att ta "
-"bort det"
+"”%s” innehåller ändrade eller ospårade filer, använd --force för att ta bort "
+"det"
 
 #, c-format
 msgid "failed to run 'git status' on '%s', code %d"
-msgstr "misslyckades köra \"git status\" på \"%s\", kod %d"
+msgstr "misslyckades köra ”git status” på ”%s”, kod %d"
 
 msgid "force removal even if worktree is dirty or locked"
 msgstr "tvinga ta bort även om arbetskatalogen är smutsig eller låst"
@@ -13329,14 +13560,14 @@
 "use 'remove -f -f' to override or unlock first"
 msgstr ""
 "kan inte ta bort en låst arbetskatalog, orsak till låset: %s\n"
-"använd \"remove -f -f\" för att överstyra, eller lås upp först"
+"använd ”remove -f -f” för att överstyra, eller lås upp först"
 
 msgid ""
 "cannot remove a locked working tree;\n"
 "use 'remove -f -f' to override or unlock first"
 msgstr ""
 "kan inte ta bort en låst arbetskatalog;\n"
-"använd \"remove -f -f\" för att överstyra, eller lås upp först"
+"använd ”remove -f -f” för att överstyra, eller lås upp först"
 
 #, c-format
 msgid "validation failed, cannot remove working tree: %s"
@@ -13367,11 +13598,11 @@
 
 #, c-format
 msgid "could not parse bundle list key %s with value '%s'"
-msgstr "kunde inte tolka listnyckeln %s med värdet \"%s\""
+msgstr "kunde inte tolka listnyckeln %s med värdet ”%s”"
 
 #, c-format
 msgid "bundle list at '%s' has no mode"
-msgstr "buntlistan på \"%s\" har inget läge"
+msgstr "buntlistan på ”%s” har inget läge"
 
 msgid "failed to create temporary file"
 msgstr "misslyckades skapa temporär fil"
@@ -13381,14 +13612,14 @@
 
 #, c-format
 msgid "file downloaded from '%s' is not a bundle"
-msgstr "filen hämtad från \"%s\" är inte en bunt"
+msgstr "filen hämtad från ”%s” är inte en bunt"
 
 msgid "failed to store maximum creation token"
 msgstr "misslyckades lagra maximal skaparsymbol"
 
 #, c-format
 msgid "unrecognized bundle mode from URI '%s'"
-msgstr "okänt buntlägre från URI:en \"%s\""
+msgstr "okänt buntlägre från URI:en ”%s”"
 
 #, c-format
 msgid "exceeded bundle URI recursion limit (%d)"
@@ -13396,35 +13627,35 @@
 
 #, c-format
 msgid "failed to download bundle from URI '%s'"
-msgstr "kunde inte hämta bunt från URI:en \"%s\""
+msgstr "kunde inte hämta bunt från URI:en ”%s”"
 
 #, c-format
 msgid "file at URI '%s' is not a bundle or bundle list"
-msgstr "filen på URI:en \"%s\" är inte en bunt eller buntlista"
+msgstr "filen på URI:en ”%s” är inte en bunt eller buntlista"
 
 #, c-format
 msgid "bundle-uri: unexpected argument: '%s'"
-msgstr "bundle-uri: okänt argument: \"%s\""
+msgstr "bundle-uri: okänt argument: ”%s”"
 
 msgid "bundle-uri: expected flush after arguments"
-msgstr "bundle-uri: förväntade \"flush\" efter argument"
+msgstr "bundle-uri: förväntade ”flush” efter argument"
 
 msgid "bundle-uri: got an empty line"
 msgstr "bunt-uri: fick en tom rad"
 
 msgid "bundle-uri: line is not of the form 'key=value'"
-msgstr "bunt-uri: raden är inte på formen \"nyckel=värde\""
+msgstr "bunt-uri: raden är inte på formen ”nyckel=värde”"
 
 msgid "bundle-uri: line has empty key or value"
 msgstr "bunt-uri: raden har tom nyckel eller värde"
 
 #, 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'"
-msgstr "okänd kapabilitet \"%s\""
+msgstr "okänd kapabilitet ”%s”"
 
 #, c-format
 msgid "'%s' does not look like a v2 or v3 bundle file"
@@ -13462,6 +13693,14 @@
 msgstr[0] "Bunten kräver denna referens:"
 msgstr[1] "Bunten kräver dessa %<PRIuMAX> referenser:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Bunten använder denna hashningsalgoritm: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Bunten använder detta filter. %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "kan inte duplicera bunthandtag"
 
@@ -13473,7 +13712,7 @@
 
 #, c-format
 msgid "ref '%s' is excluded by the rev-list options"
-msgstr "referensen \"%s\" exkluderas av argumenten till rev-list"
+msgstr "referensen ”%s” exkluderas av argumenten till rev-list"
 
 #, c-format
 msgid "unsupported bundle version %d"
@@ -13488,7 +13727,7 @@
 
 #, c-format
 msgid "cannot create '%s'"
-msgstr "kan inte skapa \"%s\""
+msgstr "kan inte skapa ”%s”"
 
 msgid "index-pack died"
 msgstr "index-pack dog"
@@ -13497,6 +13736,10 @@
 msgstr "avslutande stycke-id förekommer tidigare än förväntat"
 
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "stycke-id %<PRIx32> är inte %d-byte-justerad"
+
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "felaktigt stycke-offset %<PRIx64> och %<PRIx64>"
 
@@ -13548,9 +13791,8 @@
 msgid "Move objects and refs by archive"
 msgstr "Flytta objekt och referenser efter arkiv"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr ""
-"Visa innehåller eller typ- och storleksinformation för objekt i arkivet"
+msgid "Provide contents or details of repository objects"
+msgstr "Visa innehåll eller detaljer för objekt i arkivet"
 
 msgid "Display gitattributes information"
 msgstr "Visa information från gitattributes"
@@ -13687,8 +13929,8 @@
 msgid "A portable graphical interface to Git"
 msgstr "Ett portabelt grafiskt gränssnitt för Git"
 
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Beräkna objekt-id och möjligen skapa en blob från en fil"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Beräkna objekt-ID och möjligen skapa ett objekt från en fil"
 
 msgid "Display help information about Git"
 msgstr "Visa hjälpinformation om Git"
@@ -13835,6 +14077,11 @@
 msgid "Create, list, delete refs to replace objects"
 msgstr "Skapa, visa, ta bort referenser för att ersätta objekt"
 
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPERIMENTELLT: Spela om incheckningar ovanpå en ny bas, fungerar även med "
+"nakna arkiv"
+
 msgid "Generates a summary of pending changes"
 msgstr "Skapar en sammanfattning av väntande ändringar"
 
@@ -13875,7 +14122,7 @@
 msgstr "Begränsat inloggningsskal för SSH-åtkomst till bara Git"
 
 msgid "Summarize 'git log' output"
-msgstr "Summera \"git log\"-utdata"
+msgstr "Summera ”git log”-utdata"
 
 msgid "Show various types of objects"
 msgstr "Visa olika sorters objekt"
@@ -13955,8 +14202,8 @@
 msgid "Display version information about Git"
 msgstr "Visa versionsinformation om Git"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Visa loggar med differenser varje incheckning introducerar"
+msgid "Show logs with differences each commit introduces"
+msgstr "Visa loggar med ändringarna varje incheckning introducerar"
 
 msgid "Manage multiple working trees"
 msgstr "Hantera ytterligare arbetskataloger"
@@ -14072,6 +14319,32 @@
 msgid "commit-graph file is too small"
 msgstr "incheckningsgraffilen %s är för liten"
 
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "incheckningsgrafens oid-utbredningsstycke har fel storlek"
+
+msgid "commit-graph fanout values out of order"
+msgstr "incheckningsgrafens utbredningsvärden är i fel ordning"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "incheckningsgrafens OID-uppslagningsstycket har fel storlek"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "incheckningsgrafens incheckningsdatastycke har fel storlek"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "incheckningsgrafens generationsstycke har fel storlek"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "incheckningsgrafens ändrade-sökvägar-indexstycke är förö litet"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"ignorerar för litet ändrade-sökvägar-stycke (%<PRIuMAX> < %<PRIuMAX>) i "
+"incheckningsgraffilen"
+
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr "incheckningsgrafens signatur %X stämmer inte med signaturen %X"
@@ -14088,15 +14361,37 @@
 msgid "commit-graph file is too small to hold %u chunks"
 msgstr "incheckningsgraffilen är för liten för att innehålla %u stycken"
 
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"incheckningsgrafens nödvändiga OID-utbredningsstycke saknas eller är trasigt"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"incheckningsgrafens nödvändiga OID-uppslagningsstycke saknas eller är trasigt"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"incheckningsgrafens nödvändiga incheckningsdatastycke saknas eller är trasigt"
+
 msgid "commit-graph has no base graphs chunk"
 msgstr "incheckningsgrafen har inga bas-graf-stycken"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "incheckningsgrafens bas-graf-stycken är för små"
+
 msgid "commit-graph chain does not match"
 msgstr "incheckningsgrafens kedja stämmer inte"
 
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "antalet incheckningar i basgrafen för högt: %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "incheckningsgrafens kedjefil är för liten"
+
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
-msgstr "ogiltig incheckingsgrafkedja: rad \"%s\" är inte ett hash-värde"
+msgstr "ogiltig incheckingsgrafkedja: rad ”%s” är inte ett hash-värde"
 
 msgid "unable to find all commit-graph files"
 msgstr "kan inte hitta alla incheckingsgraffiler"
@@ -14111,6 +14406,12 @@
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "incheckningsgraf kräver spillgenerationsdata, men har ingen"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "incheckningsgrafens spillgenerationsdata är för liten"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "incheckningsgrafens extra-kant-pekare är utanför intervallet"
+
 msgid "Loading known commits in commit graph"
 msgstr "Läser in kända incheckningar i incheckningsgraf"
 
@@ -14160,7 +14461,7 @@
 
 #, c-format
 msgid "unable to adjust shared permissions for '%s'"
-msgstr "kan inte justera delade behörigheter för \"%s\""
+msgstr "kan inte justera delade behörigheter för ”%s”"
 
 #, c-format
 msgid "Writing out commit graph in %d pass"
@@ -14177,6 +14478,14 @@
 msgid "failed to rename temporary commit-graph file"
 msgstr "kunde inte byta namn på temporär incheckningsgraffil"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "kan inte slå ihop grafer med %<PRIuMAX>, %<PRIuMAX> incheckningar"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "kan inte slå ihop grafen %s, med för många incheckningar: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Söker sammanslagna incheckningar"
 
@@ -14185,7 +14494,7 @@
 
 msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
 msgstr ""
-"försöker skriva en incheckningsgraf, men \"core.commitGraph\" är inaktiverad"
+"försöker skriva en incheckningsgraf, men ”core.commitGraph” är inaktiverad"
 
 msgid "too many commits to write graph"
 msgstr "för många incheckningar för att skriva graf"
@@ -14208,9 +14517,6 @@
 msgid "failed to parse commit %s from commit-graph"
 msgstr "kunde inte tolka incheckning %s från incheckningsgraf"
 
-msgid "Verifying commits in commit graph"
-msgstr "Bekräftar incheckningar i incheckningsgrafen"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14234,20 +14540,6 @@
 "incheckningsgrafens föräldralista för incheckningen %s avslutas för tidigt"
 
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"incheckningsgrafen har generationsnummer noll för incheckningen %s, men icke-"
-"noll på annan plats"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"incheckningsgrafen har generationsnummer skilt från noll för incheckningen "
-"%s, men noll på annan plats"
-
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr ""
 "incheckningsgrafens generation för incheckningen %s är %<PRIuMAX> < "
@@ -14260,6 +14552,17 @@
 "= %<PRIuMAX>"
 
 #, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"incheckningsgrafen har generationsnummer som både är noll och icke-noll "
+"(dvs, incheckningarna ”%s” och ”%s”)"
+
+msgid "Verifying commits in commit graph"
+msgstr "Bekräftar incheckningar i incheckningsgrafen"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s är inte en incheckning!"
 
@@ -14276,11 +14579,16 @@
 "Stöd för <GIT_DIR>/info/grafts avråds från och\n"
 "kommer tas bort i en framtida version av Git.\n"
 "\n"
-"Använd \"git replace --convert-graft-file\"\n"
+"Använd ”git replace --convert-graft-file”\n"
 "för att omvandla grafts till ersättningsreferenser.\n"
 "\n"
 "Slå av detta meddelande genom att skriva\n"
-"\"git config advice.graftFileDeprecated false\""
+"”git config advice.graftFileDeprecated false”"
+
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr ""
+"incheckningen %s finns i incheckningsgrafen, men inte i objektdatabasen"
 
 #, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
@@ -14317,31 +14625,31 @@
 
 #, c-format
 msgid "could not determine free disk size for '%s'"
-msgstr "kunde inte ta reda på ledigt diskutrymme för \"%s\""
+msgstr "kunde inte ta reda på ledigt diskutrymme för ”%s”"
 
 #, c-format
 msgid "could not get info for '%s'"
-msgstr "kunde inte hämta info för \"%s\""
+msgstr "kunde inte hämta info för ”%s”"
 
 #, c-format
 msgid "[GLE %ld] health thread could not open '%ls'"
-msgstr "[GLE %ld] hälsotråden kunde inte öppna \"%ls\""
+msgstr "[GLE %ld] hälsotråden kunde inte öppna ”%ls”"
 
 #, c-format
 msgid "[GLE %ld] health thread getting BHFI for '%ls'"
-msgstr "[GLE %ld] hälsotråden hämtar BHFI för \"%ls\""
+msgstr "[GLE %ld] hälsotråden hämtar BHFI för ”%ls”"
 
 #, c-format
 msgid "could not convert to wide characters: '%s'"
-msgstr "kunde inte konvertera till breda tecken: \"%s\""
+msgstr "kunde inte konvertera till breda tecken: ”%s”"
 
 #, c-format
 msgid "BHFI changed '%ls'"
-msgstr "BHFI ändrade \"%ls\""
+msgstr "BHFI ändrade ”%ls”"
 
 #, c-format
 msgid "unhandled case in 'has_worktree_moved': %d"
-msgstr "ohanterat fall i \"has_worktree_moved\": %d"
+msgstr "ohanterat fall i ”has_worktree_moved”: %d"
 
 #, c-format
 msgid "health thread wait failed [GLE %ld]"
@@ -14359,23 +14667,23 @@
 
 #, c-format
 msgid "[GLE %ld] could not convert path to UTF-8: '%.*ls'"
-msgstr "[GLE %ld] kunde inte konvertera sökväg till UTF-8: \"%.*ls\""
+msgstr "[GLE %ld] kunde inte konvertera sökväg till UTF-8: ”%.*ls”"
 
 #, c-format
 msgid "[GLE %ld] could not watch '%s'"
-msgstr "[GLE %ld] kunde inte övervaka \"%s\""
+msgstr "[GLE %ld] kunde inte övervaka ”%s”"
 
 #, c-format
 msgid "[GLE %ld] could not get longname of '%s'"
-msgstr "[GLE %ld] kunde inte hämta långt namn för \"%s\""
+msgstr "[GLE %ld] kunde inte hämta långt namn för ”%s”"
 
 #, c-format
 msgid "ReadDirectoryChangedW failed on '%s' [GLE %ld]"
-msgstr "ReadDirectoryChangedW misslyckades på \"%s\" [GLE %ld]"
+msgstr "ReadDirectoryChangedW misslyckades på ”%s” [GLE %ld]"
 
 #, c-format
 msgid "GetOverlappedResult failed on '%s' [GLE %ld]"
-msgstr "GetOverlappedResult misslyckades på \"%s\" [GLE %ld]"
+msgstr "GetOverlappedResult misslyckades på ”%s” [GLE %ld]"
 
 #, c-format
 msgid "could not read directory changes [GLE %ld]"
@@ -14399,11 +14707,11 @@
 
 #, c-format
 msgid "[GLE %ld] unable to open for read '%ls'"
-msgstr "[GLE %ld] kunde inte öppna \"%ls\" för läsning"
+msgstr "[GLE %ld] kunde inte öppna ”%ls” för läsning"
 
 #, c-format
 msgid "[GLE %ld] unable to get protocol information for '%ls'"
-msgstr "[GLE %ld] kunde inte hämta protokollinformation för \"%ls\""
+msgstr "[GLE %ld] kunde inte hämta protokollinformation för ”%ls”"
 
 #, c-format
 msgid "failed to copy SID (%ld)"
@@ -14411,7 +14719,7 @@
 
 #, c-format
 msgid "failed to get owner for '%s' (%ld)"
-msgstr "misslyckades hämta ägaren för \"%s\" (%ld)"
+msgstr "misslyckades hämta ägaren för ”%s” (%ld)"
 
 msgid "memory exhausted"
 msgstr "minnet slut"
@@ -14478,15 +14786,15 @@
 
 #, c-format
 msgid "could not start accept_thread '%s'"
-msgstr "kunde inte ta status \"accept_thread\" \"%s\""
+msgstr "kunde inte ta status ”accept_thread” ”%s”"
 
 #, c-format
 msgid "could not start worker[0] for '%s'"
-msgstr "kunde inte starta \"worker[0]\" för \"%s\""
+msgstr "kunde inte starta ”worker[0]” för ”%s”"
 
 #, c-format
 msgid "ConnectNamedPipe failed for '%s' (%lu)"
-msgstr "ConnectNamedPipe misslyckades för \"%s\" (%lu)"
+msgstr "ConnectNamedPipe misslyckades för ”%s” (%lu)"
 
 #, c-format
 msgid "could not create fd from pipe for '%s'"
@@ -14494,14 +14802,14 @@
 
 #, c-format
 msgid "could not start thread[0] for '%s'"
-msgstr "kunde inte starta thread[0] för \"%s\""
+msgstr "kunde inte starta thread[0] för ”%s”"
 
 #, c-format
 msgid "wait for hEvent failed for '%s'"
-msgstr "misslyckades vänta på hEvent för \"%s\""
+msgstr "misslyckades vänta på hEvent för ”%s”"
 
 msgid "cannot resume in the background, please use 'fg' to resume"
-msgstr "kan inte fortsätta i bakgrunden, använd \"fg\" för att återuppta"
+msgstr "kan inte fortsätta i bakgrunden, använd ”fg” för att återuppta"
 
 msgid "cannot restore terminal settings"
 msgstr "kan inte återställa terminalinställningar"
@@ -14522,7 +14830,7 @@
 
 #, c-format
 msgid "could not expand include path '%s'"
-msgstr "kunde inte expandera inkluderingssökväg \"%s\""
+msgstr "kunde inte expandera inkluderingssökväg ”%s”"
 
 msgid "relative config includes must come from files"
 msgstr "relativa konfigureringsinkluderingar måste komma från filer"
@@ -14543,11 +14851,11 @@
 
 #, c-format
 msgid "missing environment variable name for configuration '%.*s'"
-msgstr "miljövariabelnamn saknas för konfigurationen \"%.*s\""
+msgstr "miljövariabelnamn saknas för konfigurationen ”%.*s”"
 
 #, c-format
 msgid "missing environment variable '%s' for configuration '%.*s'"
-msgstr "miljövariabeln \"%s\" saknas för konfigurationen \"%.*s\""
+msgstr "miljövariabeln ”%s” saknas för konfigurationen ”%.*s”"
 
 #, c-format
 msgid "key does not contain a section: %s"
@@ -14624,38 +14932,35 @@
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s': %s"
-msgstr "felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\": %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s”: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in blob %s: %s"
-msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i blob:en %s: %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i blob:en %s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in file %s: %s"
-msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i filen %s: %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i filen %s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in standard input: %s"
 msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i standard in: %s"
+"felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i standard in: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in submodule-blob %s: %s"
 msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i undermodul-blob:"
-"en %s: %s"
+"felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i undermodul-blob:en "
+"%s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in command line %s: %s"
 msgstr ""
-"felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i kommandoraden "
-"%s: %s"
+"felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i kommandoraden %s: %s"
 
 #, c-format
 msgid "bad numeric config value '%s' for '%s' in %s: %s"
-msgstr "felaktigt numeriskt konfigurationsvärde \"%s\" för \"%s\" i %s: %s"
+msgstr "felaktigt numeriskt konfigurationsvärde ”%s” för ”%s” i %s: %s"
 
 #, c-format
 msgid "invalid value for variable %s"
@@ -14663,19 +14968,19 @@
 
 #, c-format
 msgid "ignoring unknown core.fsync component '%s'"
-msgstr "ignorerar okänd core.fsync-komponent \"%s\""
+msgstr "ignorerar okänd core.fsync-komponent ”%s”"
 
 #, c-format
 msgid "bad boolean config value '%s' for '%s'"
-msgstr "felaktigt booleskt konfigurationsvärde \"%s\" för \"%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\""
+msgstr "misslyckades expandera användarkatalog i: ”%s”"
 
 #, c-format
 msgid "'%s' for '%s' is not a valid timestamp"
-msgstr "\"%s\" för \"%s\" är inte en giltig tidsstämpel"
+msgstr "”%s” för ”%s” är inte en giltig tidsstämpel"
 
 #, c-format
 msgid "abbrev length out of range: %d"
@@ -14685,12 +14990,12 @@
 msgid "bad zlib compression level %d"
 msgstr "felaktigt zlib-komprimeringsgrad %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar kan bara vara ett tecken"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar kan bara vara ett ASCII-tecken"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
-msgstr "ignorerar okänt core.fsyncMethod-värde \"%s\""
+msgstr "ignorerar okänt core.fsyncMethod-värde ”%s”"
 
 msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead"
 msgstr "core.fsyncObjectFiles avråds från; använd core.fsync istället"
@@ -14712,19 +15017,15 @@
 
 #, c-format
 msgid "unable to load config blob object '%s'"
-msgstr "kunde inte läsa konfigurerings-blobobjektet \"%s\""
+msgstr "kunde inte läsa konfigurerings-blobobjektet ”%s”"
 
 #, c-format
 msgid "reference '%s' does not point to a blob"
-msgstr "referensen \"%s\" pekar inte på en blob"
+msgstr "referensen ”%s” pekar inte på en blob"
 
 #, c-format
 msgid "unable to resolve config blob '%s'"
-msgstr "kan inte slå upp konfigurerings-bloben \"%s\""
-
-#, c-format
-msgid "failed to parse %s"
-msgstr "kunde inte tolka %s"
+msgstr "kan inte slå upp konfigurerings-bloben ”%s”"
 
 msgid "unable to parse command-line config"
 msgstr "kan inte tolka kommandoradskonfiguration"
@@ -14734,24 +15035,24 @@
 
 #, c-format
 msgid "Invalid %s: '%s'"
-msgstr "Felaktigt %s: \"%s\""
+msgstr "Felaktigt %s: ”%s”"
 
 #, c-format
 msgid "splitIndex.maxPercentChange value '%d' should be between 0 and 100"
 msgstr ""
-"värdet \"%d\" för splitIndex.maxPercentChange borde vara mellan 0 och 100"
+"värdet ”%d” för splitIndex.maxPercentChange borde vara mellan 0 och 100"
 
 #, c-format
 msgid "unable to parse '%s' from command-line config"
-msgstr "kunde inte tolka värdet \"%s\" från kommandoradskonfiguration"
+msgstr "kunde inte tolka värdet ”%s” från kommandoradskonfiguration"
 
 #, c-format
 msgid "bad config variable '%s' in file '%s' at line %d"
-msgstr "felaktig konfigurationsvariabel \"%s\" i filen \"%s\" på rad %d"
+msgstr "felaktig konfigurationsvariabel ”%s” i filen ”%s” på rad %d"
 
 #, c-format
 msgid "invalid section name '%s'"
-msgstr "felaktigt sektionsnamn \"%s\""
+msgstr "felaktigt sektionsnamn ”%s”"
 
 #, c-format
 msgid "%s has multiple values"
@@ -14759,7 +15060,7 @@
 
 #, c-format
 msgid "failed to write new configuration file %s"
-msgstr "kan inte skriva nya konfigurationsfilen \"%s\""
+msgstr "kan inte skriva nya konfigurationsfilen ”%s”"
 
 #, c-format
 msgid "could not lock config file %s"
@@ -14771,7 +15072,7 @@
 
 #, c-format
 msgid "invalid config file %s"
-msgstr "ogiltig konfigurationsfil: \"%s\""
+msgstr "ogiltig konfigurationsfil: ”%s”"
 
 #, c-format
 msgid "fstat on %s failed"
@@ -14779,7 +15080,7 @@
 
 #, c-format
 msgid "unable to mmap '%s'%s"
-msgstr "kunde inte utföra mmap på \"%s\"%s"
+msgstr "kunde inte utföra mmap på ”%s”%s"
 
 #, c-format
 msgid "chmod on %s failed"
@@ -14791,15 +15092,19 @@
 
 #, c-format
 msgid "could not set '%s' to '%s'"
-msgstr "kunde inte ställa in \"%s\" till \"%s\""
+msgstr "kunde inte ställa in ”%s” till ”%s”"
 
 #, c-format
 msgid "invalid section name: %s"
 msgstr "felaktigt namn på stycke: %s"
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "vägrar arbeta med för långa rader i ”%s” på rad %<PRIuMAX>"
+
+#, c-format
 msgid "missing value for '%s'"
-msgstr "värde saknas för \"%s\""
+msgstr "värde saknas för ”%s”"
 
 msgid "the remote end hung up upon initial contact"
 msgstr "fjärren lade på vid inledande kontakt"
@@ -14817,25 +15122,25 @@
 
 #, c-format
 msgid "server doesn't support '%s'"
-msgstr "Servern stöder inte \"%s\""
+msgstr "Servern stöder inte ”%s”"
 
 #, c-format
 msgid "server doesn't support feature '%s'"
-msgstr "servern stöder inte funktionen \"%s\""
+msgstr "servern stöder inte funktionen ”%s”"
 
 msgid "expected flush after capabilities"
-msgstr "förväntade \"flush\" efter förmågor"
+msgstr "förväntade ”flush” efter förmågor"
 
 #, c-format
 msgid "ignoring capabilities after first line '%s'"
-msgstr "ignorerar förmågor efter första raden \"%s\""
+msgstr "ignorerar förmågor efter första raden ”%s”"
 
 msgid "protocol error: unexpected capabilities^{}"
 msgstr "protokollfel: förväntade inte capabilities^{}"
 
 #, c-format
 msgid "protocol error: expected shallow sha-1, got '%s'"
-msgstr "protokollfel: förväntade \"shallow sha-1\" fick \"%s\""
+msgstr "protokollfel: förväntade ”shallow sha-1” fick ”%s”"
 
 msgid "repository on the other end cannot be shallow"
 msgstr "arkivet på andra sidan kan inte vara grunt"
@@ -14845,18 +15150,18 @@
 
 #, c-format
 msgid "protocol error: unexpected '%s'"
-msgstr "protokollfel: förväntade inte \"%s\""
+msgstr "protokollfel: förväntade inte ”%s”"
 
 #, c-format
 msgid "unknown object format '%s' specified by server"
-msgstr "okänt objektformat \"%s\" angavs av servern"
+msgstr "okänt objektformat ”%s” angavs av servern"
 
 #, c-format
 msgid "error on bundle-uri response line %d: %s"
 msgstr "fel på bundle-uri-svar rad %d: %s"
 
 msgid "expected flush after bundle-uri listing"
-msgstr "förväntade \"flush\" efter bundle-uri-listan"
+msgstr "förväntade ”flush” efter bundle-uri-listan"
 
 msgid "expected response end packet after ref listing"
 msgstr "förväntade svarsavslutningspaket efter ref-listan"
@@ -14866,11 +15171,11 @@
 msgstr "ogiltigt svar på ls-refs: %s"
 
 msgid "expected flush after ref listing"
-msgstr "förväntade \"flush\" efter ref-listan"
+msgstr "förväntade ”flush” efter ref-listan"
 
 #, c-format
 msgid "protocol '%s' is not supported"
-msgstr "protokollet \"%s\" stöds inte"
+msgstr "protokollet ”%s” stöds inte"
 
 msgid "unable to set SO_KEEPALIVE on socket"
 msgstr "kunde inte sätta SO_KEEPALIVE på uttaget"
@@ -14914,40 +15219,40 @@
 
 #, c-format
 msgid "strange hostname '%s' blocked"
-msgstr "konstigt värdnamn \"%s\" blockerat"
+msgstr "konstigt värdnamn ”%s” blockerat"
 
 #, c-format
 msgid "strange port '%s' blocked"
-msgstr "konstig port \"%s\" blockerad"
+msgstr "konstig port ”%s” blockerad"
 
 #, c-format
 msgid "cannot start proxy %s"
 msgstr "kan inte starta mellanserver (proxy) %s"
 
 msgid "no path specified; see 'git help pull' for valid url syntax"
-msgstr "ingen sökväg angavs; se \"git help pull\" för giltig URL-syntax"
+msgstr "ingen sökväg angavs; se ”git help pull” för giltig URL-syntax"
 
 msgid "newline is forbidden in git:// hosts and repo paths"
 msgstr "radbrytningar är förbjudna i git://-värdnamn och arkivsökvägar"
 
 msgid "ssh variant 'simple' does not support -4"
-msgstr "ssh-varianten \"simple\" stöder inte -4"
+msgstr "ssh-varianten ”simple” stöder inte -4"
 
 msgid "ssh variant 'simple' does not support -6"
-msgstr "ssh-varianten \"simple\" stöder inte -6"
+msgstr "ssh-varianten ”simple” stöder inte -6"
 
 msgid "ssh variant 'simple' does not support setting port"
-msgstr "ssh-varianten \"simple\" stöder inte val av port"
+msgstr "ssh-varianten ”simple” stöder inte val av port"
 
 #, c-format
 msgid "strange pathname '%s' blocked"
-msgstr "konstigt sökvägsnamn \"%s\" blockerat"
+msgstr "konstigt sökvägsnamn ”%s” blockerat"
 
 msgid "unable to fork"
 msgstr "kunde inte grena (fork)"
 
 msgid "Could not run 'git rev-list'"
-msgstr "Kunde inte köra \"git rev-list\""
+msgstr "Kunde inte köra ”git rev-list”"
 
 msgid "failed write to rev-list"
 msgstr "kunde inte skriva till rev-list"
@@ -14968,7 +15273,7 @@
 "in the working copy of '%s', CRLF will be replaced by LF the next time Git "
 "touches it"
 msgstr ""
-"CRLF i arbetskopian av \"%s\" kommer ersättas med LF nästa gång Git rör den"
+"CRLF i arbetskopian av ”%s” kommer ersättas med LF nästa gång Git rör den"
 
 #, c-format
 msgid "LF would be replaced by CRLF in %s"
@@ -14979,60 +15284,59 @@
 "in the working copy of '%s', LF will be replaced by CRLF the next time Git "
 "touches it"
 msgstr ""
-"LF i arbetskopian av \"%s\" kommer ersättas med CRLF nästa gång Git rör den"
+"LF i arbetskopian av ”%s” kommer ersättas med CRLF nästa gång Git rör den"
 
 #, c-format
 msgid "BOM is prohibited in '%s' if encoded as %s"
-msgstr "BOM är förbjudet i \"%s\" om kodat som %s"
+msgstr "BOM är förbjudet i ”%s” om kodat som %s"
 
 #, c-format
 msgid ""
 "The file '%s' contains a byte order mark (BOM). Please use UTF-%.*s as "
 "working-tree-encoding."
 msgstr ""
-"Filen \"%s\" innehåller byte order mark (BOM). Använd UTF-%.*s som "
+"Filen ”%s” innehåller byte order mark (BOM). Använd UTF-%.*s som "
 "teckenkodning i arbetskatalogen."
 
 #, c-format
 msgid "BOM is required in '%s' if encoded as %s"
-msgstr "BOM krävs om \"%s\" kodas som %s"
+msgstr "BOM krävs om ”%s” kodas som %s"
 
 #, c-format
 msgid ""
 "The file '%s' is missing a byte order mark (BOM). Please use UTF-%sBE or UTF-"
 "%sLE (depending on the byte order) as working-tree-encoding."
 msgstr ""
-"Filen \"%s\" saknar byte order mark (BOM). Använd UTF-%sBE eller UTF-%sLE "
+"Filen ”%s” saknar byte order mark (BOM). Använd UTF-%sBE eller UTF-%sLE "
 "(beroende på byteordning) som teckenkodning i arbetskatalogen."
 
 #, c-format
 msgid "failed to encode '%s' from %s to %s"
-msgstr "misslyckades omkoda \"%s\" från %s till %s"
+msgstr "misslyckades omkoda ”%s” från %s till %s"
 
 #, c-format
 msgid "encoding '%s' from %s to %s and back is not the same"
-msgstr ""
-"omkodning av \"%s\" från %s till %s och tillbaka ger inte samma resultat"
+msgstr "omkodning av ”%s” från %s till %s och tillbaka ger inte samma resultat"
 
 #, c-format
 msgid "cannot fork to run external filter '%s'"
-msgstr "kan inte grena (fork) för att köra externt filter \"%s\""
+msgstr "kan inte grena (fork) för att köra externt filter ”%s”"
 
 #, c-format
 msgid "cannot feed the input to external filter '%s'"
-msgstr "kunde inte skicka indata till externt filter \"%s\""
+msgstr "kunde inte skicka indata till externt filter ”%s”"
 
 #, c-format
 msgid "external filter '%s' failed %d"
-msgstr "externt filter \"%s\" misslyckades %d"
+msgstr "externt filter ”%s” misslyckades %d"
 
 #, c-format
 msgid "read from external filter '%s' failed"
-msgstr "läsning från externt filter \"%s\" misslyckades"
+msgstr "läsning från externt filter ”%s” misslyckades"
 
 #, c-format
 msgid "external filter '%s' failed"
-msgstr "externt filter \"%s\" misslyckades"
+msgstr "externt filter ”%s” misslyckades"
 
 msgid "unexpected filter type"
 msgstr "oväntad filtertyp"
@@ -15045,19 +15349,19 @@
 "external filter '%s' is not available anymore although not all paths have "
 "been filtered"
 msgstr ""
-"externt filter \"%s\" är inte längre tillgängligt trots att alla sökvägar "
-"inte har filtrerats"
+"externt filter ”%s” är inte längre tillgängligt trots att alla sökvägar inte "
+"har filtrerats"
 
 msgid "true/false are no valid working-tree-encodings"
 msgstr "true/false är inte giltig teckenkodning för arbetskatalogen"
 
 #, c-format
 msgid "%s: clean filter '%s' failed"
-msgstr "%s: \"clean\"-filtret \"%s\" misslyckades"
+msgstr "%s: ”clean”-filtret ”%s” misslyckades"
 
 #, c-format
 msgid "%s: smudge filter %s failed"
-msgstr "%s: \"smudge\"-filtret \"%s\" misslyckades"
+msgstr "%s: ”smudge”-filtret ”%s” misslyckades"
 
 #, c-format
 msgid "skipping credential lookup for key: credential.%s"
@@ -15148,7 +15452,7 @@
 
 #, c-format
 msgid "failed to load island regex for '%s': %s"
-msgstr "kunde inte hämta ö-regex för \"%s\": %s"
+msgstr "kunde inte hämta ö-regex för ”%s”: %s"
 
 #, c-format
 msgid "island regex from config has too many capture groups (max=%d)"
@@ -15160,26 +15464,26 @@
 
 #, c-format
 msgid "invalid --%s value '%s'"
-msgstr "ogiltigt värde för --%s: \"%s\""
+msgstr "ogiltigt värde för --%s: ”%s”"
 
 #, c-format
 msgid "could not archive missing directory '%s'"
-msgstr "kunde inte arkivera saknad katalog \"%s\""
+msgstr "kunde inte arkivera saknad katalog ”%s”"
 
 #, c-format
 msgid "could not open directory '%s'"
-msgstr "kunde inte öppna katalogen \"%s\""
+msgstr "kunde inte öppna katalogen ”%s”"
 
 #, c-format
 msgid "skipping '%s', which is neither file nor directory"
-msgstr "hoppar över \"%s\", som varken är en fil eller en katalog"
+msgstr "hoppar över ”%s”, som varken är en fil eller en katalog"
 
 msgid "could not duplicate stdout"
 msgstr "kunde inte duplicera standard ut"
 
 #, c-format
 msgid "could not add directory '%s' to archiver"
-msgstr "kunde inte lägga till katalogen \"%s\" till arkiveraren"
+msgstr "kunde inte lägga till katalogen ”%s” till arkiveraren"
 
 msgid "failed to write archive"
 msgstr "misslyckades skriva arkiv"
@@ -15187,9 +15491,6 @@
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base fungerar inte med intervall"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base fungerar bara med incheckningar"
-
 msgid "unable to get HEAD"
 msgstr "kan inte hämta HEAD"
 
@@ -15199,6 +15500,12 @@
 msgid "multiple merge bases found"
 msgstr "flera sammanslagningsbaser hittades"
 
+msgid "cannot compare stdin to a directory"
+msgstr "kan inte jämföra standard in med en katalog"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "kan inte jämföra ett namngivet rör med en katalog"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<flaggor>] <sökväg> <sökväg>"
 
@@ -15211,27 +15518,26 @@
 
 #, c-format
 msgid "  Failed to parse dirstat cut-off percentage '%s'\n"
-msgstr "  Misslyckades tolka dirstat-avskärningsprocentandel \"%s\"\n"
+msgstr "  Misslyckades tolka dirstat-avskärningsprocentandel ”%s”\n"
 
 #, c-format
 msgid "  Unknown dirstat parameter '%s'\n"
-msgstr "  Okänd dirstat-parameter \"%s\"\n"
+msgstr "  Okänd dirstat-parameter ”%s”\n"
 
 msgid ""
 "color moved setting must be one of 'no', 'default', 'blocks', 'zebra', "
 "'dimmed-zebra', 'plain'"
 msgstr ""
-"färginställningen för flyttade block måste vara en av \"no\", \"default\", "
-"\"blocks\", \"zebra\", \"dimmed-zebra\", \"plain\""
+"färginställningen för flyttade block måste vara en av ”no”, ”default”, "
+"”blocks”, ”zebra”, ”dimmed-zebra”, ”plain”"
 
 #, c-format
 msgid ""
 "unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', "
 "'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"
 msgstr ""
-"okänt läge \"%s\" för color-moved-ws, möjliga värden är \"ignore-space-change"
-"\", \"ignore-space-at-eol\", \"ignore-all-space\", \"allow-indentation-change"
-"\""
+"okänt läge ”%s” för color-moved-ws, möjliga värden är ”ignore-space-change”, "
+"”ignore-space-at-eol”, ”ignore-all-space”, ”allow-indentation-change”"
 
 msgid ""
 "color-moved-ws: allow-indentation-change cannot be combined with other "
@@ -15242,40 +15548,46 @@
 
 #, c-format
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
-msgstr "Okänt värde för konfigurationsvariabeln \"diff.submodule\": \"%s\""
+msgstr "Okänt värde för konfigurationsvariabeln ”diff.submodule”: ”%s”"
+
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "okänt värde för inställningen ”%s”: %s"
 
 #, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
 msgstr ""
-"Hittade fel i konfigurationsvariabeln \"diff.dirstat\":\n"
+"Hittade fel i konfigurationsvariabeln ”diff.dirstat”:\n"
 "%s"
 
 #, c-format
 msgid "external diff died, stopping at %s"
 msgstr "extern diff dog, stannar vid %s"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow kräver exakt en sökvägsangivelse"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "sökvägs-magi stöds inte av --follow: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
-msgstr ""
-"flaggorna \"%s\", \"%s\", \"%s\" och \"%s\" kan inte användas samtidigt"
+msgstr "flaggorna ”%s”, ”%s”, ”%s” och ”%s” kan inte användas samtidigt"
 
 #, c-format
 msgid "options '%s' and '%s' cannot be used together, use '%s' with '%s'"
 msgstr ""
-"flaggorna \"%s\" och \"%s\" kan inte användas samtidigt, använd \"%s\" med "
-"\"%s\""
+"flaggorna ”%s” och ”%s” kan inte användas samtidigt, använd ”%s” med ”%s”"
 
 #, c-format
 msgid ""
 "options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
 msgstr ""
-"flaggorna \"%s\" och  \"%s\" kan inte användas samtidigt, använd \"%s\" med "
-"\"%s\" och \"%s\""
-
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow kräver exakt en sökvägsangivelse"
+"flaggorna ”%s” och  ”%s” kan inte användas samtidigt, använd ”%s” med ”%s” "
+"och ”%s”"
 
 #, c-format
 msgid "invalid --stat value: %s"
@@ -15295,7 +15607,7 @@
 
 #, c-format
 msgid "unknown change class '%c' in --diff-filter=%s"
-msgstr "okänd ändringsklass \"%c\" i --diff-filter=%s"
+msgstr "okänd ändringsklass ”%c” i --diff-filter=%s"
 
 #, c-format
 msgid "unknown value after ws-error-highlight=%.*s"
@@ -15303,7 +15615,7 @@
 
 #, c-format
 msgid "unable to resolve '%s'"
-msgstr "kunde inte slå upp \"%s\""
+msgstr "kunde inte slå upp ”%s”"
 
 #, c-format
 msgid "%s expects <n>/<m> form"
@@ -15311,7 +15623,7 @@
 
 #, c-format
 msgid "%s expects a character, got '%s'"
-msgstr "%s förväntar ett tecken, fick \"%s\""
+msgstr "%s förväntar ett tecken, fick ”%s”"
 
 #, c-format
 msgid "bad --color-moved argument: %s"
@@ -15319,14 +15631,7 @@
 
 #, c-format
 msgid "invalid mode '%s' in --color-moved-ws"
-msgstr "ogiltigt läge %s\" i --color-moved-ws"
-
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"flaggan diff-algorithm godtar\"myers\", \"minimal\", \"patience\" och "
-"\"histogram\""
+msgstr "ogiltigt läge %s” i --color-moved-ws"
 
 #, c-format
 msgid "invalid argument to %s"
@@ -15334,11 +15639,11 @@
 
 #, c-format
 msgid "invalid regex given to -I: '%s'"
-msgstr "ogiltigt reguljärt uttryck angavs för -I: \"%s\""
+msgstr "ogiltigt reguljärt uttryck angavs för -I: ”%s”"
 
 #, c-format
 msgid "failed to parse --submodule option parameter: '%s'"
-msgstr "misslyckades tolka argument till flaggan --submodule: \"%s\""
+msgstr "misslyckades tolka argument till flaggan --submodule: ”%s”"
 
 #, c-format
 msgid "bad --word-diff argument: %s"
@@ -15360,10 +15665,10 @@
 msgstr "generera diff i råformat"
 
 msgid "synonym for '-p --raw'"
-msgstr "synonym till \"-p --raw\""
+msgstr "synonym till ”-p --raw”"
 
 msgid "synonym for '-p --stat'"
-msgstr "synonym till \"-p --stat\""
+msgstr "synonym till ”-p --stat”"
 
 msgid "machine friendly --stat"
 msgstr "maskinläsbar --stat"
@@ -15371,8 +15676,8 @@
 msgid "output only the last line of --stat"
 msgstr "skriv bara ut den sista raden för --stat"
 
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
 
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
@@ -15382,8 +15687,8 @@
 msgid "synonym for --dirstat=cumulative"
 msgstr "synonym för --dirstat=cumulative"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "synonym för --dirstat=filer,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "synonym för --dirstat=filer,<param1>,<param2>..."
 
 msgid "warn if changes introduce conflict markers or whitespace errors"
 msgstr "varna om ändringar introducerar konfliktmarkörer eller blankstegsfel"
@@ -15429,7 +15734,7 @@
 
 msgid "show full pre- and post-image object names on the \"index\" lines"
 msgstr ""
-"visa fullständiga objektnamn i \"index\"-rader för läget både före och efter"
+"visa fullständiga objektnamn i ”index”-rader för läget både före och efter"
 
 msgid "show colored diff"
 msgstr "visa färgad diff"
@@ -15441,8 +15746,8 @@
 "highlight whitespace errors in the 'context', 'old' or 'new' lines in the "
 "diff"
 msgstr ""
-"ljusmarkera blankstegsfel i \"context\" (sammanhang), \"old\" (gamla) eller "
-"\"new\" (nya) rader i diffen"
+"ljusmarkera blankstegsfel i ”context” (sammanhang), ”old” (gamla) eller "
+"”new” (nya) rader i diffen"
 
 msgid ""
 "do not munge pathnames and use NULs as output field terminators in --raw or "
@@ -15455,10 +15760,10 @@
 msgstr "<prefix>"
 
 msgid "show the given source prefix instead of \"a/\""
-msgstr "visa givet källprefix istället för \"a/\""
+msgstr "visa givet källprefix istället för ”a/”"
 
 msgid "show the given destination prefix instead of \"b/\""
-msgstr "visa givet målprefix istället för \"b/\""
+msgstr "visa givet målprefix istället för ”b/”"
 
 msgid "prepend an additional prefix to every line of output"
 msgstr "lägg till ytterligare prefix på alla rader i utdata"
@@ -15466,6 +15771,9 @@
 msgid "do not show any source or destination prefix"
 msgstr "visa inte käll- eller målprefix"
 
+msgid "use default prefixes a/ and b/"
+msgstr "använd standardprefixen a/ och b/"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "visa sammnhang mellan diff-stycken upp till angivet antal rader"
 
@@ -15473,13 +15781,13 @@
 msgstr "<tecken>"
 
 msgid "specify the character to indicate a new line instead of '+'"
-msgstr "ange tecken för att ange ny rad istället för \"+\""
+msgstr "ange tecken för att ange ny rad istället för ”+”"
 
 msgid "specify the character to indicate an old line instead of '-'"
-msgstr "ange tecken för att ange gammal rad istället för \"-\""
+msgstr "ange tecken för att ange gammal rad istället för ”-”"
 
 msgid "specify the character to indicate a context instead of ' '"
-msgstr "ange tecken för att ange sammanhang istället för \" \""
+msgstr "ange tecken för att ange sammanhang istället för ” ”"
 
 msgid "Diff rename options"
 msgstr "Diff-namnbytesflaggor"
@@ -15549,22 +15857,16 @@
 msgstr "heuristik för att flytta diff-gränser för lättare läsning"
 
 msgid "generate diff using the \"patience diff\" algorithm"
-msgstr "skapa diffar med algoritmen \"patience diff\""
+msgstr "skapa diffar med algoritmen ”patience diff”"
 
 msgid "generate diff using the \"histogram diff\" algorithm"
-msgstr "skapa diffar med algoritmen \"histogram diff\""
-
-msgid "<algorithm>"
-msgstr "<algoritm>"
-
-msgid "choose a diff algorithm"
-msgstr "välj en diff-algoritm"
+msgstr "skapa diffar med algoritmen ”histogram diff”"
 
 msgid "<text>"
 msgstr "<text>"
 
 msgid "generate diff using the \"anchored diff\" algorithm"
-msgstr "skapa diffar med algoritmen \"anchored diff\""
+msgstr "skapa diffar med algoritmen ”anchored diff”"
 
 msgid "<mode>"
 msgstr "<läge>"
@@ -15623,10 +15925,10 @@
 msgstr "ange hur ändringar i undermoduler visas"
 
 msgid "hide 'git add -N' entries from the index"
-msgstr "dölj \"git add -N\"-poster från indexet"
+msgstr "dölj ”git add -N”-poster från indexet"
 
 msgid "treat 'git add -N' entries as real in the index"
-msgstr "tolka \"git add -N\"-poster som äkta i indexet"
+msgstr "tolka ”git add -N”-poster som äkta i indexet"
 
 msgid "<string>"
 msgstr "<sträng>"
@@ -15696,18 +15998,18 @@
 
 #, c-format
 msgid "failed to read orderfile '%s'"
-msgstr "kunde inte läsa orderfilen \"%s\""
+msgstr "kunde inte läsa orderfilen ”%s”"
 
 msgid "Performing inexact rename detection"
 msgstr "Utför onöjaktig namnbytesdetektering"
 
 #, c-format
 msgid "No such path '%s' in the diff"
-msgstr "Sökvägen \"%s\" finns inte i diffen"
+msgstr "Sökvägen ”%s” finns inte i diffen"
 
 #, c-format
 msgid "pathspec '%s' did not match any file(s) known to git"
-msgstr "sökvägsangivelsen \"%s\" motsvarade inte några av git kända filer"
+msgstr "sökvägsangivelsen ”%s” motsvarade inte några av git kända filer"
 
 #, c-format
 msgid "unrecognized pattern: '%s'"
@@ -15720,7 +16022,7 @@
 #, c-format
 msgid "your sparse-checkout file may have issues: pattern '%s' is repeated"
 msgstr ""
-"din \"sparse-checkout\"-fil kan ha problem: mönstret \"%s\" förekommer flera "
+"din ”sparse-checkout”-fil kan ha problem: mönstret ”%s” förekommer flera "
 "gånger"
 
 msgid "disabling cone pattern matching"
@@ -15753,22 +16055,30 @@
 
 #, c-format
 msgid "could not migrate git directory from '%s' to '%s'"
-msgstr "kunde inte migrera git-katalog från \"%s\" till \"%s\""
+msgstr "kunde inte migrera git-katalog från ”%s” till ”%s”"
 
 #, c-format
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "tips: Väntar på att textredigeringsprogrammet ska stänga filen...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "kunde inte skriva till ”%s”"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "kunde inte redigera ”%s”"
+
 msgid "Filtering content"
 msgstr "Filtrerar innehåll"
 
 #, c-format
 msgid "could not stat file '%s'"
-msgstr "kunde inte ta status på filen \"%s\""
+msgstr "kunde inte ta status på filen ”%s”"
 
 #, c-format
 msgid "bad git namespace path \"%s\""
-msgstr "felaktig git-namnrymdssökväg \"%s\""
+msgstr "felaktig git-namnrymdssökväg ”%s”"
 
 #, c-format
 msgid "too many args to run %s"
@@ -15785,7 +16095,7 @@
 
 #, c-format
 msgid "git fetch-pack: expected ACK/NAK, got '%s'"
-msgstr "git fetch-pack: förväntade ACK/NAK, fick \"%s\""
+msgstr "git fetch-pack: förväntade ACK/NAK, fick ”%s”"
 
 msgid "unable to write to remote"
 msgstr "kunde inte skriva till fjärren"
@@ -15795,11 +16105,11 @@
 
 #, c-format
 msgid "invalid shallow line: %s"
-msgstr "ogiltig \"shallow\"-rad: %s"
+msgstr "ogiltig ”shallow”-rad: %s"
 
 #, c-format
 msgid "invalid unshallow line: %s"
-msgstr "ogiltig \"unshallow\"-rad: %s"
+msgstr "ogiltig ”unshallow”-rad: %s"
 
 #, c-format
 msgid "object not found: %s"
@@ -15811,7 +16121,7 @@
 
 #, c-format
 msgid "no shallow found: %s"
-msgstr "ingen \"shallow\" hittades: %s"
+msgstr "ingen ”shallow” hittades: %s"
 
 #, c-format
 msgid "expected shallow/unshallow, got %s"
@@ -15898,7 +16208,7 @@
 
 #, c-format
 msgid "the server does not support algorithm '%s'"
-msgstr "servern stöder inte algoritmen \"%s\""
+msgstr "servern stöder inte algoritmen ”%s”"
 
 msgid "Server does not support shallow requests"
 msgstr "Servern stöder inte grunda förfrågningar"
@@ -15908,15 +16218,15 @@
 
 #, c-format
 msgid "expected '%s', received '%s'"
-msgstr "förväntade \"%s\", tog emot \"%s\""
+msgstr "förväntade ”%s”, tog emot ”%s”"
 
 #, c-format
 msgid "expected '%s'"
-msgstr "förväntade \"%s\""
+msgstr "förväntade ”%s”"
 
 #, c-format
 msgid "unexpected acknowledgment line: '%s'"
-msgstr "oväntad bekräftelserad: \"%s\""
+msgstr "oväntad bekräftelserad: ”%s”"
 
 #, c-format
 msgid "error processing acks: %d"
@@ -15927,19 +16237,18 @@
 #.
 #, c-format
 msgid "expected packfile to be sent after '%s'"
-msgstr "väntade att paketfil skulle sändas efter \"%s\""
+msgstr "väntade att paketfil skulle sändas efter ”%s”"
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
 #.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
-msgstr ""
-"väntade inte att några ytterligare sektioner skulle sändas efter \"%s\""
+msgstr "väntade inte att några ytterligare sektioner skulle sändas efter ”%s”"
 
 #, c-format
 msgid "error processing shallow info: %d"
-msgstr "fel vid hantering av grund (\"shallow\") info: %d"
+msgstr "fel vid hantering av grund (”shallow”) info: %d"
 
 #, c-format
 msgid "expected wanted-ref, got '%s'"
@@ -15947,7 +16256,7 @@
 
 #, c-format
 msgid "unexpected wanted-ref: '%s'"
-msgstr "oväntad wanted-ref: \"%s\""
+msgstr "oväntad wanted-ref: ”%s”"
 
 #, c-format
 msgid "error processing wanted refs: %d"
@@ -15960,7 +16269,7 @@
 msgstr "inget motsvarande fjärrhuvud"
 
 msgid "unexpected 'ready' from remote"
-msgstr "oväntat \"ready\" från fjärr"
+msgstr "oväntat ”ready” från fjärr"
 
 #, c-format
 msgid "no such remote ref %s"
@@ -15972,42 +16281,42 @@
 
 #, c-format
 msgid "fsmonitor_ipc__send_query: invalid path '%s'"
-msgstr "fsmonitor_ipc__send_query: ogilitg sökväg \"%s\""
+msgstr "fsmonitor_ipc__send_query: ogilitg sökväg ”%s”"
 
 #, c-format
 msgid "fsmonitor_ipc__send_query: unspecified error on '%s'"
-msgstr "fsmonitor_ipc__send_query: ospecificerat fel på \"%s\""
+msgstr "fsmonitor_ipc__send_query: ospecificerat fel på ”%s”"
 
 msgid "fsmonitor--daemon is not running"
 msgstr "fsmonitor--daemon kör inte"
 
 #, c-format
 msgid "could not send '%s' command to fsmonitor--daemon"
-msgstr "kunde inte sända kommandot \"%s\" till fsmonitor--daemon"
+msgstr "kunde inte sända kommandot ”%s” till fsmonitor--daemon"
 
 #, c-format
 msgid "bare repository '%s' is incompatible with fsmonitor"
-msgstr "naket arkiv \"%s\" är inkompatibelt med fsmonitor"
+msgstr "naket arkiv ”%s” är inkompatibelt med fsmonitor"
 
 #, c-format
 msgid "repository '%s' is incompatible with fsmonitor due to errors"
-msgstr "arkivet \"%s\" är inkompatibelt med fsmonitor på grund av fel"
+msgstr "arkivet ”%s” är inkompatibelt med fsmonitor på grund av fel"
 
 #, c-format
 msgid "remote repository '%s' is incompatible with fsmonitor"
-msgstr "fjärrarkivet \"%s\" är inkompatibelt med fsmonitor"
+msgstr "fjärrarkivet ”%s” är inkompatibelt med fsmonitor"
 
 #, c-format
 msgid "virtual repository '%s' is incompatible with fsmonitor"
-msgstr "det virtuella arkivet \"%s\" är inkompatibelt med fsmonitor"
+msgstr "det virtuella arkivet ”%s” är inkompatibelt med fsmonitor"
 
 #, c-format
 msgid ""
 "socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
 "sockets support"
 msgstr ""
-"uttagskatalogen \"%s\" är inkompatibelt med fsmonitor på grund av avsaknad "
-"av Unix-uttag"
+"uttagskatalogen ”%s” är inkompatibelt med fsmonitor på grund av avsaknad av "
+"Unix-uttag"
 
 msgid ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
@@ -16031,18 +16340,18 @@
 "to read about a specific subcommand or concept.\n"
 "See 'git help git' for an overview of the system."
 msgstr ""
-"\"git help -a\" och \"git help -g\" visar tillgängliga underkommandon och\n"
-"några konceptvägledningar. Se \"git help <kommando>\" eller \"git help\n"
-"<koncept>\" för att läsa mer om specifika underkommandon och koncept.\n"
-"See \"git help git\" för en översikt över systemet."
+"”git help -a” och ”git help -g” visar tillgängliga underkommandon och\n"
+"några konceptvägledningar. Se ”git help <kommando>” eller ”git help\n"
+"<koncept>” för att läsa mer om specifika underkommandon och koncept.\n"
+"See ”git help git” för en översikt över systemet."
 
 #, c-format
 msgid "unsupported command listing type '%s'"
-msgstr "okänd kommandolisttyp \"%s\""
+msgstr "okänd kommandolisttyp ”%s”"
 
 #, c-format
 msgid "no directory given for '%s' option\n"
-msgstr "ingen katalog angavs för flaggan \"%s\"\n"
+msgstr "ingen katalog angavs för flaggan ”%s”\n"
 
 #, c-format
 msgid "no namespace given for --namespace\n"
@@ -16057,20 +16366,24 @@
 msgstr "ingen konfigurationsnyckel angavs för --config-env\n"
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "ingen attributkälla angavs för --attr-source\n"
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "okänd flagga: %s\n"
 
 #, c-format
 msgid "while expanding alias '%s': '%s'"
-msgstr "vid expandering av aliaset \"%s\": \"%s\""
+msgstr "vid expandering av aliaset ”%s”: ”%s”"
 
 #, c-format
 msgid ""
 "alias '%s' changes environment variables.\n"
 "You can use '!git' in the alias to do this"
 msgstr ""
-"aliaset \"%s\" ändrar miljövariabler.\n"
-"Du kan använda \"!git\" i aliaset för att göra det"
+"aliaset ”%s” ändrar miljövariabler.\n"
+"Du kan använda ”!git” i aliaset för att göra det"
 
 #, c-format
 msgid "empty alias for %s"
@@ -16091,7 +16404,7 @@
 
 #, c-format
 msgid "alias loop detected: expansion of '%s' does not terminate:%s"
-msgstr "alias-slinga detekterades: expansionen av \"%s\" avslutas aldrig:%s"
+msgstr "alias-slinga detekterades: expansionen av ”%s” avslutas aldrig:%s"
 
 #, c-format
 msgid "cannot handle %s as a builtin"
@@ -16108,18 +16421,18 @@
 #, c-format
 msgid "expansion of alias '%s' failed; '%s' is not a git command\n"
 msgstr ""
-"expandering av alias \"%s\" misslyckades; \"%s\" är inte ett git-kommando\n"
+"expandering av alias ”%s” misslyckades; ”%s” är inte ett git-kommando\n"
 
 #, c-format
 msgid "failed to run command '%s': %s\n"
-msgstr "misslyckades köra kommandot \"%s\": %s\n"
+msgstr "misslyckades köra kommandot ”%s”: %s\n"
 
 msgid "could not create temporary file"
 msgstr "kunde inte skapa temporära fil"
 
 #, c-format
 msgid "failed writing detached signature to '%s'"
-msgstr "misslyckades skriva fristående signatur till \"%s\""
+msgstr "misslyckades skriva fristående signatur till ”%s”"
 
 msgid ""
 "gpg.ssh.allowedSignersFile needs to be configured and exist for ssh "
@@ -16132,7 +16445,7 @@
 "ssh-keygen -Y find-principals/verify is needed for ssh signature "
 "verification (available in openssh version 8.2p1+)"
 msgstr ""
-"\"ssh-keygen -Y find-principals/verify\" behövs för att bekräfta ssh-"
+"”ssh-keygen -Y find-principals/verify” behövs för att bekräfta ssh-"
 "signaturer (tillgängligt i openssh version 8.2p1+)"
 
 #, c-format
@@ -16141,11 +16454,11 @@
 
 #, c-format
 msgid "bad/incompatible signature '%s'"
-msgstr "felaktig/inkompatibel signatur \"%s\""
+msgstr "felaktig/inkompatibel signatur ”%s”"
 
 #, c-format
 msgid "failed to get the ssh fingerprint for key '%s'"
-msgstr "misslyckades hämta ssh-fingeravtrycket för nyckeln \"%s\""
+msgstr "misslyckades hämta ssh-fingeravtrycket för nyckeln ”%s”"
 
 msgid ""
 "either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"
@@ -16173,26 +16486,26 @@
 
 #, c-format
 msgid "failed writing ssh signing key to '%s'"
-msgstr "misslyckades skriva ssh-signeringsnyckel till \"%s\""
+msgstr "misslyckades skriva ssh-signeringsnyckel till ”%s”"
 
 #, c-format
 msgid "failed writing ssh signing key buffer to '%s'"
-msgstr "misslyckades skriva ssh-signeringsnyckelbuffert till \"%s\""
+msgstr "misslyckades skriva ssh-signeringsnyckelbuffert till ”%s”"
 
 msgid ""
 "ssh-keygen -Y sign is needed for ssh signing (available in openssh version "
 "8.2p1+)"
 msgstr ""
-"\"ssh-keygen -Y sign\" behövs för ssh-signering (tillgängligt i openssh "
+"”ssh-keygen -Y sign” behövs för ssh-signering (tillgängligt i openssh "
 "version 8.2p1+)"
 
 #, c-format
 msgid "failed reading ssh signing data buffer from '%s'"
-msgstr "misslyckades läsa ssh-signeringsdatabuffert från \"%s\""
+msgstr "misslyckades läsa ssh-signeringsdatabuffert från ”%s”"
 
 #, c-format
 msgid "ignored invalid color '%.*s' in log.graphColors"
-msgstr "ignorerade felaktig färg \"%.*s\" i log.graphColors"
+msgstr "ignorerade felaktig färg ”%.*s” i log.graphColors"
 
 msgid ""
 "given pattern contains NULL byte (via -f <file>). This is only supported "
@@ -16203,11 +16516,11 @@
 
 #, c-format
 msgid "'%s': unable to read %s"
-msgstr "\"%s\" kunde inte läsa %s"
+msgstr "”%s” kunde inte läsa %s"
 
 #, c-format
 msgid "'%s': short read"
-msgstr "\"%s\": kort läsning"
+msgstr "”%s”: kort läsning"
 
 msgid "start a working area (see also: git help tutorial)"
 msgstr "starta arbetskatalog (se också: git help tutorial)"
@@ -16256,7 +16569,7 @@
 
 #, c-format
 msgid "available git commands in '%s'"
-msgstr "git-kommandon tillgängliga i \"%s\""
+msgstr "git-kommandon tillgängliga i ”%s”"
 
 msgid "git commands available from elsewhere on your $PATH"
 msgstr "git-kommandon från andra platser i din $PATH"
@@ -16280,39 +16593,39 @@
 msgstr "Kommadoalias"
 
 msgid "See 'git help <command>' to read about a specific subcommand"
-msgstr "Se \"git help <kommando>\" för att läsa om ett specifikt underkommando"
+msgstr "Se ”git help <kommando>” för att läsa om ett specifikt underkommando"
 
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
 "able to execute it. Maybe git-%s is broken?"
 msgstr ""
-"\"%s\" verkar vara ett git-kommando, men vi kan inte\n"
+"”%s” verkar vara ett git-kommando, men vi kan inte\n"
 "köra det. Kanske git-%s är trasigt?"
 
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
-msgstr "git: \"%s\" är inte ett git-kommando. Se \"git --help\"."
+msgstr "git: ”%s” är inte ett git-kommando. Se ”git --help”."
 
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr "Oj då. Ditt system rapporterar inga Git-kommandon alls."
 
 #, c-format
 msgid "WARNING: You called a Git command named '%s', which does not exist."
-msgstr "VARNING: Du anropade ett Git-kommando vid namn \"%s\", som inte finns."
+msgstr "VARNING: Du anropade ett Git-kommando vid namn ”%s”, som inte finns."
 
 #, c-format
 msgid "Continuing under the assumption that you meant '%s'."
-msgstr "Fortsätter under förutsättningen att du menade \"%s\"."
+msgstr "Fortsätter under förutsättningen att du menade ”%s”."
 
 #, 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'."
 msgstr ""
-"Fortsätter om %0.1f sekunder, under förutsättningen att du menade \"%s\"."
+"Fortsätter om %0.1f sekunder, under förutsättningen att du menade ”%s”."
 
 msgid ""
 "\n"
@@ -16352,8 +16665,8 @@
 "The '%s' hook was ignored because it's not set as executable.\n"
 "You can disable this warning with `git config advice.ignoredHook false`."
 msgstr ""
-"Kroken \"%s\" ignorerades eftersom den inte är markerad som körbar.\n"
-"Du kan inaktivera varningen med \"git config advice.ignoredHook false\"."
+"Kroken ”%s” ignorerades eftersom den inte är markerad som körbar.\n"
+"Du kan inaktivera varningen med ”git config advice.ignoredHook false”."
 
 #, c-format
 msgid "argument to --packfile must be a valid hash (got '%s')"
@@ -16378,15 +16691,15 @@
 
 #, c-format
 msgid "Unsupported SSL backend '%s'. Supported SSL backends:"
-msgstr "SSL-bakändan \"%s\" stöds inte. Dessa SSL-bakändor stöds:"
+msgstr "SSL-bakändan ”%s” stöds inte. Dessa SSL-bakändor stöds:"
 
 #, c-format
 msgid "Could not set SSL backend to '%s': cURL was built without SSL backends"
-msgstr "Kan inte sätta SSL-bakända till \"%s\": cURL byggdes utan SSL-bakändor"
+msgstr "Kan inte sätta SSL-bakända till ”%s”: cURL byggdes utan SSL-bakändor"
 
 #, c-format
 msgid "Could not set SSL backend to '%s': already set"
-msgstr "Kunde inte sätta SSL-bakända till \"%s\": redan valt"
+msgstr "Kunde inte sätta SSL-bakända till ”%s”: redan valt"
 
 #, c-format
 msgid ""
@@ -16434,14 +16747,14 @@
 
 #, c-format
 msgid "unable to auto-detect email address (got '%s')"
-msgstr "kunde inte autodetektera e-postadress (fick \"%s\")"
+msgstr "kunde inte autodetektera e-postadress (fick ”%s”)"
 
 msgid "no name was given and auto-detection is disabled"
 msgstr "inget namn angavs och autodetektering är inaktiverad"
 
 #, c-format
 msgid "unable to auto-detect name (got '%s')"
-msgstr "kunde inte autodetektera namn (fick \"%s\")"
+msgstr "kunde inte autodetektera namn (fick ”%s”)"
 
 #, c-format
 msgid "empty ident name (for <%s>) not allowed"
@@ -16452,22 +16765,22 @@
 msgstr "namnet består enbart av ej tillåtna tecken: %s"
 
 msgid "expected 'tree:<depth>'"
-msgstr "förväntade \"tree:<djup>\""
+msgstr "förväntade ”tree:<djup>”"
 
 msgid "sparse:path filters support has been dropped"
 msgstr "sparse:sökväg-filter stöds inte längre"
 
 #, c-format
 msgid "'%s' for 'object:type=<type>' is not a valid object type"
-msgstr "\"%s\" för \"object:type=<typ>\" är inte en giltig objekttyp"
+msgstr "”%s” för ”object:type=<typ>” är inte en giltig objekttyp"
 
 #, c-format
 msgid "invalid filter-spec '%s'"
-msgstr "felaktig filterspecifikation: \"%s\""
+msgstr "felaktig filterspecifikation: ”%s”"
 
 #, c-format
 msgid "must escape char in sub-filter-spec: '%c'"
-msgstr "måste använda specialsekvens i delfilter-spec: \"%c\""
+msgstr "måste använda specialsekvens i delfilter-spec: ”%c”"
 
 msgid "expected something after combine:"
 msgstr "förväntade någonting efter combine:"
@@ -16486,7 +16799,7 @@
 
 #, c-format
 msgid "unable to access sparse blob in '%s'"
-msgstr "kunde inte nå gles blob på \"%s\""
+msgstr "kunde inte nå gles blob på ”%s”"
 
 #, c-format
 msgid "unable to parse sparse filter data in %s"
@@ -16494,11 +16807,11 @@
 
 #, c-format
 msgid "entry '%s' in tree %s has tree mode, but is not a tree"
-msgstr "posten \"%s\" i trädet %s har träd-läge, men är inte ett träd"
+msgstr "posten ”%s” i trädet %s har träd-läge, men är inte ett träd"
 
 #, c-format
 msgid "entry '%s' in tree %s has blob mode, but is not a blob"
-msgstr "posten \"%s\" i trädet %s har blob-läge, men är inte en blob"
+msgstr "posten ”%s” i trädet %s har blob-läge, men är inte en blob"
 
 #, c-format
 msgid "unable to load root tree for commit %s"
@@ -16514,10 +16827,10 @@
 "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.lock”: %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"
+"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"
@@ -16525,14 +16838,14 @@
 
 #, c-format
 msgid "Unable to create '%s.lock': %s"
-msgstr "Kunde inte skapa \"%s.lock\": %s"
+msgstr "Kunde inte skapa ”%s.lock”: %s"
 
 #, c-format
 msgid "unexpected line: '%s'"
-msgstr "oväntad rad: \"%s\""
+msgstr "oväntad rad: ”%s”"
 
 msgid "expected flush after ls-refs arguments"
-msgstr "förväntade \"flush\" efter ls-refs-argument"
+msgstr "förväntade ”flush” efter ls-refs-argument"
 
 msgid "quoted CRLF detected"
 msgstr "citerad CRLF upptäcktes"
@@ -16577,12 +16890,12 @@
 "finns:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "Misslyckades exekvera intern sammanslagning"
+msgid "failed to execute internal merge"
+msgstr "misslyckades exekvera intern sammanslagning"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "Kunde inte lägga till %s till databasen"
+msgid "unable to add %s to database"
+msgstr "kunde inte lägga till %s till databasen"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -16619,7 +16932,7 @@
 "WARNING: Avoiding applying %s -> %s rename to %s, because %s itself was "
 "renamed."
 msgstr ""
-"VARNING: Undviker att applicera namnändring %s -> %s på %s, då %s själv har "
+"VARNING: Undviker att applicera namnändring %s → %s på %s, då %s själv har "
 "bytt namn."
 
 #, c-format
@@ -16665,7 +16978,7 @@
 "conflicts AND collides with another path; this may result in nested conflict "
 "markers."
 msgstr ""
-"KONFLIKT (namnbyte involverad i krock): namnbyte av %s -> %s har "
+"KONFLIKT (namnbyte involverad i krock): namnbyte av %s → %s har "
 "innehållskonflikter OCH krockar med en annan sökväg; detta kan leda till "
 "nästlade konfliktmarkörer."
 
@@ -16780,17 +17093,17 @@
 #, c-format
 msgid "add_cacheinfo failed for path '%s'; merge aborting."
 msgstr ""
-"add_cacheinfo misslyckades för sökvägen \"%s\"; avslutar sammanslagningen."
+"add_cacheinfo misslyckades för sökvägen ”%s”; avslutar sammanslagningen."
 
 #, c-format
 msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting."
 msgstr ""
-"add_cacheinfo misslyckades uppdatera för sökvägen \"%s\"; avslutar "
+"add_cacheinfo misslyckades uppdatera för sökvägen ”%s”; avslutar "
 "sammanslagningen."
 
 #, c-format
 msgid "failed to create path '%s'%s"
-msgstr "misslyckades skapa sökvägen \"%s\"%s"
+msgstr "misslyckades skapa sökvägen ”%s”%s"
 
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
@@ -16801,23 +17114,23 @@
 
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
-msgstr "vägrar förlora ospårad fil vid \"%s\""
+msgstr "vägrar förlora ospårad fil vid ”%s”"
 
 #, c-format
 msgid "blob expected for %s '%s'"
-msgstr "blob förväntades för %s \"%s\""
+msgstr "blob förväntades för %s ”%s”"
 
 #, c-format
 msgid "failed to open '%s': %s"
-msgstr "misslyckades öppna \"%s\": %s"
+msgstr "misslyckades öppna ”%s”: %s"
 
 #, c-format
 msgid "failed to symlink '%s': %s"
-msgstr "misslyckades skapa symboliska länken \"%s\": %s"
+msgstr "misslyckades skapa symboliska länken ”%s”: %s"
 
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
-msgstr "vet inte hur %06o %s \"%s\" ska hanteras"
+msgstr "vet inte hur %06o %s ”%s” ska hanteras"
 
 #, c-format
 msgid "Fast-forwarding submodule %s to the following commit:"
@@ -16905,7 +17218,7 @@
 
 #, c-format
 msgid "Refusing to lose dirty file at %s"
-msgstr "Vägrar förlora lortig fil vid \"%s\""
+msgstr "Vägrar förlora lortig fil vid ”%s”"
 
 #, c-format
 msgid "Refusing to lose untracked file at %s, even though it's in the way."
@@ -16913,7 +17226,7 @@
 
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s.  Added %s in %s"
-msgstr "KONFLIKT (namnbyte/tillägg): Namnbyte %s->%s i %s. Lade till %s i %s"
+msgstr "KONFLIKT (namnbyte/tillägg): Namnbyte %s→%s i %s. Lade till %s i %s"
 
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
@@ -16925,19 +17238,18 @@
 
 #, c-format
 msgid ""
-"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
-"\"->\"%s\" in \"%s\"%s"
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
 msgstr ""
-"KONFLIKT (namnbyte/namnbyte): Namnbyte \"%s\"->\"%s\" på grenen \"%s\" "
-"namnbyte \"%s\"->\"%s\" i \"%s\"%s"
+"KONFLIKT (namnbyte/namnbyte): Namnbyte ”%s”→”%s” på grenen ”%s” namnbyte "
+"”%s”→”%s” i ”%s”%s"
 
 msgid " (left unresolved)"
 msgstr " (lämnad olöst)"
 
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
-msgstr ""
-"KONFLIKT (namnbyte/namnbyte): Namnbyte %s->%s i %s. Namnbyte %s->%s i %s"
+msgstr "KONFLIKT (namnbyte/namnbyte): Namnbyte %s→%s i %s. Namnbyte %s→%s i %s"
 
 #, c-format
 msgid ""
@@ -16954,8 +17266,8 @@
 "CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-"
 ">%s in %s"
 msgstr ""
-"KONFLIKT (namnbyte/namnbyte): Namnbytt katalog %s->%s i %s. Namnbytt katalog "
-"%s->%s i %s"
+"KONFLIKT (namnbyte/namnbyte): Namnbytt katalog %s→%s i %s. Namnbytt katalog "
+"%s→%s i %s"
 
 msgid "modify"
 msgstr "ändra"
@@ -17012,13 +17324,25 @@
 
 #, c-format
 msgid "Could not parse object '%s'"
-msgstr "Kunde inte tolka objektet \"%s\""
+msgstr "Kunde inte tolka objektet ”%s”"
 
 msgid "failed to read the cache"
 msgstr "misslyckades läsa cachen"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "multi-pack-indexets OID-utbredning har fel storlek"
+msgstr "OID-utbredning för multi-pack-index har fel storlek"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"oid-utbredning i fel ordning: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "OID-uppslagningsstycket för multi-pack-index har fel storlek"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "objekt-offset-stycket för multi-pack-index har fel storlek"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17036,36 +17360,54 @@
 msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "multi-pack-index-hashversionen %u stämmer inte med versionen %u"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "multi-pack-index saknar krävd paketnamn-stycke"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr ""
+"nödvändigt paketnamn-stycke för multi-pack-index saknas eller är trasigt"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "multi-pack-index saknar krävt OID-utbredningsstycke"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"nödvändigt OID-utbredningsstycke för multi-pack-index saknas eller är trasigt"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "multi-pack-index saknar krävt OID-uppslagnignsstycke"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"nödvändigt OID-uppslagningsstycke för multi-pack-index saknas eller är "
+"trasigt"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "multi-pack-index saknar krävt objekt-offsetstycke"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"nödvändigt objekt-offsetstycke för multi-pack-index saknas eller är trasigt"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "paketnamnstycke för multi-pack-index är för kort"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
-msgstr "multi-pack-index-paketnamn i fel ordning: \"%s\" före \"%s\""
+msgstr "paketnamn för multi-pack-index i fel ordning: ”%s” före ”%s”"
 
 #, c-format
 msgid "bad pack-int-id: %u (%u total packs)"
 msgstr "bad pack-int-id: %u (%u paket totalt)"
 
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX innehåller inte BTMP-stycket"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "kunde inte läsa det bitmappade paketet %<PRIu32>"
+
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
-msgstr "multi-pack-index skriver 64-bitars offset, men off_t är för liten"
+msgstr "multi-pack-index innehåller 64-bitars offset, men off_t är för liten"
+
+msgid "multi-pack-index large offset out of bounds"
+msgstr "stort offset för mult-pack-index utanför gränsen"
 
 #, c-format
 msgid "failed to add packfile '%s'"
-msgstr "misslyckades läsa paketfilen \"%s\""
+msgstr "misslyckades läsa paketfilen ”%s”"
 
 #, c-format
 msgid "failed to open pack-index '%s'"
-msgstr "misslyckades öppna paketindexet \"%s\""
+msgstr "misslyckades öppna paketindexet ”%s”"
 
 #, c-format
 msgid "failed to locate object %d in packfile"
@@ -17109,7 +17451,7 @@
 
 #, c-format
 msgid "preferred pack '%s' is expired"
-msgstr "föredraget paket \"%s\" har löpt ut"
+msgstr "föredraget paket ”%s” har löpt ut"
 
 msgid "no pack files to index."
 msgstr "inga paketfiler att indexera."
@@ -17136,12 +17478,6 @@
 msgid "Looking for referenced packfiles"
 msgstr "Ser efter refererade packfiler"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"oid-utbredning i fel ordning: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
 msgid "the midx contains no oid"
 msgstr "midx saknar oid"
 
@@ -17201,9 +17537,9 @@
 "commit/abort the previous merge before you start a new notes merge."
 msgstr ""
 "Du har inte avslutat föregående antecknings-sammanslagning (%s finns).\n"
-"Använd \"git notes merge --commit\" eller \"git notes merge --abort\" för "
-"att checka in eller avbryta föregående sammanslagning innan du påbörjar en "
-"ny antecknings-sammanslagning."
+"Använd ”git notes merge --commit” eller ”git notes merge --abort” för att "
+"checka in eller avbryta föregående sammanslagning innan du påbörjar en ny "
+"antecknings-sammanslagning."
 
 #, c-format
 msgid "You have not concluded your notes merge (%s exists)."
@@ -17226,7 +17562,7 @@
 #.
 #, c-format
 msgid "Bad %s value: '%s'"
-msgstr "Felaktigt värde på %s: \"%s\""
+msgstr "Felaktigt värde på %s: ”%s”"
 
 #, c-format
 msgid "object directory %s does not exist; check .git/objects/info/alternates"
@@ -17241,33 +17577,33 @@
 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"
+msgstr "kan inte utföra ”fdopen” på suppleantlåsfil"
 
 msgid "unable to read alternates file"
-msgstr "kan inte läsa \"alternates\"-filen"
+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"
+msgstr "kan inte flytta ny ”alternates”-fil på plats"
 
 #, c-format
 msgid "path '%s' does not exist"
-msgstr "sökvägen \"%s\" finns inte"
+msgstr "sökvägen ”%s” finns inte"
 
 #, c-format
 msgid "reference repository '%s' as a linked checkout is not supported yet."
-msgstr "referensarkivet \"%s\" som en länkad utcheckning stöds inte ännu."
+msgstr "referensarkivet ”%s” som en länkad utcheckning stöds inte ännu."
 
 #, c-format
 msgid "reference repository '%s' is not a local repository."
-msgstr "referensarkivet \"%s\" är inte ett lokalt arkiv."
+msgstr "referensarkivet ”%s” är inte ett lokalt arkiv."
 
 #, c-format
 msgid "reference repository '%s' is shallow"
-msgstr "referensarkivet \"%s\" är grunt"
+msgstr "referensarkivet ”%s” är grunt"
 
 #, c-format
 msgid "reference repository '%s' is grafted"
-msgstr "referensarkivet \"%s\" är ympat"
+msgstr "referensarkivet ”%s” är ympat"
 
 #, c-format
 msgid "could not find object directory matching %s"
@@ -17279,7 +17615,7 @@
 
 #, c-format
 msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>"
-msgstr "försök att utföra \"mmap\" på %<PRIuMAX> över gränsen %<PRIuMAX>"
+msgstr "försök att utföra ”mmap” på %<PRIuMAX> över gränsen %<PRIuMAX>"
 
 #, c-format
 msgid "mmap failed%s"
@@ -17291,11 +17627,11 @@
 
 #, c-format
 msgid "corrupt loose object '%s'"
-msgstr "trasigt löst objekt \"%s\""
+msgstr "trasigt löst objekt ”%s”"
 
 #, c-format
 msgid "garbage at end of loose object '%s'"
-msgstr "skräp i slutet av löst objekt \"%s\""
+msgstr "skräp i slutet av löst objekt ”%s”"
 
 #, c-format
 msgid "unable to open loose object %s"
@@ -17334,7 +17670,7 @@
 
 #, c-format
 msgid "unable to set permission to '%s'"
-msgstr "kan inte sätta behörigheten till \"%s\""
+msgstr "kan inte sätta behörigheten till ”%s”"
 
 msgid "error when closing loose object file"
 msgstr "fel vid stängning av fil för löst objekt"
@@ -17352,11 +17688,11 @@
 
 #, c-format
 msgid "unable to deflate new object %s (%d)"
-msgstr "kan inte utföra \"deflate\" på nytt objekt %s (%d)"
+msgstr "kan inte utföra ”deflate” på nytt objekt %s (%d)"
 
 #, c-format
 msgid "deflateEnd on object %s failed (%d)"
-msgstr "\"deflateend\" på objektet %s misslyckades (%d)"
+msgstr "”deflateEnd” på objektet %s misslyckades (%d)"
 
 #, c-format
 msgid "confused by unstable object source data for %s"
@@ -17368,11 +17704,11 @@
 
 #, c-format
 msgid "unable to stream deflate new object (%d)"
-msgstr "kan inte utföra \"deflate\" på nytt strömobjekt (%d)"
+msgstr "kan inte utföra ”deflate” på nytt strömobjekt (%d)"
 
 #, c-format
 msgid "deflateEnd on stream object failed (%d)"
-msgstr "\"deflateend\" på strömobjektet misslyckades (%d)"
+msgstr "”deflatEend” på strömobjektet misslyckades (%d)"
 
 #, c-format
 msgid "unable to create directory %s"
@@ -17407,7 +17743,7 @@
 
 #, c-format
 msgid "%s is not a valid '%s' object"
-msgstr "%s är inte ett giltigt \"%s\"-objekt"
+msgstr "%s är inte ett giltigt ”%s”-objekt"
 
 #, c-format
 msgid "unable to open %s"
@@ -17419,7 +17755,7 @@
 
 #, c-format
 msgid "unable to mmap %s"
-msgstr "kan inte utföra \"mmap\" för %s"
+msgstr "kan inte utföra ”mmap” för %s"
 
 #, c-format
 msgid "unable to unpack header of %s"
@@ -17522,72 +17858,72 @@
 "\n"
 "  git switch -c $br $(git rev-parse ...)\n"
 "\n"
-"där \"$br\" på något sätt blivit tomt och en 40-hex-referens skapats.\n"
+"där ”$br” på något sätt blivit tomt och en 40-hex-referens skapats.\n"
 "Undersök referenserna och ta kanske bort dem. Stäng av meddelandet\n"
-"genom att köra \"git config advice.objectNameWarning false\""
+"genom att köra ”git config advice.objectNameWarning false”"
 
 #, c-format
 msgid "log for '%.*s' only goes back to %s"
-msgstr "loggen för \"%.*s\" räcker bara tillbaka till %s"
+msgstr "loggen för ”%.*s” räcker bara tillbaka till %s"
 
 #, c-format
 msgid "log for '%.*s' only has %d entries"
-msgstr "loggen för \"%.*s\" har bara %d poster"
+msgstr "loggen för ”%.*s” har bara %d poster"
 
 #, c-format
 msgid "path '%s' exists on disk, but not in '%.*s'"
-msgstr "Sökvägen \"%s\" finns på disken, men inte i \"%.*s\""
+msgstr "Sökvägen ”%s” finns på disken, men inte i ”%.*s”"
 
 #, c-format
 msgid ""
 "path '%s' exists, but not '%s'\n"
 "hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?"
 msgstr ""
-"sökvägen \"%s\" finns, men inte i \"%s\"\n"
-"tips: Menade du \"%.*s:%s\", även känd som \"%.*s:./%s\"?"
+"sökvägen ”%s” finns, men inte i ”%s”\n"
+"tips: Menade du ”%.*s:%s”, även känd som ”%.*s:./%s”?"
 
 #, c-format
 msgid "path '%s' does not exist in '%.*s'"
-msgstr "sökvägen \"%s\" finns inte i \"%.*s\""
+msgstr "sökvägen ”%s” finns inte i ”%.*s”"
 
 #, c-format
 msgid ""
 "path '%s' is in the index, but not at stage %d\n"
 "hint: Did you mean ':%d:%s'?"
 msgstr ""
-"sökvägen \"%s\" finns i indexet men inte i etapp %d\n"
-"tips: Menade du \":%d:%s\"?"
+"sökvägen ”%s” finns i indexet men inte i etapp %d\n"
+"tips: Menade du ”:%d:%s”?"
 
 #, c-format
 msgid ""
 "path '%s' is in the index, but not '%s'\n"
 "hint: Did you mean ':%d:%s' aka ':%d:./%s'?"
 msgstr ""
-"sökvägen \"%s\" finns i indexet, men inte i \"%s\"\n"
-"tips: Menade du \":%d:%s\", även känd som \":%d:./%s\"?"
+"sökvägen ”%s” finns i indexet, men inte i ”%s”\n"
+"tips: Menade du ”:%d:%s”, även känd som ”:%d:./%s”?"
 
 #, c-format
 msgid "path '%s' exists on disk, but not in the index"
-msgstr "sökvägen \"%s\" finns på disk, men inte i indexet"
+msgstr "sökvägen ”%s” finns på disk, men inte i indexet"
 
 #, c-format
 msgid "path '%s' does not exist (neither on disk nor in the index)"
-msgstr "sökvägen \"%s\" finns inte (varken i disken eller i indexet)"
+msgstr "sökvägen ”%s” finns inte (varken i disken eller i indexet)"
 
 msgid "relative path syntax can't be used outside working tree"
 msgstr "relativ sökväg kan inte användas utanför arbetskatalogen"
 
 #, c-format
 msgid "<object>:<path> required, only <object> '%s' given"
-msgstr "<objekt>:<sökväg> krävs, endast <objekt> \"%s\" har angivits"
+msgstr "<objekt>:<sökväg> krävs, endast <objekt> ”%s” har angivits"
 
 #, c-format
 msgid "invalid object name '%.*s'."
-msgstr "felaktigt objektnamn \"%.*s\"."
+msgstr "felaktigt objektnamn ”%.*s”."
 
 #, c-format
 msgid "invalid object type \"%s\""
-msgstr "ogiltig objekttyp \"%s\""
+msgstr "ogiltig objekttyp ”%s”"
 
 #, c-format
 msgid "object %s is a %s, not a %s"
@@ -17619,7 +17955,7 @@
 
 #, c-format
 msgid "unsupported version '%d' for bitmap index file"
-msgstr "versionen \"%d\" i bitkarteindexfilen stöds inte"
+msgstr "versionen ”%d” i bitkarteindexfilen stöds inte"
 
 msgid "corrupted bitmap index file (too short to fit hash cache)"
 msgstr "trasigt bitkarteindex (för kort för att få plats för hash-cache)"
@@ -17629,7 +17965,7 @@
 
 #, c-format
 msgid "duplicate entry in bitmap index: '%s'"
-msgstr "duplicerad post i bitkarteindex: \"%s\""
+msgstr "duplicerad post i bitkarteindex: ”%s”"
 
 #, c-format
 msgid "corrupt ewah bitmap: truncated header for entry %d"
@@ -17646,7 +17982,7 @@
 msgstr "ogiltigt XOR-offset i bitkarte-packindex"
 
 msgid "cannot fstat bitmap file"
-msgstr "kan inte utföra \"fstat\" på bitkartefil"
+msgstr "kan inte utföra ”fstat” på bitkartefil"
 
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "checksumman stämmer inte i MIDX och bitkarta"
@@ -17658,6 +17994,9 @@
 msgid "could not open pack %s"
 msgstr "kunde inte öppna paketfilen %s"
 
+msgid "could not determine MIDX preferred pack"
+msgstr "kunde inte bestämma det föredragna MIDX-paketet"
+
 #, c-format
 msgid "preferred pack (%s) is invalid"
 msgstr "föredragen paketfil (%s) är ogiltig"
@@ -17675,20 +18014,23 @@
 
 #, c-format
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
-msgstr ""
-"trasig ewah-bitkarta: avhugget huvud för bitkarta för incheckning \"%s\""
+msgstr "trasig ewah-bitkarta: avhugget huvud för bitkarta för incheckning ”%s”"
+
+#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "kunde inte läsa paketet: ”%s”, inaktiverar återanvändning av paket"
 
 #, c-format
 msgid "object '%s' not found in type bitmaps"
-msgstr "objektet \"%s\" hittades inte i typbitkartor"
+msgstr "objektet ”%s” hittades inte i typbitkartor"
 
 #, c-format
 msgid "object '%s' does not have a unique type"
-msgstr "objektet \"%s\" har inte en unik typ"
+msgstr "objektet ”%s” har inte en unik typ"
 
 #, c-format
 msgid "object '%s': real type '%s', expected: '%s'"
-msgstr "objektet \"%s\": riktig typ \"%s\", förväntade \"%s\""
+msgstr "objektet ”%s”: riktig typ ”%s”, förväntade ”%s”"
 
 #, c-format
 msgid "object not in bitmap: '%s'"
@@ -17702,18 +18044,22 @@
 
 #, c-format
 msgid "commit '%s' doesn't have an indexed bitmap"
-msgstr "incheckningen \"%s\" har inte en indexerad bitkarta"
+msgstr "incheckningen ”%s” har inte en indexerad bitkarta"
 
 msgid "mismatch in bitmap results"
 msgstr "bitkarteresultat stämmer inte överens"
 
 #, c-format
 msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
-msgstr "kunde inte hitta \"%s\" i paketet \"%s\" på offset %<PRIuMAX>"
+msgstr "kunde inte hitta ”%s” i paketet ”%s” på offset %<PRIuMAX>"
 
 #, c-format
 msgid "unable to get disk usage of '%s'"
-msgstr "kan inte hämta diskanvändning för \"%s\""
+msgstr "kan inte hämta diskanvändning för ”%s”"
+
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "bitkartefilen ”%s” har ogiltig kontrollsumma"
 
 #, c-format
 msgid "mtimes file %s is too small"
@@ -17755,6 +18101,19 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "reverse-index-filen %s har hash-ID %<PRIu32> som inte stöds"
 
+msgid "invalid checksum"
+msgstr "ogiltig kontrollsumma"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "ogiltig rev-indexposition vid %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+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"
 
@@ -17768,7 +18127,7 @@
 
 #, c-format
 msgid "could not write '%s' promisor file"
-msgstr "kunde inte skriva kontraktsfilen \"%s\""
+msgstr "kunde inte skriva kontraktsfilen ”%s”"
 
 msgid "offset before end of packfile (broken .idx?)"
 msgstr "offset före slutet av packfilen (trasig .idx?)"
@@ -17787,34 +18146,25 @@
 
 #, c-format
 msgid "malformed expiration date '%s'"
-msgstr "trasigt utlöpsdatum: \"%s\""
+msgstr "trasigt utlöpsdatum: ”%s”"
 
 #, c-format
 msgid "option `%s' expects \"always\", \"auto\", or \"never\""
-msgstr ""
-"flaggan \"%s\" antar \"always\" (alltid), \"auto\" eller \"never\" (aldrig)"
+msgstr "flaggan ”%s” antar ”always” (alltid), ”auto” eller ”never” (aldrig)"
 
 #, c-format
 msgid "malformed object name '%s'"
-msgstr "felformat objektnamn \"%s\""
+msgstr "felformat objektnamn ”%s”"
 
 #, c-format
 msgid "option `%s' expects \"%s\" or \"%s\""
-msgstr "flaggan \"%s\" kräver \"%s\" eller \"%s\""
+msgstr "flaggan ”%s” kräver ”%s” eller ”%s”"
 
 #, c-format
 msgid "%s requires a value"
 msgstr "%s behöver ett värde"
 
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s är inkompatibel med %s"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: inkompatibelt med något annat"
-
-#, c-format
 msgid "%s takes no value"
 msgstr "%s tar inget värde"
 
@@ -17832,7 +18182,7 @@
 
 #, c-format
 msgid "did you mean `--%s` (with two dashes)?"
-msgstr "menade du \"--%s\" (med två bindestreck)?"
+msgstr "menade du ”--%s” (med två bindestreck)?"
 
 #, c-format
 msgid "alias of --%s"
@@ -17843,15 +18193,15 @@
 
 #, c-format
 msgid "unknown option `%s'"
-msgstr "okänd flagga \"%s\""
+msgstr "okänd flagga ”%s”"
 
 #, c-format
 msgid "unknown switch `%c'"
-msgstr "okänd flagga \"%c\""
+msgstr "okänd flagga ”%c”"
 
 #, c-format
 msgid "unknown non-ascii option in string: `%s'"
-msgstr "okänd icke-ascii-flagga i strängen: \"%s\""
+msgstr "okänd icke-ascii-flagga i strängen: ”%s”"
 
 msgid "..."
 msgstr "..."
@@ -17897,6 +18247,10 @@
 msgid "-NUM"
 msgstr "-TAL"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "motsatsen mot --no-%s"
+
 msgid "expiry-date"
 msgstr "giltig-till"
 
@@ -17926,14 +18280,22 @@
 msgstr "med --pathspec-from-file, sökvägsangivelser avdelas med NUL-tecken"
 
 #, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "felaktigt booleskt miljövariabelvärde ”%s” för ”%s”"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "kunde inte tolka %s"
+
+#, c-format
 msgid "Could not make %s writable by group"
 msgstr "Kunde inte göra %s skrivbar för gruppen"
 
 msgid "Escape character '\\' not allowed as last character in attr value"
-msgstr "Specialtecknet \"\\\" tillåts inte som sista tecken i attributvärde"
+msgstr "Specialtecknet ”\\” tillåts inte som sista tecken i attributvärde"
 
 msgid "Only one 'attr:' specification is allowed."
-msgstr "Endast en \"attr:\"-angivelse tillåten."
+msgstr "Endast en ”attr:”-angivelse tillåten."
 
 msgid "attr spec must not be empty"
 msgstr "attr-angivelse kan inte vara tom"
@@ -17943,42 +18305,45 @@
 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ägsinställningarna ”glob” och ”noglob” är inkompatibla"
 
 msgid ""
 "global 'literal' pathspec setting is incompatible with all other global "
 "pathspec settings"
 msgstr ""
-"den globala sökvägsinställningen \"literal\" är inkompatibel med alla andra "
+"den globala sökvägsinställningen ”literal” är inkompatibel med alla andra "
 "globala sökvägsinställningar"
 
 msgid "invalid parameter for pathspec magic 'prefix'"
-msgstr "ogiltig parameter för sökvägsuttrycket för \"prefix\""
+msgstr "ogiltig parameter för sökvägsuttrycket för ”prefix”"
 
 #, c-format
 msgid "Invalid pathspec magic '%.*s' in '%s'"
-msgstr "Felaktigt sökvägsuttryck \"%.*s\" i \"%s\""
+msgstr "Felaktigt sökvägsuttryck ”%.*s” i ”%s”"
 
 #, c-format
 msgid "Missing ')' at the end of pathspec magic in '%s'"
-msgstr "\")\" saknas i slutet av sökvägsuttrycket för \"%s\""
+msgstr "”)” saknas i slutet av sökvägsuttrycket för ”%s”"
 
 #, c-format
 msgid "Unimplemented pathspec magic '%c' in '%s'"
-msgstr "Ej implementerat sökvägsuttryckmagi \"%c\" i \"%s\""
+msgstr "Ej implementerat sökvägsuttryckmagi ”%c” i ”%s”"
 
 #, c-format
 msgid "%s: 'literal' and 'glob' are incompatible"
-msgstr "%s: \"literal\" och \"glob\" är inkompatibla"
+msgstr "%s: ”literal” och ”glob” är inkompatibla"
+
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "”%s” är utanför katalogträdet"
 
 #, c-format
 msgid "%s: '%s' is outside repository at '%s'"
-msgstr "%s: \"%s\" är utanför arkivet på \"%s\""
+msgstr "%s: ”%s” är utanför arkivet på ”%s”"
 
 #, c-format
 msgid "'%s' (mnemonic: '%c')"
-msgstr "\"%s\" (minnesstöd: \"%c\")"
+msgstr "”%s” (minnesstöd: ”%c”)"
 
 #, c-format
 msgid "%s: pathspec magic not supported by this command: %s"
@@ -17986,7 +18351,7 @@
 
 #, c-format
 msgid "pathspec '%s' is beyond a symbolic link"
-msgstr "sökvägsangivelsen \"%s\" är på andra sidan av en symbolisk länk"
+msgstr "sökvägsangivelsen ”%s” är på andra sidan av en symbolisk länk"
 
 #, c-format
 msgid "line is badly quoted: %s"
@@ -18002,7 +18367,7 @@
 msgstr "kunde inte skriva svarsavslutningspaket"
 
 msgid "flush packet write failed"
-msgstr "fel vid skrivning av \"flush\"-paket"
+msgstr "fel vid skrivning av ”flush”-paket"
 
 msgid "protocol error: impossibly long line"
 msgstr "protokollfel: omöjligt lång rad"
@@ -18057,50 +18422,56 @@
 
 #, c-format
 msgid "promisor remote name cannot begin with '/': %s"
-msgstr "kontraktsfjärr kan inte börja med \"/\": %s"
+msgstr "kontraktsfjärr kan inte börja med ”/”: %s"
 
 #, c-format
 msgid "could not fetch %s from promisor remote"
 msgstr "kunde inte hämta %s från kontraktsfjärr"
 
 msgid "object-info: expected flush after arguments"
-msgstr "object-info: förväntade \"flush\" efter argument"
+msgstr "object-info: förväntade ”flush” efter argument"
 
 msgid "Removing duplicate objects"
 msgstr "Tar bort duplicerade objekt"
 
 msgid "could not start `log`"
-msgstr "kunde inte starta \"log\""
+msgstr "kunde inte starta ”log”"
 
 msgid "could not read `log` output"
-msgstr "kunde inte läsa utdata från \"log\""
+msgstr "kunde inte läsa utdata från ”log”"
 
 #, c-format
 msgid "could not parse commit '%s'"
-msgstr "kunde inte tolka incheckningen \"%s\""
+msgstr "kunde inte tolka incheckningen ”%s”"
 
 #, c-format
 msgid ""
 "could not parse first line of `log` output: did not start with 'commit ': "
 "'%s'"
 msgstr ""
-"kunde inte tolka första raden i \"log\"-updata: börjar inte med \"commit \": "
-"\"%s\""
+"kunde inte tolka första raden i ”log”-updata: börjar inte med ”commit ”: ”%s”"
 
 #, c-format
 msgid "could not parse git header '%.*s'"
-msgstr "kunde inte tolka git-huvudet \"%.*s\""
+msgstr "kunde inte tolka git-huvudet ”%.*s”"
 
 msgid "failed to generate diff"
 msgstr "misslyckades skapa diff"
 
 #, c-format
 msgid "could not parse log for '%s'"
-msgstr "kunde inte tolka loggen för \"%s\""
+msgstr "kunde inte tolka loggen för ”%s”"
+
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "ogiltig extra överbliven ände: ”%s”"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "kan inte räkna ytterligare nyliga objekt"
 
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
-msgstr "lägger inte till filalias \"%s\" (\"%s\" finns redan i indexet)"
+msgstr "lägger inte till filalias ”%s” (”%s” finns redan i indexet)"
 
 msgid "cannot create an empty blob in the object database"
 msgstr "kan inte skapa tom blob i objektdatabasen"
@@ -18112,19 +18483,15 @@
 
 #, c-format
 msgid "unable to index file '%s'"
-msgstr "kan inte indexera filen \"%s\""
+msgstr "kan inte indexera filen ”%s”"
 
 #, c-format
 msgid "unable to add '%s' to index"
-msgstr "kan inte lägga till \"%s\" till indexet"
-
-#, c-format
-msgid "unable to stat '%s'"
-msgstr "kan inte ta status på \"%s\""
+msgstr "kan inte lägga till ”%s” till indexet"
 
 #, c-format
 msgid "'%s' appears as both a file and as a directory"
-msgstr "\"%s\" finns både som en fil och en katalog"
+msgstr "”%s” finns både som en fil och en katalog"
 
 msgid "Refresh index"
 msgstr "Uppdatera indexet"
@@ -18170,18 +18537,18 @@
 
 #, c-format
 msgid "malformed name field in the index, near path '%s'"
-msgstr "felformat namnfält i indexet, nära sökvägen \"%s\""
+msgstr "felformat namnfält i indexet, nära sökvägen ”%s”"
 
 msgid "unordered stage entries in index"
 msgstr "osorterade köposter i index"
 
 #, c-format
 msgid "multiple stage entries for merged file '%s'"
-msgstr "flera köposter för den sammanslagna filen \"%s\""
+msgstr "flera köposter för den sammanslagna filen ”%s”"
 
 #, c-format
 msgid "unordered stage entries for '%s'"
-msgstr "osorterade köposter för \"%s\""
+msgstr "osorterade köposter för ”%s”"
 
 #, c-format
 msgid "unable to create load_cache_entries thread: %s"
@@ -18217,7 +18584,7 @@
 
 #, c-format
 msgid "could not freshen shared index '%s'"
-msgstr "kunde inte uppdatera delat index \"%s\""
+msgstr "kunde inte uppdatera delat index ”%s”"
 
 #, c-format
 msgid "broken index, expect %s in %s, got %s"
@@ -18230,10 +18597,6 @@
 msgstr "misslyckades omvandla till glest index"
 
 #, c-format
-msgid "could not stat '%s'"
-msgstr "kunde inte ta status på \"%s\""
-
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "kunde inte öppna git-katalog: %s"
 
@@ -18243,20 +18606,28 @@
 
 #, c-format
 msgid "cannot fix permission bits on '%s'"
-msgstr "kan inte rätta behörighetsbitar på \"%s\""
+msgstr "kan inte rätta behörighetsbitar på ”%s”"
 
 #, c-format
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: kan inte återgå till kö 0"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "diff-status %c förväntades inte"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "ta bort ”%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 ""
-"Du kan rätta detta med \"git rebase --edit-todo\" följt av \"git rebase --"
-"continue\".\n"
-"Avbryt ombaseringen med \"git rebase --abort\".\n"
+"Du kan rätta detta med ”git rebase --edit-todo” följt av ”git rebase --"
+"continue”.\n"
+"Avbryt ombaseringen med ”git rebase --abort”.\n"
 
 #, c-format
 msgid ""
@@ -18298,18 +18669,17 @@
 "e, edit <incheckning> = använd incheckning, men stanna för tillägg\n"
 "s, squash <incheckning> = använd incheckning, men infoga i föregående "
 "incheckning\n"
-"f, fixup [-C | -c] <incheckning> = som \"squash\" men behåll bara "
+"f, fixup [-C | -c] <incheckning> = som ”squash” men behåll bara "
 "loggmeddelandet\n"
 "                   från föregående incheckning, såvida inte -C används, då "
 "används\n"
 "                   istället bara den här incheckningens meddelande; -c är "
 "samma\n"
 "                   som -C, men öppnar redigeringsprogrammet\n"
-"f, fixup <incheckning> = som \"squash\", men förkasta "
-"incheckningsmeddelandet\n"
+"f, fixup <incheckning> = som ”squash”, men förkasta incheckningsmeddelandet\n"
 "x, exec <kommando> = kör kommando (resten av raden) i skalet\n"
-"b, break = stoppa här (fortsätt ombaseringen senare med \"git rebase --"
-"continue\")\n"
+"b, break = stoppa här (fortsätt ombaseringen senare med ”git rebase --"
+"continue”)\n"
 "d, drop <incheckning> = ta bort incheckning\n"
 "l, label <etikett> = ge aktuellt HEAD ett namn\n"
 "t, reset <etikett> = återställ HEAD till en etikett\n"
@@ -18334,7 +18704,7 @@
 "Do not remove any line. Use 'drop' explicitly to remove a commit.\n"
 msgstr ""
 "\n"
-"Ta inte bort rader. Använd \"drop\" för att specifikt förkasta en "
+"Ta inte bort rader. Använd ”drop” för att specifikt förkasta en "
 "incheckning.\n"
 
 msgid ""
@@ -18352,7 +18722,7 @@
 "\n"
 msgstr ""
 "\n"
-"Du redigerar \"todo\"-filen för en pågående interaktiv ombasering.\n"
+"Du redigerar ”todo”-filen för en pågående interaktiv ombasering.\n"
 "För att forsätta ombasera efter redigeringen, kör:\n"
 "    git rebase --continue\n"
 "\n"
@@ -18368,7 +18738,7 @@
 
 #, c-format
 msgid "could not write '%s'."
-msgstr "kunde inte skriva \"%s\"."
+msgstr "kunde inte skriva ”%s”."
 
 #, c-format
 msgid ""
@@ -18387,19 +18757,16 @@
 "The possible behaviours are: ignore, warn, error.\n"
 "\n"
 msgstr ""
-"För att undvika det här meddelandet kan du använda \"drop\" för att "
-"explicit\n"
+"För att undvika det här meddelandet kan du använda ”drop” för att explicit\n"
 "kasta en incheckning.\n"
 "\n"
-"Använd \"git config rebase.missingCommitsCheck\" för att ändra "
-"varningsnivån.\n"
-"Möjliga bettenden är: \"ignore\" (ignorera), \"warn\" (varna), \"error"
-"\" (fel).\n"
+"Använd ”git config rebase.missingCommitsCheck” för att ändra varningsnivån.\n"
+"Möjliga bettenden är: ”ignore” (ignorera), ”warn” (varna), ”error” (fel).\n"
 "\n"
 
 #, c-format
 msgid "%s: 'preserve' superseded by 'merges'"
-msgstr "%s: \"preserve\" har ersatts av \"merges\""
+msgstr "%s: ”preserve” har ersatts av ”merges”"
 
 msgid "gone"
 msgstr "försvunnen"
@@ -18453,8 +18820,24 @@
 msgstr "positivt värde förväntat contents:lines=%s"
 
 #, c-format
+msgid "argument expected for %s"
+msgstr "argument förväntades för %s"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "positivt värde förväntat %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "kan inte helt tolka %s=%s"
+
+#, c-format
+msgid "value expected %s="
+msgstr "vädre förväntades %s="
+
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
-msgstr "positivt värde förväntat \"%s\" i %%(%s)"
+msgstr "positivt värde förväntat ”%s” i %%(%s)"
 
 #, c-format
 msgid "expected format: %%(align:<width>,<position>)"
@@ -18477,6 +18860,10 @@
 msgstr "positiv bredd förväntad med atomen %%(align)"
 
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "förväntat format: %%(ahead-behind:<incheckning-igt>)"
+
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "felformat fältnamn: %.*s"
 
@@ -18487,8 +18874,7 @@
 #, c-format
 msgid ""
 "not a git repository, but the field '%.*s' requires access to object data"
-msgstr ""
-"inte ett git-arkiv, men fältet \"%.*s\" kräver tillgång till objektdata"
+msgstr "inte ett git-arkiv, men fältet ”%.*s” kräver tillgång till objektdata"
 
 #, c-format
 msgid "format: %%(%s) atom used without a %%(%s) atom"
@@ -18522,6 +18908,9 @@
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s kan inte användas med --python, --shell, --tcl"
 
+msgid "failed to run 'describe'"
+msgstr "misslyckades att köra ”describe”"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(ingen gren, ombaserar %s)"
@@ -18532,7 +18921,7 @@
 
 #, c-format
 msgid "(no branch, bisect started on %s)"
-msgstr "(ingen gren, \"bisect\" startad på %s)"
+msgstr "(ingen gren, ”bisect” startad på %s)"
 
 #, c-format
 msgid "(HEAD detached at %s)"
@@ -18555,7 +18944,7 @@
 
 #, c-format
 msgid "malformed object at '%s'"
-msgstr "felformat objekt vid \"%s\""
+msgstr "felformat objekt vid ”%s”"
 
 #, c-format
 msgid "ignoring ref with broken name %s"
@@ -18575,7 +18964,7 @@
 
 #, c-format
 msgid "option `%s' must point to a commit"
-msgstr "flaggan \"%s\" måste peka på en incheckning"
+msgstr "flaggan ”%s” måste peka på en incheckning"
 
 msgid "key"
 msgstr "nyckel"
@@ -18583,17 +18972,20 @@
 msgid "field name to sort on"
 msgstr "fältnamn att sortera på"
 
+msgid "exclude refs which match pattern"
+msgstr "uteslut referenser som motsvarar mönster"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "inte en referenslogg: %s"
 
 #, c-format
 msgid "no reflog for '%s'"
-msgstr "ingen referenslogg för \"%s\""
+msgstr "ingen referenslogg för ”%s”"
 
 #, c-format
 msgid "%s does not point to a valid object!"
-msgstr "\"%s\" pekar inte på ett giltigt objekt!"
+msgstr "”%s” pekar inte på ett giltigt objekt!"
 
 #, c-format
 msgid ""
@@ -18608,20 +19000,20 @@
 "\n"
 "\tgit branch -m <name>\n"
 msgstr ""
-"Använder \"%s\" som namn för den inledande grenen. Detta förvalda grennamn\n"
+"Använder ”%s” som namn för den inledande grenen. Detta förvalda grennamn\n"
 "kan ändras i framtiden. För att välja vilket namn som ska användas på\n"
 "den inledande grenen i alla nya arkiv, och dölja denna varning, kör du:\n"
 "\n"
 "\tgit config --global init.defaultBranch <namn>\n"
 "\n"
-"Namn som ofta används istället för \"master\" är \"main\", \"trunk\" och\n"
-"\"development\". Den nyskapade grenen kan ges nytt namn med kommandot:\n"
+"Namn som ofta används istället för ”master” är ”main”, ”trunk” och\n"
+"”development”. Den nyskapade grenen kan ges nytt namn med kommandot:\n"
 "\n"
 "\tgit branch -m <namn>\n"
 
 #, c-format
 msgid "could not retrieve `%s`"
-msgstr "kunde inte hämta \"%s\""
+msgstr "kunde inte hämta ”%s”"
 
 #, c-format
 msgid "invalid branch name: %s = %s"
@@ -18645,15 +19037,15 @@
 
 #, c-format
 msgid "refusing to update ref with bad name '%s'"
-msgstr "vägrar uppdatera referens med trasigt namn \"%s\""
+msgstr "vägrar uppdatera referens med trasigt namn ”%s”"
 
 #, c-format
 msgid "update_ref failed for ref '%s': %s"
-msgstr "update_ref misslyckades för referensen \"%s\": %s"
+msgstr "update_ref misslyckades för referensen ”%s”: %s"
 
 #, c-format
 msgid "multiple updates for ref '%s' not allowed"
-msgstr "flera uppdateringar för referensen \"%s\" tillåts inte"
+msgstr "flera uppdateringar för referensen ”%s” tillåts inte"
 
 msgid "ref updates forbidden inside quarantine environment"
 msgstr "referensuppdateringar förbjudna i karantänmiljö"
@@ -18663,15 +19055,11 @@
 
 #, c-format
 msgid "'%s' exists; cannot create '%s'"
-msgstr "\"%s\" finns; kan inte skapa \"%s\""
+msgstr "”%s” finns; kan inte skapa ”%s”"
 
 #, c-format
 msgid "cannot process '%s' and '%s' at the same time"
-msgstr "kan inte hantera \"%s\" och \"%s\" samtidigt"
-
-#, c-format
-msgid "could not remove reference %s"
-msgstr "kunde inte ta bort referensen %s"
+msgstr "kan inte hantera ”%s” och ”%s” samtidigt"
 
 #, c-format
 msgid "could not delete reference %s: %s"
@@ -18683,11 +19071,11 @@
 
 #, c-format
 msgid "invalid refspec '%s'"
-msgstr "felaktig referensspecifikation: \"%s\""
+msgstr "felaktig referensspecifikation: ”%s”"
 
 #, c-format
 msgid "invalid quoting in push-option value: '%s'"
-msgstr "felaktig citering på värde för push-option: \"%s\""
+msgstr "felaktig citering på värde för push-option: ”%s”"
 
 #, c-format
 msgid "%sinfo/refs not valid: is this a git repository?"
@@ -18698,23 +19086,23 @@
 
 #, c-format
 msgid "invalid server response; got '%s'"
-msgstr "ogiltigt svar från servern; fick \"%s\""
+msgstr "ogiltigt svar från servern; fick ”%s”"
 
 #, c-format
 msgid "repository '%s' not found"
-msgstr "arkivet \"%s\" hittades inte"
+msgstr "arkivet ”%s” hittades inte"
 
 #, c-format
 msgid "Authentication failed for '%s'"
-msgstr "Autentisering misslyckades \"%s\""
+msgstr "Autentisering misslyckades ”%s”"
 
 #, c-format
 msgid "unable to access '%s' with http.pinnedPubkey configuration: %s"
-msgstr "kan inte nå \"%s\" med http.pinnedPubkey inställt till: %s"
+msgstr "kan inte nå ”%s” med http.pinnedPubkey inställt till: %s"
 
 #, c-format
 msgid "unable to access '%s': %s"
-msgstr "kan inte komma åt \"%s\": %s"
+msgstr "kan inte komma åt ”%s”: %s"
 
 #, c-format
 msgid "redirecting to %s"
@@ -18770,18 +19158,18 @@
 
 #, c-format
 msgid "protocol error: expected sha/ref, got '%s'"
-msgstr "protokollfel: förväntade sha/ref, fick \"%s\""
+msgstr "protokollfel: förväntade sha/ref, fick ”%s”"
 
 #, c-format
 msgid "http transport does not support %s"
 msgstr "http-transporten stöder inte %s"
 
 msgid "protocol error: expected '<url> <path>', missing space"
-msgstr "protokollfel: förväntade \"<url> <sökväg>\", saknar blanksteg"
+msgstr "protokollfel: förväntade ”<url> <sökväg>”, saknar blanksteg"
 
 #, c-format
 msgid "failed to download file at URL '%s'"
-msgstr "misslyckades hämta filen på URL \"%s\""
+msgstr "misslyckades hämta filen på URL ”%s”"
 
 msgid "git-http-push failed"
 msgstr "git-http-push misslyckades"
@@ -18797,11 +19185,11 @@
 
 #, c-format
 msgid "remote-curl: unknown command '%s' from git"
-msgstr "remote-curl: okänt kommando \"%s\" från git"
+msgstr "remote-curl: okänt kommando ”%s” från git"
 
 #, c-format
 msgid "config remote shorthand cannot begin with '/': %s"
-msgstr "konfigurerad kortform för fjärr kan inte börja med \"/\": %s"
+msgstr "konfigurerad kortform för fjärr kan inte börja med ”/”: %s"
 
 msgid "more than one receivepack given, using the first"
 msgstr "mer än en receivepack angavs, använder den första"
@@ -18811,11 +19199,11 @@
 
 #, c-format
 msgid "unrecognized value transfer.credentialsInUrl: '%s'"
-msgstr "okänt värde transfer.credentialsInUrl: \"%s\""
+msgstr "okänt värde transfer.credentialsInUrl: ”%s”"
 
 #, c-format
 msgid "URL '%s' uses plaintext credentials"
-msgstr "URL \"%s\" använder inloggningsuppgifter i klartext"
+msgstr "URL ”%s” använder inloggningsuppgifter i klartext"
 
 #, c-format
 msgid "Cannot fetch both %s and %s to %s"
@@ -18831,11 +19219,11 @@
 
 #, c-format
 msgid "key '%s' of pattern had no '*'"
-msgstr "nyckeln \"%s\" i mönstret innehåller ingen \"*\""
+msgstr "nyckeln ”%s” i mönstret innehåller ingen ”*”"
 
 #, c-format
 msgid "value '%s' of pattern has no '*'"
-msgstr "värdet \"%s\" i mönstret innehåller ingen \"*\""
+msgstr "värdet ”%s” i mönstret innehåller ingen ”*”"
 
 #, c-format
 msgid "src refspec %s does not match any"
@@ -18862,12 +19250,12 @@
 "Neither worked, so we gave up. You must fully qualify the ref."
 msgstr ""
 "Målet du angav är inte ett komplett referensamn (dvs.,\n"
-"startar med \"refs/\"). Vi försökte gissa vad du menade genom att:\n"
+"startar med ”refs/”). Vi försökte gissa vad du menade genom att:\n"
 "\n"
-"- Se efter en referens som motsvarar \"%s\" på fjärrsidan.\n"
-"- Se om <källan> som sänds (\"%s\")\n"
-"  är en referens i \"refs/{heads,tags}/\". Om så lägger vi till\n"
-"  motsvarande refs/{heads,tags}/-prefix på fjärrsidan.\n"
+"- Se efter en referens som motsvarar ”%s” på fjärrsidan.\n"
+"- Se om <källan> som sänds (”%s”)\n"
+"  är en referens i ”refs/{heads,tags}/”. Om så lägger vi till\n"
+"  motsvarande ”refs/{heads,tags}/”-prefix på fjärrsidan.\n"
 "\n"
 "Inget av dem fungerade, så vi gav upp. Ange fullständig referens."
 
@@ -18879,7 +19267,7 @@
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett incheckningsobjekt.\n"
 "Var det meningen att skapa en ny gren genom att sända\n"
-"till \"%s:refs/heads/%s\"?"
+"till ”%s:refs/heads/%s”?"
 
 #, c-format
 msgid ""
@@ -18889,7 +19277,7 @@
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett taggobjekt.\n"
 "Var det meningen att skapa en ny tagg genom att sända\n"
-"till \"%s:refs/tags/%s\"?"
+"till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid ""
@@ -18899,7 +19287,7 @@
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett trädobjekt.\n"
 "Var det meningen att tagga ett nytt träd genom att sända\n"
-"till \"%s:refs/tags/%s\"?"
+"till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid ""
@@ -18909,7 +19297,7 @@
 msgstr ""
 "<Källa>-delen av ref.spec-en är ett blobobjekt.\n"
 "Var det meningen att tagga en ny blob genom att sända\n"
-"till \"%s:refs/tags/%s\"?"
+"till ”%s:refs/tags/%s”?"
 
 #, c-format
 msgid "%s cannot be resolved to branch"
@@ -18917,48 +19305,48 @@
 
 #, c-format
 msgid "unable to delete '%s': remote ref does not exist"
-msgstr "kan inte ta bort \"%s\": fjärreferensen finns inte"
+msgstr "kan inte ta bort ”%s”: fjärreferensen finns inte"
 
 #, c-format
 msgid "dst refspec %s matches more than one"
-msgstr "fjärr-referensspecifikationen \"%s\" motsvarar mer än en"
+msgstr "fjärr-referensspecifikationen ”%s” motsvarar mer än en"
 
 #, 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 "fjärr-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"
 
 #, c-format
 msgid "no such branch: '%s'"
-msgstr "okänd gren: \"%s\""
+msgstr "okänd gren: ”%s”"
 
 #, c-format
 msgid "no upstream configured for branch '%s'"
-msgstr "ingen standarduppström angiven för grenen \"%s\""
+msgstr "ingen standarduppström angiven för grenen ”%s”"
 
 #, c-format
 msgid "upstream branch '%s' not stored as a remote-tracking branch"
-msgstr "uppströmsgrenen \"%s\" är inte lagrad som en fjärrspårande gren"
+msgstr "uppströmsgrenen ”%s” är inte lagrad som en fjärrspårande gren"
 
 #, c-format
 msgid "push destination '%s' on remote '%s' has no local tracking branch"
-msgstr "push-målet \"%s\" på fjärren \"%s\" har ingen lokalt spårande gren"
+msgstr "push-målet ”%s” på fjärren ”%s” har ingen lokalt spårande gren"
 
 #, c-format
 msgid "branch '%s' has no remote for pushing"
-msgstr "grenen \"%s\" har ingen fjärr för \"push\""
+msgstr "grenen ”%s” har ingen fjärr för ”push”"
 
 #, c-format
 msgid "push refspecs for '%s' do not include '%s'"
-msgstr "\"push\"-referensspecifikation för \"%s\" innehåller inte \"%s\""
+msgstr "”push”-referensspecifikation för ”%s” innehåller inte ”%s”"
 
 msgid "push has no destination (push.default is 'nothing')"
-msgstr "\"push\" har inget mål (push.default är \"ingenting\")"
+msgstr "”push” har inget mål (push.default är ”ingenting”)"
 
 msgid "cannot resolve 'simple' push to a single destination"
-msgstr "\"enkel push\" motsvarar flera olika mål"
+msgstr "”enkel push” motsvarar flera olika mål"
 
 #, c-format
 msgid "couldn't find remote ref %s"
@@ -18966,47 +19354,47 @@
 
 #, c-format
 msgid "* Ignoring funny ref '%s' locally"
-msgstr "* Ignorerar märklig referens \"%s\" lokalt"
+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"
+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"
+msgstr "  (använd ”git branch --unset-upstream” för att rätta)\n"
 
 #, c-format
 msgid "Your branch is up to date with '%s'.\n"
-msgstr "Din gren är à jour med \"%s\".\n"
+msgstr "Din gren är à jour med ”%s”.\n"
 
 #, c-format
 msgid "Your branch and '%s' refer to different commits.\n"
-msgstr "Din gren och \"%s\" pekar på olika incheckningar.\n"
+msgstr "Din gren och ”%s” pekar på olika incheckningar.\n"
 
 #, c-format
 msgid "  (use \"%s\" for details)\n"
-msgstr "  (använd \"%s\" för detaljer)\n"
+msgstr "  (använd ”%s” för detaljer)\n"
 
 #, 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] "Din gren ligger före \"%s\" med %d incheckning.\n"
-msgstr[1] "Din gren ligger före \"%s\" med %d incheckningar.\n"
+msgstr[0] "Din gren ligger före ”%s” med %d incheckning.\n"
+msgstr[1] "Din gren ligger före ”%s” med %d incheckningar.\n"
 
 msgid "  (use \"git push\" to publish your local commits)\n"
-msgstr "  (använd \"git push\" för att publicera dina lokala incheckningar)\n"
+msgstr "  (använd ”git push” för att publicera dina lokala incheckningar)\n"
 
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
 "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
 msgstr[0] ""
-"Din gren ligger efter \"%s\" med %d incheckning, och kan snabbspolas.\n"
+"Din gren ligger efter ”%s” med %d incheckning, och kan snabbspolas.\n"
 msgstr[1] ""
-"Din gren ligger efter \"%s\" med %d incheckningar, och kan snabbspolas.\n"
+"Din gren ligger efter ”%s” med %d incheckningar, och kan snabbspolas.\n"
 
 msgid "  (use \"git pull\" to update your local branch)\n"
-msgstr "  (använd \"git pull\" för att uppdatera din lokala gren)\n"
+msgstr "  (använd ”git pull” för att uppdatera din lokala gren)\n"
 
 #, c-format
 msgid ""
@@ -19016,22 +19404,23 @@
 "Your branch and '%s' have diverged,\n"
 "and have %d and %d different commits each, respectively.\n"
 msgstr[0] ""
-"Din gren och \"%s\" har divergerat,\n"
+"Din gren och ”%s” har divergerat,\n"
 "och har %d respektive %d olika incheckning.\n"
 msgstr[1] ""
-"Din gren och \"%s\" har divergerat,\n"
+"Din gren och ”%s” har divergerat,\n"
 "och har %d respektive %d olika incheckningar.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (använd \"git pull\" för att slå ihop fjärrgrenen med din egen)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr "  (använd ”git pull” om du vill integrera fjärrgrenen med din egen)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
-msgstr "kan inte tolka förväntat objektnamn \"%s\""
+msgstr "kan inte tolka förväntat objektnamn ”%s”"
 
 #, c-format
 msgid "cannot strip one component off url '%s'"
-msgstr "kan inte ta bort en komponent från url:en \"%s\""
+msgstr "kan inte ta bort en komponent från url:en ”%s”"
 
 #, c-format
 msgid "bad replace ref name: %s"
@@ -19053,59 +19442,55 @@
 
 #, c-format
 msgid "there were errors while writing '%s' (%s)"
-msgstr "fel vid skrivning av \"%s\" (%s)"
+msgstr "fel vid skrivning av ”%s” (%s)"
 
 #, c-format
 msgid "could not parse conflict hunks in '%s'"
-msgstr "kunde inte tolka konflikt-stycket i \"%s\""
+msgstr "kunde inte tolka konflikt-stycket i ”%s”"
 
 #, c-format
 msgid "failed utime() on '%s'"
-msgstr "\"utime()\" misslyckades på \"%s\""
+msgstr "”utime()” misslyckades på ”%s”"
 
 #, c-format
 msgid "writing '%s' failed"
-msgstr "misslyckades skriva \"%s\""
+msgstr "misslyckades skriva ”%s”"
 
 #, c-format
 msgid "Staged '%s' using previous resolution."
-msgstr "Köade \"%s\" med sparad lösning."
+msgstr "Köade ”%s” med sparad lösning."
 
 #, c-format
 msgid "Recorded resolution for '%s'."
-msgstr "Sparade lösning för \"%s\"."
+msgstr "Sparade lösning för ”%s”."
 
 #, c-format
 msgid "Resolved '%s' using previous resolution."
-msgstr "Löste \"%s\" med tidigare lösning."
+msgstr "Löste ”%s” med tidigare lösning."
 
 #, c-format
 msgid "cannot unlink stray '%s'"
-msgstr "kan inte ta bort lös länk \"%s\""
+msgstr "kan inte ta bort lös länk ”%s”"
 
 #, c-format
 msgid "Recorded preimage for '%s'"
-msgstr "Sparade förhandsbild för \"%s\""
+msgstr "Sparade förhandsbild för ”%s”"
 
 #, c-format
 msgid "failed to update conflicted state in '%s'"
-msgstr "misslyckades uppdatera tillstånd för sammanslagningsproblem i \"%s\""
+msgstr "misslyckades uppdatera tillstånd för sammanslagningsproblem i ”%s”"
 
 #, c-format
 msgid "no remembered resolution for '%s'"
-msgstr "inget sparat sammanslagningsresultat för \"%s\""
-
-#, c-format
-msgid "cannot unlink '%s'"
-msgstr "kan inte ta bort länken \"%s\""
+msgstr "inget sparat sammanslagningsresultat för ”%s”"
 
 #, c-format
 msgid "Updated preimage for '%s'"
-msgstr "Uppdaterade förhandsbild för \"%s\""
+msgstr "Uppdaterade förhandsbild för ”%s”"
 
 #, c-format
 msgid "Forgot resolution for '%s'\n"
-msgstr "Glömde lösning för \"%s\"\n"
+msgstr "Glömde lösning för ”%s”\n"
 
 msgid "unable to open rr-cache directory"
 msgstr "kan inte uppdatera katalogen rr-cache"
@@ -19129,21 +19514,25 @@
 
 #, c-format
 msgid "resolve-undo records `%s` which is missing"
-msgstr "resolve-undo registrerar \"%s\" som saknas"
+msgstr "resolve-undo registrerar ”%s” som saknas"
 
 #, c-format
 msgid "could not get commit for ancestry-path argument %s"
-msgstr "kunde inte hämta incheckning för \"ancestry-path\"-argumentet %s"
+msgstr "kunde inte hämta incheckning för ”ancestry-path”-argumentet %s"
 
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<paketfil> stöds inte längre"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "ogiltig flagga ”%s” i --stdin-läge"
+
 msgid "your current branch appears to be broken"
 msgstr "din nuvarande gren verkar vara trasig"
 
 #, c-format
 msgid "your current branch '%s' does not have any commits yet"
-msgstr "din nuvarande gren \"%s\" innehåller ännu inte några incheckningar"
+msgstr "din nuvarande gren ”%s” innehåller ännu inte några incheckningar"
 
 msgid "object filtering requires --objects"
 msgstr "objektfiltrering kräver --objects"
@@ -19157,11 +19546,11 @@
 
 #, c-format
 msgid "'%s' does not exist"
-msgstr "\"%s\" finns inte"
+msgstr "”%s” finns inte"
 
 #, c-format
 msgid "could not switch to '%s'"
-msgstr "kunde inte växla till \"%s\""
+msgstr "kunde inte växla till ”%s”"
 
 msgid "need a working directory"
 msgstr "behöver en arbetskatalog"
@@ -19196,7 +19585,7 @@
 
 #, c-format
 msgid "remote HEAD is not a branch: '%.*s'"
-msgstr "HEAD hos fjärren är inte en gren: \"%.*s\""
+msgstr "HEAD hos fjärren är inte en gren: ”%.*s”"
 
 msgid "failed to get default branch name from remote; using local default"
 msgstr ""
@@ -19223,28 +19612,35 @@
 msgid "only download metadata for the branch that will be checked out"
 msgstr "hämta endast metadata för grenen som skall checkas ut"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<flaggor>] [--] <arkiv> [<kat>]"
+msgid "create repository within 'src' directory"
+msgstr "skapa arkiv inuti katalogen ”src”"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <huvudgren>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enrollering>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
-msgstr "Kan inte härleda arbetsträdsnamn från \"%s\""
+msgstr "Kan inte härleda arbetsträdsnamn från ”%s”"
 
 #, c-format
 msgid "directory '%s' exists already"
-msgstr "katalogen \"%s\" finns redan"
+msgstr "katalogen ”%s” finns redan"
 
 #, c-format
 msgid "failed to get default branch for '%s'"
-msgstr "misslyckades hämta standardgren för \"%s\""
+msgstr "misslyckades hämta standardgren för ”%s”"
 
 #, c-format
 msgid "could not configure remote in '%s'"
-msgstr "kunde inte ställa in fjärr i \"%s\""
+msgstr "kunde inte ställa in fjärr i ”%s”"
 
 #, c-format
 msgid "could not configure '%s'"
-msgstr "kunde inte ställa in \"%s\""
+msgstr "kunde inte ställa in ”%s”"
 
 msgid "partial clone failed; attempting full clone"
 msgstr "delvis klon misslyckades; försöker med fullständig klon"
@@ -19256,7 +19652,7 @@
 msgstr "scalar diagnose [<enrollering>]"
 
 msgid "`scalar list` does not take arguments"
-msgstr "\"scalar list\" tar inte argument"
+msgstr "”scalar list” tar inte argument"
 
 msgid "scalar register [<enlistment>]"
 msgstr "scalar register [<enrollering>]"
@@ -19272,15 +19668,31 @@
 
 #, c-format
 msgid "could not remove stale scalar.repo '%s'"
-msgstr "kunde inte ta bort gammal scalar.repo \"%s\""
+msgstr "kunde inte ta bort gammal scalar.repo ”%s”"
 
 #, c-format
-msgid "removing stale scalar.repo '%s'"
-msgstr "tar bort gammal scalar.repo \"%s\""
+msgid "removed stale scalar.repo '%s'"
+msgstr "tog bort gammal scalar.repo ”%s”"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "git-arkiv försvunnet i \"%s\""
+msgid "repository at '%s' has different owner"
+msgstr "arkivet ”%s” har en annan ägare"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "arkivet ”%s” har ett formatproblem"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "arkivet hittades inte i ”%s”"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"för att avregistrera arkivet från Scalar, kör\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19291,7 +19703,7 @@
 
 #, c-format
 msgid "no such task: '%s'"
-msgstr "okänd uppgift: \"%s\""
+msgstr "okänd uppgift: ”%s”"
 
 msgid "scalar unregister [<enlistment>]"
 msgstr "scalar unregister [<enrollering>]"
@@ -19316,7 +19728,7 @@
 
 #, c-format
 msgid "could not change to '%s'"
-msgstr "kunde inte byta till \"%s\""
+msgstr "kunde inte byta till ”%s”"
 
 msgid "-c requires a <key>=<value> argument"
 msgstr "-c kräver ett argument på formen <nyckel>=<värde>"
@@ -19371,11 +19783,11 @@
 
 #, c-format
 msgid "invalid commit message cleanup mode '%s'"
-msgstr "felaktigt incheckningsmeddelandestädningsläge \"%s\""
+msgstr "felaktigt incheckningsmeddelandestädningsläge ”%s”"
 
 #, c-format
 msgid "could not delete '%s'"
-msgstr "kunde inte ta bort \"%s\""
+msgstr "kunde inte ta bort ”%s”"
 
 msgid "revert"
 msgstr "revert"
@@ -19395,7 +19807,7 @@
 "with 'git add <paths>' or 'git rm <paths>'"
 msgstr ""
 "efter att ha löst konflikterna, markera de rättade sökvägarna\n"
-"med \"git add <sökvägar>\" eller \"git rm <sökvägar>\""
+"med ”git add <sökvägar>” eller ”git rm <sökvägar>”"
 
 msgid ""
 "After resolving the conflicts, mark them with\n"
@@ -19406,11 +19818,11 @@
 "run \"git cherry-pick --abort\"."
 msgstr ""
 "Efter att ha löst konflikterna, märk dem med\n"
-"\"git add/rm <sökvägsangivelse>\" och kör sedan\n"
-"\"git cherry-pick --continue\".\n"
-"Du kan hoppa över incheckningen istället med \"git cherry-pick --skip\"\n"
-"För att avbryta och återgå till där du var före \"git cherry-pick\",\n"
-"kör \"git cherry-pick --abort\"."
+"”git add/rm <sökvägsangivelse>” och kör sedan\n"
+"”git cherry-pick --continue”.\n"
+"Du kan hoppa över incheckningen istället med ”git cherry-pick --skip”\n"
+"För att avbryta och återgå till där du var före ”git cherry-pick”,\n"
+"kör ”git cherry-pick --abort”."
 
 msgid ""
 "After resolving the conflicts, mark them with\n"
@@ -19421,34 +19833,30 @@
 "run \"git revert --abort\"."
 msgstr ""
 "Efter att ha löst konflikterna, märk dem med\n"
-"\"git add/rm <sökvägsangivelse>\" och kör sedan\n"
-"\"git revert --continue\".\n"
-"Du kan hoppa över incheckningen istället med \"git revert --skip\"\n"
-"För att avbryta och återgå till där du var före \"git revert\",\n"
-"kör \"git revert --abort\"."
+"”git add/rm <sökvägsangivelse>” och kör sedan\n"
+"”git revert --continue”.\n"
+"Du kan hoppa över incheckningen istället med ”git revert --skip”\n"
+"För att avbryta och återgå till där du var före ”git revert”,\n"
+"kör ”git revert --abort”."
 
 #, c-format
 msgid "could not lock '%s'"
-msgstr "kunde inte låsa \"%s\""
-
-#, c-format
-msgid "could not write to '%s'"
-msgstr "kunde inte skriva till \"%s\""
+msgstr "kunde inte låsa ”%s”"
 
 #, c-format
 msgid "could not write eol to '%s'"
-msgstr "kunde inte skriva radslut till \"%s\""
+msgstr "kunde inte skriva radslut till ”%s”"
 
 #, c-format
 msgid "failed to finalize '%s'"
-msgstr "misslyckades färdigställa \"%s\""
+msgstr "misslyckades färdigställa ”%s”"
 
 #, c-format
 msgid "your local changes would be overwritten by %s."
 msgstr "dina lokala ändringar skulle skrivas över av %s."
 
 msgid "commit your changes or stash them to proceed."
-msgstr "checka in dina ändringar eller använd \"stash\" för att fortsätta."
+msgstr "checka in dina ändringar eller använd ”stash” för att fortsätta."
 
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
@@ -19465,33 +19873,33 @@
 
 #, c-format
 msgid "no key present in '%.*s'"
-msgstr "ingen nyckel i  \"%.*s\""
+msgstr "ingen nyckel i  ”%.*s”"
 
 #, c-format
 msgid "unable to dequote value of '%s'"
-msgstr "kan inte ta bort citering av värdet \"%s\""
+msgstr "kan inte ta bort citering av värdet ”%s”"
 
 msgid "'GIT_AUTHOR_NAME' already given"
-msgstr "\"GIT_AUTHOR_NAME\" har redan angivits"
+msgstr "”GIT_AUTHOR_NAME” har redan angivits"
 
 msgid "'GIT_AUTHOR_EMAIL' already given"
-msgstr "\"GIT_AUTHOR_EMAIL\" har redan angivits"
+msgstr "”GIT_AUTHOR_EMAIL” har redan angivits"
 
 msgid "'GIT_AUTHOR_DATE' already given"
-msgstr "\"GIT_AUTHOR_DATE\" har redan angivits"
+msgstr "”GIT_AUTHOR_DATE” har redan angivits"
 
 #, c-format
 msgid "unknown variable '%s'"
-msgstr "okänd variabel \"%s\""
+msgstr "okänd variabel ”%s”"
 
 msgid "missing 'GIT_AUTHOR_NAME'"
-msgstr "\"GIT_AUTHOR_NAME\" saknas"
+msgstr "”GIT_AUTHOR_NAME” saknas"
 
 msgid "missing 'GIT_AUTHOR_EMAIL'"
-msgstr "\"GIT_AUTHOR_EMAIL\" saknas"
+msgstr "”GIT_AUTHOR_EMAIL” saknas"
 
 msgid "missing 'GIT_AUTHOR_DATE'"
-msgstr "\"GIT_AUTHOR_DATE\" saknas"
+msgstr "”GIT_AUTHOR_DATE” saknas"
 
 #, c-format
 msgid ""
@@ -19522,7 +19930,7 @@
 "  git rebase --continue\n"
 
 msgid "'prepare-commit-msg' hook failed"
-msgstr "kroken \"prepare-commit-msg\" misslyckades"
+msgstr "kroken ”prepare-commit-msg” misslyckades"
 
 msgid ""
 "Your name and email address were configured automatically based\n"
@@ -19601,11 +20009,11 @@
 
 #, c-format
 msgid "unable to read commit message from '%s'"
-msgstr "kunde inte läsa incheckningsmeddelande från \"%s\""
+msgstr "kunde inte läsa incheckningsmeddelande från ”%s”"
 
 #, c-format
 msgid "invalid author identity '%s'"
-msgstr "ogiltig författar-identitet \"%s\""
+msgstr "ogiltig författar-identitet ”%s”"
 
 msgid "corrupt author: missing date information"
 msgstr "trasig författare: saknar datuminformation"
@@ -19646,7 +20054,7 @@
 
 #, c-format
 msgid "cannot write '%s'"
-msgstr "kan inte skriva \"%s\""
+msgstr "kan inte skriva ”%s”"
 
 msgid "need a HEAD to fixup"
 msgstr "behöver en HEAD-incheckning att rätta"
@@ -19665,7 +20073,7 @@
 msgstr "din indexfil har inte slagits ihop."
 
 msgid "cannot fixup root commit"
-msgstr "kan inte göra \"fixup\" på rotincheckning"
+msgstr "kan inte göra ”fixup” på rotincheckning"
 
 #, c-format
 msgid "commit %s is a merge but no -m option was given."
@@ -19686,10 +20094,6 @@
 msgstr "%s: kan inte tolka föräldraincheckningen %s"
 
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "kunde inte byta namn på \"%s\" till \"%s\""
-
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "kunde inte ångra %s... %s"
 
@@ -19711,11 +20115,11 @@
 
 #, c-format
 msgid "'%s' is not a valid label"
-msgstr "\"%s\" är inte en giltig etikett"
+msgstr "”%s” är inte en giltig etikett"
 
 #, c-format
 msgid "'%s' is not a valid refname"
-msgstr "\"%s\" är inte ett giltigt referensnamn"
+msgstr "”%s” är inte ett giltigt referensnamn"
 
 #, c-format
 msgid "update-ref requires a fully qualified refname e.g. refs/heads/%s"
@@ -19723,11 +20127,11 @@
 
 #, c-format
 msgid "invalid command '%.*s'"
-msgstr "ogiltigt kommando \"%.*s\""
+msgstr "ogiltigt kommando ”%.*s”"
 
 #, c-format
 msgid "%s does not accept arguments: '%s'"
-msgstr "%s tar inte argument: \"%s\""
+msgstr "%s tar inte argument: ”%s”"
 
 #, c-format
 msgid "missing arguments for %s"
@@ -19735,7 +20139,7 @@
 
 #, c-format
 msgid "could not parse '%s'"
-msgstr "kunde inte tolka \"%s\""
+msgstr "kunde inte tolka ”%s”"
 
 #, c-format
 msgid "invalid line %d: %.*s"
@@ -19743,63 +20147,60 @@
 
 #, c-format
 msgid "cannot '%s' without a previous commit"
-msgstr "kan inte utföra \"%s\" utan en föregående incheckning"
+msgstr "kan inte utföra ”%s” utan en föregående incheckning"
 
 msgid "cancelling a cherry picking in progress"
-msgstr "avbryter pågående \"cherry-pick\""
+msgstr "avbryter pågående ”cherry-pick”"
 
 msgid "cancelling a revert in progress"
-msgstr "avbryter pågående \"revert\""
+msgstr "avbryter pågående ”revert”"
 
 msgid "please fix this using 'git rebase --edit-todo'."
-msgstr "rätta det med \"git rebase --edit-todo\"."
+msgstr "rätta det med ”git rebase --edit-todo”."
 
 #, c-format
 msgid "unusable instruction sheet: '%s'"
-msgstr "oanvändbart manus: \"%s\""
+msgstr "oanvändbart manus: ”%s”"
 
 msgid "no commits parsed."
 msgstr "inga incheckningar lästes."
 
 msgid "cannot cherry-pick during a revert."
-msgstr "kan inte utföra \"cherry-pick\" under en \"revert\"."
+msgstr "kan inte utföra ”cherry-pick” under en ”revert”."
 
 msgid "cannot revert during a cherry-pick."
-msgstr "kan inte utföra \"revert\" under en \"cherry-pick\"."
+msgstr "kan inte utföra ”revert” under en ”cherry-pick”."
 
 msgid "unusable squash-onto"
 msgstr "oanvändbar squash-onto"
 
 #, c-format
 msgid "malformed options sheet: '%s'"
-msgstr "trasigt manus: \"%s\""
+msgstr "trasigt manus: ”%s”"
 
 msgid "empty commit set passed"
 msgstr "den angivna uppsättningen incheckningar är tom"
 
 msgid "revert is already in progress"
-msgstr "en \"revert\" pågår redan"
+msgstr "en ”revert” pågår redan"
 
 #, c-format
 msgid "try \"git revert (--continue | %s--abort | --quit)\""
-msgstr "testa \"git revert (--continue | %s--abort | --quit)\""
+msgstr "testa ”git revert (--continue | %s--abort | --quit)”"
 
 msgid "cherry-pick is already in progress"
-msgstr "en \"cherry-pick\" pågår redan"
+msgstr "en ”cherry-pick” pågår redan"
 
 #, c-format
 msgid "try \"git cherry-pick (--continue | %s--abort | --quit)\""
-msgstr "testa \"git cherry-pick (--continue | %s--abort | --quit)\""
+msgstr "testa ”git cherry-pick (--continue | %s--abort | --quit)”"
 
 #, c-format
 msgid "could not create sequencer directory '%s'"
-msgstr "kunde inte skapa \"sequencer\"-katalogen \"%s\""
-
-msgid "could not lock HEAD"
-msgstr "kunde inte låsa HEAD"
+msgstr "kunde inte skapa ”sequencer”-katalogen ”%s”"
 
 msgid "no cherry-pick or revert in progress"
-msgstr "ingen \"cherry-pick\" eller \"revert\" pågår"
+msgstr "ingen ”cherry-pick” eller ”revert” pågår"
 
 msgid "cannot resolve HEAD"
 msgstr "kan inte bestämma HEAD"
@@ -19809,14 +20210,14 @@
 
 #, c-format
 msgid "cannot read '%s': %s"
-msgstr "kan inte läsa \"%s\": %s"
+msgstr "kan inte läsa ”%s”: %s"
 
 msgid "unexpected end of file"
 msgstr "oväntat filslut"
 
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
-msgstr "sparad HEAD-fil från före \"cherry-pick\", \"%s\", är trasig"
+msgstr "sparad HEAD-fil från före ”cherry-pick”, ”%s”, är trasig"
 
 msgid "You seem to have moved HEAD. Not rewinding, check your HEAD!"
 msgstr ""
@@ -19824,10 +20225,10 @@
 "Spolar inte tillbaka, kontrollera HEAD!"
 
 msgid "no revert in progress"
-msgstr "ingen \"revers\" pågår"
+msgstr "ingen ”revert” pågår"
 
 msgid "no cherry-pick in progress"
-msgstr "ingen \"cherry-pick\" pågår"
+msgstr "ingen ”cherry-pick” pågår"
 
 msgid "failed to skip the commit"
 msgstr "kunde inte hoppa över incheckningen"
@@ -19841,14 +20242,14 @@
 "try \"git %s --continue\""
 msgstr ""
 "har du redan checkat in?\n"
-"testa \"git %s --continue\""
+"testa ”git %s --continue”"
 
 msgid "cannot read HEAD"
 msgstr "kan inte läsa HEAD"
 
 #, c-format
 msgid "unable to copy '%s' to '%s'"
-msgstr "kan inte kopiera in \"%s\" till \"%s\""
+msgstr "kan inte kopiera in ”%s” till ”%s”"
 
 #, c-format
 msgid ""
@@ -19894,32 +20295,32 @@
 "\tgit rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "och gjorde ändringar till indexet och/eller arbetskatalogen\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "och gjorde ändringar till indexet och/eller arbetskatalogen.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "körningen lyckades: %s\n"
-"men lämnade kvar ändringar i indexet och/eller arbetskatalogen\n"
-"Checka in eller utför \"stash\" på ändringarna och kör sedan\n"
+"men lämnade kvar ändringar i indexet och/eller arbetskatalogen.\n"
+"Checka in eller utför ”stash” på ändringarna och kör sedan\n"
 "\n"
 "\tgit rebase --continue\n"
 "\n"
 
 #, c-format
 msgid "illegal label name: '%.*s'"
-msgstr "ogiltigt etikettnamn: \"%.*s\""
+msgstr "ogiltigt etikettnamn: ”%.*s”"
 
 #, c-format
 msgid "could not resolve '%s'"
-msgstr "kunde inte upplösa \"%s\""
+msgstr "kunde inte upplösa ”%s”"
 
 msgid "writing fake root commit"
 msgstr "skriver fejkad rotincheckning"
@@ -19932,22 +20333,22 @@
 
 #, c-format
 msgid "unable to parse '%.*s'"
-msgstr "kan inte tolka \"%.*s\""
+msgstr "kan inte tolka ”%.*s”"
 
 #, c-format
 msgid "nothing to merge: '%.*s'"
-msgstr "inget att slå samman: \"%.*s\""
+msgstr "inget att slå samman: ”%.*s”"
 
 msgid "octopus merge cannot be executed on top of a [new root]"
-msgstr "\"octopus\"-sammanslagning kan inte köras ovanpå en [ny rot]"
+msgstr "”octopus”-sammanslagning kan inte köras ovanpå en [ny rot]"
 
 #, c-format
 msgid "could not get commit message of '%s'"
-msgstr "kunde inte läsa incheckningsmeddelande för \"%s\""
+msgstr "kunde inte läsa incheckningsmeddelande för ”%s”"
 
 #, c-format
 msgid "could not even attempt to merge '%.*s'"
-msgstr "kunde inte ens försöka slå ihop \"%.*s\""
+msgstr "kunde inte ens försöka slå ihop ”%.*s”"
 
 msgid "merge: Unable to write new index file"
 msgstr "sammanslagning: Kunde inte skriva ny indexfil"
@@ -19955,7 +20356,7 @@
 #, c-format
 msgid ""
 "another 'rebase' process appears to be running; '%s.lock' already exists"
-msgstr "en annan \"rebase\"-process verkar vara aktiv; \"%s.lock\" finns redan"
+msgstr "en annan ”rebase”-process verkar vara aktiv; ”%s.lock” finns redan"
 
 #, c-format
 msgid ""
@@ -19974,22 +20375,22 @@
 "%s"
 
 msgid "Cannot autostash"
-msgstr "Kan inte utföra \"autostash\""
+msgstr "Kan inte utföra ”autostash”"
 
 #, c-format
 msgid "Unexpected stash response: '%s'"
-msgstr "Oväntat svar från stash: \"%s\""
+msgstr "Oväntat svar från stash: ”%s”"
 
 #, c-format
 msgid "Could not create directory for '%s'"
-msgstr "Kunde inte skapa katalog för \"%s\""
+msgstr "Kunde inte skapa katalog för ”%s”"
 
 #, c-format
 msgid "Created autostash: %s\n"
 msgstr "Skapade autostash: %s\n"
 
 msgid "could not reset --hard"
-msgstr "kunde inte utföra \"reset --hard\""
+msgstr "kunde inte utföra ”reset --hard”"
 
 #, c-format
 msgid "Applied autostash.\n"
@@ -20007,7 +20408,7 @@
 msgstr ""
 "%s\n"
 "Dina ändringar är säkra i stashen.\n"
-"Du kan när som helst använda \"git stash pop\" eller \"git stash drop\".\n"
+"Du kan när som helst använda ”git stash pop” eller ”git stash drop”.\n"
 
 msgid "Applying autostash resulted in conflicts."
 msgstr "Tillämpning av autostash gav konflikter."
@@ -20015,6 +20416,9 @@
 msgid "Autostash exists; creating a new stash entry."
 msgstr "Autostash finns; skapar ny stash-post."
 
+msgid "autostash reference is a symref"
+msgstr "autostash-referensen är en symbolisk referens"
+
 msgid "could not detach HEAD"
 msgstr "kunde inte koppla från HEAD"
 
@@ -20047,14 +20451,14 @@
 "    git rebase --continue\n"
 
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Ombaserar (%d/%d)%s"
-
-#, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "Stoppade på %s... %.*s\n"
 
 #, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Ombaserar (%d/%d)%s"
+
+#, c-format
 msgid "unknown command %d"
 msgstr "okänt kommando %d"
 
@@ -20062,7 +20466,7 @@
 msgstr "kunde inte läsa orig-head"
 
 msgid "could not read 'onto'"
-msgstr "kunde inte läsa \"onto\""
+msgstr "kunde inte läsa ”onto”"
 
 #, c-format
 msgid "could not update HEAD to %s"
@@ -20080,11 +20484,11 @@
 
 #, c-format
 msgid "invalid file: '%s'"
-msgstr "ogiltig fil: \"%s\""
+msgstr "ogiltig fil: ”%s”"
 
 #, c-format
 msgid "invalid contents: '%s'"
-msgstr "ogiltigt innehåll: \"%s\""
+msgstr "ogiltigt innehåll: ”%s”"
 
 msgid ""
 "\n"
@@ -20093,11 +20497,11 @@
 msgstr ""
 "\n"
 "Du har ändringar i arbetskatalogen som inte checkats in. Checka in dem\n"
-"först och kör sedan \"git rebase --continue\" igen."
+"först och kör sedan ”git rebase --continue” igen."
 
 #, c-format
 msgid "could not write file: '%s'"
-msgstr "kunde inte skriva fil: \"%s\""
+msgstr "kunde inte skriva fil: ”%s”"
 
 msgid "could not remove CHERRY_PICK_HEAD"
 msgstr "kunde inte ta bort CHERRY_PICK_HEAD"
@@ -20107,7 +20511,7 @@
 
 #, c-format
 msgid "%s: can't cherry-pick a %s"
-msgstr "%s: kan inte göra \"cherry-pick\" på typen \"%s\""
+msgstr "%s: kan inte göra ”cherry-pick” på typen ”%s”"
 
 #, c-format
 msgid "%s: bad revision"
@@ -20133,18 +20537,18 @@
 msgstr "inget att göra"
 
 msgid "could not skip unnecessary pick commands"
-msgstr "kunde inte hoppa över onödiga \"pick\"-kommandon"
+msgstr "kunde inte hoppa över onödiga ”pick”-kommandon"
 
 msgid "the script was already rearranged."
 msgstr "skriptet har redan omordnats."
 
 #, c-format
 msgid "update-refs file at '%s' is invalid"
-msgstr "update-refs-filen vid \"%s\" är ogiltig"
+msgstr "update-refs-filen vid ”%s” är ogiltig"
 
 #, c-format
 msgid "'%s' is outside repository at '%s'"
-msgstr "\"%s\" är utanför arkivet på \"%s\""
+msgstr "”%s” är utanför arkivet på ”%s”"
 
 #, c-format
 msgid ""
@@ -20152,7 +20556,7 @@
 "Use 'git <command> -- <path>...' to specify paths that do not exist locally."
 msgstr ""
 "%s: sökvägen finns inte i arbetskatalogen.\n"
-"Använd \"git <kommando> -- <sökväg>..\" för att ange sökvägar som inte finns "
+"Använd ”git <kommando> -- <sökväg>..” för att ange sökvägar som inte finns "
 "lokalt."
 
 #, c-format
@@ -20161,14 +20565,14 @@
 "Use '--' to separate paths from revisions, like this:\n"
 "'git <command> [<revision>...] -- [<file>...]'"
 msgstr ""
-"tvetydigt argument \"%s\": okänd revision eller sökväg inte i "
+"tvetydigt argument ”%s”: okänd revision eller sökväg inte i "
 "arbetskatalogen.\n"
-"Använd \"--\" för att skilja sökvägar från revisioner, så här:\n"
-"\"git <kommando> [<revision>...] -- [<fil>...]\""
+"Använd ”--” för att skilja sökvägar från revisioner, så här:\n"
+"”git <kommando> [<revision>...] -- [<fil>...]”"
 
 #, c-format
 msgid "option '%s' must come before non-option arguments"
-msgstr "flaggan \"%s\" måste anges före argument som inte är flaggor"
+msgstr "flaggan ”%s” måste anges före argument som inte är flaggor"
 
 #, c-format
 msgid ""
@@ -20176,9 +20580,9 @@
 "Use '--' to separate paths from revisions, like this:\n"
 "'git <command> [<revision>...] -- [<file>...]'"
 msgstr ""
-"tvetydigt argument \"%s\": både revision och filnamn\n"
-"Använd \"--\" för att skilja sökvägar från revisioner, så här:\n"
-"\"git <kommando> [<revision>...] -- [<fil>...]\""
+"tvetydigt argument ”%s”: både revision och filnamn\n"
+"Använd ”--” för att skilja sökvägar från revisioner, så här:\n"
+"”git <kommando> [<revision>...] -- [<fil>...]”"
 
 msgid "unable to set up work tree using invalid config"
 msgstr "kan inte skapa arbetskatalog med felaktig konfiguration"
@@ -20199,11 +20603,11 @@
 
 #, c-format
 msgid "error opening '%s'"
-msgstr "fel vid öppning av \"%s\""
+msgstr "fel vid öppning av ”%s”"
 
 #, c-format
 msgid "too large to be a .git file: '%s'"
-msgstr "för stor för att vara en .git-fil: \"%s\""
+msgstr "för stor för att vara en .git-fil: ”%s”"
 
 #, c-format
 msgid "error reading %s"
@@ -20223,29 +20627,29 @@
 
 #, c-format
 msgid "'$%s' too big"
-msgstr "\"$%s\" för stor"
+msgstr "”$%s” för stor"
 
 #, c-format
 msgid "not a git repository: '%s'"
-msgstr "inte ett git-arkiv: \"%s\""
+msgstr "inte ett git-arkiv: ”%s”"
 
 #, c-format
 msgid "cannot chdir to '%s'"
-msgstr "kan inte byta katalog (chdir) till \"%s\""
+msgstr "kan inte byta katalog (chdir) till ”%s”"
 
 msgid "cannot come back to cwd"
 msgstr "kan inte gå tillbaka till arbetskatalogen (cwd)"
 
 #, c-format
 msgid "failed to stat '%*s%s%s'"
-msgstr "misslyckades ta status på \"%*ss%s%s\""
+msgstr "misslyckades ta status på ”%*ss%s%s”"
 
 msgid "Unable to read current working directory"
 msgstr "Kan inte läsa aktuell arbetskatalog"
 
 #, c-format
 msgid "cannot change to '%s'"
-msgstr "kan inte byta till \"%s\""
+msgstr "kan inte byta till ”%s”"
 
 #, c-format
 msgid "not a git repository (or any of the parent directories): %s"
@@ -20267,14 +20671,14 @@
 "\n"
 "\tgit config --global --add safe.directory %s"
 msgstr ""
-"upptäckte tveksamt ägarskap i arkivet i \"%s\"\n"
+"upptäckte tveksamt ägarskap i arkivet i ”%s”\n"
 "%sFör att lägga till ett undantag för denna katalog, kör:\n"
 "\n"
 "\tgit config --global --add safe.directory %s"
 
 #, c-format
 msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
-msgstr "kan inte använda naket arkiv \"%s\" (safe.bareRepository är \"%s\")"
+msgstr "kan inte använda naket arkiv ”%s” (safe.bareRepository är ”%s”)"
 
 #, c-format
 msgid ""
@@ -20285,10 +20689,85 @@
 "Ägaren av filerna måste alltid ha läs- och skrivbehörighet."
 
 msgid "fork failed"
-msgstr "\"fork\" misslyckades"
+msgstr "”fork” misslyckades"
 
 msgid "setsid failed"
-msgstr "\"setsid\" misslyckades"
+msgstr "”setsid” misslyckades"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "kan inte ta status på mallen ”%s”"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kan inte öppna katalogen (opendir) ”%s”"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kan inte läsa länk (readlink) ”%s”"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kan inte skapa symbolisk länk ”%s” ”%s”"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kan inte kopiera ”%s” till ”%s”"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignorerar mallen %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "mallarna hittades inte i %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "kopierade inte mallar från ”%s”: %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "ogiltigt namn på första gren: ”%s”"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: ignorerade --initial-branch=%s"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kan inte hantera filtyp %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "kan inte flytta %s till %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "försöker initiera arkivet på nytt med annan hash"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "försöker initiera arkivet på nytt med annat referenslagringsformat"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s finns redan"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Ominitierade befintligt delat Git-arkiv i %s%s\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Ominitierade befintligt Git-arkiv i %s%s\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Initierade tomt delat Git-arkiv i %s%s\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Initierade tomt Git-arkiv i %s%s\n"
 
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -20342,10 +20821,6 @@
 msgstr[1] "%u bytes/s"
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr "kunde inte redigera \"%s\""
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "ignorerar misstänkt undermodulnamn: %s"
 
@@ -20354,7 +20829,7 @@
 
 #, c-format
 msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
-msgstr "ignorerar \"%s\" som kan tolkas som en kommandoradsflagga: %s"
+msgstr "ignorerar ”%s” som kan tolkas som en kommandoradsflagga: %s"
 
 #, c-format
 msgid "Could not update .gitmodules entry %s"
@@ -20378,11 +20853,11 @@
 
 #, c-format
 msgid "in unpopulated submodule '%s'"
-msgstr "i ej utcheckad undermodul \"%s\""
+msgstr "i ej utcheckad undermodul ”%s”"
 
 #, c-format
 msgid "Pathspec '%s' is in submodule '%.*s'"
-msgstr "Sökvägsangivelsen \"%s\" är i undermodulen \"%.*s\""
+msgstr "Sökvägsangivelsen ”%s” är i undermodulen ”%.*s”"
 
 #, c-format
 msgid "bad --ignore-submodules argument: %s"
@@ -20393,32 +20868,32 @@
 "Submodule in commit %s at path: '%s' collides with a submodule named the "
 "same. Skipping it."
 msgstr ""
-"Undermodulen i incheckning %s på sökvägen: \"%s\" krockar med en undermodul "
+"Undermodulen i incheckning %s på sökvägen: ”%s” krockar med en undermodul "
 "med samma namn. Hoppar över den."
 
 #, c-format
 msgid "submodule entry '%s' (%s) is a %s, not a commit"
-msgstr "undermodulposten \"%s\" (%s) är en %s, inte en incheckning"
+msgstr "undermodulposten ”%s” (%s) är en %s, inte en incheckning"
 
 #, c-format
 msgid ""
 "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 "
-"undermodulen \"%s\""
+"kunde inte köra ”git rev-list <incheckningar> --not --remotes -n 1” i "
+"undermodulen ”%s”"
 
 #, c-format
 msgid "process for submodule '%s' failed"
-msgstr "process för undermodulen \"%s\" misslyckades"
+msgstr "process för undermodulen ”%s” misslyckades"
 
 #, c-format
 msgid "Pushing submodule '%s'\n"
-msgstr "Sänder undermodulen \"%s\"\n"
+msgstr "Sänder undermodulen ”%s”\n"
 
 #, c-format
 msgid "Unable to push submodule '%s'\n"
-msgstr "Kunde inte sända undermodulen \"%s\"\n"
+msgstr "Kunde inte sända undermodulen ”%s”\n"
 
 #, c-format
 msgid "Fetching submodule %s%s\n"
@@ -20426,11 +20901,11 @@
 
 #, c-format
 msgid "Could not access submodule '%s'\n"
-msgstr "Kunde inte komma åt undermodulen \"%s\"\n"
+msgstr "Kunde inte komma åt undermodulen ”%s”\n"
 
 #, c-format
 msgid "Could not access submodule '%s' at commit %s\n"
-msgstr "Kunde inte komma åt undermodulen \"%s\" vid incheckningen %s\n"
+msgstr "Kunde inte komma åt undermodulen ”%s” vid incheckningen %s\n"
 
 #, c-format
 msgid "Fetching submodule %s%s at commit %s\n"
@@ -20446,61 +20921,61 @@
 
 #, c-format
 msgid "'%s' not recognized as a git repository"
-msgstr "\"%s\" känns inte igen som ett git-arkiv"
+msgstr "”%s” känns inte igen som ett git-arkiv"
 
 #, c-format
 msgid "Could not run 'git status --porcelain=2' in submodule %s"
-msgstr "Kunde inte köra \"git status --porcelain=2\" i undermodulen \"%s\""
+msgstr "Kunde inte köra ”git status --porcelain=2” i undermodulen ”%s”"
 
 #, c-format
 msgid "'git status --porcelain=2' failed in submodule %s"
-msgstr "\"git status --porcelain=2\" misslyckades i undermodulen \"%s\""
+msgstr "”git status --porcelain=2” misslyckades i undermodulen ”%s”"
 
 #, c-format
 msgid "could not start 'git status' in submodule '%s'"
-msgstr "kunde inte starta \"git status\" i undermodulen \"%s\""
+msgstr "kunde inte starta ”git status” i undermodulen ”%s”"
 
 #, c-format
 msgid "could not run 'git status' in submodule '%s'"
-msgstr "kunde inte köra \"git status\" i undermodulen \"%s\""
+msgstr "kunde inte köra ”git status” i undermodulen ”%s”"
 
 #, c-format
 msgid "Could not unset core.worktree setting in submodule '%s'"
-msgstr "Kunde inte ta bort inställningen core.worktree i undermodulen \"%s\""
+msgstr "Kunde inte ta bort inställningen core.worktree i undermodulen ”%s”"
 
 #, c-format
 msgid "could not recurse into submodule '%s'"
-msgstr "kunde inte rekursera in i undermodulen \"%s\""
+msgstr "kunde inte rekursera in i undermodulen ”%s”"
 
 msgid "could not reset submodule index"
 msgstr "kunde inte återställa indexet i undermodul"
 
 #, c-format
 msgid "submodule '%s' has dirty index"
-msgstr "undermodulen \"%s\" har ett smutsigt index"
+msgstr "undermodulen ”%s” har ett smutsigt index"
 
 #, c-format
 msgid "Submodule '%s' could not be updated."
-msgstr "Undermoduler \"%s\" kunde inte uppdateras."
+msgstr "Undermoduler ”%s” kunde inte uppdateras."
 
 #, c-format
 msgid "submodule git dir '%s' is inside git dir '%.*s'"
-msgstr "undermodul-gitkatalogen \"%s\" är inuti gitkatalogen \"%.*s\""
+msgstr "undermodul-gitkatalogen ”%s” är inuti gitkatalogen ”%.*s”"
 
 #, c-format
 msgid ""
 "relocate_gitdir for submodule '%s' with more than one worktree not supported"
 msgstr ""
-"relocate_gitdir för undermodulen \"%s\", som har mer än en arbetskatalog, "
+"relocate_gitdir för undermodulen ”%s”, som har mer än en arbetskatalog, "
 "stöds ej"
 
 #, c-format
 msgid "could not lookup name for submodule '%s'"
-msgstr "kunde inte slå upp namnet för undermodulen \"%s\""
+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"
+msgstr "vägrar flytta ”%s” till en befintlig gitkatalog"
 
 #, c-format
 msgid ""
@@ -20508,9 +20983,9 @@
 "'%s' to\n"
 "'%s'\n"
 msgstr ""
-"Migrerar git-katalogen för \"%s%s\" från\n"
-"\"%s\" till\n"
-"\"%s\"\n"
+"Migrerar git-katalogen för ”%s%s” från\n"
+"”%s” till\n"
+"”%s”\n"
 
 msgid "could not start ls-files in .."
 msgstr "kunde inte starta ls-files i .."
@@ -20521,14 +20996,14 @@
 
 #, c-format
 msgid "failed to lstat '%s'"
-msgstr "misslyckades ta status (lstat) på \"%s\""
+msgstr "misslyckades ta status (lstat) på ”%s”"
 
 msgid "no remote configured to get bundle URIs from"
 msgstr "ingen fjärr att hämta bunt-URI:er från inställd"
 
 #, c-format
 msgid "remote '%s' has no configured URL"
-msgstr "fjärren \"%s\" har ingen URL konfigurerad"
+msgstr "fjärren ”%s” har ingen URL konfigurerad"
 
 msgid "could not get the bundle-uri list"
 msgstr "kunde inte hämta bundle-uri-listan"
@@ -20542,12 +21017,6 @@
 msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr "antal poster i cacheträdet att ogiltigförklara (förval är 0)"
 
-msgid "unhandled options"
-msgstr "flaggor som inte hanterats"
-
-msgid "error preparing revisions"
-msgstr "fel när revisioner skulle förberedas"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "incheckning %s är inte märkt nåbar"
@@ -20619,19 +21088,19 @@
 
 #, c-format
 msgid "running trailer command '%s' failed"
-msgstr "misslyckades utföra släpradskommandot \"%s\""
+msgstr "misslyckades utföra släpradskommandot ”%s”"
 
 #, c-format
 msgid "unknown value '%s' for key '%s'"
-msgstr "okänt värde \"%s\" för nyckeln \"%s\""
+msgstr "okänt värde ”%s” för nyckeln ”%s”"
 
 #, c-format
 msgid "empty trailer token in trailer '%.*s'"
-msgstr "tom släpradssymbol i släpraden \"%.*s\""
+msgstr "tom släpradssymbol i släpraden ”%.*s”"
 
 #, c-format
 msgid "could not read input file '%s'"
-msgstr "kunde inte läsa indatafilen \"%s\""
+msgstr "kunde inte läsa indatafilen ”%s”"
 
 #, c-format
 msgid "could not stat %s"
@@ -20657,7 +21126,7 @@
 
 #, c-format
 msgid "unable to find remote helper for '%s'"
-msgstr "kan inte hitta fjärrhjälpare för \"%s\""
+msgstr "kan inte hitta fjärrhjälpare för ”%s”"
 
 msgid "can't dup helper output fd"
 msgstr "kunde inte duplicera utdata-filhandtag"
@@ -20677,7 +21146,7 @@
 
 #, c-format
 msgid "%s unexpectedly said: '%s'"
-msgstr "%s sade oväntat: \"%s\""
+msgstr "%s sade oväntat: ”%s”"
 
 #, c-format
 msgid "%s also locked %s"
@@ -20703,9 +21172,6 @@
 msgid "invalid remote service path"
 msgstr "felaktig sökväg till fjärrtjänst"
 
-msgid "operation not supported by protocol"
-msgstr "funktionen stöds inte av protokollet"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "kan inte ansluta till undertjänsten %s"
@@ -20714,11 +21180,11 @@
 msgstr "--negotiate-only kräver protokoll v2"
 
 msgid "'option' without a matching 'ok/error' directive"
-msgstr "\"option\" utan mostsvarande \"ok/error\"-direktiv"
+msgstr "”option” utan mostsvarande ”ok/error”-direktiv"
 
 #, c-format
 msgid "expected ok/error, helper said '%s'"
-msgstr "förväntade ok/error, hjälpprogrammet svarade \"%s\""
+msgstr "förväntade ok/error, hjälpprogrammet svarade ”%s”"
 
 #, c-format
 msgid "helper reported unexpected status of %s"
@@ -20746,14 +21212,14 @@
 
 #, c-format
 msgid "helper %s does not support 'push-option'"
-msgstr "hjälparen %s stöder inte \"push-option\""
+msgstr "hjälparen %s stöder inte ”push-option”"
 
 msgid "remote-helper doesn't support push; refspec needed"
 msgstr "fjärrhjälparen stöder inte push; referensspecifikation krävs"
 
 #, c-format
 msgid "helper %s does not support 'force'"
-msgstr "hjälparen %s stöder inte \"force\""
+msgstr "hjälparen %s stöder inte ”force”"
 
 msgid "couldn't run fast-export"
 msgstr "kunde inte köra fast-export"
@@ -20771,7 +21237,7 @@
 
 #, c-format
 msgid "unsupported object format '%s'"
-msgstr "objektformatet \"%s\" stöds ej"
+msgstr "objektformatet ”%s” stöds ej"
 
 #, c-format
 msgid "malformed response in ref list: %s"
@@ -20810,18 +21276,18 @@
 
 #, c-format
 msgid "Would set upstream of '%s' to '%s' of '%s'\n"
-msgstr "Skulle sätta uppströms för \"%s\" till \"%s\" från \"%s\"\n"
+msgstr "Skulle sätta uppströms för ”%s” till ”%s” från ”%s”\n"
 
 #, c-format
 msgid "could not read bundle '%s'"
-msgstr "kunde inte läsa bunten \"%s\""
+msgstr "kunde inte läsa bunten ”%s”"
 
 #, c-format
 msgid "transport: invalid depth option '%s'"
-msgstr "transport: ogiltig flagga för depth: \"%s\""
+msgstr "transport: ogiltig flagga för depth: ”%s”"
 
 msgid "see protocol.version in 'git help config' for more details"
-msgstr "se protocol.version i \"git help config\" för mer information"
+msgstr "se protocol.version i ”git help config” för mer information"
 
 msgid "server options require protocol version 2 or later"
 msgstr "serverflaggor kräver protokollversion 2 eller senare"
@@ -20836,12 +21302,8 @@
 msgstr "stöd för protokoll v2 ännu ej implementerat"
 
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "okänt värde för inställningen \"%s\": %s"
-
-#, c-format
 msgid "transport '%s' not allowed"
-msgstr "transporten \"%s\" tillåts inte"
+msgstr "transporten ”%s” tillåts inte"
 
 msgid "git-over-rsync is no longer supported"
 msgstr "git-over-rsync stöds inte längre"
@@ -20892,6 +21354,9 @@
 msgid "could not retrieve server-advertised bundle-uri list"
 msgstr "kunde inte hämta bundle-uri-listan som servern annonserade"
 
+msgid "operation not supported by protocol"
+msgstr "funktionen stöds inte av protokollet"
+
 msgid "too-short tree object"
 msgstr "trädobjekt för kort"
 
@@ -20910,7 +21375,7 @@
 "%%sPlease commit your changes or stash them before you switch branches."
 msgstr ""
 "Dina lokala ändringar av följande filer skulle skrivas över av utcheckning:\n"
-"%%sChecka in dina ändringar eller använd \"stash\" innan du byter gren."
+"%%sChecka in dina ändringar eller använd ”stash” innan du byter gren."
 
 #, c-format
 msgid ""
@@ -20927,7 +21392,7 @@
 msgstr ""
 "Dina lokala ändringar av följande filer skulle skrivas över av "
 "sammanslagning:\n"
-"%%sChecka in dina ändringar eller använd \"stash\" innan du byter gren."
+"%%sChecka in dina ändringar eller använd ”stash” innan du byter gren."
 
 #, c-format
 msgid ""
@@ -20943,15 +21408,15 @@
 "Your local changes to the following files would be overwritten by %s:\n"
 "%%sPlease commit your changes or stash them before you %s."
 msgstr ""
-"Dina lokala ändringar av följande filer skulle skrivas över av \"%s\":\n"
-"%%sChecka in dina ändringar eller använd \"stash\" innan du \"%s\"."
+"Dina lokala ändringar av följande filer skulle skrivas över av ”%s”:\n"
+"%%sChecka in dina ändringar eller använd ”stash” innan du ”%s”."
 
 #, c-format
 msgid ""
 "Your local changes to the following files would be overwritten by %s:\n"
 "%%s"
 msgstr ""
-"Dina lokala ändringar av följande filer skulle skrivas över av \"%s\":\n"
+"Dina lokala ändringar av följande filer skulle skrivas över av ”%s”:\n"
 "%%s"
 
 #, c-format
@@ -21010,15 +21475,15 @@
 "The following untracked working tree files would be removed by %s:\n"
 "%%sPlease move or remove them before you %s."
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle tas bort av \"%s\":\n"
-"%%sFlytta eller ta bort dem innan du \"%s\"."
+"Följande ospårade filer i arbetskatalogen skulle tas bort av ”%s”:\n"
+"%%sFlytta eller ta bort dem innan du ”%s”."
 
 #, c-format
 msgid ""
 "The following untracked working tree files would be removed by %s:\n"
 "%%s"
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle tas bort av \"%s\":\n"
+"Följande ospårade filer i arbetskatalogen skulle tas bort av ”%s”:\n"
 "%%s"
 
 #, c-format
@@ -21064,20 +21529,20 @@
 "The following untracked working tree files would be overwritten by %s:\n"
 "%%sPlease move or remove them before you %s."
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle skrivas över av \"%s\":\n"
-"%%sFlytta eller ta bort dem innan du \"%s\"."
+"Följande ospårade filer i arbetskatalogen skulle skrivas över av ”%s”:\n"
+"%%sFlytta eller ta bort dem innan du ”%s”."
 
 #, c-format
 msgid ""
 "The following untracked working tree files would be overwritten by %s:\n"
 "%%s"
 msgstr ""
-"Följande ospårade filer i arbetskatalogen skulle skrivas över av \"%s\":\n"
+"Följande ospårade filer i arbetskatalogen skulle skrivas över av ”%s”:\n"
 "%%s"
 
 #, c-format
 msgid "Entry '%s' overlaps with '%s'.  Cannot bind."
-msgstr "Posten \"%s\" överlappar \"%s\". Kan inte binda."
+msgstr "Posten ”%s” överlappar ”%s”. Kan inte binda."
 
 #, c-format
 msgid ""
@@ -21124,7 +21589,7 @@
 "After fixing the above paths, you may want to run `git sparse-checkout "
 "reapply`.\n"
 msgstr ""
-"Du bör köra \"git sparse-checkout reapply\" efter att ha fixat sökvägarna "
+"Du bör köra ”git sparse-checkout reapply” efter att ha fixat sökvägarna "
 "ovan.\n"
 
 msgid "Updating files"
@@ -21147,20 +21612,20 @@
 msgstr "arbetskatalog och ospårad incheckning har dublettposter: %s"
 
 msgid "expected flush after fetch arguments"
-msgstr "förväntade \"flush\" efter \"fetch\"-argument"
+msgstr "förväntade ”flush” efter ”fetch”-argument"
 
 msgid "invalid URL scheme name or missing '://' suffix"
-msgstr "ogiltig URL-schemanamn eller saknat \"://\"-suffix"
+msgstr "ogiltig URL-schemanamn eller saknat ”://”-suffix"
 
 #, c-format
 msgid "invalid %XX escape sequence"
 msgstr "ogiltig %XX-teckensekvens"
 
 msgid "missing host and scheme is not 'file:'"
-msgstr "värd saknas och schemat är inte \"file:\""
+msgstr "värd saknas och schemat är inte ”file:”"
 
 msgid "a 'file:' URL may not have a port number"
-msgstr "en \"file:\"-URL kan inte innehålla portnummer"
+msgstr "en ”file:”-URL kan inte innehålla portnummer"
 
 msgid "invalid characters in host name"
 msgstr "ogiltiga tecken i värdnamnet"
@@ -21169,7 +21634,7 @@
 msgstr "felaktigt portnummer"
 
 msgid "invalid '..' path segment"
-msgstr "felaktigt \"..\"-sökvägssegment"
+msgstr "felaktigt ”..”-sökvägssegment"
 
 msgid "usage: "
 msgstr "användning: "
@@ -21188,19 +21653,19 @@
 
 #, c-format
 msgid "'%s' at main working tree is not the repository directory"
-msgstr "\"%s\" i huvudarbetskatalogen är inte arkivkatalogen"
+msgstr "”%s” i huvudarbetskatalogen är inte arkivkatalogen"
 
 #, c-format
 msgid "'%s' file does not contain absolute path to the working tree location"
-msgstr "filen \"%s\" innehåller inte absolut sökväg till arbetskatalogen"
+msgstr "filen ”%s” innehåller inte absolut sökväg till arbetskatalogen"
 
 #, c-format
 msgid "'%s' is not a .git file, error code %d"
-msgstr "\"%s\" är inte en .git-fil, felkod %d"
+msgstr "”%s” är inte en .git-fil, felkod %d"
 
 #, c-format
 msgid "'%s' does not point back to '%s'"
-msgstr "\"%s\" pekar inte tillbaka till \"%s\""
+msgstr "”%s” pekar inte tillbaka till ”%s”"
 
 msgid "not a directory"
 msgstr "inte en katalog"
@@ -21254,57 +21719,60 @@
 
 #, c-format
 msgid "unable to set %s in '%s'"
-msgstr "kan inte sätta %s i \"%s\""
+msgstr "kan inte sätta %s i ”%s”"
 
 #, c-format
 msgid "unable to unset %s in '%s'"
-msgstr "kan inte slå av %s i \"%s\""
+msgstr "kan inte slå av %s i ”%s”"
 
 msgid "failed to set extensions.worktreeConfig setting"
 msgstr "misslyckades ändra inställningen extensions.worktreeConfig"
 
 #, c-format
 msgid "could not setenv '%s'"
-msgstr "kunde inte lagra miljövariabeln \"%s\""
+msgstr "kunde inte lagra miljövariabeln ”%s”"
 
 #, c-format
 msgid "unable to create '%s'"
-msgstr "kunde inte skapa \"%s\""
+msgstr "kunde inte skapa ”%s”"
 
 #, c-format
 msgid "could not open '%s' for reading and writing"
-msgstr "kunde inte öppna \"%s\" för läsning och skrivning"
+msgstr "kunde inte öppna ”%s” för läsning och skrivning"
 
 #, c-format
 msgid "unable to access '%s'"
-msgstr "kan inte komma åt \"%s\""
+msgstr "kan inte komma åt ”%s”"
 
 msgid "unable to get current working directory"
 msgstr "kan inte hämta aktuell arbetskatalog"
 
+msgid "unable to get random bytes"
+msgstr "kunde inte hämta slumpdata"
+
 msgid "Unmerged paths:"
 msgstr "Ej sammanslagna sökvägar:"
 
 msgid "  (use \"git restore --staged <file>...\" to unstage)"
-msgstr "  (använd \"git restore --staged <fil>...\" för att ta bort från kö)"
+msgstr "  (använd ”git restore --staged <fil>...” för att ta bort från kö)"
 
 #, c-format
 msgid "  (use \"git restore --source=%s --staged <file>...\" to unstage)"
 msgstr ""
-"  (använd \"git restore --source=%s --staged <fil>...\" för att ta bort från "
+"  (använd ”git restore --source=%s --staged <fil>...” för att ta bort från "
 "kö)"
 
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
-msgstr "  (använd \"git rm --cached <fil>...\" för att ta bort från kö)"
+msgstr "  (använd ”git rm --cached <fil>...” för att ta bort från kö)"
 
 msgid "  (use \"git add <file>...\" to mark resolution)"
-msgstr "  (använd \"git add <fil>...\" för att ange lösning)"
+msgstr "  (använd ”git add <fil>...” för att ange lösning)"
 
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
-msgstr "  (använd \"git add/rm <fil>...\" som lämpligt för att ange lösning)"
+msgstr "  (använd ”git add/rm <fil>...” som lämpligt för att ange lösning)"
 
 msgid "  (use \"git rm <file>...\" to mark resolution)"
-msgstr "  (använd \"git rm <fil>...\" för att ange lösning)"
+msgstr "  (använd ”git rm <fil>...” för att ange lösning)"
 
 msgid "Changes to be committed:"
 msgstr "Ändringar att checka in:"
@@ -21313,17 +21781,16 @@
 msgstr "Ändringar ej i incheckningskön:"
 
 msgid "  (use \"git add <file>...\" to update what will be committed)"
-msgstr ""
-"  (använd \"git add <fil>...\" för att uppdatera vad som ska checkas in)"
+msgstr "  (använd ”git add <fil>...” för att uppdatera vad som ska checkas in)"
 
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr ""
-"  (använd \"git add/rm <fil>...\" för att uppdatera vad som ska checkas in)"
+"  (använd ”git add/rm <fil>...” för att uppdatera vad som ska checkas in)"
 
 msgid ""
 "  (use \"git restore <file>...\" to discard changes in working directory)"
 msgstr ""
-"  (använd \"git restore <fil>...\" för att förkasta ändringar i "
+"  (använd ”git restore <fil>...” för att förkasta ändringar i "
 "arbetskatalogen)"
 
 msgid "  (commit or discard the untracked or modified content in submodules)"
@@ -21332,7 +21799,7 @@
 
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
-msgstr "  (använd \"git %s <fil>...\" för att ta med i det som ska checkas in)"
+msgstr "  (använd ”git %s <fil>...” för att ta med i det som ska checkas in)"
 
 msgid "both deleted:"
 msgstr "borttaget av bägge:"
@@ -21415,43 +21882,43 @@
 msgstr ""
 "\n"
 "Det tog %.2f sekunder att räkna före/bakom-värden.\n"
-"Du kan använda \"--no-ahead-behind\" för undvika detta.\n"
+"Du kan använda ”--no-ahead-behind” för undvika detta.\n"
 
 msgid "You have unmerged paths."
 msgstr "Du har ej sammanslagna sökvägar."
 
 msgid "  (fix conflicts and run \"git commit\")"
-msgstr "  (rätta konflikter och kör \"git commit\")"
+msgstr "  (rätta konflikter och kör ”git commit”)"
 
 msgid "  (use \"git merge --abort\" to abort the merge)"
-msgstr "  (använd \"git merge --abort\" för att avbryta sammanslagningen)"
+msgstr "  (använd ”git merge --abort” för att avbryta sammanslagningen)"
 
 msgid "All conflicts fixed but you are still merging."
 msgstr "Alla konflikter har rättats men du är fortfarande i en sammanslagning."
 
 msgid "  (use \"git commit\" to conclude merge)"
-msgstr "  (använd \"git commit\" för att slutföra sammanslagningen)"
+msgstr "  (använd ”git commit” för att slutföra sammanslagningen)"
 
 msgid "You are in the middle of an am session."
-msgstr "Du är i mitten av en körning av \"git am\"."
+msgstr "Du är i mitten av en körning av ”git am”."
 
 msgid "The current patch is empty."
 msgstr "Aktuell patch är tom."
 
 msgid "  (fix conflicts and then run \"git am --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git am --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git am --continue”)"
 
 msgid "  (use \"git am --skip\" to skip this patch)"
-msgstr "  (använd \"git am --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git am --skip” för att hoppa över patchen)"
 
 msgid ""
 "  (use \"git am --allow-empty\" to record this patch as an empty commit)"
 msgstr ""
-"  (använd \"git am --allow-empty\" för att registrera patchen som en tom "
+"  (använd ”git am --allow-empty” för att registrera patchen som en tom "
 "incheckning)"
 
 msgid "  (use \"git am --abort\" to restore the original branch)"
-msgstr "  (använd \"git am --abort\" för att återställa ursprungsgrenen)"
+msgstr "  (använd ”git am --abort” för att återställa ursprungsgrenen)"
 
 msgid "git-rebase-todo is missing."
 msgstr "git-rebase-todo saknas."
@@ -21479,79 +21946,79 @@
 msgstr[1] "Följande kommandon att utföra (%<PRIuMAX> kommandon återstår):"
 
 msgid "  (use \"git rebase --edit-todo\" to view and edit)"
-msgstr "  (använd \"git rebase --edit-todo\" för att visa och redigera)"
+msgstr "  (använd ”git rebase --edit-todo” för att visa och redigera)"
 
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
-msgstr "Du håller på att ombasera grenen \"%s\" ovanpå \"%s\"."
+msgstr "Du håller på att ombasera grenen ”%s” ovanpå ”%s”."
 
 msgid "You are currently rebasing."
 msgstr "Du håller på med en ombasering."
 
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git rebase --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git rebase --continue”)"
 
 msgid "  (use \"git rebase --skip\" to skip this patch)"
-msgstr "  (använd \"git rebase --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git rebase --skip” för att hoppa över patchen)"
 
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
-msgstr "  (använd \"git rebase --abort\" för att checka ut ursprungsgrenen)"
+msgstr "  (använd ”git rebase --abort” för att checka ut ursprungsgrenen)"
 
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
-msgstr "  (alla konflikter rättade: kör \"git rebase --continue\")"
+msgstr "  (alla konflikter rättade: kör ”git rebase --continue”)"
 
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
 msgstr ""
-"Du håller på att dela upp en incheckning medan du ombaserar grenen \"%s\" "
-"ovanpå \"%s\"."
+"Du håller på att dela upp en incheckning medan du ombaserar grenen ”%s” "
+"ovanpå ”%s”."
 
 msgid "You are currently splitting a commit during a rebase."
 msgstr "Du håller på att dela upp en incheckning i en ombasering."
 
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
-msgstr "  (Så fort din arbetskatalog är ren, kör \"git rebase --continue\")"
+msgstr "  (Så fort din arbetskatalog är ren, kör ”git rebase --continue”)"
 
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr ""
-"Du håller på att redigera en incheckning medan du ombaserar grenen \"%s\" "
-"ovanpå \"%s\"."
+"Du håller på att redigera en incheckning medan du ombaserar grenen ”%s” "
+"ovanpå ”%s”."
 
 msgid "You are currently editing a commit during a rebase."
 msgstr "Du håller på att redigera en incheckning under en ombasering."
 
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr ""
-"  (använd \"git commit --amend\" för att lägga till på aktuell incheckning)"
+"  (använd ”git commit --amend” för att lägga till på aktuell incheckning)"
 
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
-msgstr "  (använd \"git rebase --continue\" när du är nöjd med dina ändringar)"
+msgstr "  (använd ”git rebase --continue” när du är nöjd med dina ändringar)"
 
 msgid "Cherry-pick currently in progress."
 msgstr "Cherry-pick pågår."
 
 #, c-format
 msgid "You are currently cherry-picking commit %s."
-msgstr "Du håller på med en \"cherry-pick\" av incheckningen %s."
+msgstr "Du håller på med en ”cherry-pick” av incheckningen %s."
 
 msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git cherry-pick --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git cherry-pick --continue”)"
 
 msgid "  (run \"git cherry-pick --continue\" to continue)"
-msgstr "  (kör \"git cherry-pick --continue\" för att fortsätta)"
+msgstr "  (kör ”git cherry-pick --continue” för att fortsätta)"
 
 msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
-msgstr "  (alla konflikter rättade: kör \"git cherry-pick --continue\")"
+msgstr "  (alla konflikter rättade: kör ”git cherry-pick --continue”)"
 
 msgid "  (use \"git cherry-pick --skip\" to skip this patch)"
-msgstr "  (använd \"git cherry-pick --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git cherry-pick --skip” för att hoppa över patchen)"
 
 msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
 msgstr ""
-"  (använd \"git cherry-pick --abort\" för att avbryta \"cherry-pick\"-"
+"  (använd ”git cherry-pick --abort” för att avbryta ”cherry-pick”-"
 "operationen)"
 
 msgid "Revert currently in progress."
@@ -21562,30 +22029,30 @@
 msgstr "Du håller på med att ångra incheckningen %s."
 
 msgid "  (fix conflicts and run \"git revert --continue\")"
-msgstr "  (rätta konflikter och kör sedan \"git revert --continue\")"
+msgstr "  (rätta konflikter och kör sedan ”git revert --continue”)"
 
 msgid "  (run \"git revert --continue\" to continue)"
-msgstr "  (kör \"git revert --continue\" för att fortsätta)"
+msgstr "  (kör ”git revert --continue” för att fortsätta)"
 
 msgid "  (all conflicts fixed: run \"git revert --continue\")"
-msgstr "  (alla konflikter rättade: kör \"git revert --continue\")"
+msgstr "  (alla konflikter rättade: kör ”git revert --continue”)"
 
 msgid "  (use \"git revert --skip\" to skip this patch)"
-msgstr "  (använd \"git revert --skip\" för att hoppa över patchen)"
+msgstr "  (använd ”git revert --skip” för att hoppa över patchen)"
 
 msgid "  (use \"git revert --abort\" to cancel the revert operation)"
-msgstr "  (använd \"git revert --abort\" för att avbryta ångrandet)"
+msgstr "  (använd ”git revert --abort” för att avbryta ångrandet)"
 
 #, c-format
 msgid "You are currently bisecting, started from branch '%s'."
-msgstr "Du håller på med en \"bisect\", startad från grenen \"%s\"."
+msgstr "Du håller på med en ”bisect”, startad från grenen ”%s”."
 
 msgid "You are currently bisecting."
-msgstr "Du håller på med en \"bisect\"."
+msgstr "Du håller på med en ”bisect”."
 
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr ""
-"  (använd \"git bisect reset\" för att komma tillbaka till ursprungsgrenen)"
+"  (använd ”git bisect reset” för att komma tillbaka till ursprungsgrenen)"
 
 msgid "You are in a sparse checkout."
 msgstr "Du är i en gles utcheckning."
@@ -21637,7 +22104,7 @@
 msgstr "Det tog %.2f sekunder att räkna upp ospårade filer."
 
 msgid "See 'git help status' for information on how to improve this."
-msgstr "Se \"git help status\" för information om hur du kan förbättra detta."
+msgstr "Se ”git help status” för information om hur du kan förbättra detta."
 
 # %s är nästa sträng eller tom.
 #, c-format
@@ -21653,8 +22120,7 @@
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr ""
-"inga ändringar att checka in (använd \"git add\" och/eller \"git commit -a"
-"\")\n"
+"inga ändringar att checka in (använd ”git add” och/eller ”git commit -a”)\n"
 
 #, c-format
 msgid "no changes added to commit\n"
@@ -21665,8 +22131,7 @@
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
 msgstr ""
-"inget köat för incheckning, men ospårade filer finns (spåra med \"git add"
-"\")\n"
+"inget köat för incheckning, men ospårade filer finns (spåra med ”git add”)\n"
 
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
@@ -21674,7 +22139,7 @@
 
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
-msgstr "inget att checka in (skapa/kopiera filer och spåra med \"git add\")\n"
+msgstr "inget att checka in (skapa/kopiera filer och spåra med ”git add”)\n"
 
 #, c-format
 msgid "nothing to commit\n"
@@ -21715,6 +22180,10 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "kan inte %s: Ditt index innehåller ändringar som inte checkats in."
 
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "okänd stil ”%s” angavs för ”%s”"
+
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
 "merge"
@@ -21788,7 +22257,7 @@
 
 #, perl-format
 msgid "fatal: command '%s' died with exit code %d"
-msgstr "ödesdigert: kommandot \"%s\" dog med slutkoden %d"
+msgstr "ödesdigert: kommandot ”%s” dog med slutkoden %d"
 
 msgid "the editor exited uncleanly, aborting everything"
 msgstr "textredigeringsprogrammet avslutades med fel, avbryter allting"
@@ -21796,12 +22265,11 @@
 #, perl-format
 msgid ""
 "'%s' contains an intermediate version of the email you were composing.\n"
-msgstr ""
-"\"%s\" innehåller en mellanliggande version av e-postbrevet du skrev.\n"
+msgstr "”%s” innehåller en mellanliggande version av e-postbrevet du skrev.\n"
 
 #, perl-format
 msgid "'%s.final' contains the composed email.\n"
-msgstr "\"%s.final\" innehåller det skrivna brevet.\n"
+msgstr "”%s.final” innehåller det skrivna brevet.\n"
 
 msgid "--dump-aliases incompatible with other options\n"
 msgstr "--dump-aliases är inkompatibelt med andra flaggor\n"
@@ -21811,9 +22279,9 @@
 "git-send-email is configured with the sendemail.* options - note the 'e'.\n"
 "Set sendemail.forbidSendmailVariables to false to disable this check.\n"
 msgstr ""
-"ödesdigert: hittade konfigurationsflaggor för \"sendmail\"\n"
-"git-send-email konfigureras med \"sendemail.*\"-flaggor - lägg märke till \"e"
-"\".\n"
+"ödesdigert: hittade konfigurationsflaggor för ”sendmail”\n"
+"git-send-email konfigureras med ”sendemail.*”-flaggor - lägg märke till "
+"”e”.\n"
 "Sätt sendemail.forbidSendmailVariables till false för att inaktivera denna "
 "kontroll.\n"
 
@@ -21824,16 +22292,16 @@
 "`batch-size` and `relogin` must be specified together (via command-line or "
 "configuration option)\n"
 msgstr ""
-"\"batch-size\" och \"relogin\" måste anges tillsammans (via kommandorad "
-"eller konfigurationsflagga)\n"
+"”batch-size” och ”relogin” måste anges tillsammans (via kommandorad eller "
+"konfigurationsflagga)\n"
 
 #, perl-format
 msgid "Unknown --suppress-cc field: '%s'\n"
-msgstr "Okänt fält i --suppress-cc: \"%s\"\n"
+msgstr "Okänt fält i --suppress-cc: ”%s”\n"
 
 #, perl-format
 msgid "Unknown --confirm setting: '%s'\n"
-msgstr "Okänd inställning i --confirm: \"%s\"\n"
+msgstr "Okänd inställning i --confirm: ”%s”\n"
 
 #, perl-format
 msgid "warning: sendmail alias with quotes is not supported: %s\n"
@@ -21841,11 +22309,11 @@
 
 #, perl-format
 msgid "warning: `:include:` not supported: %s\n"
-msgstr "varning: \":include:\" stöds inte: %s\n"
+msgstr "varning: ”:include:” stöds inte: %s\n"
 
 #, perl-format
 msgid "warning: `/file` or `|pipe` redirection not supported: %s\n"
-msgstr "varning: omdirigering til \"/fil\" eller \"|rör\" stöds inte: %s\n"
+msgstr "varning: omdirigering til ”/fil” eller ”|rör” stöds inte: %s\n"
 
 #, perl-format
 msgid "warning: sendmail line is not recognized: %s\n"
@@ -21859,10 +22327,10 @@
 "    * Saying \"./%s\" if you mean a file; or\n"
 "    * Giving --format-patch option if you mean a range.\n"
 msgstr ""
-"Filen \"%s\" finns men kan också vara ett incheckningsintervall\n"
+"Filen ”%s” finns men kan också vara ett incheckningsintervall\n"
 "att skapa patchar för. Gör otvetydigt genom att...\n"
 "\n"
-"    * Säga \"./%s\" om du menar en fil; eller\n"
+"    * Säga ”./%s” om du menar en fil; eller\n"
 "    * Ange flaggan --format-patch om du menar ett intervall.\n"
 
 #, perl-format
@@ -21893,20 +22361,20 @@
 "\n"
 "Clear the body content if you don't wish to send a summary.\n"
 msgstr ""
-"Rader som börjar med \"GIT:\" kommer tas bort.\n"
+"Rader som börjar med ”GIT:” kommer tas bort.\n"
 "Överväg att ta med en övergripande diffstatus eller\n"
 "innehållsförteckning för patchen du skriver.\n"
 "\n"
 "Rensa brevkroppen om du inte vill sända någon sammanfattning.\n"
 
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "Misslyckades öppna %s: %s"
-
-#, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "Misslyckades öppna %s.final: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Misslyckades öppna %s: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Sammanfattande brev tomt, hoppar över\n"
 
@@ -21933,15 +22401,15 @@
 msgstr ""
 "Vägrar sända eftersom patchen\n"
 "\t%s\n"
-"har mallärendet \"*** SUBJECT HERE ***\". Använd --force om du verkligen "
-"vill sända.\n"
+"har mallärendet ”*** SUBJECT HERE ***”. Använd --force om du verkligen vill "
+"sända.\n"
 
 msgid "To whom should the emails be sent (if anyone)?"
 msgstr "Till vem ska breven sändas (om någon)?"
 
 #, perl-format
 msgid "fatal: alias '%s' expands to itself\n"
-msgstr "ödesdigert: aliaset \"%s\" expanderar till sig själv\n"
+msgstr "ödesdigert: aliaset ”%s” expanderar till sig själv\n"
 
 msgid "Message-ID to be used as In-Reply-To for the first email (if any)? "
 msgstr ""
@@ -21959,7 +22427,7 @@
 
 #, perl-format
 msgid "CA path \"%s\" does not exist"
-msgstr "CA-sökvägen \"%s\" finns inte"
+msgstr "CA-sökvägen ”%s” finns inte"
 
 msgid ""
 "    The Cc list above has been expanded by additional\n"
@@ -21979,9 +22447,9 @@
 "    Beteendet styrs av konfigurationsinställningen\n"
 "    sendemail.confirm\n"
 "\n"
-"    För ytterligare information, kör \"git send-email --help\".\n"
+"    För ytterligare information, kör ”git send-email --help”.\n"
 "    För att behålla nuvarande beteende, men dölja detta\n"
-"    meddelande, kör \"git config --global sendemail.confirm auto\".\n"
+"    meddelande, kör ”git config --global sendemail.confirm auto”.\n"
 "\n"
 
 #. TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your
@@ -21991,7 +22459,7 @@
 msgstr "Sända brevet? (y=ja, n=nej, e=redigera, q=avsluta, a=alla): "
 
 msgid "Send this email reply required"
-msgstr "Svar krävs på frågan \"Sända brevet?\""
+msgstr "Svar krävs på frågan ”Sända brevet?”"
 
 msgid "The required SMTP server is not properly defined."
 msgstr "Nödvändig SMTP-server har inte angivits korrekt."
@@ -22039,31 +22507,35 @@
 
 #, perl-format
 msgid "(mbox) Adding cc: %s from line '%s'\n"
-msgstr "(mbox) Lägger till cc: %s från raden \"%s\"\n"
+msgstr "(mbox) Lägger till cc: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(mbox) Adding to: %s from line '%s'\n"
-msgstr "(mbox) Lägger till to: %s från raden \"%s\"\n"
+msgstr "(mbox) Lägger till to: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(non-mbox) Adding cc: %s from line '%s'\n"
-msgstr "(icke-mbox) Lägger till cc: %s från raden \"%s\"\n"
+msgstr "(icke-mbox) Lägger till cc: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(body) Adding cc: %s from line '%s'\n"
-msgstr "(kropp) Lägger till cc: %s från raden \"%s\"\n"
+msgstr "(kropp) Lägger till cc: %s från raden ”%s”\n"
 
 #, perl-format
 msgid "(%s) Could not execute '%s'"
-msgstr "(%s) Kunde inte köra \"%s\""
+msgstr "(%s) Kunde inte köra ”%s”"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) Lägger till %s: %s från: \"%s\"\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Felformaterad utdata från ”%s”"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
-msgstr "(%s) misslyckades stänga röret till \"%s\""
+msgstr "(%s) misslyckades stänga röret till ”%s”"
+
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Lägger till %s: %s från: ”%s”\n"
 
 msgid "cannot send message as 7bit"
 msgstr "kan inte sända brev som sjubitars"
@@ -22096,8 +22568,7 @@
 #, perl-format
 msgid "Skipping %s with backup suffix '%s'.\n"
 msgstr ""
-"Hoppar över %s med filnamnstillägget \"%s\" som används för "
-"säkerhetskopior.\n"
+"Hoppar över %s med filnamnstillägget ”%s” som används för säkerhetskopior.\n"
 
 #. TRANSLATORS: please keep "[y|N]" as is.
 #, perl-format
diff --git a/po/tr.po b/po/tr.po
index a24a7ae..19d6661 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -1,8 +1,8 @@
 # Turkish translations for Git
 # Git Türkçe çevirileri
-# Copyright (C) 2020-2023 Emir SARI <emir_sari@icloud.com>
+# Copyright (C) 2020-2024 Emir SARI <emir_sari@icloud.com>
 # This file is distributed under the same license as the Git package.
-# Emir SARI <emir_sari@icloud.com>, 2020-2023
+# Emir SARI <emir_sari@icloud.com>, 2020-2024
 #
 # ######################################################### #
 #     Git Türkçe kavramlar dizini / Git Turkish Glossary    #
@@ -27,6 +27,7 @@
 # detached HEAD               | ayrık HEAD                  #
 # dirty                       | kirli                       #
 # evil merge                  | uğursuz birleştirme         #
+# fanout                      | çıkış sayısı                #
 # fast-forward                | ileri sarım/sarmak          #
 # fetch                       | getirme(k)                  #
 # fixup                       | düzeltmek                   #
@@ -39,6 +40,8 @@
 # mark                        | im(lemek)                   #
 # merge                       | birleştirme(k)              #
 # octopus                     | ahtapot                     #
+# orphan                      | yetim                       #
+# orphaned                    | yetim bırakılmış            #
 # overlay                     | yerpaylaşım                 #
 # pack                        | paket                       #
 # parent                      | üst öge                     #
@@ -46,10 +49,10 @@
 # pathspec                    | yol belirteci               #
 # pattern                     | dizgi                       #
 # porcelain                   | okunabilir                  #
-# prune                       | budamak                     #
+# prune                       | buda(mak)                     #
 # pseudoref                   | yalancıktan başvuru         #
-# pull                        | çekme(k)                    #
-# push                        | itme(k)                     #
+# pull                        | çek(mek)                    #
+# push                        | it(mek)                     #
 # rebase                      | yeniden temellendirme(k)    #
 # record                      | kayıt yaz(mak)              #
 # ref                         | başvuru                     #
@@ -83,6 +86,7 @@
 # trailer                     | artbilgi                    #
 # tree                        | ağaç                        #
 # treeish                     | ağacımsı                    #
+# unborn                      | henüz doğmamış (dal)        #
 # unstage                     | hazırlıktan çıkar(mak)      #
 # upstream                    | üstkaynak                   #
 # worktree/working tree       | çalışma ağacı               #
@@ -92,8 +96,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: 2023-03-03 11:32+0300\n"
-"PO-Revision-Date: 2023-03-03 11:40+0300\n"
+"POT-Creation-Date: 2024-02-16 22:04+0300\n"
+"PO-Revision-Date: 2024-02-16 22:00+0300\n"
 "Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
 "Language-Team: Turkish (https://github.com/bitigchi/git-po/)\n"
 "Language: tr\n"
@@ -718,9 +722,10 @@
 msgid "Reverting is not possible because you have unmerged files."
 msgstr "Geriye al yapılamaz; birleştirmesi tamamlanmamış dosyalarınız var."
 
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "%s yapılamıyor; birleştirmesi tamamlanmamış dosyalarınız var."
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr ""
+"Yeniden temellendirme yapılamaz; birleştirmesi tamamlanmamış dosyalarınız "
+"var."
 
 msgid ""
 "Fix them up in the work tree, and then use 'git add/rm <file>'\n"
@@ -741,6 +746,23 @@
 msgid "Exiting because of unfinished merge."
 msgstr "Tamamlanmamış birleştirmeden dolayı çıkılıyor."
 
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Iraksak dallar ileri sarılamaz; şunlardan birini yapmanız gerekiyor:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"veya\n"
+"\n"
+"\tgit rebase\n"
+
 msgid "Not possible to fast-forward, aborting."
 msgstr "İleri sarma olanaklı değil, iptal ediliyor."
 
@@ -849,6 +871,12 @@
 msgid "'%s' outside a repository"
 msgstr "'%s' bir depo dışında"
 
+msgid "failed to read patch"
+msgstr "yama okunamadı"
+
+msgid "patch too large"
+msgstr "yama pek büyük"
+
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "Zaman damgası düzenli ifadesi %s hazırlanamıyor"
@@ -1183,6 +1211,10 @@
 msgstr "%s açılamıyor"
 
 #, c-format
+msgid "cannot unlink '%s'"
+msgstr "'%s' bağlantısı kesilemiyor"
+
+#, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Parça #%d sorunsuzca uygulandı."
 
@@ -1366,8 +1398,12 @@
 msgstr "'%s' okunamıyor"
 
 #, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "yol belirteci '%s', geçerli dizinin dışındaki dosyalarla eşleşiyor"
+
+#, c-format
 msgid "pathspec '%s' did not match any files"
-msgstr "yol belirteci '%s' hiçbir dosya ile eşleşmedi"
+msgstr "yol belirteci '%s', hiçbir dosya ile eşleşmedi"
 
 #, c-format
 msgid "no such ref: %.*s"
@@ -1381,9 +1417,6 @@
 msgid "not a tree object: %s"
 msgstr "bir ağaç nesnesi değil: %s"
 
-msgid "current working directory is untracked"
-msgstr "geçerli çalışma dizini izlenmiyor"
-
 #, c-format
 msgid "File not found: %s"
 msgstr "Dosya bulunamadı: %s"
@@ -1469,6 +1502,10 @@
 msgstr "Beklenmedik seçenek --output"
 
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "fazladan komut satırı parametresi '%s'"
+
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "Bilinmeyen arşiv biçimi '%s'"
 
@@ -1510,6 +1547,17 @@
 msgid "ignoring overly large gitattributes blob '%s'"
 msgstr "pek büyük gitattributes ikili nesnesi '%s' yok sayılıyor"
 
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "hatalı --attr-source veya GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "'%s' dosyasının bilgileri alınamıyor"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "%s okunamıyor"
+
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
 msgstr "'%s' dosyasında hatalı tırnağa alınmış içerik: %s"
@@ -1615,9 +1663,6 @@
 msgid "--contents and --reverse do not blend well."
 msgstr "--contents ve --reverse birlikte pek iyi gitmiyor."
 
-msgid "cannot use --contents with final commit object name"
-msgstr "--contents son işleme nesnesi adı ile kullanılamıyor"
-
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr ""
 "--reverse ve --first-parent birlikte en son işlemenin belirtilmesini "
@@ -1738,8 +1783,10 @@
 msgstr "'%s' adında bir dal halihazırda var"
 
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "'%s' dalı zorla güncellenemiyor, '%s' konumunda çıkış yapılmış"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"şuradaki çalışma ağacı tarafından kullanılan '%s' dalı zorla "
+"güncellenemiyor: '%s'"
 
 #, c-format
 msgid "cannot set up tracking information; starting point '%s' is not a branch"
@@ -1797,12 +1844,8 @@
 msgstr "'%s' altmodülü: '%s' dalı oluşturulamıyor"
 
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' çıkışı '%s' konumunda halihazırda yapılmış"
-
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "%s çalışma ağacının HEAD'i güncellenmemiş"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s', '%s' konumunda halihazırda çalışma ağacı tarafından kullanılıyor"
 
 msgid "git add [<options>] [--] <pathspec>..."
 msgstr "git add [<seçenekler>] [--] <yol-blrtç>..."
@@ -1811,17 +1854,6 @@
 msgid "cannot chmod %cx '%s'"
 msgstr "%cx '%s' chmod yapılamıyor"
 
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "beklenmedik diff durumu %c"
-
-msgid "updating files failed"
-msgstr "dosyaları güncelleme başarısız"
-
-#, c-format
-msgid "remove '%s'\n"
-msgstr "kaldır: '%s'\n"
-
 msgid "Unstaged changes after refreshing the index:"
 msgstr "İndeksi yeniledikten sonra hazırlanmamış değişiklikler:"
 
@@ -1832,24 +1864,21 @@
 "add.interactive.useBuiltin ayarı kaldırıldı!\n"
 "Ayrıntılar için onun 'git help config' içindeki girdisine bakın."
 
-msgid "Could not read the index"
-msgstr "İndeks okunamadı"
-
-msgid "Could not write patch"
-msgstr "Yama yazılamadı"
+msgid "could not read the index"
+msgstr "indeks okunamadı"
 
 msgid "editing patch failed"
 msgstr "yamayı düzenleme başarısız"
 
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "'%s' dosya bilgileri alınamadı"
+msgid "could not stat '%s'"
+msgstr "'%s' bilgileri alınamadı"
 
-msgid "Empty patch. Aborted."
-msgstr "Boş yama. İptal edildi."
+msgid "empty patch. aborted"
+msgstr "boş yama. iptal edildi."
 
 #, c-format
-msgid "Could not apply '%s'"
+msgid "could not apply '%s'"
 msgstr "'%s' uygulanamadı"
 
 msgid "The following paths are ignored by one of your .gitignore files:\n"
@@ -1980,6 +2009,9 @@
 msgid "index file corrupt"
 msgstr "indeks dosyası hasarlı"
 
+msgid "unable to write new index file"
+msgstr "yeni indeks dosyası yazılamıyor"
+
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "hatalı eylem '%s', '%s' için"
@@ -2188,9 +2220,6 @@
 "Bir dosyanın \"onlar sildi\" olduğunu kabul etmek için dosya ile 'git rm' "
 "yapabilirsiniz."
 
-msgid "unable to write new index file"
-msgstr "yeni indeks dosyası yazılamıyor"
-
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "'%s' nesnesi ayrıştırılamadı."
@@ -2209,10 +2238,6 @@
 msgid "failed to read '%s'"
 msgstr "'%s' okunamadı"
 
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "'%s=%s' ve '%s=%s' seçenekleri birlikte kullanılamaz"
-
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<seçenekler>] [(<mbox> | <posta-dizin>)...]"
 
@@ -2252,10 +2277,6 @@
 msgid "pass --keep-cr flag to git-mailsplit for mbox format"
 msgstr "'git-mailsplit'e mbox biçimi için --keep-cr bayrağını geçir"
 
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr ""
-"'git-mailsplit'e 'am.keepcr'dan bağımsız olarak --keep-cr bayrağını geçirme"
-
 msgid "strip everything before a scissors line"
 msgstr "bir kesim çizgisinden önceki her şeyi çıkar"
 
@@ -2368,11 +2389,11 @@
 msgstr "git archive: Floş bekleniyordu"
 
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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}=<terim> --term-{old,good}=<terim>]    [--"
-"no-checkout] [--first-parent] [<kötü> [<iyi>...]] [--]    [<yol-blrtç>...]"
+"git bisect start [--term-(new|bad)=<uçbirim> --term-(old|good)=<uçbirim>]    "
+"[--no-checkout] [--first-parent] [<kötü> [<iyi>...]] [--]   [<yol-blrtç>...]"
 
 msgid "git bisect (good|bad) [<rev>...]"
 msgstr "git bisect (good|bad) [<rev>...]"
@@ -2386,8 +2407,8 @@
 msgid "git bisect replay <logfile>"
 msgstr "git bisect replay <günlük-dosyası>"
 
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <komut>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <komut> [<argüman>...]"
 
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
@@ -2798,7 +2819,7 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "'%s' dalı siliniyor: Bu dal '%s'\n"
 "         dalına birleştirilmiş; ancak HEAD'e henüz birleştirilmemiş."
@@ -2806,40 +2827,49 @@
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
 "'%s' dalı silinmiyor: Bu dal HEAD'e birleştirilmiş olmasına rağmen\n"
 "         '%s' dalına birleştirilmemiş."
 
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
+msgid "couldn't look up commit object for '%s'"
 msgstr "'%s' için işleme nesnesi aranamadı"
 
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"'%s' dalı tümüyle birleştirilmemiş.\n"
-"Eğer silmek istediğinizden eminseniz 'git branch -D %s' çalıştırın."
+msgid "the branch '%s' is not fully merged"
+msgstr "'%s' dalı tümüyle birleştirilmedi"
 
-msgid "Update of config-file failed"
-msgstr "config-file güncellemesi başarısız"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Onu silmek istediğinizden eminseniz 'git branch -D %s' çalıştırın."
+
+msgid "update of config-file failed"
+msgstr "config-file güncellenemedi"
 
 msgid "cannot use -a with -d"
 msgstr "-a, -d ile kullanılamıyor"
 
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "'%s' dalı silinemiyor, şurada çıkış yapılmış: '%s'"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"'%s' konumundaki çalışma ağacı tarafından kullanılan '%s' dalı silinemiyor"
 
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "Uzak izleme dalı '%s' bulunamadı."
+msgid "remote-tracking branch '%s' not found"
+msgstr "uzak izleme dalı '%s' bulunamadı"
 
 #, c-format
-msgid "branch '%s' not found."
-msgstr "'%s' dalı bulunamadı."
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"'%s' dalı bulunamadı.\n"
+"--remote yazmayı mı unuttunuz?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "'%s' dalı bulunamadı"
 
 #, c-format
 msgid "Deleted remote-tracking branch %s (was %s).\n"
@@ -2860,48 +2890,52 @@
 msgstr "HEAD (%s), refs/heads/ dışına işaret ediyor"
 
 #, c-format
-msgid "Branch %s is being rebased at %s"
+msgid "branch %s is being rebased at %s"
 msgstr "%s dalı %s konumunda yeniden temellendiriliyor"
 
 #, c-format
-msgid "Branch %s is being bisected at %s"
+msgid "branch %s is being bisected at %s"
 msgstr "%s dalı %s konumunda ikili aranıyor"
 
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "Geçersiz dal adı: '%s'"
+msgid "HEAD of working tree %s is not updated"
+msgstr "%s çalışma ağacının HEAD'i güncellenmemiş"
 
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "'%s' dalında henüz bir işleme yok."
+msgid "invalid branch name: '%s'"
+msgstr "geçersiz dal adı: '%s'"
 
 #, c-format
-msgid "No branch named '%s'."
-msgstr "'%s' adında bir dal yok."
-
-msgid "Branch rename failed"
-msgstr "Dal yeniden adlandırması başarısız"
-
-msgid "Branch copy failed"
-msgstr "Dal kopyalaması başarısız"
+msgid "no commit on branch '%s' yet"
+msgstr "'%s' dalında henüz bir işleme yok"
 
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "Yanlış adlandırılan '%s' dalının bir kopyası oluşturuldu"
+msgid "no branch named '%s'"
+msgstr "'%s' adında bir dal yok"
+
+msgid "branch rename failed"
+msgstr "dal yeniden adlandırılamadı"
+
+msgid "branch copy failed"
+msgstr "dal kopyalanamadı"
 
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "Yanlış adlandırılan '%s' dalı yeniden adlandırıldı"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "yanlış adlandırılan '%s' dalının bir kopyası oluşturuldu"
 
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Dal %s olarak yeniden adlandırıldı; ancak HEAD güncellenmedi!"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "yanlış adlandırılan '%s' dalı yeniden adlandırıldı"
 
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "Dal yeniden adlandırıldı; ancak config-file güncellemesi başarısız"
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "dal %s olarak yeniden adlandırıldı; ancak HEAD güncellenmedi!"
 
-msgid "Branch is copied, but update of config-file failed"
-msgstr "Dal kopyalandı; ancak config-file güncellemesi başarısız"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "dal yeniden adlandırıldı; ancak config-file güncellenemedi"
+
+msgid "branch is copied, but update of config-file failed"
+msgstr "dal kopyalandı; ancak config-file güncellenemedi"
 
 #, c-format
 msgid ""
@@ -2967,6 +3001,9 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "bir dalı taşı/yeniden adlandır, hedef var olsa bile"
 
+msgid "do not output a newline after empty formatted refs"
+msgstr "boş biçimli başvurulardan sonra bir yenisatır çıktılama"
+
 msgid "copy a branch and its reflog"
 msgstr "bir dalı ve onun başvuru günlüğünü kopyala"
 
@@ -3012,8 +3049,8 @@
 msgid "format to use for the output"
 msgstr "çıktı için kullanılacak biçim"
 
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "HEAD geçerli bir başvuru olarak çözülemedi."
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "HEAD geçerli bir başvuru olarak çözülemedi"
 
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD, refs/heads altında bulunamadı!"
@@ -3031,17 +3068,17 @@
 msgid "branch name required"
 msgstr "dal adı gerekli"
 
-msgid "Cannot give description to detached HEAD"
-msgstr "Ayrılmış HEAD'e açıklama verilemiyor"
+msgid "cannot give description to detached HEAD"
+msgstr "ayrık HEAD'e açıklama verilemiyor"
 
 msgid "cannot edit description of more than one branch"
 msgstr "birden çok dalın açıklaması düzenlenemiyor"
 
-msgid "cannot copy the current branch while not on any."
-msgstr "Bir dalın üzerinde değilken geçerli dal kopyalanamaz."
+msgid "cannot copy the current branch while not on any"
+msgstr "bir dalın üzerinde değilken geçerli dal kopyalanamaz"
 
-msgid "cannot rename the current branch while not on any."
-msgstr "Bir dalın üzerinde değilken geçerli dal yeniden adlandırılamaz."
+msgid "cannot rename the current branch while not on any"
+msgstr "bir dalın üzerinde değilken geçerli dal yeniden adlandırılamaz"
 
 msgid "too many branches for a copy operation"
 msgstr "bir kopyalama işlemi için pek fazla dal"
@@ -3054,10 +3091,10 @@
 
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
+"could not set upstream of HEAD to %s when it does not point to any branch"
 msgstr ""
 "HEAD'in üst kaynağı %s olarak ayarlanamadı; çünkü herhangi bir dala işaret "
-"etmiyor."
+"etmiyor"
 
 #, c-format
 msgid "no such branch '%s'"
@@ -3070,16 +3107,16 @@
 msgid "too many arguments to unset upstream"
 msgstr "üst kaynağı kaldırmak için pek fazla argüman"
 
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr ""
-"HEAD'in üst kaynağı kaldırılamadı; çünkü herhangi bir dala işaret etmiyor."
+"HEAD'in üst kaynağı kaldırılamadı; çünkü herhangi bir dala işaret etmiyor"
 
 #, c-format
-msgid "Branch '%s' has no upstream information"
+msgid "branch '%s' has no upstream information"
 msgstr "'%s' dalının üstkaynak bilgisi yok"
 
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "'git branch'in -a ve -r seçenekleri bir dal adı almaz.\n"
@@ -3087,10 +3124,10 @@
 
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
+"'--set-upstream-to' instead"
 msgstr ""
 "--set-upstream seçeneği artık desteklenmiyor. Lütfen --track veya --set-"
-"upstream-to kullanın."
+"upstream-to kullanın"
 
 msgid "git version:\n"
 msgstr "git sürümü:\n"
@@ -3163,6 +3200,10 @@
 msgstr "dosya adları için bir strftime biçim soneki belirtin"
 
 #, c-format
+msgid "unknown argument `%s'"
+msgstr "bilinmeyen argüman '%s'"
+
+#, c-format
 msgid "could not create leading directories for '%s'"
 msgstr "'%s' için öncü dizinler oluşturulamadı"
 
@@ -3185,13 +3226,11 @@
 msgstr "Hata raporu '%s' dosyasına yazıldı.\n"
 
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
-"                  [--version=<sürüm>] <dosya> <git-rev-liste-argümanları>"
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<sürüm>] <dosya> <git-rev-listesi-argümanları>"
 
 msgid "git bundle verify [-q | --quiet] <file>"
 msgstr "git bundle verify [-q | --quiet] <dosya>"
@@ -3211,11 +3250,11 @@
 msgid "show progress meter"
 msgstr "ilerleme çubuğunu göster"
 
-msgid "show progress meter during object writing phase"
-msgstr "ilerleme çubuğunu nesne yazımı aşaması sırasında göster"
+msgid "historical; same as --progress"
+msgstr "eski seçenek; --progress ile aynı"
 
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "ilerleme çubuğu gösterildiğinde --all-progress'e benzer"
+msgid "historical; does nothing"
+msgstr "eski seçenek; bir şey yapmaz"
 
 msgid "specify bundle format version"
 msgstr "demet biçim sürümünü belirt"
@@ -3271,23 +3310,23 @@
 msgstr "git cat-file (-t | -s) [--allow-unknown-type] <nesne>"
 
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-
-msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
 "git cat-file (--textconv | --filters)\n"
 "             [<başvuru>:<yol|ağacımsı> | --path=<yol|ağacımsı> <revizyon>]"
 
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
 msgid "Check object existence or emit object contents"
 msgstr "Nesne varlığını denetle veya nesne içeriğini yay"
 
@@ -3324,6 +3363,9 @@
 msgid "stdin is NUL-terminated"
 msgstr "stdin, NUL ile sonlandırılmış"
 
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin ve stdout NUL ile sonlandırılmış"
+
 msgid "read commands from stdin"
 msgstr "komutları stdin'den oku"
 
@@ -3581,6 +3623,10 @@
 msgstr "'%s' veya '%s', %s ile birlikte kullanılamaz"
 
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "'%s', '%s' veya '%s' bir ağaçtan çıkış yaparken kullanılamaz"
+
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "'%s' yolu birleştirilmemiş"
 
@@ -3831,8 +3877,8 @@
 msgid "new-branch"
 msgstr "yeni dal"
 
-msgid "new unparented branch"
-msgstr "yeni üst ögesi olmayan dal"
+msgid "new unborn branch"
+msgstr "yeni henüz doğmamış dal"
 
 msgid "update ignored files (default)"
 msgstr "yok sayılan dosyaları güncelle (öntanımlı)"
@@ -4083,9 +4129,6 @@
 "clean.requireForce öntanımlı olarak 'true' ve ne -i ne -n ne de -f verilmiş; "
 "temizleme reddediliyor"
 
-msgid "-x and -X cannot be used together"
-msgstr "-x ve -X birlikte kullanılamaz"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<seçenekler>] [--] <depo> [<dizin>]"
 
@@ -4173,6 +4216,9 @@
 msgid "separate git dir from working tree"
 msgstr "git dizinini çalışma ağacından ayır"
 
+msgid "specify the reference format to use"
+msgstr "kullanılacak başvuru biçimini belirt"
+
 msgid "key=value"
 msgstr "anahtar=değer"
 
@@ -4185,12 +4231,6 @@
 msgid "option to transmit"
 msgstr "iletme seçeneği"
 
-msgid "use IPv4 addresses only"
-msgstr "yalnızca IPv4 adresleri kullan"
-
-msgid "use IPv6 addresses only"
-msgstr "yalnızca IPv6 adresleri kullan"
-
 msgid "apply partial clone filters to submodules"
 msgstr "altmodüllere kısımsal klon süzgeçlerini uygula"
 
@@ -4221,6 +4261,10 @@
 msgstr "%s var ve bir dizin değil"
 
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' bir sembolik bağlantı; --local ile klonlama reddediliyor"
+
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "yineleyici '%s' üzerinden çalıştırılamadı"
 
@@ -4292,11 +4336,9 @@
 msgid "You must specify a repository to clone."
 msgstr "Klonlamak için bir depo belirtmelisiniz."
 
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri; --depth, --shallow-since ve --shallow-exclude ile uyumsuz"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "bilinmeyen başvuru depolama biçimi '%s'"
 
 #, c-format
 msgid "repository '%s' does not exist"
@@ -4424,11 +4466,11 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <dizin>] [--append]\n"
-"                       [--split[=<<strateji>]] [--reachable | --stdin-packs "
-"| --stdin-commits]\n"
+"                       [--split[=<strateji>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
 "                       <bölme-seçenekleri>"
@@ -4447,6 +4489,10 @@
 msgstr "commit-graph '%s' açılamadı"
 
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "commit-graph zinciri '%s' açılamadı"
+
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "tanımlanamayan --split argümanı, %s"
 
@@ -4626,6 +4672,9 @@
 "\tgit cherry-pick --skip\n"
 "\n"
 
+msgid "updating files failed"
+msgstr "dosyaları güncelleme başarısız"
+
 msgid "failed to unpack HEAD tree object"
 msgstr "HEAD ağaç nesnesi açılamadı"
 
@@ -4644,9 +4693,6 @@
 msgid "Failed to update main cache tree"
 msgstr "Ana önbellek ağacı güncellenemedi"
 
-msgid "unable to write new_index file"
-msgstr "new_index dosyası yazılamıyor"
-
 msgid "cannot do a partial commit during a merge."
 msgstr "Bir birleştirme sırasında kısmi işleme yapılamaz."
 
@@ -4685,8 +4731,8 @@
 "karakteri seçilemiyor"
 
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "%s işlemesi aranamadı"
+msgid "could not lookup commit '%s'"
+msgstr "'%s' işlemesi aranamadı"
 
 #, c-format
 msgid "(reading log message from standard input)\n"
@@ -5052,10 +5098,10 @@
 
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"Depo güncellendi; ancak new_index dosyası yazılamıyor.\n"
+"Depo güncellendi; ancak yeni indeks dosyası yazılamıyor.\n"
 "Diskin dolu olup olmadığını ve kotanızı aşıp aşmadığınızı denetleyin,\n"
 "sonra kurtarmak için \"git restore --staged :/\" kullanın."
 
@@ -5733,6 +5779,174 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel negatif olamaz"
 
+msgid "couldn't find remote ref HEAD"
+msgstr "uzak HEAD başvurusu bulunamadı"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Şu konumdan: %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "%s nesnesi bulunamadı"
+
+msgid "[up to date]"
+msgstr "[güncel]"
+
+msgid "[rejected]"
+msgstr "[reddedildi]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "çıkış yapılmış dala getirilemiyor"
+
+msgid "[tag update]"
+msgstr "[etiket güncellemesi]"
+
+msgid "unable to update local ref"
+msgstr "yerel başvuru güncellenemiyor"
+
+msgid "would clobber existing tag"
+msgstr "var olan etiketi değiştirecektir"
+
+msgid "[new tag]"
+msgstr "[yeni etiket]"
+
+msgid "[new branch]"
+msgstr "[yeni dal]"
+
+msgid "[new ref]"
+msgstr "[yeni başvuru]"
+
+msgid "forced update"
+msgstr "zorlanmış güncelleme"
+
+msgid "non-fast-forward"
+msgstr "ileri sarım değil"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "'%s' açılamıyor"
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"Getirme, normalde hangi dallarda zorla güncelleme yapıldığını belirtir;\n"
+"ancak bu denetleme kapatılmış; yeniden açmak için '--show-forced-updates'\n"
+"bayrağını kullanın veya 'git config fetch.showForcedUpdates true' çalıştırın"
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"Zorla güncellemeleri denetleme %.2f saniye sürdü. '--no-show-forced-"
+"updates'\n"
+"kullanarak veya 'git config fetch.showForcedUpdates false' çalıştırarak\n"
+"bu denetlemeden kaçınabilirsiniz.\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s tüm gerekli nesneleri göndermedi\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr "%s reddedildi; çünkü sığ köklerin güncellenmesine izin verilmiyor"
+
+#, c-format
+msgid ""
+"some local refs could not be updated; try running\n"
+" 'git remote prune %s' to remove any old, conflicting branches"
+msgstr ""
+"bazı yerel başvurular güncellenemedi; 'git remote prune %s'\n"
+"kullanarak eski ve çakışan dalları kaldırmayı deneyin"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s sarkacak)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s sarkmaya başladı)"
+
+msgid "[deleted]"
+msgstr "[silindi]"
+
+msgid "(none)"
+msgstr "(hiçbiri)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "'%s' dalına getirme reddediliyor, '%s' konumunda çıkış yapıldı"
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "\"%s\" seçeneği \"%s\" değeri %s için geçerli değil"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "\"%s\" seçeneği %s için yok sayılıyor\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s geçerli bir nesne değil"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "%s diye bir nesne yok"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "birden çok dal algılandı, --set-upstream ile uyumsuz"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"HEAD'in üst kaynağı '%s' olarak '%s' konumundan ayarlanamadı; çünkü herhangi "
+"bir dala işaret etmiyor."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "bir uzak konum uzak izleme dalı için üstkaynak ayarlanmıyor"
+
+msgid "not setting upstream for a remote tag"
+msgstr "bir uzak konum etiketi için üstkaynak ayarlanmıyor"
+
+msgid "unknown branch type"
+msgstr "bilinmeyen dal türü"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"Kaynak dal bulunamadı;\n"
+"--set-upstream seçeneği ile tam olarak bir dal belirtmeniz gerekiyor"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "%s getiriliyor\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "%s getirilemedi"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "'%s' getirilemedi (çıkış kodu: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"Bir uzak dal belirtilmedi; lütfen yeni revizyonların\n"
+"getirileceği bir URL veya uzak konum adı belirtin"
+
+msgid "you need to specify a tag name"
+msgstr "bir etiket adı belirtmeniz gerekiyor"
+
 msgid "fetch from all remotes"
 msgstr "tüm uzak konumlardan getir"
 
@@ -5842,174 +6056,6 @@
 msgid "accept refspecs from stdin"
 msgstr "başvuru belirteçlerini stdin'den oku"
 
-msgid "couldn't find remote ref HEAD"
-msgstr "uzak HEAD başvurusu bulunamadı"
-
-#, c-format
-msgid "object %s not found"
-msgstr "%s nesnesi bulunamadı"
-
-msgid "[up to date]"
-msgstr "[güncel]"
-
-msgid "[rejected]"
-msgstr "[reddedildi]"
-
-msgid "can't fetch into checked-out branch"
-msgstr "çıkış yapılmış dala getirilemiyor"
-
-msgid "[tag update]"
-msgstr "[etiket güncellemesi]"
-
-msgid "unable to update local ref"
-msgstr "yerel başvuru güncellenemiyor"
-
-msgid "would clobber existing tag"
-msgstr "var olan etiketi değiştirecektir"
-
-msgid "[new tag]"
-msgstr "[yeni etiket]"
-
-msgid "[new branch]"
-msgstr "[yeni dal]"
-
-msgid "[new ref]"
-msgstr "[yeni başvuru]"
-
-msgid "forced update"
-msgstr "zorlanmış güncelleme"
-
-msgid "non-fast-forward"
-msgstr "ileri sarım değil"
-
-#, c-format
-msgid "cannot open '%s'"
-msgstr "'%s' açılamıyor"
-
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"Getirme, normalde hangi dallarda zorla güncelleme yapıldığını belirtir;\n"
-"ancak bu denetleme kapatılmış; yeniden açmak için '--show-forced-updates'\n"
-"bayrağını kullanın veya 'git config fetch.showForcedUpdates true' çalıştırın"
-
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"Zorla güncellemeleri denetleme %.2f saniye sürdü. '--no-show-forced-"
-"updates'\n"
-"kullanarak veya 'git config fetch.showForcedUpdates false' çalıştırarak\n"
-"bu denetlemeden kaçınabilirsiniz.\n"
-
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s tüm gerekli nesneleri göndermedi\n"
-
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr "%s reddedildi; çünkü sığ köklerin güncellenmesine izin verilmiyor"
-
-#, c-format
-msgid "From %.*s\n"
-msgstr "Şu konumdan: %.*s\n"
-
-#, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
-msgstr ""
-"bazı yerel başvurular güncellenemedi; 'git remote prune %s'\n"
-"kullanarak eski ve çakışan dalları kaldırmayı deneyin"
-
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s sarkacak)"
-
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s sarkmaya başladı)"
-
-msgid "[deleted]"
-msgstr "[silindi]"
-
-msgid "(none)"
-msgstr "(hiçbiri)"
-
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "'%s' dalına getirme reddediliyor, '%s' konumunda çıkış yapıldı"
-
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "\"%s\" seçeneği \"%s\" değeri %s için geçerli değil"
-
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "\"%s\" seçeneği %s için yok sayılıyor\n"
-
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s geçerli bir nesne değil"
-
-#, c-format
-msgid "the object %s does not exist"
-msgstr "%s diye bir nesne yok"
-
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "birden çok dal algılandı, --set-upstream ile uyumsuz"
-
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"HEAD'in üst kaynağı '%s' olarak '%s' konumundan ayarlanamadı; çünkü herhangi "
-"bir dala işaret etmiyor."
-
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "bir uzak konum uzak izleme dalı için üstkaynak ayarlanmıyor"
-
-msgid "not setting upstream for a remote tag"
-msgstr "bir uzak konum etiketi için üstkaynak ayarlanmıyor"
-
-msgid "unknown branch type"
-msgstr "bilinmeyen dal türü"
-
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"Kaynak dal bulunamadı;\n"
-"--set-upstream seçeneği ile tam olarak bir dal belirtmeniz gerekiyor"
-
-#, c-format
-msgid "Fetching %s\n"
-msgstr "%s getiriliyor\n"
-
-#, c-format
-msgid "could not fetch %s"
-msgstr "%s getirilemedi"
-
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "'%s' getirilemedi (çıkış kodu: %d)\n"
-
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"Bir uzak dal belirtilmedi; lütfen yeni revizyonların\n"
-"getirileceği bir URL veya uzak konum adı belirtin"
-
-msgid "you need to specify a tag name"
-msgstr "bir etiket adı belirtmeniz gerekiyor"
-
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only'e bir veya daha çok --negotiation-tip=* gerekiyor"
 
@@ -6125,6 +6171,12 @@
 msgid "print only refs which don't contain the commit"
 msgstr "yalnızca işlemeyi içermeyen başvuruları yazdır"
 
+msgid "read reference patterns from stdin"
+msgstr "başvuru dizgilerini stdin'den oku"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "--stdin ile bilinmeyen argümanlar verilmiş"
+
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<yapılandırma> [--] <argümanlar>"
 
@@ -6137,6 +6189,10 @@
 msgid "missing --config=<config>"
 msgstr "--config=<yapılandırma> eksik"
 
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "hatayı yapılandırma alındı, --config=%s"
+
 msgid "unknown"
 msgstr "bilinmeyen"
 
@@ -6283,19 +6339,28 @@
 msgid "notice: %s points to an unborn branch (%s)"
 msgstr "Uyarı: %s henüz doğmamış bir dala işaret ediyor (%s)"
 
-msgid "Checking cache tree"
-msgstr "Önbellek ağacı denetleniyor"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "%s ögesinin önbellek ağacı denetleniyor"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s: cache-tree içinde geçersiz sha1 işaretçisi"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: %s ögesinin cache-tree'si içinde geçersiz sha1 işaretçisi"
 
 msgid "non-tree in cache-tree"
 msgstr "cache-tree içinde ağaç olmayan öge"
 
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s: resolve-undo içinde geçersiz sha1 işaretçisi"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: %s ögesinin resolve-undo'su içinde geçersiz sha1 işaretçisi"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "'%s' paketi için rev-index yüklenemiyor"
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "'%s' paketi için geçersiz rev-index"
 
 msgid ""
 "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
@@ -6481,6 +6546,9 @@
 msgid "pack unreferenced objects separately"
 msgstr "başvurulmamış nesneleri ayrı olarak paketle"
 
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "--cruft ile yeni süprüntü paketlerinin boyutunu sınırla"
+
 msgid "be more thorough (increased runtime)"
 msgstr "biraz daha titiz ol (artırılmış işleyiş süresi)"
 
@@ -6651,12 +6719,6 @@
 msgid "'crontab' died"
 msgstr "'crontab' beklenmedik bir biçimde sonlandı"
 
-msgid "failed to start systemctl"
-msgstr "systemctl başlatılamadı"
-
-msgid "failed to run systemctl"
-msgstr "systemctl çalıştırılamadı"
-
 #, c-format
 msgid "failed to delete '%s'"
 msgstr "'%s' silinemedi"
@@ -6665,6 +6727,12 @@
 msgid "failed to flush '%s'"
 msgstr "'%s' floş yapılamadı"
 
+msgid "failed to start systemctl"
+msgstr "systemctl başlatılamadı"
+
+msgid "failed to run systemctl"
+msgstr "systemctl çalıştırılamadı"
+
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "tanımlanamayan --scheduler argümanı, '%s'"
@@ -6688,6 +6756,9 @@
 msgid "scheduler to trigger git maintenance run"
 msgstr "git bakımını tetikleyecek görev planlayıcı"
 
+msgid "failed to set up maintenance schedule"
+msgstr "bakım programı ayarlanamadı"
+
 msgid "failed to add repo to global config"
 msgstr "depo, global yapılandırmaya eklenemedi"
 
@@ -6719,6 +6790,10 @@
 msgstr "ağaç okunamıyor (%s)"
 
 #, c-format
+msgid "unable to read tree %s"
+msgstr "%s ağacı okunamıyor"
+
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "%s türündeki bir nesneden grep yapılamıyor"
 
@@ -6762,8 +6837,8 @@
 msgid "search in subdirectories (default)"
 msgstr "altdizinlerde ara (öntanımlı)"
 
-msgid "descend at most <depth> levels"
-msgstr "en çok <derinlik> düzey in"
+msgid "descend at most <n> levels"
+msgstr "en çok <n> düzey aşağı in"
 
 msgid "use extended POSIX regular expressions"
 msgstr "genişletilmiş POSIX düzenli ifadelerini kullan"
@@ -7131,10 +7206,6 @@
 msgstr "%s İLE SHA1 ÇARPIŞMASI BULUNDU!"
 
 #, c-format
-msgid "unable to read %s"
-msgstr "%s okunamıyor"
-
-#, c-format
 msgid "cannot read existing object info %s"
 msgstr "var olan nesne bilgisi %s okunamıyor"
 
@@ -7271,87 +7342,18 @@
 msgid "fsck error in pack objects"
 msgstr "paket nesnelerinde fsck hatası"
 
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "'%s' şablonunun bilgileri alınamıyor"
-
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "'%s' opendir yapılamıyor"
-
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "'%s' readlink yapılamıyor"
-
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "'%s', '%s' ögesine sembolik bağla bağlanamıyor"
-
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "'%s' şuraya kopyalanamıyor: '%s'"
-
-#, c-format
-msgid "ignoring template %s"
-msgstr "%s şablonu yok sayılıyor"
-
-#, c-format
-msgid "templates not found in %s"
-msgstr "şablonlar %s içinde bulunamadı"
-
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "şablonlar '%s' konumundan kopyalanmıyor: %s"
-
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "geçersiz başlangıç dalı adı: '%s'"
-
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "%d dosya türü ele alınamıyor"
-
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "%s şuraya taşınamıyor: %s"
-
-msgid "attempt to reinitialize repository with different hash"
-msgstr "depoyu başka bir sağlama ile yeniden ilklendirme deneniyor"
-
-#, c-format
-msgid "%s already exists"
-msgstr "%s halihazırda var"
-
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s yok sayıldı"
-
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "%s%s içindeki var olan paylaşılan Git deposu yeniden ilklendirildi\n"
-
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "%s%s içindeki var olan Git deposu yeniden ilklendirildi\n"
-
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "%s%s içinde paylaşılan boş Git deposu ilklendirildi\n"
-
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "%s%s içinde boş Git deposu ilklendirildi\n"
-
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<şablon-dizini>]\n"
 "         [--separate-git-dir <git-dizini>] [--object-format=<biçim>]\n"
+"         [--ref-format=<biçim>]\n"
 "         [-b <dal-adı> | --initial-branch=<dal-adı>]\n"
-"         [--shared[=<izinler>]] [<dizin>]"
+"         [--shared[=<izin>]] [<dizin>]"
 
 msgid "permissions"
 msgstr "izinler"
@@ -7393,11 +7395,12 @@
 
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <jeton>[(=|:)<değer>])...]\n"
+"                       [(--trailer (<anahtar>|<anArması>)"
+"[(=|:)<değer>])...]\n"
 "                       [--parse] [<dosya>...]"
 
 msgid "edit files in place"
@@ -7406,6 +7409,9 @@
 msgid "trim empty trailers"
 msgstr "boş artbilgileri kırp"
 
+msgid "placement"
+msgstr "yerleştirme"
+
 msgid "where to place the new trailer"
 msgstr "yeni artbilgiler nereye yerleştirilecek"
 
@@ -7418,17 +7424,17 @@
 msgid "output only the trailers"
 msgstr "yalnızca artbilgileri çıktı ver"
 
-msgid "do not apply config rules"
-msgstr "yapılandırma kurallarını uygulama"
+msgid "do not apply trailer.* configuration variables"
+msgstr "trailer.* yapılandırma değişkenlerini uygulama"
 
-msgid "join whitespace-continued values"
-msgstr "boşluk ile sürdürülen değerleri uç uca ekle"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "çok satırlı artbilgileri tek satırlı değerler olarak biçimlendir"
 
-msgid "set parsing options"
-msgstr "ayrıştırma seçeneklerini ayarla"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "--only-trailers --only-input --unfold için arma"
 
-msgid "do not treat --- specially"
-msgstr "ayırma çizgilerine (---) özel davranma"
+msgid "do not treat \"---\" as the end of input"
+msgstr "\"---\" dizgisine satır sonu olarak davranma"
 
 msgid "trailer(s) to add"
 msgstr "eklenecek artbilgi(ler)"
@@ -7517,6 +7523,10 @@
 msgid "not a range"
 msgstr "bir erim değil"
 
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "'%s' dal açıklama dosyası okunamıyor"
+
 msgid "cover letter needs email format"
 msgstr "ön yazı için e-posta biçimi gerekli"
 
@@ -7615,6 +7625,9 @@
 msgid "generate parts of a cover letter based on a branch's description"
 msgstr "ön yazının bazı kısımlarını dalın açıklamasından oluştur"
 
+msgid "use branch description from file"
+msgstr "dal açıklamasını dosyadan oku"
+
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "[PATCH] yerine [<önek>] kullan"
 
@@ -7772,6 +7785,10 @@
 msgstr "İzlenen bir uzak dal bulunamadı, lütfen el ile <üstkaynak> belirtin.\n"
 
 #, c-format
+msgid "could not get object info about '%s'"
+msgstr "'%s' hakkında nesne bilgisi alınamadı"
+
+#, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
 msgstr "hatalı ls-files biçimi: '%s' ögesi, '(' ile başlamıyor"
 
@@ -7914,10 +7931,6 @@
 msgstr "git ls-tree [<seçenekler>] <ağacımsı> [<yol>...]"
 
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "'%s' hakkında nesne bilgisi alınamadı"
-
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "hatalı ls-tree biçimi: '%s' ögesi '(' ile başlamıyor"
 
@@ -8040,9 +8053,19 @@
 "git merge-file [<seçenekler>] [-L <ad1> [-L <orij> [-L <ad2>]]] <dosya1> "
 "<orij-dosya> <dosya2>"
 
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"diff-algorithm seçeneği şunları kabul eder: \"myers\", \"minimal\", "
+"\"patience\" ve \"histogram\""
+
 msgid "send results to standard output"
 msgstr "sonuçları standart çıktıya gönder"
 
+msgid "use object IDs instead of filenames"
+msgstr "dosya adları yerine nesne kimlikleri kullan"
+
 msgid "use a diff3 based merge"
 msgstr "diff3 tabanlı birleştirme kullan"
 
@@ -8058,6 +8081,12 @@
 msgid "for conflicts, use a union version"
 msgstr "çakışmalarda birlik olmuş bir sürüm kullan"
 
+msgid "<algorithm>"
+msgstr "<algoritma>"
+
+msgid "choose a diff algorithm"
+msgstr "bir diff algoritması seç"
+
 msgid "for conflicts, use this marker size"
 msgstr "çakışmalarda bu imleyici boyutunu kullan"
 
@@ -8068,6 +8097,13 @@
 msgstr "file1/orig-file/file2 için etiketler yapıştır"
 
 #, c-format
+msgid "object '%s' does not exist"
+msgstr "'%s' diye bir nesne yok"
+
+msgid "Could not write object file"
+msgstr "nesne dosyası yazılamadı"
+
+#, c-format
 msgid "unknown option %s"
 msgstr "bilinmeyen seçenek %s"
 
@@ -8128,11 +8164,18 @@
 msgid "specify a merge-base for the merge"
 msgstr "birleştirme için bir birleştirme temeli belirtilmeli"
 
+msgid "option=value"
+msgstr "seçenek=değer"
+
+msgid "option for selected merge strategy"
+msgstr "seçili birleştirme stratejisi için seçenekler"
+
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge, tüm diğer seçeneklerle uyumsuz"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base, --stdin ile uyumsuz"
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "bilinmeyen strateji seçeneği: -X%s"
 
 #, c-format
 msgid "malformed input line: '%s'."
@@ -8201,12 +8244,6 @@
 msgid "merge strategy to use"
 msgstr "kullanılacak birleştirme stratejisi"
 
-msgid "option=value"
-msgstr "seçenek=değer"
-
-msgid "option for selected merge strategy"
-msgstr "seçili birleştirme stratejisi için seçenekler"
-
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "birleştirme işlemesi iletisi (ileri sarım olmayan bir birleştirme için)"
@@ -8268,10 +8305,6 @@
 msgstr "İki uç işlemenin birleştirilmesi dışında bir şey yapılmıyor."
 
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "bilinmeyen strateji seçeneği: -X%s"
-
-#, c-format
 msgid "unable to write %s"
 msgstr "%s yazılamıyor"
 
@@ -8561,8 +8594,8 @@
 msgid "can not move directory into itself"
 msgstr "dizin kendi içine taşınamıyor"
 
-msgid "cannot move directory over file"
-msgstr "dizin dosya üzerinden taşınamıyor"
+msgid "destination already exists"
+msgstr "hedef halihazırda var"
 
 msgid "source directory is empty"
 msgstr "kaynak dizin boş"
@@ -8641,21 +8674,25 @@
 msgstr "git notes [--ref <not-bşvr>] [list [<nesne>]]"
 
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <not-bşvr>] add [-f] [--allow-empty] [-m <ileti> | -F "
-"<dosya> | (-c | -C) <nesne>] [<nesne>]"
+"git notes [--ref <not-bşv>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraf-sonu>] [--[no-]stripspace] [-m <ilet> | -F <dosya> | (-c "
+"| -C) <nesne>] [<nesne>]"
 
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
 msgstr "git notes [--ref <not-bşvr>] copy [-f] <nesneden> <nesneye>"
 
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <not-bşvr>] append [--allow-empty] [-m <ileti> | -F <dosya> "
-"| (-c | -C) <nesne>] [<nesne>]"
+"git notes [--ref <not-bşv>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraf-sonu>] [--[no-]stripspace] [-m <ileti> | -F <dosya> | (-"
+"c | -C) <nesne>] [<nesne>]"
 
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
 msgstr "git notes [--ref <not-bşvr>] edit [--allow-empty] [<nesne>]"
@@ -8786,6 +8823,15 @@
 msgid "replace existing notes"
 msgstr "var olan notları başkalarıyla değiştir"
 
+msgid "<paragraph-break>"
+msgstr "<paragraf-sonu>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "paragraflar arasında <paragraf-sonu> ekle"
+
+msgid "remove unnecessary whitespace"
+msgstr "gereksiz boşlukları kaldır"
+
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -9056,6 +9102,10 @@
 msgstr "delta sayımında tutarsızlık"
 
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "geçersiz pack.allowPackReuse değeri: '%s'"
+
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -9139,6 +9189,12 @@
 msgid "bad index version '%s'"
 msgstr "hatalı indeks sürümü '%s'"
 
+msgid "show progress meter during object writing phase"
+msgstr "ilerleme çubuğunu nesne yazımı aşaması sırasında göster"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "ilerleme çubuğu gösterildiğinde --all-progress'e benzer"
+
 msgid "<version>[,<offset>]"
 msgstr "<sürüm>[,<ofset>]"
 
@@ -9286,9 +9342,6 @@
 msgid "--thin cannot be used to build an indexable pack"
 msgstr "--thin bir indekslenebilir paket yapımında kullanılamaz"
 
-msgid "cannot use --filter without --stdout"
-msgstr "--filter, --stdout olmadan kullanılamaz"
-
 msgid "cannot use --filter with --stdin-packs"
 msgstr "--filter, --stdin-packs ile birlikte kullanılamıyor"
 
@@ -9301,19 +9354,16 @@
 msgid "cannot use --stdin-packs with --cruft"
 msgstr "--stdin-packs, --cruft ile birlikte kullanılamıyor"
 
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "--max-pack-size, --cruft ile birlikte kullanılamıyor"
-
 msgid "Enumerating objects"
 msgstr "Nesneler ortaya dökülüyor"
 
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "Toplam %<PRIu32> (delta %<PRIu32>), yeniden kullanılan %<PRIu32> (delta "
-"%<PRIu32>), yeniden kullanılan paket %<PRIu32>"
+"%<PRIu32>), yeniden kullanılan paket %<PRIu32> (%<PRIuMAX> konumundan)"
 
 msgid ""
 "'git pack-redundant' is nominated for removal.\n"
@@ -9329,8 +9379,15 @@
 "<git@vger.kernel.org> adresine bir e-posta atarak\n"
 "bize haber verin. Sağ olun.\n"
 
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid "refusing to run without --i-still-use-this"
+msgstr "--i-still-use-this olmadan çalıştırma reddediliyor"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune]git pack-refs [--all] [--no-prune] [--"
+"include <dizgi>] [--exclude <dizgi>]"
 
 msgid "pack everything"
 msgstr "her şeyi paketle"
@@ -9338,6 +9395,12 @@
 msgid "prune loose refs (default)"
 msgstr "gevşek başvuruları buda (öntanımlı)"
 
+msgid "references to include"
+msgstr "içerilecek başvurular"
+
+msgid "references to exclude"
+msgstr "dışarıda tutulacak başvurular"
+
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
 
@@ -9396,6 +9459,12 @@
 msgid "number of submodules pulled in parallel"
 msgstr "paralelde çekilen altmodüllerin sayısı"
 
+msgid "use IPv4 addresses only"
+msgstr "yalnızca IPv4 adresleri kullan"
+
+msgid "use IPv6 addresses only"
+msgstr "yalnızca IPv6 adresleri kullan"
+
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
 "fetched."
@@ -9499,7 +9568,7 @@
 msgid "pull with rebase"
 msgstr "yeniden temellendirme ile çekim"
 
-msgid "please commit or stash them."
+msgid "Please commit or stash them."
 msgstr "Lütfen onları işleyin veya zulalayın."
 
 #, c-format
@@ -9654,39 +9723,39 @@
 
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Güncellemeler reddedildi; çünkü geçerli dalınızın ucu kendisinin\n"
-"uzak konum karşıtından geride. Yeniden itmeden önce uzak konumdaki\n"
-"değişiklikleri tümleştirin (örn. 'git pull ...').\n"
+"uzak konum karşıtından geride. Uzaktaki değişiklikleri tümleştirmek\n"
+"istiyorsanız yeniden itmeden önce 'git pull' yapın.\n"
 "Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
 "bakın."
 
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Güncellemeler reddedildi; çünkü itilmiş bir dal ucu kendisinin\n"
-"uzak konum karşıtından geride. Yeniden itmeden önce bu dalı çıkış\n"
-"yapın ve uzak konumdaki değişiklikleri tümleştirin (örn. 'git pull\n"
-"...'). Ayrıntılar için 'git push --help' içinde 'Notes about\n"
-"fast-forwards'a bakın."
+"uzak konum karşıtından geride. Uzaktaki değişiklikleri tümleştirmek\n"
+"istiyorsanız yeniden itmeden önce 'git pull' yapın.\n"
+"Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
+"bakın."
 
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Güncellemeler reddedildi; çünkü uzak konumda henüz yerelde sizde olmayan\n"
 "değişiklikler var. Bu genelde başka bir deponun aynı başvuruya itmesinden\n"
-"dolayı olur. Yeniden itmeden önce uzak konumdaki değişiklikleri tümleş-\n"
-"tirmek isteyebilirsiniz (örn. 'git pull ...').\n"
+"dolayı olur. Uzaktaki değişiklikleri tümleştirmek istiyorsanız yeniden\n"
+"itmeden önce 'git pull' yapın..\n"
 "Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
 "bakın."
 
@@ -9703,19 +9772,20 @@
 "olmayan bir nesneye işaret etmesini sağlaması için güncelleyemezsiniz.\n"
 
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Güncellemeler reddedildi; çünkü uzak izleme dalının ucu son\n"
-"çıkıştan bu yana güncellenmiş. Bir güncellemeyi zorlamadan\n"
-"önce bu değişiklikleri yerel olarak tümleştirmek isteye-\n"
-"bilirsiniz (örn. 'git pull ...'\n"
+"Güncellemeler reddedildi; çünkü geçerli dalınızın ucu kendisinin\n"
+"uzak konum karşıtından geride. Uzaktaki değişiklikleri tümleştirmek\n"
+"istiyorsanız yeniden itmeden önce 'git pull' yapın.\n"
+"Ayrıntılar için 'git push --help' içinde 'Notes about fast-forwards'a\n"
+"bakın."
 
 #, c-format
 msgid "Pushing to %s\n"
-msgstr "İtme konumu: %s\n"
+msgstr "Şuraya itiliyor: %s\n"
 
 #, c-format
 msgid "failed to push some refs to '%s'"
@@ -9735,8 +9805,8 @@
 msgid "repository"
 msgstr "depo"
 
-msgid "push all refs"
-msgstr "tüm başvuruları it"
+msgid "push all branches"
+msgstr "tüm dalları it"
 
 msgid "mirror all refs"
 msgstr "tüm başvuruları yansıla"
@@ -9744,11 +9814,11 @@
 msgid "delete refs"
 msgstr "başvuruları sil"
 
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "etiketleri it (--all veya --mirror ile kullanılamaz)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "etiketleri it (--all, --branches  veya --mirror ile kullanılamaz)"
 
 msgid "force updates"
-msgstr "zorla güncelle"
+msgstr "güncellemeleri zorla"
 
 msgid "<refname>:<expect>"
 msgstr "<bşvr-adı>:<bekle>"
@@ -10009,6 +10079,10 @@
 "Bunun sonucu olarak git onları yeniden temellendiremiyor."
 
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Bilinmeyen rebase-merges kipi: %s"
+
+#, c-format
 msgid "could not switch to %s"
 msgstr "şuraya geçilemedi: %s"
 
@@ -10022,6 +10096,15 @@
 msgstr ""
 "Tanımlanamayan boş tür '%s'; geçerli türler: \"drop\", \"keep\" ve \"ask\"."
 
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"bir boş dizi argümanıyla --rebase-merges yapmak artık kullanılmıyor ve "
+"Git'in ileriki bir sürümünde tümüyle kaldırılacak. Bunun yerine, aynı şeyi "
+"yapan argümansız bir --rebase-merges kullanın."
+
 #, c-format
 msgid ""
 "%s\n"
@@ -10243,18 +10326,11 @@
 msgid "switch `C' expects a numerical value"
 msgstr "'C' anahtarı sayısal bir değer bekliyor"
 
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "Bilinmeyen kip: %s"
-
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy, --merge veya --interactive gerektiriyor"
-
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
 msgstr ""
-"uygulama seçenekleri, rebase.autosquash ile uyumlu değil. --no-autosquash "
+"seçenekleri uygula, rebase.rebaseMerges ile uyumlu değil. --no-rebase-merges "
 "eklemeyi düşünün"
 
 msgid ""
@@ -10301,9 +10377,6 @@
 msgid "Does not point to a valid commit '%s'"
 msgstr "'%s' geçerli bir işlemeye işaret etmiyor"
 
-msgid "Please commit or stash them."
-msgstr "Lütfen onları işleyin veya zulalayın."
-
 msgid "HEAD is up to date."
 msgstr "HEAD güncel."
 
@@ -10560,11 +10633,12 @@
 msgid "fetch the remote branches"
 msgstr "uzak konum dallarını getir"
 
-msgid "import all tags and associated objects when fetching"
-msgstr "getirirken tüm etiketleri ve ilişkili nesneleri içe aktar"
-
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "veya hiçbir etiketi getirme (--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"getirirken tüm etiketleri ve ilişkili nesneleri\n"
+"içe aktar veya etiketleri hiç içe aktarma (--no-tags)"
 
 msgid "branch(es) to track"
 msgstr "izlenecek dal(lar)"
@@ -10664,10 +10738,10 @@
 "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
 "to delete them, use:"
 msgstr[0] ""
-"Not: refs/remotes hiyerarşisi dışındaki bir dal kaldırılmadı;\n"
+"Not: refs/remotes/ hiyerarşisi dışındaki bir dal kaldırılmadı;\n"
 "onu silmek için şunu kullanın:"
 msgstr[1] ""
-"Not: refs/remotes hiyerarşisi dışındaki bazı dallar kaldırılmadı;\n"
+"Not: refs/remotes/ hiyerarşisi dışındaki bazı dallar kaldırılmadı;\n"
 "onları silmek için şunu kullanın:"
 
 #, c-format
@@ -10953,6 +11027,10 @@
 msgid "could not remove stale bitmap: %s"
 msgstr "eskimiş biteşlem kaldırılamadı: %s"
 
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "paket öneki %s, nesne dizini %s ile başlamıyor"
+
 msgid "pack everything in a single pack"
 msgstr "her şeyi tek bir pakete sığdır"
 
@@ -10965,8 +11043,8 @@
 msgid "approxidate"
 msgstr "yaklaşık tarih"
 
-msgid "with -C, expire objects older than this"
-msgstr "-C ile, bundan daha eski nesneleri yürürlükten kaldır"
+msgid "with --cruft, expire objects older than this"
+msgstr "--cruft ile bundan daha eski nesneleri yürürlükten kaldır"
 
 msgid "remove redundant packs, and run git-prune-packed"
 msgstr "gereksiz paketleri kaldır ve 'git-prune-packed' çalıştır"
@@ -11028,17 +11106,20 @@
 msgid "pack prefix to store a pack containing pruned objects"
 msgstr "budanan nesneler içeren paketi depolamak için paket öneki"
 
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "süzülen nesneler içeren paketi depolamak için paket öneki"
+
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "bir precious-objects deposundaki paketler silinemiyor"
 
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "'%s' seçeneği, yalnızca '%s' ile birlikte kullanılabilir"
+
 msgid "Nothing new to pack."
 msgstr "Paketlenecek yeni bir şey yok."
 
 #, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "paket öneki %s, nesne dizini %s ile başlamıyor"
-
-#, c-format
 msgid "renaming pack to '%s' failed"
 msgstr "paketi '%s' olarak yeniden adlandırma başarısız"
 
@@ -11238,6 +11319,76 @@
 msgid "only one pattern can be given with -l"
 msgstr "-l ile yalnızca bir dizgi verilebilir"
 
+msgid "need some commits to replay"
+msgstr "yeniden oynatmak için birkaç işleme gerekli"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto ve --advance birbiriyle uyumsuz"
+
+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"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"bunun --advance veya --onto işlemi olup olmadığı örtük olarak algılanamıyor"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"birden çok kaynak dallı hedef ilerletilemiyor; çünkü sıralama hatalı "
+"tanımlanmış olurdu"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "--onto için olan doğru temel örtük olarak algılanamıyor"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(DENEYSEL!) git replay ([--contained] --onto <yeni-temel> | --advance <dal>) "
+"<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"
+
+msgid "advance all branches contained in revision-range"
+msgstr "revizyon eriminde içerilen tüm dalları ilerlet"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "--onto veya --advance seçeneğinin kullanımı zorunlu"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"'struct rev_info' içindeki '%s' biti zorlanacağından kimi revizyon yürütme "
+"seçenekleri geçersiz kılınacak"
+
+msgid "error preparing revisions"
+msgstr "revizyonlar hazırlanırken hata"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "kök işlemeye kadar yeniden oynatma henüz desteklenmiyor!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "birleştirme işlemelerini yeniden oynatma henüz desteklenmiyor!"
+
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
 msgstr ""
@@ -11446,18 +11597,12 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "--abbrev-ref için bilinmeyen kip: %s"
 
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden, --branches ile birlikte kullanılamıyor"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden, --tags ile birlikte kullanılamıyor"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden, --remotes ile birlikte kullanılamıyor"
-
 msgid "this operation must be run in a work tree"
 msgstr "bu işlem bir çalışma ağacı içinde çalıştırılmalı"
 
+msgid "Could not read the index"
+msgstr "İndeks okunamadı"
+
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "--show-object-format için bilinmeyen kip: %s"
@@ -11634,6 +11779,9 @@
 msgid "remote name"
 msgstr "uzak konum adı"
 
+msgid "push all refs"
+msgstr "tüm başvuruları it"
+
 msgid "use stateless RPC protocol"
 msgstr "durumsuz RPC protokolünü kullan"
 
@@ -11798,23 +11946,44 @@
 msgstr "bilinmeyen sağlama algoritması '%s'"
 
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<dizgi>...]"
 
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<başvuru>...]"
+
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<dizgi>]"
 
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <başvuru>"
+
+msgid "reference does not exist"
+msgstr "başvuru yok"
+
+msgid "failed to look up reference"
+msgstr "başvuru bakılamadı"
+
 msgid "only show tags (can be combined with heads)"
 msgstr "yalnızca etiketleri göster (dal uçlarıyla birlikte kullanılabilir)"
 
 msgid "only show heads (can be combined with tags)"
 msgstr "yalnızca dal uçlarını göster (etiketlerle birlikte kullanılabilir)"
 
+msgid "check for reference existence without resolving"
+msgstr "çözmeden başvuru varlığını denetle"
+
 msgid "stricter reference checking, requires exact ref path"
 msgstr "daha sıkı başvuru denetlemesi; kesin başvuru yolu gerektirir"
 
@@ -11834,9 +12003,11 @@
 msgstr "stdin'den yerel bir depoda olmayan başvuruları göster"
 
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<sçnklr>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<seçenekler>]"
 
 msgid "this worktree is not sparse"
 msgstr "bu çalışma ağacı aralıklı değil"
@@ -11958,6 +12129,23 @@
 msgid "error while refreshing working directory"
 msgstr "çalışma dizini yenilenirken hata"
 
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <dosya>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "girdi ve çıktı dosyalarını bir NUL karakteri ile sonlandır"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"--rules-file ile kullanıldığında dizgileri koni kipi dizgileri olarak yorumla"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "geçerli dizgiler yerine <dosya> içindekileri kullan."
+
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<günlük-seçenekleri>]"
 
@@ -12472,6 +12660,10 @@
 msgstr "'%s' altmodülü atlanıyor"
 
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "bir URL olmadan '%s' altmodülü içe aktarılamıyor"
+
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "'%s' klonlanamadı. Yeniden deneme zamanlandı"
 
@@ -12603,6 +12795,9 @@
 "shallow] [--reference <depo>] [--recursive] [--[no-]single-branch] [--] "
 "[<yol>...]"
 
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "HEAD geçerli bir başvuru olarak çözülemedi."
+
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<seçenekler>] [<yol>...]"
 
@@ -13061,6 +13256,9 @@
 msgid "write index in this format"
 msgstr "indeksi bu biçimle yaz"
 
+msgid "report on-disk index format version"
+msgstr "diskteki indeks biçimi sürümünü raporla"
+
 msgid "enable or disable split index"
 msgstr "bölünmüş indeksi etkinleştir veya devre dışı bırak"
 
@@ -13085,6 +13283,14 @@
 msgid "clear fsmonitor valid bit"
 msgstr "dosya sistemi monitöründe geçerli kısmını temizle"
 
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version: %d idi, %d olarak ayarlandı"
+
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -13208,10 +13414,10 @@
 
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <dizi>]]\n"
-"                 [-b <yeni-dal>] <yol> [<işlememsi>]"
+"                 [--orphan] [(-b | -B) <yeni-dal>] <yol> [<işlememsi>]"
 
 msgid "git worktree list [-v | --porcelain [-z]]"
 msgstr "git worktree list [-v | --porcelain [-z]]"
@@ -13234,6 +13440,37 @@
 msgid "git worktree unlock <worktree>"
 msgstr "git worktree unlock <çalışma-ağacı>"
 
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Olası kaynak dal yok, '--orphan' anlamı çıkarılıyor"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Bu depo için henüz doğmamış bir dal (işleme içermeyen dal) içeren\n"
+"bir çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağını\n"
+"kullanarak yapabilirsiniz:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Bu depo için henüz doğmamış bir dal (işleme içermeyen dal) içeren\n"
+"bir çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağını\n"
+"kullanarak yapabilirsiniz:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "%s/%s kaldırılıyor: %s"
@@ -13289,6 +13526,10 @@
 msgstr "ilklendiriliyor"
 
 #, c-format
+msgid "could not find created worktree '%s'"
+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')"
 
@@ -13301,9 +13542,30 @@
 msgstr "Çalışma ağacı hazırlanıyor ('%s' çıkış yapılıyor)"
 
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "erişilemiyor: geçersiz başvuru: %s"
+
+#, 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 ""
+"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 ""
+"Bir uzak konum olmasına rağmen hiçbir yerel/uzak başvuru yok, durduruluyor;\n"
+"geçersiz kılmak veya önce bir uzak konum getirmek için 'add -f' kullanın"
+
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "diğer çalışma ağacında çıkış yapılmış olsa bile <dal> çıkışını yap"
 
@@ -13313,6 +13575,9 @@
 msgid "create or reset a branch"
 msgstr "yeni bir dal oluştur veya sıfırla"
 
+msgid "create unborn branch"
+msgstr "henüz doğmamış dal oluştur"
+
 msgid "populate the new working tree"
 msgstr "yeni çalışma ağacını doldur"
 
@@ -13332,6 +13597,10 @@
 msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "'%s', '%s' ve '%s' seçenekleri birlikte kullanılamaz"
 
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "'%s' seçeneği ve işlememsiler birlikte kullanılamaz"
+
 msgid "added with --lock"
 msgstr "--lock ile eklendi"
 
@@ -13559,6 +13828,14 @@
 msgstr[0] "Demet bu başvuruyu gerektiriyor:"
 msgstr[1] "Demet bu %<PRIuMAX> başvuruyu gerektiriyor:"
 
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Demet, bu sağlama algoritmasını kullanıyor: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Demet, bu süzgeci kullanıyor: %s"
+
 msgid "unable to dup bundle descriptor"
 msgstr "demet açıklayıcısı çoğaltılamıyor"
 
@@ -13594,6 +13871,10 @@
 msgstr "iri parça numarası sonlandırması beklenenden önce ortaya çıkıyor"
 
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "iri parça kimliği %<PRIx32>, %d bayt hizalı değil"
+
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "düzgün olmayan iri parça ofseti %<PRIx64> ve %<PRIx64>"
 
@@ -13645,8 +13926,8 @@
 msgid "Move objects and refs by archive"
 msgstr "Nesneleri ve başvuruları arşive göre taşı"
 
-msgid "Provide content or type and size information for repository objects"
-msgstr "Depo nesneleri için içerik veya tür/boyut bilgisi sağla"
+msgid "Provide contents or details of repository objects"
+msgstr "Depo nesnelerinin içeriğini veya ayrıntılarını sağla"
 
 msgid "Display gitattributes information"
 msgstr "gitattributes bilgisini görüntüle"
@@ -13783,8 +14064,8 @@
 msgid "A portable graphical interface to Git"
 msgstr "Git için taşınabilir bir grafik arabirim"
 
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "Sağlamayı hesapla ve isteğe göre dosyadan ikili oluştur"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Nesne kimliğini hesapla/dosyadan isteğe bağlı nesne oluştur"
 
 msgid "Display help information about Git"
 msgstr "Git yardım bilgisini görüntüle"
@@ -13930,6 +14211,10 @@
 msgid "Create, list, delete refs to replace objects"
 msgstr "Nesne değiştirmek için başvurular oluştur, sil, listele"
 
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"DENEYSEL: İşlemeleri yeni temelde yeniden oynat, çıplak depolarla da çalışır"
+
 msgid "Generates a summary of pending changes"
 msgstr "Bekleyen değişikliklerin bir özetini çıkart"
 
@@ -14051,8 +14336,8 @@
 msgid "Display version information about Git"
 msgstr "Git sürüm bilgisini görüntüle"
 
-msgid "Show logs with difference each commit introduces"
-msgstr "Günlükleri her işlemenin sunduğu değişikliklerle göster"
+msgid "Show logs with differences each commit introduces"
+msgstr "Günlükleri her işlemenin değişiklikleriyle göster"
 
 msgid "Manage multiple working trees"
 msgstr "Birden çok çalışma ağacını yönet"
@@ -14168,6 +14453,32 @@
 msgid "commit-graph file is too small"
 msgstr "commit-graph dosyası pek küçük"
 
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "commit-graph OID çıkış sayısı iri parçası boyutu yanlış"
+
+msgid "commit-graph fanout values out of order"
+msgstr "commit-graph çıkış sayısı değerleri sırasız: fanout[%d] = %u != %u"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "commit-graph OID arama iri parçası boyutu yanlış"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "commit-graph işleme verisi iri parçası boyutu yanlış"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "commit-graph kuşaklar iri parçası boyutu yanlış"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "commit-graph changed-path indeksi iri parçası boyutu pek küçük"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"commit-graph dosyasındaki pek küçük changed-path iri parçası (%<PRIuMAX> < "
+"%<PRIuMAX>) yok sayılıyor"
+
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr "commit-graph imzası %X, %X ile eşleşmiyor"
@@ -14184,13 +14495,34 @@
 msgid "commit-graph file is too small to hold %u chunks"
 msgstr "commit-graph dosyası %u iri parça tutmak için pek küçük"
 
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"commit-graph'ten gerekli OID çıkış sayısı iri parçası eksik veya hasarlı"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "commit-graph'ten gerekli OID arama iri parçası eksik veya hasarlı"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"commit-graph'ten gerekli OID çıkış sayısı iri parçası eksik veya hasarlı"
+
 msgid "commit-graph has no base graphs chunk"
 msgstr "commit-graph temel grafiği iri parçasına iye değil"
 
+msgid "commit-graph base graphs chunk is too small"
+msgstr "commit-graph temel grafiği iri parçası pek küçük"
+
 msgid "commit-graph chain does not match"
 msgstr "commit-graph zinciri eşleşmiyor"
 
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "temel grafikteki işleme sayısı pek yüksek: %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "commit-graph zincir dosyası pek küçük"
+
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
 msgstr "geçersiz commit-graph zinciri: '%s'. satır bir sağlama değil"
 
@@ -14207,6 +14539,12 @@
 msgid "commit-graph requires overflow generation data but has none"
 msgstr "commit-graph, taşım oluşturma verisi gerektiriyor; ancak hiç yok"
 
+msgid "commit-graph overflow generation data is too small"
+msgstr "commit-graph, taşım üretim verisi pek küçük"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "commit-graph extra-edges işaretçisi sınırlar dışında"
+
 msgid "Loading known commits in commit graph"
 msgstr "İşleme grafiğindeki bilinen işlemeler yükleniyor"
 
@@ -14274,6 +14612,14 @@
 msgid "failed to rename temporary commit-graph file"
 msgstr "geçici commit-graph dosyası yeniden adlandırılamadı"
 
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "%<PRIuMAX>, %<PRIuMAX> işlemeli grafikler birleştirilemiyor"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "%s grafiği birleştirilemiyor, pek çok işleme: %<PRIuMAX>"
+
 msgid "Scanning merged commits"
 msgstr "Birleştirilen işlemeler taranıyor"
 
@@ -14303,9 +14649,6 @@
 msgid "failed to parse commit %s from commit-graph"
 msgstr "%s işlemesi commit-graph'tan ayrıştırılamadı"
 
-msgid "Verifying commits in commit graph"
-msgstr "İşleme grafiğindeki işlemeler doğrulanıyor"
-
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr ""
@@ -14329,20 +14672,6 @@
 msgstr "%s işlemesi için olan commit-graph üst öge listesi erkenden sonlanıyor"
 
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr ""
-"%s işlemesi için commit-graph kuşak sayısı sıfır; ancak başka yerlerde "
-"sıfırdan farklı"
-
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr ""
-"%s işlemesi için commit-graph kuşak sayısı sıfırdan farklı; ancak başka "
-"yerlerde sıfır"
-
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr "%s işlemesi için commit-graph kuşağı %<PRIuMAX> < %<PRIuMAX>"
 
@@ -14352,6 +14681,17 @@
 "%s işlemesi için commit-graph içindeki işleme tarihi %<PRIuMAX> != %<PRIuMAX>"
 
 #, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"commit-graph'te hem sıfır hem de sıfır olmayan üretimler var (örneğin, "
+"'%s've '%s' işlemeleri)"
+
+msgid "Verifying commits in commit graph"
+msgstr "İşleme grafiğindeki işlemeler doğrulanıyor"
+
+#, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s bir işleme değil!"
 
@@ -14375,6 +14715,10 @@
 "kullanarak bu iletiyi kapatabilirsiniz"
 
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "%s işlemesi işleme grafiğinde var; ancak nesne veritabanında yok"
+
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr ""
 "%s işlemesinin güvenilmeyen bir GPG imzası var, iddiaya göre %s tarafından."
@@ -14778,8 +15122,8 @@
 msgid "bad zlib compression level %d"
 msgstr "hatalı zlib sıkıştırma düzeyi %d"
 
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar yalnızca bir karakter olmalı"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar yalnızca bir ASCII karakter olmalı"
 
 #, c-format
 msgid "ignoring unknown core.fsyncMethod value '%s'"
@@ -14815,10 +15159,6 @@
 msgid "unable to resolve config blob '%s'"
 msgstr "'%s' yapılandırma ikili nesnesi çözülemiyor"
 
-#, c-format
-msgid "failed to parse %s"
-msgstr "%s ayrıştırılamadı"
-
 msgid "unable to parse command-line config"
 msgstr "komut satırı yapılandırması ayrıştırılamıyor"
 
@@ -14890,6 +15230,10 @@
 msgstr "geçersiz bölüm adı: %s"
 
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "'%s' içindeki haddinden uzun %<PRIuMAX>. satırla çalışma reddediliyor"
+
+#, c-format
 msgid "missing value for '%s'"
 msgstr "'%s' için değer eksik"
 
@@ -15282,9 +15626,6 @@
 msgid "--merge-base does not work with ranges"
 msgstr "--merge-base erimlerle çalışmaz"
 
-msgid "--merge-base only works with commits"
-msgstr "--merge-base yalnızca işlemelerle çalışır"
-
 msgid "unable to get HEAD"
 msgstr "HEAD alınamıyor"
 
@@ -15294,6 +15635,12 @@
 msgid "multiple merge bases found"
 msgstr "birden çok birleştirme temeli bulundu"
 
+msgid "cannot compare stdin to a directory"
+msgstr "stdin, bir dizinle karşılaştırılamıyor"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "adlandırılmış bir veriyolu bir dizinle karşılaştırılamıyor"
+
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<seçenekler>] <yol> <yol>"
 
@@ -15339,6 +15686,10 @@
 msgstr "'diff.submodule' yapılandırma değişkeni için bilinmeyen değer: '%s'"
 
 #, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "'%s' yapılandırması için bilinmeyen değer: %s"
+
+#, c-format
 msgid ""
 "Found errors in 'diff.dirstat' config variable:\n"
 "%s"
@@ -15350,6 +15701,13 @@
 msgid "external diff died, stopping at %s"
 msgstr "dış diff sonlandı, %s konumunda durdu"
 
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow tam olarak yalnızca bir yol belirteci gerektiriyor"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "yol belirteci sihri --follow tarafından desteklenmiyor: %s"
+
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
 msgstr "'%s', '%s', '%s' ve '%s' seçenekleri birlikte kullanılamaz"
@@ -15367,9 +15725,6 @@
 "'%s' ve '%s' seçenekleri birlikte kullanılamaz, '%s' seçeneğini '%s' ve '%s' "
 "ile kullanın"
 
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow tam olarak yalnızca bir yol belirteci gerektiriyor"
-
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "geçersiz --stat değeri: %s"
@@ -15414,13 +15769,6 @@
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "--color-moved-ws içinde geçersiz kip '%s'"
 
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"diff-algorithm seçeneği şunları kabul eder: \"myers\", \"minimal\", "
-"\"patience\" ve \"histogram\""
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "%s için geçersiz argüman"
@@ -15464,8 +15812,8 @@
 msgid "output only the last line of --stat"
 msgstr "--stat'ın yalnızca son satırını çıktı ver"
 
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
 
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
@@ -15475,8 +15823,8 @@
 msgid "synonym for --dirstat=cumulative"
 msgstr "--dirstat=cumulative eşanlamlısı"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "--dirstat=files,param1,param2... eşanlamlısı"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "--dirstat=files,<param1>,<param2>... eşanlamlısı"
 
 msgid "warn if changes introduce conflict markers or whitespace errors"
 msgstr ""
@@ -15559,6 +15907,9 @@
 msgid "do not show any source or destination prefix"
 msgstr "hiçbir kaynak ve hedef önekini gösterme"
 
+msgid "use default prefixes a/ and b/"
+msgstr "a/ ve b/ ögelerinin öntanımlı öneklerini kullan"
+
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "diff parçaları arasındaki bağlamı belirtilen satır sayısı kadar göster"
 
@@ -15648,12 +15999,6 @@
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "diff'i \"histogram diff\" algoritmasını kullanarak oluştur"
 
-msgid "<algorithm>"
-msgstr "<algoritma>"
-
-msgid "choose a diff algorithm"
-msgstr "bir diff algoritması seç"
-
 msgid "<text>"
 msgstr "<metin>"
 
@@ -15856,6 +16201,14 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "İpucu: Düzenleyicinizin dosyayı kapatması bekleniyor...%c"
 
+#, c-format
+msgid "could not write to '%s'"
+msgstr "şuraya yazılamadı: '%s'"
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "'%s' düzenlenemedi"
+
 msgid "Filtering content"
 msgstr "İçerik süzülüyor"
 
@@ -16151,6 +16504,10 @@
 msgstr "--config-env için bir yapılandırma anahtarı verilmedi\n"
 
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "--attr-source için öznitelik kaynağı verilmedi\n"
+
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "bilinmeyen seçenek: %s\n"
 
@@ -16675,12 +17032,12 @@
 "%s altmodülü birleştirilemedi; ancak birden çok olası birleştirmeler var:\n"
 "%s"
 
-msgid "Failed to execute internal merge"
-msgstr "İç birleştirme yürütülemedi"
+msgid "failed to execute internal merge"
+msgstr "iç birleştirme yürütülemedi"
 
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "%s veritabanına eklenemedi"
+msgid "unable to add %s to database"
+msgstr "%s veritabanına eklenemiyor"
 
 #, c-format
 msgid "Auto-merging %s"
@@ -17124,7 +17481,19 @@
 msgstr "önbellek okunamadı"
 
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "multi-pack-index OID ikiye bölümünün boyutu hatalı"
+msgstr "multi-pack-index OID çıkış sayısı boyutu yanlış"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"oid çıkış sayısı sırasız: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "multi-pack-index OID arama iri parçası yanlış boyutlu"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "multi-pack-index OID nesne ofseti iri parçası yanlış boyutlu"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17142,17 +17511,21 @@
 msgid "multi-pack-index hash version %u does not match version %u"
 msgstr "multi-pack-index sağlama sürümü %u, %u sürümü ile eşleşmiyor"
 
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "multi-pack-index'ten gerekli pack-name iri parçası eksik"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "multi-pack-index'ten gerekli pack-name iri parçası eksik veya hasarlı"
 
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "multi-pack-index'ten gerekli OID fanout iri parçası eksik"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr "multi-pack-index'ten gerekli OID fanout iri parçası eksik veya hasarlı"
 
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "multi-pack-index'ten gerekli OID arama iri parçası eksik"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "multi-pack-index'ten gerekli OID arama iri parçası eksik veya hasarlı"
 
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "multi-pack-index'ten gerekli nesne ofsetleri iri parçası eksik"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"multi-pack-index'ten gerekli nesne ofsetleri iri parçası eksik veya hasarlı"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "multi-pack-index pack-name iri parçası pek kısa"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17162,9 +17535,19 @@
 msgid "bad pack-int-id: %u (%u total packs)"
 msgstr "hatalı pack-int-id: %u (%u toplam paket)"
 
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX, BTMP iri parçasını içermiyor"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "biteşlemli %<PRIu32> paketi yüklenemedi"
+
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "multi-pack-index bir 64 bit ofset depoluyor; ancak off_t pek küçük"
 
+msgid "multi-pack-index large offset out of bounds"
+msgstr "multi-pack-index geniş ofseti sınırlar dışında"
+
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "paket dosyası '%s' eklenemedi"
@@ -17242,11 +17625,6 @@
 msgid "Looking for referenced packfiles"
 msgstr "Başvurulmuş paket dosyaları aranıyor"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "oid fanout sırasız: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
 msgid "the midx contains no oid"
 msgstr "midx bir oid içermiyor"
 
@@ -17766,9 +18144,12 @@
 msgid "could not open pack %s"
 msgstr "%s paketi açılamadı"
 
+msgid "could not determine MIDX preferred pack"
+msgstr "MIDX yeğlenen paketi algılanamadı"
+
 #, c-format
 msgid "preferred pack (%s) is invalid"
-msgstr "tercih edilen (%s) paket geçersiz"
+msgstr "yeğlenen paket (%s) geçersiz"
 
 msgid "corrupt bitmap lookup table: triplet position out of index"
 msgstr "hasarlı biteşlem arama tablosu: üçlü konum indeks dışında"
@@ -17786,6 +18167,10 @@
 "hasarlı ewah biteşlemi: \"%s\" işlemesinin biteşleminde kısaltılmış üstbilgi"
 
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "paket yüklenemiyor: '%s', pack-reuse devre dışı bırakılıyor"
+
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "'%s' nesnesi, tür biteşlemlerinde bulunamadı"
 
@@ -17823,6 +18208,10 @@
 msgstr "'%s' ögesinin disk kullanımı alınamadı"
 
 #, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "biteşlem dosyası '%s', geçersiz sağlama toplamına iye"
+
+#, c-format
 msgid "mtimes file %s is too small"
 msgstr "mtimes dosyası %s pek küçük"
 
@@ -17862,6 +18251,19 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "%s reverse-index dosyasının sağlama numarası %<PRIu32> desteklenmiyor"
 
+msgid "invalid checksum"
+msgstr "geçersiz sağlama toplamı"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "%<PRIu64> konumunda geçersiz rev-index konumu: %<PRIu32> != %<PRIu32>"
+
+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"
 
@@ -17913,14 +18315,6 @@
 msgstr "%s bir değer gerektiriyor"
 
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s, %s ile uyumsuz"
-
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s: başka bir şeyle uyumsuz"
-
-#, c-format
 msgid "%s takes no value"
 msgstr "%s bir değer almıyor"
 
@@ -18003,6 +18397,10 @@
 msgid "-NUM"
 msgstr "-SAYI"
 
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "--no-%s karşıtı"
+
 msgid "expiry-date"
 msgstr "son kullanım tarihi"
 
@@ -18030,7 +18428,15 @@
 msgid ""
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr ""
-"--pathspec-from-file ile, yol belirteci ögeleri NUL karakteri ile ayrılır"
+"--pathspec-from-file ile yol belirteci ögeleri NUL karakteri ile ayrılır"
+
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "hatalı Boole çevre değeri '%s', '%s' için"
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "%s ayrıştırılamadı"
 
 #, c-format
 msgid "Could not make %s writable by group"
@@ -18079,6 +18485,10 @@
 msgstr "%s: 'literal' ve 'glob' birbiriyle uyumsuz"
 
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s', dizin ağacının dışında"
+
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s: '%s', '%s' konumunda depo dışında"
 
@@ -18203,6 +18613,13 @@
 msgstr "'%s' günlüğü ayrıştırılamadı"
 
 #, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "geçersiz ek süprüntü ucu: '%s'"
+
+msgid "unable to enumerate additional recent objects"
+msgstr "ek son kullanılan nesneler numaralandırılamıyor"
+
+#, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
 msgstr "dosya arması '%s' eklenmeyecek ('%s' indekste halihazırda var)"
 
@@ -18223,10 +18640,6 @@
 msgstr "'%s' indekse eklenemiyor"
 
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "'%s' dosyasının bilgileri alınamıyor"
-
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "'%s' hem bir dosya hem de bir dizin olarak görünüyor"
 
@@ -18334,10 +18747,6 @@
 msgstr "bir sparse-index'e dönüştürülemedi"
 
 #, c-format
-msgid "could not stat '%s'"
-msgstr "'%s' bilgileri alınamadı"
-
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "git dizini açılamıyor: %s"
 
@@ -18353,6 +18762,14 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s: #0 numaralı hazırlama alanına bırakılamıyor"
 
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "beklenmedik diff durumu %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "kaldır: '%s'\n"
+
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
 "continue'.\n"
@@ -18549,6 +18966,22 @@
 msgstr "pozitif değer şunu bekliyordu: contents:lines=%s"
 
 #, c-format
+msgid "argument expected for %s"
+msgstr "%s için argüman bekleniyordu"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "pozitif değer şunu bekliyordu: %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "tümüyle ayrıştırılamıyor: %s=%s"
+
+#, c-format
+msgid "value expected %s="
+msgstr "değer şunu bekliyordu: %s="
+
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "pozitif değer şurada '%s' bekliyordu: %%(%s)"
 
@@ -18573,6 +19006,10 @@
 msgstr "pozitif genişlik %%(align) ögeciği ile birlikte bekleniyordu"
 
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "beklenen biçim: %%(ahead-behind:<işlememsi>)"
+
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "hatalı oluşturulmuş alan adı: %.*s"
 
@@ -18618,6 +19055,9 @@
 msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
 msgstr "--format=%.*s, --python, --shell ve --tcl ile kullanılamaz"
 
+msgid "failed to run 'describe'"
+msgstr "'describe' çalıştırılamadı"
+
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(dal yok, %s yeniden temellendiriliyor)"
@@ -18679,6 +19119,9 @@
 msgid "field name to sort on"
 msgstr "üzerine sıralanacak alan adı"
 
+msgid "exclude refs which match pattern"
+msgstr "dizgiyle eşleşen başvuruları dışarıda bırak"
+
 #, c-format
 msgid "not a reflog: %s"
 msgstr "bir başvuru günlüğü değil: %s"
@@ -18768,10 +19211,6 @@
 msgstr "'%s' ve '%s' aynı anda işlenemiyor"
 
 #, c-format
-msgid "could not remove reference %s"
-msgstr "%s başvurusu kaldırılamadı"
-
-#, c-format
 msgid "could not delete reference %s: %s"
 msgstr "%s başvurusu silinemedi: %s"
 
@@ -19120,8 +19559,11 @@
 "Sizin dalınız ve '%s' birbirinden uzaklaşmış ve sırasıyla\n"
 "her birinde %d ve %d işleme var.\n"
 
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (uzak dalı kendi dalınıza birleştirmek için \"git pull\" kullanın)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (uzak dalı kendi dalınızla birleştirmek istiyorsanız \"git pull\" "
+"kullanın)\n"
 
 #, c-format
 msgid "cannot parse expected object name '%s'"
@@ -19194,10 +19636,6 @@
 msgstr "'%s' için hatırlanan çözüm yok"
 
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "'%s' bağlantısı kesilemiyor"
-
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "'%s' için öngörüntü güncellendi"
 
@@ -19237,6 +19675,10 @@
 msgid "--unpacked=<packfile> no longer supported"
 msgstr "--unpacked=<paketdosyası> artık desteklenmiyor"
 
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "--stdin kipinde geçersiz seçenek '%s'"
+
 msgid "your current branch appears to be broken"
 msgstr "geçerli dalınız bozuk gibi görünüyor"
 
@@ -19322,8 +19764,15 @@
 msgid "only download metadata for the branch that will be checked out"
 msgstr "yalnızca çıkış yapılacak dalın üstverisini indir"
 
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<seçenekler>] [--] <depo> [<dizin>]"
+msgid "create repository within 'src' directory"
+msgstr "'src' dizininde depo oluştur"
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <ana-dal>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<yazılma>]"
 
 #, c-format
 msgid "cannot deduce worktree name from '%s'"
@@ -19374,12 +19823,28 @@
 msgstr "eskimiş scalar.repo '%s' kaldırılamadı"
 
 #, c-format
-msgid "removing stale scalar.repo '%s'"
-msgstr "eskimiş scalar.repo '%s' kaldırılıyor"
+msgid "removed stale scalar.repo '%s'"
+msgstr "eskimiş scalar.repo '%s' kaldırıldı"
 
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "git deposu '%s' içinde gitti"
+msgid "repository at '%s' has different owner"
+msgstr "'%s' konumundaki deponun sahibi başkası"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "'%s' konumundaki depoda bir biçim sorunu var"
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "'%s' konumunda depo bulunamadı"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"bu deponun kaydını Scalar'dan kaldırmak için şu komutu çalıştırın:\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 msgid ""
 "scalar run <task> [<enlistment>]\n"
@@ -19531,10 +19996,6 @@
 msgstr "'%s' kilitlenemedi"
 
 #, c-format
-msgid "could not write to '%s'"
-msgstr "şuraya yazılamadı: '%s'"
-
-#, c-format
 msgid "could not write eol to '%s'"
 msgstr "satır sonu şuraya yazılamadı: '%s'"
 
@@ -19784,10 +20245,6 @@
 msgstr "%s: üst işleme %s ayrıştırılamıyor"
 
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "'%s', '%s' olarak yeniden adlandırılamadı"
-
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "%s geri alınamadı... %s"
 
@@ -19894,9 +20351,6 @@
 msgid "could not create sequencer directory '%s'"
 msgstr "ardıştırıcı dizini '%s' oluşturulamadı"
 
-msgid "could not lock HEAD"
-msgstr "HEAD kilitlenemedi"
-
 msgid "no cherry-pick or revert in progress"
 msgstr "süren bir seç-al veya geri al yok"
 
@@ -19991,20 +20445,20 @@
 "\tgit rebase --continue\n"
 "\n"
 
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "ve indekse ve/veya çalışma ağacına değişiklikler yapıldı\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "ve indekse ve/veya çalışma ağacına değişiklikler yapıldı.\n"
 
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
-"Yürütme başarılı oldu: %s,\n"
-"ancak indeksinize ve/veya çalışma ağacınıza değişiklikler bıraktı\n"
+"Yürütme başarılı oldu: %s;\n"
+"ancak indeksinize ve/veya çalışma ağacınıza değişiklikler bıraktı.\n"
 "Değişikliklerinizi işleyin veya zulalayın, ardından şunu çalıştırın:\n"
 "\n"
 "\tgit rebase --continue\n"
@@ -20112,6 +20566,9 @@
 msgid "Autostash exists; creating a new stash entry."
 msgstr "Kendiliğinden zulalama mevcut; yeni bir zula girdisi oluşturuluyor."
 
+msgid "autostash reference is a symref"
+msgstr "kendiliğinden zulalama başvurusu bir sembol başvurusu"
+
 msgid "could not detach HEAD"
 msgstr "HEAD ayrılamadı"
 
@@ -20144,14 +20601,14 @@
 "\tgit rebase --continue\n"
 
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "Yeniden temellendiriliyor: (%d/%d)%s"
-
-#, c-format
 msgid "Stopped at %s...  %.*s\n"
 msgstr "%s konumunda durdu... %.*s\n"
 
 #, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Yeniden temellendiriliyor: (%d/%d)%s"
+
+#, c-format
 msgid "unknown command %d"
 msgstr "bilinmeyen komut %d"
 
@@ -20387,6 +20844,82 @@
 msgstr "setsid başarısız"
 
 #, c-format
+msgid "cannot stat template '%s'"
+msgstr "'%s' şablonunun bilgileri alınamıyor"
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "'%s' opendir yapılamıyor"
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "'%s' readlink yapılamıyor"
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "'%s', '%s' ögesine sembolik bağla bağlanamıyor"
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "'%s' şuraya kopyalanamıyor: '%s'"
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "%s şablonu yok sayılıyor"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "şablonlar %s içinde bulunamadı"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "şablonlar '%s' konumundan kopyalanmıyor: %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "geçersiz başlangıç dalı adı: '%s'"
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: --initial-branch=%s yok sayıldı"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "%d dosya türü ele alınamıyor"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "%s şuraya taşınamıyor: %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "depoyu başka bir sağlama ile yeniden ilklendirme deneniyor"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"depo başka bir başvuru depolama biçimiyle yeniden ilklendirilmeye çalışılıyor"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s halihazırda var"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "%s%s içindeki var olan paylaşılan Git deposu yeniden ilklendirildi\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "%s%s içindeki var olan Git deposu yeniden ilklendirildi\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "%s%s içinde paylaşılan boş Git deposu ilklendirildi\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "%s%s içinde boş Git deposu ilklendirildi\n"
+
+#, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
 msgstr "indeks girdisi bir dizin; ancak aralıklı değil (%08x)"
 
@@ -20438,10 +20971,6 @@
 msgstr[1] "%u bayt/sn"
 
 #, c-format
-msgid "could not edit '%s'"
-msgstr "'%s' düzenlenemedi"
-
-#, c-format
 msgid "ignoring suspicious submodule name: %s"
 msgstr "kuşku doğuran altmodül yok sayılıyor: %s"
 
@@ -20638,12 +21167,6 @@
 msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr "önbellek ağacındaki geçersizleştirilecek girdi sayısı (öntanımlı 0)"
 
-msgid "unhandled options"
-msgstr "beklenmeyen seçenekler"
-
-msgid "error preparing revisions"
-msgstr "revizyonlar hazırlanırken hata"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "%s işlemesi ulaşılabilir olarak imlenmedi"
@@ -20797,9 +21320,6 @@
 msgid "invalid remote service path"
 msgstr "geçersiz uzak konum servis yolu"
 
-msgid "operation not supported by protocol"
-msgstr "işlem protokol tarafından desteklenmiyor"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "%s altservisine bağlanılamıyor"
@@ -20930,10 +21450,6 @@
 msgstr "protokol v2 desteği henüz yerine getirilmedi"
 
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "'%s' yapılandırması için bilinmeyen değer: %s"
-
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "'%s' taşıyıcısına izin verilmiyor"
 
@@ -20986,6 +21502,9 @@
 msgid "could not retrieve server-advertised bundle-uri list"
 msgstr "sunucu tarafından tanıtılan bundle-uri listesi alınamadı"
 
+msgid "operation not supported by protocol"
+msgstr "işlem protokol tarafından desteklenmiyor"
+
 msgid "too-short tree object"
 msgstr "ağaç nesnesi çok kısa"
 
@@ -21372,6 +21891,9 @@
 msgid "unable to get current working directory"
 msgstr "geçerli çalışma dizini alınamıyor"
 
+msgid "unable to get random bytes"
+msgstr "rastgele baytlar alınamıyor"
+
 msgid "Unmerged paths:"
 msgstr "Birleştirilmemiş yollar:"
 
@@ -21806,6 +22328,10 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "%s yapılamıyor: İndeksiniz işlenmemiş değişiklikler içeriyor."
 
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "'%s' bilinmeyen biçemi şunun için verildi: '%s'"
+
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
 "merge"
@@ -21988,13 +22514,13 @@
 "Bir özet göndermek istemiyorsanız gövde kısmını temizleyin.\n"
 
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "%s açılamadı: %s"
-
-#, perl-format
 msgid "Failed to open %s.final: %s"
 msgstr "%s.final açılamadı: %s"
 
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "%s açılamadı: %s"
+
 msgid "Summary email is empty, skipping it\n"
 msgstr "Özet e-postası boş, atlanıyor\n"
 
@@ -22147,12 +22673,16 @@
 msgstr "(%s) '%s' yürütülemedi"
 
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) %s: %s, '%s' konumundan ekleniyor\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) '%s' ögesinden hatalı oluşturulmuş çıktı"
 
 #, perl-format
 msgid "(%s) failed to close pipe to '%s'"
-msgstr "(%s) şuraya olan veri yolu kapatılamadı: '%s'"
+msgstr "(%s) şuraya olan veriyolu kapatılamadı: '%s'"
+
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) %s: %s, '%s' konumundan ekleniyor\n"
 
 msgid "cannot send message as 7bit"
 msgstr "ileti 7 bit olarak gönderilemiyor"
diff --git a/po/uk.po b/po/uk.po
new file mode 100644
index 0000000..0507e38
--- /dev/null
+++ b/po/uk.po
@@ -0,0 +1,22978 @@
+# Ukrainian translation for Git package.
+# Український переклад пакету Git.
+# Copyright (c) 2023 Arkadii Yakovets.
+# Copyright (c) 2023 Kateryna Golovanova.
+# This file is distributed under the same license as the Git package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git v2.43\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2023-11-09 14:26-0800\n"
+"PO-Revision-Date: 2024-02-11 09:26-0800\n"
+"Last-Translator: Arkadii Yakovets <ark@cho.red>\n"
+"Language-Team: Ukrainian <https://github.com/arkid15r/git-uk-l10n/>\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
+"X-Generator: Poedit 3.4.2\n"
+
+#, c-format
+msgid "Huh (%s)?"
+msgstr "Га (%s)?"
+
+msgid "could not read index"
+msgstr "не вдалося прочитати індекс"
+
+msgid "binary"
+msgstr "бінарний"
+
+msgid "nothing"
+msgstr "нічого"
+
+msgid "unchanged"
+msgstr "без змін"
+
+msgid "Update"
+msgstr "Оновити"
+
+#, c-format
+msgid "could not stage '%s'"
+msgstr "не вдалося додати до індексу %s"
+
+msgid "could not write index"
+msgstr "не вдалося записати індекс"
+
+#, c-format
+msgid "updated %d path\n"
+msgid_plural "updated %d paths\n"
+msgstr[0] "оновлено %d шлях\n"
+msgstr[1] "оновлено %d шляхи\n"
+msgstr[2] "оновлено %d шляхів\n"
+
+#, c-format
+msgid "note: %s is untracked now.\n"
+msgstr "примітка: %s зараз не відстежується.\n"
+
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "невдала спроба make_cache_entry для шляху \"%s\""
+
+msgid "Revert"
+msgstr "Вивернути"
+
+msgid "Could not parse HEAD^{tree}"
+msgstr "Не вдалося розібрати HEAD^{tree}"
+
+#, c-format
+msgid "reverted %d path\n"
+msgid_plural "reverted %d paths\n"
+msgstr[0] "вивернуто %d шлях\n"
+msgstr[1] "вивернуто %d шляхи\n"
+msgstr[2] "вивернуто %d шляхів\n"
+
+#, c-format
+msgid "No untracked files.\n"
+msgstr "Невідстежуваних файлів немає\n"
+
+msgid "Add untracked"
+msgstr "Додати невідстежувані"
+
+#, c-format
+msgid "added %d path\n"
+msgid_plural "added %d paths\n"
+msgstr[0] "додано %d шлях\n"
+msgstr[1] "додано %d шляхи\n"
+msgstr[2] "додано %d шляхів\n"
+
+#, c-format
+msgid "ignoring unmerged: %s"
+msgstr "ігноруються не злиті записи: %s"
+
+#, c-format
+msgid "Only binary files changed.\n"
+msgstr "Змінилися лише бінарні файли.\n"
+
+#, c-format
+msgid "No changes.\n"
+msgstr "Нічого не змінено.\n"
+
+msgid "Patch update"
+msgstr "Оновлення латки"
+
+msgid "Review diff"
+msgstr "Переглянути різницю"
+
+msgid "show paths with changes"
+msgstr "показати шляхи зі змінами"
+
+msgid "add working tree state to the staged set of changes"
+msgstr "додати стан робочого дерева до індексу"
+
+msgid "revert staged set of changes back to the HEAD version"
+msgstr "вивернути зміни індексу до версії HEAD"
+
+msgid "pick hunks and update selectively"
+msgstr "вибирати шматки і оновлювати вибірково"
+
+msgid "view diff between HEAD and index"
+msgstr "переглянути різницю між HEAD та індексом"
+
+msgid "add contents of untracked files to the staged set of changes"
+msgstr "додати вміст невідстежуваних файлів до індексу"
+
+msgid "Prompt help:"
+msgstr "Підказка по опціям:"
+
+msgid "select a single item"
+msgstr "вибрати один елемент"
+
+msgid "select a range of items"
+msgstr "вибрати діапазон елементів"
+
+msgid "select multiple ranges"
+msgstr "вибрати кілька діапазонів"
+
+msgid "select item based on unique prefix"
+msgstr "вибрати елемент за унікальним префіксом"
+
+msgid "unselect specified items"
+msgstr "зняти позначку із зазначених елементів"
+
+msgid "choose all items"
+msgstr "вибрати всі елементи"
+
+msgid "(empty) finish selecting"
+msgstr "(пусто) закінчити вибір"
+
+msgid "select a numbered item"
+msgstr "вибрати пронумерований елемент"
+
+msgid "(empty) select nothing"
+msgstr "(пусто) нічого не вибирати"
+
+msgid "*** Commands ***"
+msgstr "*** Команди ***"
+
+msgid "What now"
+msgstr "Що тепер"
+
+msgid "staged"
+msgstr "в індексі"
+
+msgid "unstaged"
+msgstr "поза індексом"
+
+msgid "path"
+msgstr "шлях"
+
+msgid "could not refresh index"
+msgstr "не вдалося оновити індекс"
+
+#, c-format
+msgid "Bye.\n"
+msgstr "До побачення.\n"
+
+#, 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 ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"staging."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для індексації."
+
+msgid ""
+"y - stage this hunk\n"
+"n - do not stage this hunk\n"
+"q - quit; do not stage this hunk or any of the remaining ones\n"
+"a - stage this hunk and all later hunks in the file\n"
+"d - do not stage this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - індексувати цей шматок\n"
+"n - не індексувати цей шматок\n"
+"q - вийти; не індексувати ні цей шматок, ні решту\n"
+"a - індексувати цей шматок і всі наступні шматки у файлі\n"
+"d - не індексувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Stash mode change [y,n,q,a,d%s,?]? "
+msgstr "Сховати зміну режиму [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stash deletion [y,n,q,a,d%s,?]? "
+msgstr "Сховати видалення [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Stash addition [y,n,q,a,d%s,?]? "
+msgstr "Сховати додавання [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 ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"stashing."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для схову."
+
+msgid ""
+"y - stash this hunk\n"
+"n - do not stash this hunk\n"
+"q - quit; do not stash this hunk or any of the remaining ones\n"
+"a - stash this hunk and all later hunks in the file\n"
+"d - do not stash this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - сховати цей шматок\n"
+"n - не ховати цей шматок\n"
+"q - вийти; не ховати ні цей шматок, ні решту\n"
+"a - сховати цей шматок і всі наступні шматки у файлі\n"
+"d - не ховати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Unstage mode change [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу зміну режиму [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Unstage deletion [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу видалення [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Unstage addition [y,n,q,a,d%s,?]? "
+msgstr "Прибрати з індексу додавання [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 ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"unstaging."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для розіндексації."
+
+msgid ""
+"y - unstage this hunk\n"
+"n - do not unstage this hunk\n"
+"q - quit; do not unstage this hunk or any of the remaining ones\n"
+"a - unstage this hunk and all later hunks in the file\n"
+"d - do not unstage this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - прибрати з індексу цей шматок\n"
+"n - не прибирати з індексу цей шматок\n"
+"q - вийти; не прибирати з індексу ні цей шматок, ні решту\n"
+"a - прибрати з індексу цей шматок і всі наступні шматки у файлі\n"
+"d - не прибирати з індексу цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
+msgstr "Застосувати зміну режиму до індексу [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,?]? "
+
+#, c-format
+msgid "Apply addition to index [y,n,q,a,d%s,?]? "
+msgstr "Застосувати додавання до індексу [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 ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"applying."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для застосування."
+
+msgid ""
+"y - apply this hunk to index\n"
+"n - do not apply this hunk to index\n"
+"q - quit; do not apply this hunk or any of the remaining ones\n"
+"a - apply this hunk and all later hunks in the file\n"
+"d - do not apply this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - застосувати цей шматок до індексу\n"
+"n - не застосовувати цей шматок до індексу\n"
+"q - вийти; не застосовувати ні цей шматок, ні решту\n"
+"a - застосувати цей шматок і всі наступні шматки у файлі\n"
+"d - не застосовувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути зміну режиму з робочого дерева [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,?] "
+
+#, c-format
+msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути додавання з робочого дерева [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 ""
+"If the patch applies cleanly, the edited hunk will immediately be marked for "
+"discarding."
+msgstr ""
+"Якщо латка буде застосована без помилок, відредагований шматок буде одразу ж "
+"позначено для відкидання."
+
+msgid ""
+"y - discard this hunk from worktree\n"
+"n - do not discard this hunk from worktree\n"
+"q - quit; do not discard this hunk or any of the remaining ones\n"
+"a - discard this hunk and all later hunks in the file\n"
+"d - do not discard this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - відкинути цей шматок з робочого дерева\n"
+"n - не відкидати цей шматок з робочого дерева\n"
+"q - вийти; не відкидати ні цей шматок, ні решту\n"
+"a - відкинути цей шматок і всі наступні шматки у файлі\n"
+"d - не відкидати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути зміну режиму з індексу та робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути видалення з індексу та робочого дерева [y,n,q,a,d%s,?]? "
+
+#, c-format
+msgid "Discard addition from index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Відкинути додавання з індексу та робочого дерева [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 ""
+"y - discard this hunk from index and worktree\n"
+"n - do not discard this hunk from index and worktree\n"
+"q - quit; do not discard this hunk or any of the remaining ones\n"
+"a - discard this hunk and all later hunks in the file\n"
+"d - do not discard this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - відкинути цей шматок з індексу та робочого дерева\n"
+"n - не відкидати цей шматок з індексу та робочого дерева\n"
+"q - вийти; не відкидати ні цей шматок, ні решту\n"
+"a - відкинути цей шматок і всі наступні шматки у файлі\n"
+"d - не відкидати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
+msgstr ""
+"Застосувати зміну режиму до індексу та робочого дерева [y,n,q,a,d%s,?] "
+
+#, c-format
+msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати видалення до індексу та робочого дерева [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,?] "
+
+#, c-format
+msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати цей шматок до індексу та робочого дерева [y,n,q,a,d%s,?] "
+
+msgid ""
+"y - apply this hunk to index and worktree\n"
+"n - do not apply this hunk to index and worktree\n"
+"q - quit; do not apply this hunk or any of the remaining ones\n"
+"a - apply this hunk and all later hunks in the file\n"
+"d - do not apply this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - застосувати цей шматок до індексу та робочого дерева\n"
+"n - не застосовувати цей шматок до індексу та робочого дерева\n"
+"q - вийти; не застосовувати ні цей шматок, ні решту\n"
+"a - застосувати цей шматок і всі наступні шматки у файлі\n"
+"d - не застосовувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати зміну режиму до робочого дерева [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,?]? "
+
+#, c-format
+msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
+msgstr "Застосувати додавання до робочого дерева [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 ""
+"y - apply this hunk to worktree\n"
+"n - do not apply this hunk to worktree\n"
+"q - quit; do not apply this hunk or any of the remaining ones\n"
+"a - apply this hunk and all later hunks in the file\n"
+"d - do not apply this hunk or any of the later hunks in the file\n"
+msgstr ""
+"y - застосувати цей шматок до робочого дерева\n"
+"n - не застосовувати цей шматок до робочого дерева\n"
+"q - вийти; не застосовувати ні цей шматок, ні решту\n"
+"a - застосувати цей шматок і всі наступні шматки у файлі\n"
+"d - не застосовувати цей шматок і всі наступні шматки у файлі\n"
+
+#, c-format
+msgid "could not parse hunk header '%.*s'"
+msgstr "не вдалося розібрати заголовок шматка \"%.*s\""
+
+msgid "could not parse diff"
+msgstr "не вдалося розібрати різницю"
+
+msgid "could not parse colored diff"
+msgstr "не вдалося розібрати кольоровану різницю"
+
+#, c-format
+msgid "failed to run '%s'"
+msgstr "не вдалося запустити \"%s\""
+
+msgid "mismatched output from interactive.diffFilter"
+msgstr "неспівпадіння виводу з interactive.diffFilter"
+
+msgid ""
+"Your filter must maintain a one-to-one correspondence\n"
+"between its input and output lines."
+msgstr ""
+"Ваш фільтр повинен підтримувати один до одного відповідність\n"
+"між його вхідними та вихідними рядками."
+
+#, c-format
+msgid ""
+"expected context line #%d in\n"
+"%.*s"
+msgstr ""
+"очікувався рядок контексту #%d в\n"
+"%.*s"
+
+#, c-format
+msgid ""
+"hunks do not overlap:\n"
+"%.*s\n"
+"\tdoes not end with:\n"
+"%.*s"
+msgstr ""
+"шматки не перетинаються:\n"
+"%.*s\n"
+"\tне закінчується на:\n"
+"%.*s"
+
+msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
+msgstr "Режим ручного редагування шматка -- див. короткий посібник внизу.\n"
+
+#, c-format
+msgid ""
+"---\n"
+"To remove '%c' lines, make them ' ' lines (context).\n"
+"To remove '%c' lines, delete them.\n"
+"Lines starting with %c will be removed.\n"
+msgstr ""
+"---\n"
+"Щоб видалити рядки \"%c\", зробіть їх рядками \" \" (контекст).\n"
+"Щоб видалити рядки \"%c\", вилучіть їх.\n"
+"Буде видалено рядки, що починаються з %c.\n"
+
+msgid ""
+"If it does not apply cleanly, you will be given an opportunity to\n"
+"edit again.  If all lines of the hunk are removed, then the edit is\n"
+"aborted and the hunk is left unchanged.\n"
+msgstr ""
+"Якщо шматок буде застосований з помилками, вам буде надана можливість\n"
+"відредагувати ще раз.  Якщо всі рядки шматка буде вилучено, то редагування "
+"буде\n"
+"перервано, і шматок залишиться без змін.\n"
+
+msgid "could not parse hunk header"
+msgstr "не вдалося розібрати заголовок шматка"
+
+msgid "'git apply --cached' failed"
+msgstr "\"git apply --cached\" завершився невдало"
+
+#. TRANSLATORS: do not translate [y/n]
+#. The program will only accept that input at this point.
+#. Consider translating (saying "no" discards!) as
+#. (saying "n" for "no" discards!) if the translation
+#. of the word "no" does not start with n.
+#.
+msgid ""
+"Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
+msgstr ""
+"Ваш відредагований шматок неможливо застосувати. Відредагувати ще раз "
+"(\"no\" - відкинути!) [y/n]? "
+
+msgid "The selected hunks do not apply to the index!"
+msgstr "Вибрані шматки неможливо застосувати до індексу!"
+
+msgid "Apply them to the worktree anyway? "
+msgstr "Все одно застосувати їх до робочого дерева? "
+
+msgid "Nothing was applied.\n"
+msgstr "Нічого не застосовано.\n"
+
+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"
+"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"
+"? - print help\n"
+msgstr ""
+"j - залишити цей шматок невизначеним, перейти до наступного невизначеного "
+"шматка\n"
+"J - залишити цей шматок невизначеним, перейти до наступного шматка\n"
+"k - залишити цей шматок невизначеним, перейти до попереднього невизначеного "
+"шматка\n"
+"K - залишити цей шматок невизначеним, перейти до попереднього шматка\n"
+"g - вибрати шматок, до якого ви хочете перейти\n"
+"/ - шукати шматок, що відповідає заданому регвиру\n"
+"s - розбити поточний шматок на менші шматки\n"
+"e - редагувати поточний шматок вручну\n"
+"? - показати довідку\n"
+
+msgid "No previous hunk"
+msgstr "Немає попереднього шматка"
+
+msgid "No next hunk"
+msgstr "Немає наступного шматка"
+
+msgid "No other hunks to goto"
+msgstr "Немає інших шматків для переходу"
+
+msgid "go to which hunk (<ret> to see more)? "
+msgstr "до якого шматка перейти (<ret>, щоб побачити більше)? "
+
+msgid "go to which hunk? "
+msgstr "до якого шматка перейти? "
+
+#, c-format
+msgid "Invalid number: '%s'"
+msgstr "Неприпустиме число: \"%s\""
+
+#, c-format
+msgid "Sorry, only %d hunk available."
+msgid_plural "Sorry, only %d hunks available."
+msgstr[0] "Вибачайте, доступний лише %d шматок."
+msgstr[1] "Вибачайте, доступно лише %d шматки."
+msgstr[2] "Вибачайте, доступно лише %d шматків."
+
+msgid "No other hunks to search"
+msgstr "Немає інших шматків для пошуку"
+
+msgid "search for regex? "
+msgstr "шукати регвир? "
+
+#, c-format
+msgid "Malformed search regexp %s: %s"
+msgstr "Невірно сформований регвир пошуку %s: %s"
+
+msgid "No hunk matches the given pattern"
+msgstr "Жоден шматок не відповідає заданому шаблону"
+
+msgid "Sorry, cannot split this hunk"
+msgstr "Вибачайте, не можу розщепити цей шматок"
+
+#, c-format
+msgid "Split into %d hunks."
+msgstr "Розщепити на %d шматків."
+
+msgid "Sorry, cannot edit this hunk"
+msgstr "Вибачайте, не можу редагувати цей шматок"
+
+msgid "'git apply' failed"
+msgstr "\"git apply\" завершився невдало"
+
+#, c-format
+msgid ""
+"\n"
+"Disable this message with \"git config advice.%s false\""
+msgstr ""
+"\n"
+"Вимкнути це повідомлення можна за допомогою \"git config advice.%s false\""
+
+#, c-format
+msgid "%shint: %.*s%s\n"
+msgstr "%sпідказка: %.*s%s\n"
+
+msgid "Cherry-picking is not possible because you have unmerged files."
+msgstr "Висмикування неможливе, оскільки у вас є не злиті файли."
+
+msgid "Committing is not possible because you have unmerged files."
+msgstr "Створення коміту неможливе, оскільки у вас є не злиті файли."
+
+msgid "Merging is not possible because you have unmerged files."
+msgstr "Злиття неможливе, оскільки у вас є не злиті файли."
+
+msgid "Pulling is not possible because you have unmerged files."
+msgstr "Отримання неможливе, оскільки у вас є не злиті файли."
+
+msgid "Reverting is not possible because you have unmerged files."
+msgstr "Вивертання неможливе, оскільки у вас є не злиті файли."
+
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "Перебазування неможливе, оскільки у вас є не злиті файли."
+
+msgid ""
+"Fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution and make a commit."
+msgstr ""
+"Виправте їх у робочому дереві, а потім скористайтесь командою \"git add/rm "
+"<файл>\"\n"
+"щоб позначити вирішення і зробити коміт."
+
+msgid "Exiting because of an unresolved conflict."
+msgstr "Вихід через невирішений конфлікт."
+
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "Ви не завершили злиття (існує MERGE_HEAD)."
+
+msgid "Please, commit your changes before merging."
+msgstr "Будь ласка, зробіть коміт ваших змін перед злиттям."
+
+msgid "Exiting because of unfinished merge."
+msgstr "Вихід через незавершене злиття."
+
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"Гілки, що розходяться, не можуть бути перемотані вперед, вам потрібно "
+"зробити:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"або\n"
+"\n"
+"\tgit rebase\n"
+
+msgid "Not possible to fast-forward, aborting."
+msgstr "Неможливо перемотати вперед, переривання."
+
+#, c-format
+msgid ""
+"The following paths and/or pathspecs matched paths that exist\n"
+"outside of your sparse-checkout definition, so will not be\n"
+"updated in the index:\n"
+msgstr ""
+"Наступні шляхи та/або визначники шляхів відповідають шляхам, які існують\n"
+"за межами визначення вашого розрідженого переходу, тому не будуть\n"
+"оновлені в індексі:\n"
+
+msgid ""
+"If you intend to update such entries, try one of the following:\n"
+"* Use the --sparse option.\n"
+"* Disable or modify the sparsity rules."
+msgstr ""
+"Якщо ви маєте намір оновити такі записи, спробуйте один із наведених нижче "
+"способів:\n"
+"* Використайте параметр --sparse.\n"
+"* Вимкніть або змініть правила розрідженості."
+
+#, c-format
+msgid ""
+"Note: switching to '%s'.\n"
+"\n"
+"You are in 'detached HEAD' state. You can look around, make experimental\n"
+"changes and commit them, and you can discard any commits you make in this\n"
+"state without impacting any branches by switching back to a branch.\n"
+"\n"
+"If you want to create a new branch to retain commits you create, you may\n"
+"do so (now or later) by using -c with the switch command. Example:\n"
+"\n"
+"  git switch -c <new-branch-name>\n"
+"\n"
+"Or undo this operation with:\n"
+"\n"
+"  git switch -\n"
+"\n"
+"Turn off this advice by setting config variable advice.detachedHead to "
+"false\n"
+"\n"
+msgstr ""
+"Примітка: перемикання на \"%s\".\n"
+"\n"
+"Ви перебуваєте в стані \"відʼєднаного HEAD\". Ви можете озирнутися довкола, "
+"внести експериментальні\n"
+"зміни і зробити коміт, також ви можете відкинути будь-які коміти, зроблені у "
+"цьому\n"
+"стані, не впливаючи на інші гілки, просто перейшовши до іншої гілки.\n"
+"\n"
+"Якщо ви хочете створити нову гілку для збереження зроблених вами комітів, ви "
+"можете\n"
+"використати (зараз або пізніше) -c з командою switch. Наприклад:\n"
+"\n"
+"  git switch -c <назва-нової-гілки\n"
+"\n"
+"Або скасувати цю операцію за допомогою:\n"
+"\n"
+"  git switch -\n"
+"\n"
+"Щоб вимкнути цю пораду, встановіть конфігураційний параметр advice."
+"detachedHead у false\n"
+
+#, c-format
+msgid ""
+"The following paths have been moved outside the\n"
+"sparse-checkout definition but are not sparse due to local\n"
+"modifications.\n"
+msgstr ""
+"Наступні шляхи, що було винесено за межі визначення розрідженого\n"
+"переходу, не є розрідженими через локальні\n"
+"зміни.\n"
+
+msgid ""
+"To correct the sparsity of these paths, do the following:\n"
+"* Use \"git add --sparse <paths>\" to update the index\n"
+"* Use \"git sparse-checkout reapply\" to apply the sparsity rules"
+msgstr ""
+"Щоб виправити розрідженість цих шляхів, виконайте наступне:\n"
+"* Використайте \"git add --sparse <шляхи>\" для оновлення індексу\n"
+"* Використайте \"git sparse-checkout reapply\", щоб застосувати правила "
+"розрідженості"
+
+msgid "cmdline ends with \\"
+msgstr "cmdline завершується символом \\"
+
+msgid "unclosed quote"
+msgstr "незакриті лапки"
+
+msgid "too many arguments"
+msgstr "забагато аргументів"
+
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "нерозпізнана опція пробільних символів \"%s\""
+
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "нерозпізнана опція ігнорування пробільних символів \"%s\""
+
+#, c-format
+msgid "options '%s' and '%s' cannot be used together"
+msgstr "опції \"%s\" і \"%s\" не можна використовувати разом"
+
+#, c-format
+msgid "'%s' outside a repository"
+msgstr "\"%s\" поза сховищем"
+
+msgid "failed to read patch"
+msgstr "не вдалося прочитати латку"
+
+msgid "patch too large"
+msgstr "латка занадто велика"
+
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "Неможливо підготувати регвир позначки часу %s"
+
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "regexec повернув %d для вводу: %s"
+
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "не вдалося знайти назву файла в рядку %d латки"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr ""
+"git apply: невірний git-diff - очікувалось /dev/null, отримано %s у рядку %d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr ""
+"git apply: невірний git-diff - невідповідна назва нового файлу в рядку %d"
+
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr ""
+"git apply: невірний git-diff - невідповідна назва старого файлу в рядку %d"
+
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply: невірний git-diff - очікувалось /dev/null у рядку %d"
+
+#, c-format
+msgid "invalid mode on line %d: %s"
+msgstr "неприпустимий режим у рядку %d: %s"
+
+#, c-format
+msgid "inconsistent header lines %d and %d"
+msgstr "невідповідні рядки заголовка %d та %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 відсутня інформація про назву файла при видаленні %d "
+"провідного компонента назви шляху (рядок %d)"
+msgstr[1] ""
+"у заголовку git diff відсутня інформація про назву файла при видаленні %d "
+"провідних компонентів назви шляху (рядок %d)"
+msgstr[2] ""
+"у заголовку git diff відсутня інформація про назву файла при видаленні %d "
+"провідних компонентів назви шляху (рядок %d)"
+
+#, c-format
+msgid "git diff header lacks filename information (line %d)"
+msgstr "у заголовку git diff відсутня інформація про назву файла (рядок %d)"
+
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount: неочікуваний рядок: %.*s"
+
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "фрагмент латки без заголовка у рядку %d: %.*s"
+
+msgid "new file depends on old contents"
+msgstr "новий файл залежить від старого вмісту"
+
+msgid "deleted file still has contents"
+msgstr "видалений файл все ще має вміст"
+
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "пошкоджена латка у рядку %d"
+
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "новий файл %s залежить від старого вмісту"
+
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "видалений файл %s все ще має вміст"
+
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** попередження: файл %s стане порожнім, але не буде видалений"
+
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "пошкоджена бінарна латка у рядку %d: %.*s"
+
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "нерозпізнана бінарна латка у рядку %d"
+
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "латка, що містить лише непотріб у рядку %d"
+
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "не вдалося прочитати символьне посилання %s"
+
+#, c-format
+msgid "unable to open or read %s"
+msgstr "не вдалося відкрити або прочитати %s"
+
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "неприпустимий початок рядка: \"%c\""
+
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "Шматок #%d успішно застосовано на позиції %d (зміщення %d рядок)."
+msgstr[1] "Шматок #%d успішно застосовано на позиції %d (зміщення %d рядки)."
+msgstr[2] "Шматок #%d успішно застосовано на позиції %d (зміщення %d рядків)."
+
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr ""
+"Контекст скорочено до (%ld/%ld), щоб застосувати фрагмент на позиції %d"
+
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+"поки йде пошук:\n"
+"%.*s"
+
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "відсутні дані бінарної латки для \"%s\""
+
+#, c-format
+msgid "cannot reverse-apply a binary patch without the reverse hunk to '%s'"
+msgstr ""
+"неможливо виконати reverse-apply бінарної латки без зворотнього шматка до "
+"\"%s\""
+
+#, c-format
+msgid "cannot apply binary patch to '%s' without full index line"
+msgstr ""
+"неможливо застосувати бінарну латку до \"%s\" без повного індексного рядка"
+
+#, c-format
+msgid ""
+"the patch applies to '%s' (%s), which does not match the current contents."
+msgstr ""
+"латка застосовується до \"%s\" (%s), який не збігається з поточним вмістом."
+
+#, c-format
+msgid "the patch applies to an empty '%s' but it is not empty"
+msgstr "латка застосовується до порожнього \"%s\", який не є порожнім"
+
+#, c-format
+msgid "the necessary postimage %s for '%s' cannot be read"
+msgstr "неможливо прочитати необхідний postimage %s для \"%s\""
+
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "бінарна латка не застосовується до \"%s\""
+
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr ""
+"бінарна латка для \"%s\" призводить до некоректного результату (очікувалось "
+"%s, отримано %s)"
+
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "латання не вдалося: %s:%ld"
+
+#, c-format
+msgid "cannot checkout %s"
+msgstr "неможливо переключити стан %s"
+
+#, c-format
+msgid "failed to read %s"
+msgstr "не вдалося прочитати %s"
+
+#, c-format
+msgid "reading from '%s' beyond a symbolic link"
+msgstr "читання з \"%s\" за символьним посиланням"
+
+#, c-format
+msgid "path %s has been renamed/deleted"
+msgstr "шлях %s перейменовано/видалено"
+
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s: не існує в індексі"
+
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: не співпадає з індексом"
+
+msgid "repository lacks the necessary blob to perform 3-way merge."
+msgstr ""
+"у сховищі відсутній необхідний blob для виконання тристороннього злиття."
+
+#, c-format
+msgid "Performing three-way merge...\n"
+msgstr "Виконання тристороннього злиття...\n"
+
+#, c-format
+msgid "cannot read the current contents of '%s'"
+msgstr "неможливо прочитати поточний вміст \"%s\""
+
+#, c-format
+msgid "Failed to perform three-way merge...\n"
+msgstr "Не вдалося виконати тристороннє злиття...\n"
+
+#, c-format
+msgid "Applied patch to '%s' with conflicts.\n"
+msgstr "Латка до \"%s\" застосована з конфліктами.\n"
+
+#, c-format
+msgid "Applied patch to '%s' cleanly.\n"
+msgstr "Латка до \"%s\" застосована чисто.\n"
+
+#, c-format
+msgid "Falling back to direct application...\n"
+msgstr "Повернення до прямого застосування...\n"
+
+msgid "removal patch leaves file contents"
+msgstr "латка видалення залишає вміст файлу"
+
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s: невірний тип"
+
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s має тип %o, очікувалось %o"
+
+#, c-format
+msgid "invalid path '%s'"
+msgstr "неприпустимий шлях \"%s\""
+
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: вже існує в індексі"
+
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: вже існує в робочій директорії"
+
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "новий режим (%o) для %s не відповідає старому режиму (%o)"
+
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "новий режим (%o) для %s не відповідає старому режиму (%o) для %s"
+
+#, c-format
+msgid "affected file '%s' is beyond a symbolic link"
+msgstr "уражений файл \"%s\" знаходиться за межами символічного посилання"
+
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s: латка не може бути застосована"
+
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Перевірка латки %s..."
+
+#, c-format
+msgid "sha1 information is lacking or useless for submodule %s"
+msgstr "sha1 інформація відсутня або марна для підмодуля %s"
+
+#, c-format
+msgid "mode change for %s, which is not in current HEAD"
+msgstr "зміна режиму для %s, якого немає в поточному HEAD"
+
+#, c-format
+msgid "sha1 information is lacking or useless (%s)."
+msgstr "sha1 інформація відсутня або марна (%s)."
+
+#, c-format
+msgid "could not add %s to temporary index"
+msgstr "не вдалося додати %s до тимчасового індексу"
+
+#, c-format
+msgid "could not write temporary index to %s"
+msgstr "не вдалося записати тимчасовий індекс у %s"
+
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "не вдалося видалити %s з індексу"
+
+#, c-format
+msgid "corrupt patch for submodule %s"
+msgstr "пошкоджена латка для підмодуля %s"
+
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "не вдалося отримати інформацію про щойно створений файл \"%s\""
+
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "не вдалося зробити запис для щойно створеного файлу %s"
+
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "не вдалося додати запис кешу для %s"
+
+#, c-format
+msgid "failed to write to '%s'"
+msgstr "не вдалося записати в \"%s\""
+
+#, c-format
+msgid "closing file '%s'"
+msgstr "закриття файлу \"%s\""
+
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "не вдалося записати файл \"%s\" в режимі %o"
+
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Латка %s застосована чисто."
+
+msgid "internal error"
+msgstr "внутрішня помилка"
+
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "Застосування латки %%s з %d відкиданням..."
+msgstr[1] "Застосування латки %%s з %d відкиданнями..."
+msgstr[2] "Застосування латки %%s з %d відкиданнями..."
+
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "скорочення назви файлу .rej до %.*s.rej"
+
+#, c-format
+msgid "cannot open %s"
+msgstr "неможливо відкрити %s"
+
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "неможливо видалити \"%s\""
+
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Шматок #%d застосовано чисто."
+
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "Шматок #%d відкинуто."
+
+#, c-format
+msgid "Skipped patch '%s'."
+msgstr "Пропущена латка \"%s\"."
+
+msgid "No valid patches in input (allow with \"--allow-empty\")"
+msgstr "Немає коректних латок на вході (дозволити через \"--allow-empty\")"
+
+msgid "unable to read index file"
+msgstr "не вдалося прочитати індексний файл"
+
+#, c-format
+msgid "can't open patch '%s': %s"
+msgstr "неможливо відкрити латку \"%s\": %s"
+
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "ігнорована %d помилка пробільних символів"
+msgstr[1] "ігноровані %d помилки пробільних символів"
+msgstr[2] "ігноровані %d помилок пробільних символів"
+
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d рядок додає помилки пробільних символів."
+msgstr[1] "%d рядки додають помилки пробільних символів."
+msgstr[2] "%d рядків додають помилки пробільних символів."
+
+#, c-format
+msgid "%d line applied after fixing whitespace errors."
+msgid_plural "%d lines applied after fixing whitespace errors."
+msgstr[0] "%d рядок застосовано після виправлення помилок пробільних символів."
+msgstr[1] "%d рядки застосовано після виправлення помилок пробільних символів."
+msgstr[2] ""
+"%d рядків застосовано після виправлення помилок пробільних символів."
+
+msgid "Unable to write new index file"
+msgstr "Не вдалося записати новий файл індексу"
+
+msgid "don't apply changes matching the given path"
+msgstr "не застосовувати зміни, що відповідають вказаному шляху"
+
+msgid "apply changes matching the given path"
+msgstr "застосувати зміни, що відповідають вказаному шляху"
+
+msgid "num"
+msgstr "число"
+
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr "видалити <num> перших слешів з традиційних diff-шляхів"
+
+msgid "ignore additions made by the patch"
+msgstr "ігнорувати доповнення, зроблені латкою"
+
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "замість застосування латки вивести diffstat для вхідних даних"
+
+msgid "show number of added and deleted lines in decimal notation"
+msgstr "показати кількість доданих і видалених рядків у десятковій нотації"
+
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "замість застосування латки вивести підсумок для вхідних даних"
+
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "замість застосування латки перевірити можливість її застосування"
+
+msgid "make sure the patch is applicable to the current index"
+msgstr "переконатися, що латка може бути застосовна до поточного індексу"
+
+msgid "mark new files with `git add --intent-to-add`"
+msgstr "позначити нові файли командою \"git add --intent-to-add\""
+
+msgid "apply a patch without touching the working tree"
+msgstr "застосувати латку, не торкаючись робочого дерева"
+
+msgid "accept a patch that touches outside the working area"
+msgstr "прийняти латку, яка виходить за межі робочого простору"
+
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "також застосувати латку (використовуйте з --stat/--summary/--check)"
+
+msgid "attempt three-way merge, fall back on normal patch if that fails"
+msgstr ""
+"спробувати тристороннє злиття, повернутися до звичайного латання, якщо це не "
+"вдасться"
+
+msgid "build a temporary index based on embedded index information"
+msgstr "створити тимчасовий індекс на основі вбудованої індексної інформації"
+
+msgid "paths are separated with NUL character"
+msgstr "шляхи відокремлюються символом NUL"
+
+msgid "ensure at least <n> lines of context match"
+msgstr "забезпечити збіг принаймні <n> рядків контексту"
+
+msgid "action"
+msgstr "дія"
+
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "виявляти нові або змінені рядки з помилками пробільних символів"
+
+msgid "ignore changes in whitespace when finding context"
+msgstr "ігнорувати зміни пробільних символів при пошуку контексту"
+
+msgid "apply the patch in reverse"
+msgstr "застосувати латку у зворотному порядку"
+
+msgid "don't expect at least one line of context"
+msgstr "не очікувати на хоча б один рядок контексту"
+
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "залишити відкинуті шматки у відповідних *.rej файлах"
+
+msgid "allow overlapping hunks"
+msgstr "дозволити перекриття шматків"
+
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "дозволяти некоректно виявлену відсутність нового рядка в кінці файлу"
+
+msgid "do not trust the line counts in the hunk headers"
+msgstr "не довіряти кількості рядків у заголовках шматків"
+
+msgid "root"
+msgstr "корінь"
+
+msgid "prepend <root> to all filenames"
+msgstr "додати <корінь> до всіх назв файлів"
+
+msgid "don't return error for empty patches"
+msgstr "не повертати помилку для порожніх латок"
+
+#, c-format
+msgid "cannot stream blob %s"
+msgstr "неможливо транслювати blob %s"
+
+#, c-format
+msgid "unsupported file mode: 0%o (SHA1: %s)"
+msgstr "непідтримуваний режим файлу: 0%o (SHA1: %s)"
+
+#, c-format
+msgid "deflate error (%d)"
+msgstr "помилка пакування (%d)"
+
+#, c-format
+msgid "unable to start '%s' filter"
+msgstr "не вдалося запустити \"%s\" фільтр"
+
+msgid "unable to redirect descriptor"
+msgstr "не вдалося перенаправити дескриптор"
+
+#, c-format
+msgid "'%s' filter reported error"
+msgstr "Фільтр \"%s\" повідомив про помилку"
+
+#, c-format
+msgid "path is not valid UTF-8: %s"
+msgstr "шлях не є припустимим UTF-8: %s"
+
+#, c-format
+msgid "path too long (%d chars, SHA1: %s): %s"
+msgstr "шлях занадто довгий (%d символів, SHA1: %s): %s"
+
+#, c-format
+msgid "timestamp too large for this system: %<PRIuMAX>"
+msgstr "позначка часу занадто велика для цієї системи: %<PRIuMAX>"
+
+msgid "git archive [<options>] <tree-ish> [<path>...]"
+msgstr "git archive [<опції>] <деревоподібне-джерело> [<шлях>...]"
+
+msgid ""
+"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"
+msgstr ""
+"git archive --remote <сховище> [--exec <команда>] [<опції>] <деревоподібне-"
+"джерело> [<шлях>...]"
+
+msgid "git archive --remote <repo> [--exec <cmd>] --list"
+msgstr "git archive --remote <сховище> [--exec <команда>] --list"
+
+#, c-format
+msgid "cannot read '%s'"
+msgstr "неможливо прочитати \"%s\""
+
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "визначник шляху \"%s\" відповідає файлам поза поточною директорією"
+
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "визначник шляxу \"%s\" не відповідає жодному файлу"
+
+#, c-format
+msgid "no such ref: %.*s"
+msgstr "немає такого посилання: %.*s"
+
+#, c-format
+msgid "not a valid object name: %s"
+msgstr "невірне ім’я об’єкта: %s"
+
+#, c-format
+msgid "not a tree object: %s"
+msgstr "не є об’єктом дерева: %s"
+
+#, c-format
+msgid "File not found: %s"
+msgstr "Файл не знайдено: %s"
+
+#, c-format
+msgid "Not a regular file: %s"
+msgstr "Не звичайний файл: %s"
+
+#, c-format
+msgid "unclosed quote: '%s'"
+msgstr "незакриті лапки: \"%s\""
+
+#, c-format
+msgid "missing colon: '%s'"
+msgstr "відсутня двокрапка: \"%s\""
+
+#, c-format
+msgid "empty file name: '%s'"
+msgstr "порожня назва файлу: \"%s\""
+
+msgid "fmt"
+msgstr "fmt"
+
+msgid "archive format"
+msgstr "формат архіву"
+
+msgid "prefix"
+msgstr "префікс"
+
+msgid "prepend prefix to each pathname in the archive"
+msgstr "додати префікс до кожної назви шляху в архіві"
+
+msgid "file"
+msgstr "файл"
+
+msgid "add untracked file to archive"
+msgstr "додати невідстежуваний файл до архіву"
+
+msgid "path:content"
+msgstr "шлях:вміст"
+
+msgid "write the archive to this file"
+msgstr "записати архів до цього файлу"
+
+msgid "read .gitattributes in working directory"
+msgstr "прочитати .gitattributes робочої директорії"
+
+msgid "report archived files on stderr"
+msgstr "звітувати про заархівовані файли в stderr"
+
+msgid "time"
+msgstr "час"
+
+msgid "set modification time of archive entries"
+msgstr "встановити час модифікації архівних записів"
+
+msgid "set compression level"
+msgstr "встановити рівень компресії"
+
+msgid "list supported archive formats"
+msgstr "показати список підтримуваних форматів архівів"
+
+msgid "repo"
+msgstr "сховище"
+
+msgid "retrieve the archive from remote repository <repo>"
+msgstr "отримати архів з віддаленого <сховища>"
+
+msgid "command"
+msgstr "команда"
+
+msgid "path to the remote git-upload-archive command"
+msgstr "шлях до віддаленої команди git-upload-archive"
+
+msgid "Unexpected option --remote"
+msgstr "Неочікувана опція --remote"
+
+#, c-format
+msgid "the option '%s' requires '%s'"
+msgstr "опція \"%s\" потребує \"%s\""
+
+msgid "Unexpected option --output"
+msgstr "Неочікувана опція --output"
+
+#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "зайвий параметр командного рядка: \"%s\""
+
+#, c-format
+msgid "Unknown archive format '%s'"
+msgstr "Невідомий формат архіву \"%s\""
+
+#, c-format
+msgid "Argument not supported for format '%s': -%d"
+msgstr "Аргумент не підтримується для формату \"%s\": -%d"
+
+#, c-format
+msgid "%.*s is not a valid attribute name"
+msgstr "%.*s не є припустимою назвою атрибута"
+
+msgid "unable to add additional attribute"
+msgstr "не вдалося додати додатковий атрибут"
+
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "ігнорування надто довгого рядка атрибутів %d"
+
+#, c-format
+msgid "%s not allowed: %s:%d"
+msgstr "%s не дозволено: %s:%d"
+
+msgid ""
+"Negative patterns are ignored in git attributes\n"
+"Use '\\!' for literal leading exclamation."
+msgstr ""
+"Негативні шаблони ігноруються в git атрибутах\n"
+"Використовуйте \"\\!\" для додання знака оклику."
+
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "неможливо виконати fstat для файла git атрибутів \"%s\""
+
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "ігнорування надто великого файлу gitattributes \"%s\""
+
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "ігнорування надто великих gitattributes blob \"%s\""
+
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "невірний --attr-source або GIT_ATTR_SOURCE"
+
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "не вдалося виконати stat для \"%s\""
+
+#, c-format
+msgid "unable to read %s"
+msgstr "не вдалося прочитати %s"
+
+#, c-format
+msgid "Badly quoted content in file '%s': %s"
+msgstr "Невірно процитований вміст у файлі \"%s\": %s"
+
+#, c-format
+msgid "We cannot bisect more!\n"
+msgstr "Подальше бісектування неможливо!\n"
+
+#, c-format
+msgid "Not a valid commit name %s"
+msgstr "Не є дійсною назвою коміту %s"
+
+#, c-format
+msgid ""
+"The merge base %s is bad.\n"
+"This means the bug has been fixed between %s and [%s].\n"
+msgstr ""
+"База злиття %s є невірною.\n"
+"Це означає, що помилку було виправлено між %s та [%s].\n"
+
+#, c-format
+msgid ""
+"The merge base %s is new.\n"
+"The property has changed between %s and [%s].\n"
+msgstr ""
+"База злиття %s є новою.\n"
+"Властивість змінилася між %s та [%s].\n"
+
+#, c-format
+msgid ""
+"The merge base %s is %s.\n"
+"This means the first '%s' commit is between %s and [%s].\n"
+msgstr ""
+"Базою злиття %s є %s.\n"
+"Це означає, що перший \"%s\" коміт знаходиться між %s і [%s].\n"
+
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"Деякі %s revs не є предками %s rev.\n"
+"git bisect не може працювати належним чином в таких випадках.\n"
+"Можливо, ви переплутали %s і %s revs?\n"
+
+#, c-format
+msgid ""
+"the merge base between %s and [%s] must be skipped.\n"
+"So we cannot be sure the first %s commit is between %s and %s.\n"
+"We continue anyway."
+msgstr ""
+"базу злиття між %s і [%s] потрібно пропустити.\n"
+"Тому ми не можемо бути впевнені, що перший коміт %s знаходиться між %s і "
+"%s.\n"
+"Ми все одно продовжимо."
+
+#, c-format
+msgid "Bisecting: a merge base must be tested\n"
+msgstr "Бісекція: база злиття повинна бути протестована\n"
+
+#, c-format
+msgid "a %s revision is needed"
+msgstr "необхідна ревізія %s"
+
+#, c-format
+msgid "could not create file '%s'"
+msgstr "не вдалося створити файл \"%s\""
+
+#, c-format
+msgid "could not read file '%s'"
+msgstr "не вдалося прочитати файл \"%s\""
+
+msgid "reading bisect refs failed"
+msgstr "не вдалося прочитати бісекційні посилання"
+
+#, c-format
+msgid "%s was both %s and %s\n"
+msgstr "%s був одночасно і %s, і %s\n"
+
+#, c-format
+msgid ""
+"No testable commit found.\n"
+"Maybe you started with bad path arguments?\n"
+msgstr ""
+"Не знайдено коміт для тестування.\n"
+"Можливо, ви почали з невірних аргументів шляху?\n"
+
+#, c-format
+msgid "(roughly %d step)"
+msgid_plural "(roughly %d steps)"
+msgstr[0] "(приблизно %d крок)"
+msgstr[1] "(приблизно %d кроки)"
+msgstr[2] "(приблизно %d кроків)"
+
+#. TRANSLATORS: the last %s will be replaced with "(roughly %d
+#. steps)" translation.
+#.
+#, c-format
+msgid "Bisecting: %d revision left to test after this %s\n"
+msgid_plural "Bisecting: %d revisions left to test after this %s\n"
+msgstr[0] "Бісекція: залишилося протестувати %d ревізію після цього %s\n"
+msgstr[1] "Бісекція: залишилося протестувати %d ревізії після цього %s\n"
+msgstr[2] "Бісекція: залишилося протестувати %d ревізій після цього %s\n"
+
+msgid "--contents and --reverse do not blend well."
+msgstr "--contents і --reverse поєднуються не дуже добре."
+
+msgid "--reverse and --first-parent together require specified latest commit"
+msgstr "--reverse і --first-parent разом вимагають вказівки останнього коміту"
+
+msgid "revision walk setup failed"
+msgstr "не вдалося налаштувати проходження по ревізіям"
+
+msgid ""
+"--reverse --first-parent together require range along first-parent chain"
+msgstr ""
+"--reverse --first-parent разом вимагають вказівки діапазону вздовж ланцюжка "
+"першого батька"
+
+#, c-format
+msgid "no such path %s in %s"
+msgstr "немає шляху %s в %s"
+
+#, c-format
+msgid "cannot read blob %s for path %s"
+msgstr "неможливо прочитати blob %s для шляху %s"
+
+msgid ""
+"cannot inherit upstream tracking configuration of multiple refs when "
+"rebasing is requested"
+msgstr ""
+"неможливо успадкувати першоджерельну конфігурацію відстежування кількох "
+"посилань при запиті перебазування"
+
+#, c-format
+msgid "not setting branch '%s' as its own upstream"
+msgstr "гілку \"%s\" не встановлено власним першоджерельним сховищем"
+
+#, c-format
+msgid "branch '%s' set up to track '%s' by rebasing."
+msgstr "гілку \"%s\" налаштовано на відстежування \"%s\" через перебазування."
+
+#, c-format
+msgid "branch '%s' set up to track '%s'."
+msgstr "гілку \"%s\" налаштовано на відстежування \"%s\"."
+
+#, c-format
+msgid "branch '%s' set up to track:"
+msgstr "гілку \"%s\" налаштовано на відстежування:"
+
+msgid "unable to write upstream branch configuration"
+msgstr "не вдалося записати конфігурацію висхідної гілки"
+
+msgid ""
+"\n"
+"After fixing the error cause you may try to fix up\n"
+"the remote tracking information by invoking:"
+msgstr ""
+"\n"
+"Після усунення причини помилки ви можете спробувати виправити\n"
+"інформацію про віддалене відстежування здійснівши виклик:"
+
+#, c-format
+msgid "asked to inherit tracking from '%s', but no remote is set"
+msgstr ""
+"просили успадкувати відстежування з \"%s\", але віддалене призначення не "
+"встановлено"
+
+#, c-format
+msgid "asked to inherit tracking from '%s', but no merge configuration is set"
+msgstr ""
+"просили успадкувати відстежування з \"%s\", але конфігурацію злиття не задано"
+
+#, c-format
+msgid "not tracking: ambiguous information for ref '%s'"
+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
+#. around.
+#.
+#, c-format
+msgid "  %s\n"
+msgstr "  %s\n"
+
+#. TRANSLATORS: The second argument is a \n-delimited list of
+#. duplicate refspecs, composed above.
+#.
+#, c-format
+msgid ""
+"There are multiple remotes whose fetch refspecs map to the remote\n"
+"tracking ref '%s':\n"
+"%s\n"
+"This is typically a configuration error.\n"
+"\n"
+"To support setting up tracking branches, ensure that\n"
+"different remotes' fetch refspecs map into different\n"
+"tracking namespaces."
+msgstr ""
+"Існує кілька віддалених призначень, визначники отримання посилань яких "
+"розвʼязуються у віддалено \n"
+"відстежуване посилання \"%s\":\n"
+"%s\n"
+"Зазвичай це помилка конфігурації.\n"
+"\n"
+"Щоб підтримувати налаштування гілок відстежування, переконайтеся, що\n"
+"визначники отримання посилань різних віддалених призначень розвʼязуються у "
+"різні\n"
+"простори імен відстежування."
+
+#, c-format
+msgid "'%s' is not a valid branch name"
+msgstr "\"%s\" не є допустимою назвою гілки"
+
+#, c-format
+msgid "a branch named '%s' already exists"
+msgstr "гілка з ім’ям \"%s\" вже існує"
+
+#, c-format
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr ""
+"неможливо примусово оновити гілку \"%s\", яка використовується робочим "
+"деревом у \"%s\""
+
+#, c-format
+msgid "cannot set up tracking information; starting point '%s' is not a branch"
+msgstr ""
+"неможливо налаштувати інформацію про відстежування; початкова точка \"%s\" "
+"не є гілкою"
+
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "запитувана висхідна гілка \"%s\" не існує"
+
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"Якщо ви плануєте базувати свою роботу на основі першоджерельної\n"
+"гілки, яка вже існує на віддаленому призначенні, вам може знадобитися\n"
+"запустити \"git fetch\", щоб отримати її.\n"
+"\n"
+"Якщо ви плануєте надіслати нову локальну гілку, яка\n"
+"буде відстежувати свою віддалену гілку, вам слід скористатися командою\n"
+"\"git push -u\", щоб встановити конфігурацію першоджерельного сховища під "
+"час надсилання."
+
+#, c-format
+msgid "not a valid object name: '%s'"
+msgstr "не є допустимою назвою об’єкта: \"%s\""
+
+#, c-format
+msgid "ambiguous object name: '%s'"
+msgstr "неоднозначна назва об’єкта: \"%s\""
+
+#, c-format
+msgid "not a valid branch point: '%s'"
+msgstr "не є допустимою точкою розгалуження: \"%s\""
+
+#, c-format
+msgid "submodule '%s': unable to find submodule"
+msgstr "підмодуль \"%s\": не вдалося знайти підмодуль"
+
+#, c-format
+msgid ""
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
+msgstr ""
+"Ви можете спробувати оновити підмодулі за допомогою \"git checkout --no-"
+"recurse-submodules %s && git submodule update --init\"."
+
+#, c-format
+msgid "submodule '%s': cannot create branch '%s'"
+msgstr "підмодуль \"%s\": неможливо створити гілку \"%s\""
+
+#, c-format
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "\"%s\" вже використовується робочим деревом в \"%s\""
+
+msgid "git add [<options>] [--] <pathspec>..."
+msgstr "git add [<опції>] [--] <визначник шляху>..."
+
+#, c-format
+msgid "cannot chmod %cx '%s'"
+msgstr "неможливо виконати chmod %cx \"%s\""
+
+msgid "Unstaged changes after refreshing the index:"
+msgstr "Неіндексовані зміни після оновлення індексу:"
+
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"параметр add.interactive.useBuiltin було видалено!\n"
+"Дивіться запис у \"git help config\" для більш детальної інформації."
+
+msgid "could not read the index"
+msgstr "не вдалося прочитати індекс"
+
+msgid "editing patch failed"
+msgstr "не вдалося відредагувати латку"
+
+#, c-format
+msgid "could not stat '%s'"
+msgstr "не вдалося виконати stat \"%s\""
+
+msgid "empty patch. aborted"
+msgstr "порожня латка. перервано"
+
+#, c-format
+msgid "could not apply '%s'"
+msgstr "не вдалося застосувати \"%s\""
+
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "Наступні шляхи ігноруються одним з ваших .gitignore файлів:\n"
+
+msgid "dry run"
+msgstr "пробний запуск"
+
+msgid "be verbose"
+msgstr "розгорнутий вивід"
+
+msgid "interactive picking"
+msgstr "інтерактивний вибір"
+
+msgid "select hunks interactively"
+msgstr "обирати шматки інтерактивно"
+
+msgid "edit current diff and apply"
+msgstr "відредагувати поточну різницю і застосувати"
+
+msgid "allow adding otherwise ignored files"
+msgstr "дозволити додавання ігнорованих файлів"
+
+msgid "update tracked files"
+msgstr "оновити відстежувані файли"
+
+msgid "renormalize EOL of tracked files (implies -u)"
+msgstr "перенормувати EOL відстежуваних файлів (мається на увазі -u)"
+
+msgid "record only the fact that the path will be added later"
+msgstr "записати лише той факт, що шлях буде додано пізніше"
+
+msgid "add changes from all tracked and untracked files"
+msgstr "додати зміни з усіх відстежуваних і невідстежуваних файлів"
+
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr "ігнорувати шляхи, видалені у робочому дереві (те саме, що й --no-all)"
+
+msgid "don't add, only refresh the index"
+msgstr "не додавати, лише оновити індекс"
+
+msgid "just skip files which cannot be added because of errors"
+msgstr "просто пропустити файли, які не можуть бути додані через помилки"
+
+msgid "check if - even missing - files are ignored in dry run"
+msgstr ""
+"перевірити, чи ігноруються файли, навіть якщо вони відсутні, під час "
+"пробного запуску"
+
+msgid "allow updating entries outside of the sparse-checkout cone"
+msgstr "дозволити оновлення записів за межами конуса розрідженого переходу"
+
+msgid "override the executable bit of the listed files"
+msgstr "перевизначити біт виконання для зазначених файлів"
+
+msgid "warn when adding an embedded repository"
+msgstr "попереджувати при додаванні вбудованого сховища"
+
+#, c-format
+msgid ""
+"You've added another git repository inside your current repository.\n"
+"Clones of the outer repository will not contain the contents of\n"
+"the embedded repository and will not know how to obtain it.\n"
+"If you meant to add a submodule, use:\n"
+"\n"
+"\tgit submodule add <url> %s\n"
+"\n"
+"If you added this path by mistake, you can remove it from the\n"
+"index with:\n"
+"\n"
+"\tgit rm --cached %s\n"
+"\n"
+"See \"git help submodule\" for more information."
+msgstr ""
+"Ви додали ще одне git сховище всередині вашого поточного сховища.\n"
+"Клони зовнішнього сховища не міститимуть вмісту\n"
+"вбудованого сховища і не знатимуть, як його отримати.\n"
+"Якщо ви хочете додати підмодуль, скористайтесь командою\n"
+"\n"
+"\tgit submodule add <url> %s\n"
+"\n"
+"Якщо ви додали цей шлях помилково, ви можете видалити його з\n"
+"індексу за допомогою:\n"
+"\n"
+"\tgit rm --cached %s\n"
+"\n"
+"Докладніше дивіться у \"git help submodule\"."
+
+#, c-format
+msgid "adding embedded git repository: %s"
+msgstr "додавання вбудованого git сховища: %s"
+
+msgid ""
+"Use -f if you really want to add them.\n"
+"Turn this message off by running\n"
+"\"git config advice.addIgnoredFile false\""
+msgstr ""
+"Використовуйте -f, якщо ви дійсно хочете їх додати.\n"
+"Щоб вимкнути це повідомлення, виконайте\n"
+"\"git config advice.addIgnoredFile false\""
+
+msgid "adding files failed"
+msgstr "додавання файлів завершилося невдало"
+
+#, c-format
+msgid "--chmod param '%s' must be either -x or +x"
+msgstr "--chmod параметр \"%s\" має бути -x або +x"
+
+#, c-format
+msgid "'%s' and pathspec arguments cannot be used together"
+msgstr "\"%s\" та аргументи визначника шляху не можна використовувати разом"
+
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Нічого не зазначено, нічого не додано.\n"
+
+msgid ""
+"Maybe you wanted to say 'git add .'?\n"
+"Turn this message off by running\n"
+"\"git config advice.addEmptyPathspec false\""
+msgstr ""
+"Можливо, ви хотіли вказати \"git add .\"?\n"
+"Щоб вимкнути це повідомлення, виконайте\n"
+"\"git config advice.addEmptyPathspec false\""
+
+msgid "index file corrupt"
+msgstr "індексний файл пошкоджено"
+
+msgid "unable to write new index file"
+msgstr "не вдалося записати новий файл індексу"
+
+#, c-format
+msgid "bad action '%s' for '%s'"
+msgstr "невірна дія \"%s\" для \"%s\""
+
+#, c-format
+msgid "invalid value for '%s': '%s'"
+msgstr "неприпустиме значення для \"%s\": \"%s\""
+
+#, c-format
+msgid "could not read '%s'"
+msgstr "не вдалося прочитати \"%s\""
+
+msgid "could not parse author script"
+msgstr "не вдалося розібрати author script"
+
+#, c-format
+msgid "could not parse %s"
+msgstr "не вдалося розібрати %s"
+
+#, c-format
+msgid "'%s' was deleted by the applypatch-msg hook"
+msgstr "\"%s\" було видалено applypatch-msg гачком"
+
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Невірно сформований рядок вводу: \"%s\"."
+
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Не вдалося скопіювати нотатки з \"%s\" в \"%s\""
+
+msgid "fseek failed"
+msgstr "fseek завершився невдало"
+
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "не вдалося відкрити \"%s\" для читання"
+
+#, c-format
+msgid "could not open '%s' for writing"
+msgstr "не вдалося відкрити \"%s\" для запису"
+
+#, c-format
+msgid "could not parse patch '%s'"
+msgstr "не вдалося розібрати латку \"%s\""
+
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Можна застосовувати лише одну серію StGIT латок одразу"
+
+msgid "invalid timestamp"
+msgstr "неприпустима позначка часу"
+
+msgid "invalid Date line"
+msgstr "неприпустимий рядок дати"
+
+msgid "invalid timezone offset"
+msgstr "неприпустиме зміщення часового поясу"
+
+msgid "Patch format detection failed."
+msgstr "Не вдалося визначити формат латки."
+
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "не вдалося створити директорію \"%s\""
+
+msgid "Failed to split patches."
+msgstr "Не вдалося розщепити латки."
+
+#, c-format
+msgid "When you have resolved this problem, run \"%s --continue\"."
+msgstr "Коли ви вирішите цю проблему, виконайте \"%s --continue\"."
+
+#, c-format
+msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
+msgstr ""
+"Якщо ви бажаєте пропустити цю латку, виконайте \"%s --skip\" замість цього."
+
+#, c-format
+msgid "To record the empty patch as an empty commit, run \"%s --allow-empty\"."
+msgstr ""
+"Щоб записати порожню латку як порожній коміт, виконайте \"%s --allow-empty\"."
+
+#, c-format
+msgid "To restore the original branch and stop patching, run \"%s --abort\"."
+msgstr ""
+"Щоб повернути гілку до початкового стану і зупинити латання, виконайте \"%s "
+"--abort\"."
+
+msgid "Patch sent with format=flowed; space at the end of lines might be lost."
+msgstr ""
+"Латку надіслано з параметром format=flowed; пробіли в кінці рядків можуть "
+"бути втрачені."
+
+#, c-format
+msgid "missing author line in commit %s"
+msgstr "відсутній рядок автора в коміті %s"
+
+#, c-format
+msgid "invalid ident line: %.*s"
+msgstr "неприпустимий ідентифікаційний рядок: %.*s"
+
+#, c-format
+msgid "unable to parse commit %s"
+msgstr "не вдалося розібрати коміт %s"
+
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+"У сховищі не вистачає необхідних blob-обʼєктів, щоб повернутися до "
+"тристороннього злиття."
+
+msgid "Using index info to reconstruct a base tree..."
+msgstr ""
+"Використання індексної інформації для реконструювання базового дерева..."
+
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Ви відредагували свою латку вручну?\n"
+"Це не стосується blob-обʼєктів, записаних у його індексі."
+
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "Повернення до бази латки та тристороннього злиття..."
+
+msgid "Failed to merge in the changes."
+msgstr "Не вдалося злити зміни."
+
+msgid "git write-tree failed to write a tree"
+msgstr "git write-tree не вдалося записати дерево"
+
+msgid "applying to an empty history"
+msgstr "застосування до порожньої історії"
+
+msgid "failed to write commit object"
+msgstr "не вдалося записати об’єкт коміту"
+
+#, c-format
+msgid "cannot resume: %s does not exist."
+msgstr "неможливо продовжити: %s не існує."
+
+msgid "Commit Body is:"
+msgstr "Тіло коміту:"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#.
+#, c-format
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
+msgstr ""
+"Застосувати? [y]-так / [n]-ні / [e]-редагувати / [v]-переглянути латку / [a]-"
+"застосувати все: "
+
+msgid "unable to write index file"
+msgstr "не вдалося записати індексний файл"
+
+#, c-format
+msgid "Dirty index: cannot apply patches (dirty: %s)"
+msgstr "Індекс забруднено: неможливо застосувати латки (забруднено: %s)"
+
+#, c-format
+msgid "Skipping: %.*s"
+msgstr "Пропуск: %.*s"
+
+#, c-format
+msgid "Creating an empty commit: %.*s"
+msgstr "Створення порожнього коміту: %.*s"
+
+msgid "Patch is empty."
+msgstr "Латка порожня."
+
+#, c-format
+msgid "Applying: %.*s"
+msgstr "Застосування: %.*s"
+
+msgid "No changes -- Patch already applied."
+msgstr "Без змін - латку вже застосовано."
+
+#, c-format
+msgid "Patch failed at %s %.*s"
+msgstr "Латання не вдалося на %s %.*s"
+
+msgid "Use 'git am --show-current-patch=diff' to see the failed patch"
+msgstr ""
+"Скористайтесь \"git am --show-current-patch=diff\", щоб побачити невдалу "
+"латку"
+
+msgid "No changes - recorded it as an empty commit."
+msgstr "Без змін - записаний як порожній коміт."
+
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+"Без змін - ви забули використати \"git add\"?\n"
+"Якщо нічого індексувати, є ймовірність того, що хтось інший\n"
+"вже вніс ті самі зміни; можливо, ви захочете пропустити цю латку."
+
+msgid ""
+"You still have unmerged paths in your index.\n"
+"You should 'git add' each file with resolved conflicts to mark them as "
+"such.\n"
+"You might run `git rm` on a file to accept \"deleted by them\" for it."
+msgstr ""
+"У вашому індексі все ще залишаються не злиті шляхи.\n"
+"Вам слід виконати \"git add\" для кожного файлу з розвʼязаними конфліктами, "
+"щоб позначити їх як такі.\n"
+"Ви можете виконати \"git rm\" для файлу, щоб прийняти \"видалено ними\"."
+
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Не вдалося розібрати об'єкт '%s'."
+
+msgid "failed to clean index"
+msgstr "не вдалося очистити індекс"
+
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+"Здається, ви перемістили HEAD після останньої невдачі з \"am\".\n"
+"Перемотування вперед до ORIG_HEAD не виконується"
+
+#, c-format
+msgid "failed to read '%s'"
+msgstr "не вдалося прочитати \"%s\""
+
+msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
+msgstr "git am [<опції>] [(<скринька> [<поштова директорія>)...]"
+
+msgid "git am [<options>] (--continue | --skip | --abort)"
+msgstr "git am [<опції>] (--continue | --skip | --abort)"
+
+msgid "run interactively"
+msgstr "запустити інтерактивно"
+
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "обійти pre-applypatch та applypatch-msg гачки"
+
+msgid "historical option -- no-op"
+msgstr "стара опція -- не працює"
+
+msgid "allow fall back on 3way merging if needed"
+msgstr "дозволити повернутися до тристороннього злиття у разі потреби"
+
+msgid "be quiet"
+msgstr "працювати тихесенько"
+
+msgid "add a Signed-off-by trailer to the commit message"
+msgstr "додати Signed-off-by причіп у допис до коміту"
+
+msgid "recode into utf8 (default)"
+msgstr "перекодувати в utf8 (за замовчуванням)"
+
+msgid "pass -k flag to git-mailinfo"
+msgstr "передати -k прапорець до git-mailinfo"
+
+msgid "pass -b flag to git-mailinfo"
+msgstr "передати -b прапорець до git-mailinfo"
+
+msgid "pass -m flag to git-mailinfo"
+msgstr "передати -m прапорець до git-mailinfo"
+
+msgid "pass --keep-cr flag to git-mailsplit for mbox format"
+msgstr "передати --keep-cr прапорець до git-mailsplit для формату mbox"
+
+msgid "strip everything before a scissors line"
+msgstr "прибрати все раніше відрізної лінії"
+
+msgid "pass it through git-mailinfo"
+msgstr "передати через git-mailinfo"
+
+msgid "pass it through git-apply"
+msgstr "передати через git-apply"
+
+msgid "n"
+msgstr "н"
+
+msgid "format"
+msgstr "формат"
+
+msgid "format the patch(es) are in"
+msgstr "формат латки(-ок)"
+
+msgid "override error message when patch failure occurs"
+msgstr "перевизначити повідомлення про помилку при збої латання"
+
+msgid "continue applying patches after resolving a conflict"
+msgstr "продовжити застосування латки після вирішення конфлікту"
+
+msgid "synonyms for --continue"
+msgstr "те ж саме, що й --continue"
+
+msgid "skip the current patch"
+msgstr "пропустити поточну латку"
+
+msgid "restore the original branch and abort the patching operation"
+msgstr "відновити початкову гілку і перервати операцію латання"
+
+msgid "abort the patching operation but keep HEAD where it is"
+msgstr "перервати латання, але залишити HEAD на тому місці, де він знаходиться"
+
+msgid "show the patch being applied"
+msgstr "показати латку, що застосовується"
+
+msgid "record the empty patch as an empty commit"
+msgstr "записати порожню латку як порожній коміт"
+
+msgid "lie about committer date"
+msgstr "брехати про дату комітера"
+
+msgid "use current timestamp for author date"
+msgstr "використовувати поточну мітку часу для дати автора"
+
+msgid "key-id"
+msgstr "key-id"
+
+msgid "GPG-sign commits"
+msgstr "підписати коміти GPG-підписом"
+
+msgid "how to handle empty patches"
+msgstr "як обробляти порожні латки"
+
+msgid "(internal use for git-rebase)"
+msgstr "(внутрішнє використання для git-rebase)"
+
+msgid ""
+"The -b/--binary option has been a no-op for long time, and\n"
+"it will be removed. Please do not use it anymore."
+msgstr ""
+"Опція -b/--binary вже давно не працює і\n"
+"буде видалена. Будь ласка, не використовуйте її більше."
+
+msgid "failed to read the index"
+msgstr "не вдалося прочитати індекс"
+
+#, c-format
+msgid "previous rebase directory %s still exists but mbox given."
+msgstr ""
+"попередня директорія перебазування %s все ще існує, але було надано mbox."
+
+#, c-format
+msgid ""
+"Stray %s directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+"Знайдено блукаючу директорію %s.\n"
+"скористайтесь командою \"git am --abort\", щоб вилучити її."
+
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "Наразі не виконується операція вирішення, не поновлено."
+
+msgid "interactive mode requires patches on the command line"
+msgstr "інтерактивний режим потребує латки у командному рядку"
+
+msgid "git apply [<options>] [<patch>...]"
+msgstr "git apply [<опції>] [<латка>...]"
+
+msgid "could not redirect output"
+msgstr "неможливо перенаправити вивід"
+
+msgid "git archive: Remote with no URL"
+msgstr "git archive: віддалене призначення без URL"
+
+msgid "git archive: expected ACK/NAK, got a flush packet"
+msgstr "git archive: очікувалось ACK/NAK, отримано flush-пакет"
+
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+msgid "git archive: protocol error"
+msgstr "git archive: помилка протоколу"
+
+msgid "git archive: expected a flush"
+msgstr "git archive: очікувалось flush"
+
+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-(old,good)=<термін>]    "
+"[--no-checkout] [--first-parent] [<поганий> [<добрий>...]] [--]    "
+"[<визначник шляху>...]"
+
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (добрий|поганий) [<ревізія>...]"
+
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<ревізія>|<діапазон>)...]"
+
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<коміт>]"
+
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <лог файл>"
+
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <команда> [<аргумент>...]"
+
+#, c-format
+msgid "cannot open file '%s' in mode '%s'"
+msgstr "неможливо відкрити файл \"%s\" у режимі \"%s\""
+
+#, c-format
+msgid "could not write to file '%s'"
+msgstr "не вдалося записати в файл \"%s\""
+
+#, c-format
+msgid "cannot open file '%s' for reading"
+msgstr "неможливо відкрити файл \"%s\" для читання"
+
+#, c-format
+msgid "'%s' is not a valid term"
+msgstr "\"%s\" не є допустимим терміном"
+
+#, c-format
+msgid "can't use the builtin command '%s' as a term"
+msgstr "неможливо використати вбудовану команду \"%s\" як термін"
+
+#, c-format
+msgid "can't change the meaning of the term '%s'"
+msgstr "неможливо змінити значення терміну \"%s\""
+
+msgid "please use two different terms"
+msgstr "будь ласка, використовуйте два різних терміна"
+
+#, c-format
+msgid "We are not bisecting.\n"
+msgstr "Ми не робимо бісекцію.\n"
+
+#, c-format
+msgid "'%s' is not a valid commit"
+msgstr "\"%s\" не є дійсним комітом"
+
+#, c-format
+msgid ""
+"could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
+msgstr ""
+"не вдалося переключитися на початковий HEAD \"%s\". Спробуйте \"git bisect "
+"reset <коміт>\"."
+
+#, c-format
+msgid "Bad bisect_write argument: %s"
+msgstr "Невірний bisect_write аргумент: %s"
+
+#, c-format
+msgid "couldn't get the oid of the rev '%s'"
+msgstr "не вдалося отримати oid для rev \"%s\""
+
+#, c-format
+msgid "couldn't open the file '%s'"
+msgstr "не вдалося відкрити файл \"%s\""
+
+#, c-format
+msgid "Invalid command: you're currently in a %s/%s bisect"
+msgstr "Неприпустима команда: наразі ви в процесі %s/%s бісекції"
+
+#, c-format
+msgid ""
+"You need to give me at least one %s and %s revision.\n"
+"You can use \"git bisect %s\" and \"git bisect %s\" for that."
+msgstr ""
+"Ви маєте надати принаймні одну %s та %s ревізію.\n"
+"Для цього ви можете скористатися командами \"git bisect %s\" і \"git bisect "
+"%s\"."
+
+#, c-format
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one %s and %s revision.\n"
+"You can use \"git bisect %s\" and \"git bisect %s\" for that."
+msgstr ""
+"Вам потрібно почати з \"git bisect start\".\n"
+"Потім вам потрібно надати принаймні одну %s і %s ревізію.\n"
+"Для цього ви можете скористатися командами \"git bisect %s\" і \"git bisect "
+"%s\"."
+
+#, c-format
+msgid "bisecting only with a %s commit"
+msgstr "бісекція лише з %s комітом"
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#.
+msgid "Are you sure [Y/n]? "
+msgstr "Ви впевнені [Y/n]? "
+
+msgid "status: waiting for both good and bad commits\n"
+msgstr "статус: чекаємо на добрий і поганий коміти\n"
+
+#, c-format
+msgid "status: waiting for bad commit, %d good commit known\n"
+msgid_plural "status: waiting for bad commit, %d good commits known\n"
+msgstr[0] "статус: чекаємо на поганий коміт, відомий %d добрий коміт\n"
+msgstr[1] "статус: чекаємо на поганий коміт, відомі %d добрих коміти\n"
+msgstr[2] "статус: чекаємо на поганий коміт, відомі %d добрих комітів\n"
+
+msgid "status: waiting for good commit(s), bad commit known\n"
+msgstr "статус: чекаємо на добрий коміт(и), поганий коміт відомий\n"
+
+msgid "no terms defined"
+msgstr "терміни не визначені"
+
+#, c-format
+msgid ""
+"Your current terms are %s for the old state\n"
+"and %s for the new state.\n"
+msgstr ""
+"Ваші поточні терміни: %s для старого стану\n"
+"і %s для нового стану.\n"
+
+#, c-format
+msgid ""
+"invalid argument %s for 'git bisect terms'.\n"
+"Supported options are: --term-good|--term-old and --term-bad|--term-new."
+msgstr ""
+"неприпустимий аргумент %s для \"git bisect terms\".\n"
+"Підтримувані опції: --term-good|--term-old і --term-bad|--term-new."
+
+msgid "revision walk setup failed\n"
+msgstr "не вдалося налаштувати проходження по ревізіям\n"
+
+#, c-format
+msgid "could not open '%s' for appending"
+msgstr "не вдалося відкрити \"%s\" для додавання"
+
+msgid "'' is not a valid term"
+msgstr "\"\" не є допустимим терміном"
+
+#, c-format
+msgid "unrecognized option: '%s'"
+msgstr "нерозпізнана опція: \"%s\""
+
+#, c-format
+msgid "'%s' does not appear to be a valid revision"
+msgstr "\"%s\" не є дійсною ревізією"
+
+msgid "bad HEAD - I need a HEAD"
+msgstr "невірний HEAD - потрібен HEAD"
+
+#, c-format
+msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
+msgstr ""
+"переключення на \"%s\" завершилося невдало. Спробуйте \"git bisect start "
+"<дійсна-гілка>\"."
+
+msgid "bad HEAD - strange symbolic ref"
+msgstr "невірний HEAD - дивне символьне посилання"
+
+#, c-format
+msgid "invalid ref: '%s'"
+msgstr "неприпустиме посилання: \"%s\""
+
+msgid "You need to start by \"git bisect start\"\n"
+msgstr "Вам потрібно почати з \"git bisect start\"\n"
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#.
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Ви хочете, щоб я зробив це для вас [Y/n]? "
+
+msgid "Please call `--bisect-state` with at least one argument"
+msgstr ""
+"Будь ласка, використовуйте \"--bisect-state\" з принаймні одним аргументом"
+
+#, c-format
+msgid "'git bisect %s' can take only one argument."
+msgstr "\"git bisect %s\" приймає лише один аргумент."
+
+#, c-format
+msgid "Bad rev input: %s"
+msgstr "Невірне значення ревізії: %s"
+
+#, c-format
+msgid "Bad rev input (not a commit): %s"
+msgstr "Невірне значення ревізії (не є комітом): %s"
+
+msgid "We are not bisecting."
+msgstr "Наразі ви не робите бісекцію."
+
+#, c-format
+msgid "'%s'?? what are you talking about?"
+msgstr "\"%s\"? Що ви маєте на увазі?"
+
+#, c-format
+msgid "cannot read file '%s' for replaying"
+msgstr "не вдалося прочитати файл \"%s\" для відтворення"
+
+#, c-format
+msgid "running %s\n"
+msgstr "виконання %s\n"
+
+msgid "bisect run failed: no command provided."
+msgstr "бісекція завершилася невдало: не надано команду."
+
+#, c-format
+msgid "unable to verify %s on good revision"
+msgstr "не вдалося розпізнати %s як припустиму ревізію"
+
+#, c-format
+msgid "bogus exit code %d for good revision"
+msgstr "хибний код виходу %d для доброї ревізії"
+
+#, c-format
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "бісекція завершилася невдало: код виходу %d з %s є < 0 або >= 128"
+
+#, c-format
+msgid "cannot open file '%s' for writing"
+msgstr "не вдалося відкрити файл \"%s\" для запису"
+
+msgid "bisect run cannot continue any more"
+msgstr "неможливо продовжити бісекцію"
+
+msgid "bisect run success"
+msgstr "бісекцію завершено успішно"
+
+msgid "bisect found first bad commit"
+msgstr "бісекція знайшла перший поганий коміт"
+
+#, c-format
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr ""
+"бісекція завершилася невдало: \"git bisect %s\" завершено з кодом помилки %d"
+
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "\"%s\" потребує або відсутності аргументу, або коміт"
+
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "\"%s\" потребує 0 або 1 аргумент"
+
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "\"%s\" потребує 0 аргументів"
+
+msgid "no logfile given"
+msgstr "не надано файл журналу"
+
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "\"%s\" завершено невдало: не надано команду."
+
+msgid "need a command"
+msgstr "потрібна команда"
+
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "невідома команда: \"%s\""
+
+msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git blame [<опції>] [<rev-опції>] [<ревізія>] [--] <файл>"
+
+msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
+msgstr "git annotate [<опції>] [<rev-опції>] [<ревізія>] [--] <файл>"
+
+msgid "<rev-opts> are documented in git-rev-list(1)"
+msgstr "<rev-опції> задокументовані у git-rev-list(1)"
+
+#, c-format
+msgid "expecting a color: %s"
+msgstr "очікування кольору: %s"
+
+msgid "must end with a color"
+msgstr "повинно закінчуватися кольором"
+
+#, c-format
+msgid "cannot find revision %s to ignore"
+msgstr "неможливо знайти ревізію %s для ігнорування"
+
+msgid "show blame entries as we find them, incrementally"
+msgstr "показувати blame записи по мірі того, як ми їх знаходимо, поступово"
+
+msgid "do not show object names of boundary commits (Default: off)"
+msgstr "не показувати назви об’єктів межевих комітів (за замовчуванням: off)"
+
+msgid "do not treat root commits as boundaries (Default: off)"
+msgstr "не обробляти кореневі коміти як межі (за замовчуванням: off)"
+
+msgid "show work cost statistics"
+msgstr "показати статистику вартості робіт"
+
+msgid "force progress reporting"
+msgstr "примусово звітувати про хід виконання"
+
+msgid "show output score for blame entries"
+msgstr "показати вихідний показник для blame записів"
+
+msgid "show original filename (Default: auto)"
+msgstr "показати оригінальне ім’я файлу (за замовчуванням: auto)"
+
+msgid "show original linenumber (Default: off)"
+msgstr "показувати початковий номер рядка (за замовчуванням: off)"
+
+msgid "show in a format designed for machine consumption"
+msgstr "показувати у форматі, призначеному для машинного споживання"
+
+msgid "show porcelain format with per-line commit information"
+msgstr ""
+"показати у porcelain форматі з інформацією про кожен коміт в окремому рядку"
+
+msgid "use the same output mode as git-annotate (Default: off)"
+msgstr ""
+"використовувати той самий режим виводу, що і git-annotate (за замовчуванням: "
+"off)"
+
+msgid "show raw timestamp (Default: off)"
+msgstr "показувати необроблену мітку часу (за замовчуванням: off)"
+
+msgid "show long commit SHA1 (Default: off)"
+msgstr "показувати довгу версію SHA1 коміту (за замовчуванням: off)"
+
+msgid "suppress author name and timestamp (Default: off)"
+msgstr "приховати ім’я автора та мітку часу (за замовчуванням: off)"
+
+msgid "show author email instead of name (Default: off)"
+msgstr "показувати email автора замість імені (за замовчуванням: off)"
+
+msgid "ignore whitespace differences"
+msgstr "ігнорувати різницю між пробільними символами"
+
+msgid "rev"
+msgstr "ревізія"
+
+msgid "ignore <rev> when blaming"
+msgstr "ігнорувати <ревізію> при blame"
+
+msgid "ignore revisions from <file>"
+msgstr "ігнорувати ревізії з <файлу>"
+
+msgid "color redundant metadata from previous line differently"
+msgstr "розфарбувати надлишкові метадані з попереднього рядка інакше"
+
+msgid "color lines by age"
+msgstr "розфарбувати рядки за віком"
+
+msgid "spend extra cycles to find better match"
+msgstr "витрачати додаткові цикли для пошуку кращого варіанта"
+
+msgid "use revisions from <file> instead of calling git-rev-list"
+msgstr "використовувати ревізії з <файлу> замість виклику git-rev-list"
+
+msgid "use <file>'s contents as the final image"
+msgstr "використовувати вміст <файлу> як кінцевий образ"
+
+msgid "score"
+msgstr "показник"
+
+msgid "find line copies within and across files"
+msgstr "знаходити копії рядків у файлах та між ними"
+
+msgid "find line movements within and across files"
+msgstr "знаходити переміщення рядків у файлі та між ними"
+
+msgid "range"
+msgstr "діапазон"
+
+msgid "process only line range <start>,<end> or function :<funcname>"
+msgstr ""
+"обробити тільки діапазон рядків <початок>,<кінець> або функцію :<назва-"
+"функції>"
+
+msgid "--progress can't be used with --incremental or porcelain formats"
+msgstr ""
+"--progress не можна використовувати з форматами --incremental або porcelain"
+
+#. TRANSLATORS: This string is used to tell us the
+#. maximum display width for a relative timestamp in
+#. "git blame" output.  For C locale, "4 years, 11
+#. months ago", which takes 22 places, is the longest
+#. among various forms of relative timestamps, but
+#. your language may need more or fewer display
+#. columns.
+#.
+msgid "4 years, 11 months ago"
+msgstr "4 роки, 11 місяців тому"
+
+#, c-format
+msgid "file %s has only %lu line"
+msgid_plural "file %s has only %lu lines"
+msgstr[0] "файл %s містить лише %lu рядок"
+msgstr[1] "файл %s містить лише %lu рядки"
+msgstr[2] "файл %s містить лише %lu рядків"
+
+msgid "Blaming lines"
+msgstr "Blaming рядки"
+
+msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
+msgstr "git branch [<опції>] [-r | -a] [--merged] [--no-merged]"
+
+msgid ""
+"git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
+"point>]"
+msgstr ""
+"git branch [<опції>] [-f] [--recurse-submodules] <назва-гілки> [<початкова-"
+"точка>]"
+
+msgid "git branch [<options>] [-l] [<pattern>...]"
+msgstr "git branch [<опції>] [-l] [<шаблон>...]"
+
+msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
+msgstr "git branch [<опції>] [-r] (-d | -D) <имʼя-гілки>..."
+
+msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
+msgstr "git branch [<опції>] (-m | -M) [<стара-гілка>] <нова-гілка>"
+
+msgid "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
+msgstr "git branch [<опції>] (-c | -C) [<стара-гілка>] <нова-гілка>"
+
+msgid "git branch [<options>] [-r | -a] [--points-at]"
+msgstr "git branch [<опції>] [-r | -a] [--points-at]"
+
+msgid "git branch [<options>] [-r | -a] [--format]"
+msgstr "git branch [<опції>] [-r | -a] [--format]"
+
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD"
+msgstr ""
+"видалення гілки \"%s\", яка була злита з\n"
+"         \"%s\", але ще не злита з HEAD"
+
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD"
+msgstr ""
+"утримання гілки \"%s\", яка ще не була злита з\n"
+"         \"%s\", незважаючи на те, що вже була злита з HEAD"
+
+#, c-format
+msgid "couldn't look up commit object for '%s'"
+msgstr "не вдалося знайти об’єкт коміту для \"%s\""
+
+#, c-format
+msgid "the branch '%s' is not fully merged"
+msgstr "гілка \"%s\" злита не повністю"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr ""
+"Якщо ви впевнені, що хочете її видалити, виконайте \"git branch -D %s\""
+
+msgid "update of config-file failed"
+msgstr "не вдалося оновити конфігураційний файл"
+
+msgid "cannot use -a with -d"
+msgstr "не можна використовувати -a з -d"
+
+#, c-format
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr ""
+"неможливо видалити гілку \"%s\", яка використовується робочим деревом у "
+"\"%s\""
+
+#, c-format
+msgid "remote-tracking branch '%s' not found"
+msgstr "віддалено відстежувана гілка \"%s\" не знайдена"
+
+#, c-format
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"гілка \"%s\" не знайдена.\n"
+"Ви забули --remote?"
+
+#, c-format
+msgid "branch '%s' not found"
+msgstr "гілка \"%s\" не знайдена"
+
+#, c-format
+msgid "Deleted remote-tracking branch %s (was %s).\n"
+msgstr "Видалено гілку віддаленого відстеження %s (була %s).\n"
+
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Видалено гілку %s (була %s).\n"
+
+msgid "unable to parse format string"
+msgstr "не вдалося розібрати рядок форматування"
+
+msgid "could not resolve HEAD"
+msgstr "не вдалося розвʼязати HEAD"
+
+#, c-format
+msgid "HEAD (%s) points outside of refs/heads/"
+msgstr "HEAD (%s) пунктів за межами refs/heads/"
+
+#, c-format
+msgid "branch %s is being rebased at %s"
+msgstr "гілка %s перебазується на %s"
+
+#, c-format
+msgid "branch %s is being bisected at %s"
+msgstr "гілка %s бісектується в %s"
+
+#, c-format
+msgid "HEAD of working tree %s is not updated"
+msgstr "HEAD робочого дерева %s не оновлено"
+
+#, c-format
+msgid "invalid branch name: '%s'"
+msgstr "неприпустима назва гілки: \"%s\""
+
+#, c-format
+msgid "no commit on branch '%s' yet"
+msgstr "поки що немає комітів у гілці \"%s\""
+
+#, c-format
+msgid "no branch named '%s'"
+msgstr "немає гілки з назвою \"%s\""
+
+msgid "branch rename failed"
+msgstr "не вдалося перейменувати гілку"
+
+msgid "branch copy failed"
+msgstr "не вдалося скопіювати гілку"
+
+#, c-format
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "створено копію невірно названої гілки \"%s\""
+
+#, c-format
+msgid "renamed a misnamed branch '%s' away"
+msgstr "перейменовано невірно названу гілку \"%s\""
+
+#, c-format
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "гілку перейменовано на %s, але HEAD не оновлено"
+
+msgid "branch is renamed, but update of config-file failed"
+msgstr "гілку перейменовано, але не вдалося оновити конфігураційний файл"
+
+msgid "branch is copied, but update of config-file failed"
+msgstr "гілку скопійовано, але не вдалося оновити конфігураційний файл"
+
+#, c-format
+msgid ""
+"Please edit the description for the branch\n"
+"  %s\n"
+"Lines starting with '%c' will be stripped.\n"
+msgstr ""
+"Будь ласка, відредагуйте опис гілки\n"
+"  %s\n"
+"Рядки, що починаються з \"%c\", будуть вилучені.\n"
+
+msgid "Generic options"
+msgstr "Загальні опції"
+
+msgid "show hash and subject, give twice for upstream branch"
+msgstr ""
+"показувати хеш і тему, додайте двічі, щоб застосувати для висхідної гілки"
+
+msgid "suppress informational messages"
+msgstr "приховати інформаційні повідомлення"
+
+msgid "set branch tracking configuration"
+msgstr "налаштувати конфігурацію відстежуваних гілок"
+
+msgid "do not use"
+msgstr "не використовувати"
+
+msgid "upstream"
+msgstr "першоджерельне сховище"
+
+msgid "change the upstream info"
+msgstr "змінити інформацію щодо першоджерельного сховища"
+
+msgid "unset the upstream info"
+msgstr "скинути інформацію щодо першоджерельного сховища"
+
+msgid "use colored output"
+msgstr "використовувати кольоровий вивід"
+
+msgid "act on remote-tracking branches"
+msgstr "працювати з віддалено відстежуваними гілками"
+
+msgid "print only branches that contain the commit"
+msgstr "виводити тільки гілки, що містять цей коміт"
+
+msgid "print only branches that don't contain the commit"
+msgstr "виводити тільки гілки, що не містять цей коміт"
+
+msgid "Specific git-branch actions:"
+msgstr "Специфічні git-branch дії:"
+
+msgid "list both remote-tracking and local branches"
+msgstr "показати як віддалено відстежувані, так і локальні гілки"
+
+msgid "delete fully merged branch"
+msgstr "видалити повністю злиту гілку"
+
+msgid "delete branch (even if not merged)"
+msgstr "видалити гілку (навіть не повністю злиту)"
+
+msgid "move/rename a branch and its reflog"
+msgstr "перемістити/перейменувати гілку та її журнал посилань"
+
+msgid "move/rename a branch, even if target exists"
+msgstr "перемістити/перейменувати гілку, навіть якщо призначення існує"
+
+msgid "do not output a newline after empty formatted refs"
+msgstr "не виводити новий рядок після порожніх форматованих посилань"
+
+msgid "copy a branch and its reflog"
+msgstr "скопіювати гілку та її журнал посилань"
+
+msgid "copy a branch, even if target exists"
+msgstr "скопіювати гілку, навіть якщо призначення існує"
+
+msgid "list branch names"
+msgstr "показати назви гілок"
+
+msgid "show current branch name"
+msgstr "показати назву поточної гілки"
+
+msgid "create the branch's reflog"
+msgstr "створити журнал посилань гілки"
+
+msgid "edit the description for the branch"
+msgstr "редагувати опис гілки"
+
+msgid "force creation, move/rename, deletion"
+msgstr "примусове створення, переміщення/перейменування, видалення"
+
+msgid "print only branches that are merged"
+msgstr "вивести тільки злиті гілки"
+
+msgid "print only branches that are not merged"
+msgstr "вивести тільки не злиті гілки"
+
+msgid "list branches in columns"
+msgstr "виводити гілки в стовпчиках"
+
+msgid "object"
+msgstr "обʼєкт"
+
+msgid "print only branches of the object"
+msgstr "виводити тільки гілки об’єкта"
+
+msgid "sorting and filtering are case insensitive"
+msgstr "сортувати та фільтрувати незалежно від регістру"
+
+msgid "recurse through submodules"
+msgstr "рекурсивно через підмодулі"
+
+msgid "format to use for the output"
+msgstr "формат, що використовувати для виводу"
+
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "не вдалося розпізнати HEAD як дійсне посилання"
+
+msgid "HEAD not found below refs/heads!"
+msgstr "HEAD не знайдено під refs/heads!"
+
+msgid ""
+"branch with --recurse-submodules can only be used if submodule."
+"propagateBranches is enabled"
+msgstr ""
+"гілку з --recurse-submodules можна використовувати лише якщо увімкнено "
+"submodule.propagateBranches"
+
+msgid "--recurse-submodules can only be used to create branches"
+msgstr "--recurse-submodules можна використовувати лише для створення гілок"
+
+msgid "branch name required"
+msgstr "назва гілки є обовʼязковою"
+
+msgid "cannot give description to detached HEAD"
+msgstr "неможливо надати опис відокремленому HEAD"
+
+msgid "cannot edit description of more than one branch"
+msgstr "неможливо редагувати опис більш ніж однієї гілки"
+
+msgid "cannot copy the current branch while not on any"
+msgstr "неможливо скопіювати поточну гілку, не перебуваючи на жодній з них"
+
+msgid "cannot rename the current branch while not on any"
+msgstr "неможливо перейменувати поточну гілку, не перебуваючи на жодній з них"
+
+msgid "too many branches for a copy operation"
+msgstr "забагато гілок для операції копіювання"
+
+msgid "too many arguments for a rename operation"
+msgstr "забагато аргументів для операції перейменування"
+
+msgid "too many arguments to set new upstream"
+msgstr "забагато аргументів для встановлення нового першоджерельного сховища"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch"
+msgstr ""
+"не вдалося встановити першождерельне сховище HEAD у %s, який не вказує на "
+"жодну гілку"
+
+#, c-format
+msgid "no such branch '%s'"
+msgstr "немає такої гілки \"%s\""
+
+#, c-format
+msgid "branch '%s' does not exist"
+msgstr "гілка \"%s\" не існує"
+
+msgid "too many arguments to unset upstream"
+msgstr "забагато аргументів для скидання значення першоджерельного сховища"
+
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr ""
+"не вдалося скинути значення першоджерельного сховища для HEAD, який не "
+"вказує на жодну гілку"
+
+#, c-format
+msgid "branch '%s' has no upstream information"
+msgstr "гілка \"%s\" не має інформації щодо першоджерельного сховища"
+
+msgid ""
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
+"Did you mean to use: -a|-r --list <pattern>?"
+msgstr ""
+"опції -a та -r для \"git branch\" не приймають назву гілки.\n"
+"Ви хотіли використати -a|-r --list <шаблон>?"
+
+msgid ""
+"the '--set-upstream' option is no longer supported. Please use '--track' or "
+"'--set-upstream-to' instead"
+msgstr ""
+"опція \"--set-upstream\" більше не підтримується. Будь ласка, використовуйте "
+"\"--track\" або \"--set-upstream-to\" замість неї"
+
+msgid "git version:\n"
+msgstr "версія git:\n"
+
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() завершився невдало з помилкою \"%s\" (%d)\n"
+
+msgid "compiler info: "
+msgstr "інформація щодо компілятора: "
+
+msgid "libc info: "
+msgstr "информація щодо libc: "
+
+msgid "not run from a git repository - no hooks to show\n"
+msgstr "запущено не з git сховища - немає гачків для показу\n"
+
+msgid ""
+"git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"              [--diagnose[=<mode>]]"
+msgstr ""
+"git bugreport [(-o | --output-directory) <шлях>] [(-s | --suffix) <формат>]\n"
+"              [--diagnose[=<режим>]]"
+
+msgid ""
+"Thank you for filling out a Git bug report!\n"
+"Please answer the following questions to help us understand your issue.\n"
+"\n"
+"What did you do before the bug happened? (Steps to reproduce your issue)\n"
+"\n"
+"What did you expect to happen? (Expected behavior)\n"
+"\n"
+"What happened instead? (Actual behavior)\n"
+"\n"
+"What's different between what you expected and what actually happened?\n"
+"\n"
+"Anything else you want to add:\n"
+"\n"
+"Please review the rest of the bug report below.\n"
+"You can delete any lines you don't wish to share.\n"
+msgstr ""
+"Дякуємо, що заповнили звіт про помилку в Git!\n"
+"Будь ласка, дайте відповіді на наступні питання, щоб допомогти нам зрозуміти "
+"вашу проблему.\n"
+"\n"
+"Що ви робили до того, як сталася помилка (кроки для відтворення вашої "
+"проблеми)\n"
+"\n"
+"Який результат ви очікували? (Очікувана поведінка)\n"
+"\n"
+"Що сталося замість цього? (Фактична поведінка)\n"
+"\n"
+"Чим відрізняється те, що ви очікували, від того, що сталося насправді?\n"
+"\n"
+"Будь-що інше, що ви хочете додати:\n"
+"\n"
+"Будь ласка, перегляньте решту повідомлення про ваду нижче.\n"
+"Ви можете видалити будь-які рядки, якими не хочете ділитися.\n"
+
+msgid "mode"
+msgstr "режим"
+
+msgid ""
+"create an additional zip archive of detailed diagnostics (default 'stats')"
+msgstr ""
+"створити додатковий zip-архів з детальною діагностикою (за замовчуванням "
+"\"stats\")"
+
+msgid "specify a destination for the bugreport file(s)"
+msgstr "вказати місце призначення для файла(-ів) звіта про помилку"
+
+msgid "specify a strftime format suffix for the filename(s)"
+msgstr "вказати суфікс формату strftime для назви файла(-ів)"
+
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "невідомий аргумент \"%s\""
+
+#, c-format
+msgid "could not create leading directories for '%s'"
+msgstr "не вдалося створити провідні каталоги для \"%s\""
+
+#, c-format
+msgid "unable to create diagnostics archive %s"
+msgstr "не вдалося створити архів діагностики %s"
+
+msgid "System Info"
+msgstr "Інформація про систему"
+
+msgid "Enabled Hooks"
+msgstr "Увімкнені гачки"
+
+#, c-format
+msgid "unable to write to %s"
+msgstr "не вдалося записати до %s"
+
+#, c-format
+msgid "Created new report at '%s'.\n"
+msgstr "Створено новий звіт в \"%s\".\n"
+
+msgid ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<version>] <file> <git-rev-list-args>"
+msgstr ""
+"git bundle create [-q | --quiet | --progress]\n"
+"                  [--version=<версія>] <файл> <git-rev-list-агрументи>"
+
+msgid "git bundle verify [-q | --quiet] <file>"
+msgstr "git bundle verify [-q | --quiet] <файл>"
+
+msgid "git bundle list-heads <file> [<refname>...]"
+msgstr "git bundle list-heads <файл> [<назва-посилання>...]"
+
+msgid "git bundle unbundle [--progress] <file> [<refname>...]"
+msgstr "git bundle unbundle [--progress] <файл> [<назва-посилання>...]"
+
+msgid "need a <file> argument"
+msgstr "потрібен аргумент <файл>"
+
+msgid "do not show progress meter"
+msgstr "не показувати хід виконання"
+
+msgid "show progress meter"
+msgstr "показувати хід виконання"
+
+msgid "historical; same as --progress"
+msgstr "історичний; те саме, що --progress"
+
+msgid "historical; does nothing"
+msgstr "історична; не робить нічогісенько"
+
+msgid "specify bundle format version"
+msgstr "вказати версію формату пакунка"
+
+msgid "Need a repository to create a bundle."
+msgstr "Для створення пакунка потрібне сховище."
+
+msgid "do not show bundle details"
+msgstr "не показувати деталі пакунка"
+
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s у порядку\n"
+
+msgid "Need a repository to unbundle."
+msgstr "Потрібне сховище для розділення."
+
+msgid "Unbundling objects"
+msgstr "Розділення об’єктів"
+
+#, c-format
+msgid "cannot read object %s '%s'"
+msgstr "неможливо прочитати об’єкт %s \"%s\""
+
+msgid "flush is only for --buffer mode"
+msgstr "flush тільки для режиму --buffer"
+
+msgid "empty command in input"
+msgstr "порожня команда на вході"
+
+#, c-format
+msgid "whitespace before command: '%s'"
+msgstr "пробільний символ перед командою: \"%s\""
+
+#, c-format
+msgid "%s requires arguments"
+msgstr "%s потребує аргументів"
+
+#, c-format
+msgid "%s takes no arguments"
+msgstr "%s не потребує аргументів"
+
+msgid "only one batch option may be specified"
+msgstr "можна вказати лише одну групову опцію"
+
+msgid "git cat-file <type> <object>"
+msgstr "git cat-file <тип> <об’єкт>"
+
+msgid "git cat-file (-e | -p) <object>"
+msgstr "git cat-file (-e | -p) <об’єкт>"
+
+msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>"
+msgstr "git cat-file (-t | -s) [--allow-unknown-type] <об’єкт>"
+
+msgid ""
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+msgstr ""
+"git cat-file (--textconv | --filters)\n"
+"             [<ревізія>:<шлях|деревоподібне-джерело> | --path=<шлях|"
+"деревоподібне-джерело> <ревізія>]"
+
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
+msgid "Check object existence or emit object contents"
+msgstr "Перевірити існування об’єкта або видати вміст об’єкта"
+
+msgid "check if <object> exists"
+msgstr "перевірити, чи існує <об’єкт>"
+
+msgid "pretty-print <object> content"
+msgstr "pretty-print вміст <об’єкта>"
+
+msgid "Emit [broken] object attributes"
+msgstr "Видати [пошкоджені] атрибути об’єкта"
+
+msgid "show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"
+msgstr ""
+"показати тип об’єкта (один з \"blob\", \"tree\", \"commit\", \"tag\", ...)"
+
+msgid "show object size"
+msgstr "показати розмір об’єкта"
+
+msgid "allow -s and -t to work with broken/corrupt objects"
+msgstr "дозволити -s та -t працювати з пошкодженими/зіпсованими об’єктами"
+
+msgid "use mail map file"
+msgstr "використовувати файл відповідності поштових адрес"
+
+msgid "Batch objects requested on stdin (or --batch-all-objects)"
+msgstr "Пакетний запит об’єктів з stdin (або --batch-all-objects)"
+
+msgid "show full <object> or <rev> contents"
+msgstr "показати повний вміст <об’єкта> або <ревізії>"
+
+msgid "like --batch, but don't emit <contents>"
+msgstr "як --batch, але не виводить <вміст>"
+
+msgid "stdin is NUL-terminated"
+msgstr "stdin закінчується NUL"
+
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin та stdout закінчуються NUL"
+
+msgid "read commands from stdin"
+msgstr "читати команди з stdin"
+
+msgid "with --batch[-check]: ignores stdin, batches all known objects"
+msgstr "з --batch[-check]: ігнорує stdin, групує всі відомі об’єкти"
+
+msgid "Change or optimize batch output"
+msgstr "Змінити або оптимізувати груповий вивід"
+
+msgid "buffer --batch output"
+msgstr "буферизувати --batch вивід"
+
+msgid "follow in-tree symlinks"
+msgstr "розвʼязувати символьні посилання в дереві"
+
+msgid "do not order objects before emitting them"
+msgstr "не упорядковувати об’єкти перед випуском"
+
+msgid ""
+"Emit object (blob or tree) with conversion or filter (stand-alone, or with "
+"batch)"
+msgstr ""
+"Видавати об’єкт (blob або tree) з перетворенням або фільтром (окремо або з "
+"групою)"
+
+msgid "run textconv on object's content"
+msgstr "виконати textconv над вмістом об’єкта"
+
+msgid "run filters on object's content"
+msgstr "запустити фільтри на вміст об’єкта"
+
+msgid "blob|tree"
+msgstr "blob|дерево"
+
+msgid "use a <path> for (--textconv | --filters); Not with 'batch'"
+msgstr "використати <шлях> для (--textconv | --filters); не з \"batch\""
+
+#, c-format
+msgid "'%s=<%s>' needs '%s' or '%s'"
+msgstr "\"%s=<%s>\" потребує \"%s\" або \"%s\""
+
+msgid "path|tree-ish"
+msgstr "шлях|деревоподібне-джерело"
+
+#, c-format
+msgid "'%s' requires a batch mode"
+msgstr "\"%s\" потребує групового режиму"
+
+#, c-format
+msgid "'-%c' is incompatible with batch mode"
+msgstr "\"-%c\" несумісний з груповим режимом"
+
+msgid "batch modes take no arguments"
+msgstr "групові режими не потребують аргументів"
+
+#, c-format
+msgid "<rev> required with '%s'"
+msgstr "<ревізія> є обов’язковою для \"%s\""
+
+#, c-format
+msgid "<object> required with '-%c'"
+msgstr "<об’єкт> є обов’язковим для \"-%c\""
+
+#, c-format
+msgid "only two arguments allowed in <type> <object> mode, not %d"
+msgstr "у режимі <тип> <об’єкт> дозволено лише два аргументи, а не %d"
+
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <деревоподібне-джерело>] [-a | --all | "
+"<атрибут>...] [--] <назва-шляху>..."
+
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <деревоподібне-джерело>] [-a | --all | "
+"<атрибут>...]"
+
+msgid "report all attributes set on file"
+msgstr "звітувати про всі атрибути, встановлені у файлі"
+
+msgid "use .gitattributes only from the index"
+msgstr "використовувати .gitattributes тільки з індексу"
+
+msgid "read file names from stdin"
+msgstr "зчитувати назви файлів з stdin"
+
+msgid "terminate input and output records by a NUL character"
+msgstr "завершувати вхідні та вихідні записи символом NUL"
+
+msgid "<tree-ish>"
+msgstr "<деревоподібне-джерело>"
+
+msgid "which tree-ish to check attributes at"
+msgstr "атрибути якого деревоподібного джерела перевіряти"
+
+msgid "suppress progress reporting"
+msgstr "не звітувати про хід виконання"
+
+msgid "show non-matching input paths"
+msgstr "показати неспівпадіння вхідних шляхів"
+
+msgid "ignore index when checking"
+msgstr "ігнорувати індекс під час перевірки"
+
+msgid "cannot specify pathnames with --stdin"
+msgstr "неможливо вказувати назви шляхів із --stdin"
+
+msgid "-z only makes sense with --stdin"
+msgstr "-z має сенс тільки з --stdin"
+
+msgid "no path specified"
+msgstr "шлях не зазначено"
+
+msgid "--quiet is only valid with a single pathname"
+msgstr "--quiet дійсний лише з однією назвою шляху"
+
+msgid "cannot have both --quiet and --verbose"
+msgstr "неможливо мати одночасно --quiet і --verbose"
+
+msgid "--non-matching is only valid with --verbose"
+msgstr "--non-matching діє тільки з --verbose"
+
+msgid "git check-mailmap [<options>] <contact>..."
+msgstr "git check-mailmap [<опції>] <контакт>..."
+
+msgid "also read contacts from stdin"
+msgstr "також читати контакти з stdin"
+
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr "не вдалося розібрати контакт: %s"
+
+msgid "no contacts specified"
+msgstr "контакти не вказані"
+
+msgid "git checkout--worker [<options>]"
+msgstr "git checkout--worker [<опції>]"
+
+msgid "string"
+msgstr "строка"
+
+msgid "when creating files, prepend <string>"
+msgstr "при створенні файлів додавати <строку> напочатку"
+
+msgid "git checkout-index [<options>] [--] [<file>...]"
+msgstr "git checkout-index [<опції>] [—] [<файл>...]"
+
+msgid "stage should be between 1 and 3 or all"
+msgstr "стадія має бути від 1 до 3 або all"
+
+msgid "check out all files in the index"
+msgstr "переключити стан для всіх файлів в індексі"
+
+msgid "do not skip files with skip-worktree set"
+msgstr "не пропускати файли зі встановленим skip-worktree"
+
+msgid "force overwrite of existing files"
+msgstr "примусово перезаписати існуючі файли"
+
+msgid "no warning for existing files and files not in index"
+msgstr "не попереджувати про існуючі файли та файли, яких немає в індексі"
+
+msgid "don't checkout new files"
+msgstr "не переключати стан для нових файлів"
+
+msgid "update stat information in the index file"
+msgstr "оновити статистичну інформацію в індексному файлі"
+
+msgid "read list of paths from the standard input"
+msgstr "зчитати список шляхів зі стандартного вводу"
+
+msgid "write the content to temporary files"
+msgstr "записати вміст у тимчасові файли"
+
+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\" не містить нашої версії"
+
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "шлях \"%s\" не містить їхньої версії"
+
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "шлях \"%s\" не містить всіх необхідних версій"
+
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "шлях \"%s\" не містить необхідних версій"
+
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "шлях \"%s\": неможливо злити"
+
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Неможливо додати результат злиття для \"%s\""
+
+#, c-format
+msgid "Recreated %d merge conflict"
+msgid_plural "Recreated %d merge conflicts"
+msgstr[0] "Відтворено %d конфлікт злиття"
+msgstr[1] "Відтворено %d конфлікта злиття"
+msgstr[2] "Відтворено %d конфліктів злиття"
+
+#, c-format
+msgid "Updated %d path from %s"
+msgid_plural "Updated %d paths from %s"
+msgstr[0] "Оновлено %d шлях з %s"
+msgstr[1] "Оновлено %d шляхи з %s"
+msgstr[2] "Оновлено %d шляхів з %s"
+
+#, c-format
+msgid "Updated %d path from the index"
+msgid_plural "Updated %d paths from the index"
+msgstr[0] "Оновлено %d шлях з індексу"
+msgstr[1] "Оновлено %d шляхи з індексу"
+msgstr[2] "Оновлено %d шляхів з індексу"
+
+#, c-format
+msgid "'%s' cannot be used with updating paths"
+msgstr "\"%s\" не можна використовувати зі шляхами оновлення"
+
+#, c-format
+msgid "Cannot update paths and switch to branch '%s' at the same time."
+msgstr "Неможливо одночасно оновити шляхи та переключитись на гілку \"%s\"."
+
+#, c-format
+msgid "neither '%s' or '%s' is specified"
+msgstr "не вказано ні \"%s\", ні \"%s\""
+
+#, c-format
+msgid "'%s' must be used when '%s' is not specified"
+msgstr "\"%s\" повинен використовуватися, якщо не вказано \"%s\""
+
+#, c-format
+msgid "'%s' or '%s' cannot be used with %s"
+msgstr "\"%s\" або \"%s\" не можна використовувати з %s"
+
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr ""
+"\"%s\", \"%s\" або \"%s\" не можна використовувати при переключенні стану"
+
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "шлях '%s' не злитий"
+
+msgid "you need to resolve your current index first"
+msgstr "спочатку потрібно розібратись з вашим поточним індексом"
+
+#, c-format
+msgid ""
+"cannot continue with staged changes in the following files:\n"
+"%s"
+msgstr ""
+"неможливо продовжити з індексованими змінами в наступних файлах:\n"
+"%s"
+
+#, c-format
+msgid "Can not do reflog for '%s': %s\n"
+msgstr "Неможливо виконати reflog для \"%s\": %s\n"
+
+msgid "HEAD is now at"
+msgstr "HEAD зараз на"
+
+msgid "unable to update HEAD"
+msgstr "не вдалося оновити HEAD"
+
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Скинути гілку \"%s\"\n"
+
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Вже на \"%s\"\n"
+
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Переключено на та скинуто гілку '%s'\n"
+
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Переключено на нову гілку \"%s\"\n"
+
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Переключено на гілку \"%s\"\n"
+
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... та ще %d.\n"
+
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"Попередження: ви залишаєте позаду %d коміт, не підключений до\n"
+"жодної з ваших гілок:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Попередження: ви залишаєте позаду %d коміти, не підключених до\n"
+"жодної з ваших гілок:\n"
+"\n"
+"%s\n"
+msgstr[2] ""
+"Попередження: ви залишаєте позаду %d комітів, не підключених до\n"
+"жодної з ваших гілок:\n"
+"\n"
+"%s\n"
+
+#, c-format
+msgid ""
+"If you want to keep it by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch <new-branch-name> %s\n"
+"\n"
+msgid_plural ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch <new-branch-name> %s\n"
+"\n"
+msgstr[0] ""
+"Якщо ви хочете зберегти його, створивши нову гілку, то зараз є чудова "
+"нагода\n"
+"зробити це за допомогою:\n"
+"git branch <назва-нової-гілки> %s\n"
+"\n"
+msgstr[1] ""
+"Якщо ви хочете зберегти їх, створивши нову гілку, то зараз є чудова нагода\n"
+"зробити це за допомогою:\n"
+"git branch <назва-нової-гілки> %s\n"
+"\n"
+msgstr[2] ""
+"Якщо ви хочете зберегти їх, створивши нову гілку, то зараз є чудова нагода\n"
+"зробити це за допомогою:\n"
+"git branch <назва-нової-гілки> %s\n"
+"\n"
+
+msgid "internal error in revision walk"
+msgstr "внутрішня помилка при проходженні по ревізіям"
+
+msgid "Previous HEAD position was"
+msgstr "Попередня позиція HEAD була"
+
+msgid "You are on a branch yet to be born"
+msgstr "Ви на гілці, яка ще не iснує"
+
+#, c-format
+msgid ""
+"'%s' could be both a local file and a tracking branch.\n"
+"Please use -- (and optionally --no-guess) to disambiguate"
+msgstr ""
+"\"%s\" може бути як локальним файлом, так і відстежуваною гілкою.\n"
+"Будь ласка, використовуйте -- (і, за бажанням, --no-guess), для визначення"
+
+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"
+"\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"
+"\n"
+"    git checkout --track origin/<назва>.\n"
+"\n"
+"Якщо при переключенні з неоднозначною <назвою> ви бажаєте завжди віддавати "
+"перевагу\n"
+"певному віддаленому сховищу, наприклад, 'origin', розгляньте можливість "
+"додання\n"
+"checkout.defaultRemote=origin до вашого конфігураційного файлу."
+
+#, c-format
+msgid "'%s' matched multiple (%d) remote tracking branches"
+msgstr "\"%s\" відповідає кільком (%d) гілкам віддаленого відстежування"
+
+msgid "only one reference expected"
+msgstr "очікувалось тільки одне посилання"
+
+#, c-format
+msgid "only one reference expected, %d given."
+msgstr "очікувалось тільки одне посилання, надано %d."
+
+#, c-format
+msgid "invalid reference: %s"
+msgstr "неприпустиме посилання: %s"
+
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "посилання не є деревом: %s"
+
+#, c-format
+msgid "a branch is expected, got tag '%s'"
+msgstr "очікувалась гілка, надано тег \"%s\""
+
+#, c-format
+msgid "a branch is expected, got remote branch '%s'"
+msgstr "очікувалась гілка, надана віддалена гілка \"%s\""
+
+#, c-format
+msgid "a branch is expected, got '%s'"
+msgstr "очікувалась гілка, надано \"%s\""
+
+#, c-format
+msgid "a branch is expected, got commit '%s'"
+msgstr "очікувалась гілка, надано коміт \"%s\""
+
+msgid ""
+"If you want to detach HEAD at the commit, try again with the --detach option."
+msgstr ""
+"Якщо ви хочете від'єднати HEAD на цьому коміті, спробуйте ще раз з опцією --"
+"detach."
+
+msgid ""
+"cannot switch branch while merging\n"
+"Consider \"git merge --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час злиття\n"
+"Спробуйте \"git merge --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch in the middle of an am session\n"
+"Consider \"git am --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час am сеансу\n"
+"Спробуйте \"git am --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch while rebasing\n"
+"Consider \"git rebase --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час перебазування\n"
+"Спробуйте \"git rebase --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch while cherry-picking\n"
+"Consider \"git cherry-pick --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час висмикування\n"
+"Спробуйте \"git cherry-pick --quit\" або \"git worktree add\"."
+
+msgid ""
+"cannot switch branch while reverting\n"
+"Consider \"git revert --quit\" or \"git worktree add\"."
+msgstr ""
+"неможливо змінити гілку під час вивертання\n"
+"Спробуйте \"git revert --quit\" або \"git worktree add\"."
+
+msgid "you are switching branch while bisecting"
+msgstr "ви переключаєте гілку під час бісекції"
+
+msgid "paths cannot be used with switching branches"
+msgstr "шляхи не можуть використовуватись при переключенні гілок"
+
+#, c-format
+msgid "'%s' cannot be used with switching branches"
+msgstr "'%s' не може використовуватись при переключенні гілок"
+
+#, c-format
+msgid "'%s' cannot be used with '%s'"
+msgstr "'%s' не може використовуватись з '%s'"
+
+#, c-format
+msgid "'%s' cannot take <start-point>"
+msgstr "'%s' не може прийняти <стартова-точка>"
+
+#, c-format
+msgid "Cannot switch branch to a non-commit '%s'"
+msgstr "Неможливо переключити гілку на не коміт '%s'"
+
+msgid "missing branch or commit argument"
+msgstr "відсутня гілка або коміт"
+
+msgid "perform a 3-way merge with the new branch"
+msgstr "здійснити 3-стороннє злиття з новою гілкою"
+
+msgid "style"
+msgstr "стиль"
+
+msgid "conflict style (merge, diff3, or zdiff3)"
+msgstr "конфлікт стилю (merge, diff3 або zdiff3)"
+
+msgid "detach HEAD at named commit"
+msgstr "відʼєднати HEAD на вказаному коміті"
+
+msgid "force checkout (throw away local modifications)"
+msgstr "переключити примусово (викинути локальні зміни)"
+
+msgid "new-branch"
+msgstr "нова-гілка"
+
+msgid "new unborn branch"
+msgstr "нова ненароджена гілка"
+
+msgid "update ignored files (default)"
+msgstr "оновити ігноровані файли (за замовчуванням)"
+
+msgid "do not check if another worktree is holding the given ref"
+msgstr "не перевіряти, чи інше робоче дерево містить дане посилання"
+
+msgid "checkout our version for unmerged files"
+msgstr "використовувати нашу версію для не злитих файлів"
+
+msgid "checkout their version for unmerged files"
+msgstr "використовувати їхню версію для не злитих файлів"
+
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "не обмежувати визначники шляхів лише розрідженими записами"
+
+#, c-format
+msgid "options '-%c', '-%c', and '%s' cannot be used together"
+msgstr "опції \"-%c\", \"-%c\" та \"%s\" не можна використовувати разом"
+
+msgid "--track needs a branch name"
+msgstr "--track потребує назви гілки"
+
+#, c-format
+msgid "missing branch name; try -%c"
+msgstr "відсутня назва гілки; спробуйте -%c"
+
+#, c-format
+msgid "could not resolve %s"
+msgstr "не вдалося розвʼязати %s"
+
+msgid "invalid path specification"
+msgstr "неприпустиме зазначення шляху"
+
+#, c-format
+msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
+msgstr "'%s' не є комітом, і з нього не можна створити гілку '%s'"
+
+#, c-format
+msgid "git checkout: --detach does not take a path argument '%s'"
+msgstr "git checkout: --detach не приймає аргумент шляху '%s'"
+
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force та --merge несумісні при\n"
+"при прееключенні індексу."
+
+msgid "you must specify path(s) to restore"
+msgstr "необхідно вказати шлях(и) для відновлення"
+
+msgid "branch"
+msgstr "гілка"
+
+msgid "create and checkout a new branch"
+msgstr "створити та перейти до нової гілки"
+
+msgid "create/reset and checkout a branch"
+msgstr "створити/скинути та перейти до нової гілки"
+
+msgid "create reflog for new branch"
+msgstr "створити журнал посилань для нової гілки"
+
+msgid "second guess 'git checkout <no-such-branch>' (default)"
+msgstr "друга здогадка \"git checkout <гілка-не-існує>\" (за замовчуванням)"
+
+msgid "use overlay mode (default)"
+msgstr "використовувати режим накладення (за замовчуванням)"
+
+msgid "create and switch to a new branch"
+msgstr "створити та переключити на нову гілку"
+
+msgid "create/reset and switch to a branch"
+msgstr "створити/скинути та переключити на гілку"
+
+msgid "second guess 'git switch <no-such-branch>'"
+msgstr "друга здогадка \"git switch <гілка-не-існує>\" (за замовчуванням)"
+
+msgid "throw away local modifications"
+msgstr "викинути локальні зміни"
+
+msgid "which tree-ish to checkout from"
+msgstr "з якого деревоподібного джерела створювати"
+
+msgid "restore the index"
+msgstr "відновити індекс"
+
+msgid "restore the working tree (default)"
+msgstr "відновити робоче дерево (за замовчуванням)"
+
+msgid "ignore unmerged entries"
+msgstr "ігнорувати не злиті записи"
+
+msgid "use overlay mode"
+msgstr "використовувати режим накладення"
+
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
+msgstr ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <шаблон>] [-x | -X] [--] [<визначник "
+"шляху>...]"
+
+#, c-format
+msgid "Removing %s\n"
+msgstr "Видалення %s\n"
+
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Було б видалено %s\n"
+
+#, c-format
+msgid "Skipping repository %s\n"
+msgstr "Пропуск сховища %s\n"
+
+#, c-format
+msgid "Would skip repository %s\n"
+msgstr "Було б пропущено сховище %s\n"
+
+#, c-format
+msgid "failed to remove %s"
+msgstr "не вдалося видалити %s"
+
+#, c-format
+msgid "could not lstat %s\n"
+msgstr "не вдалося виконати lstat %s\n"
+
+msgid "Refusing to remove current working directory\n"
+msgstr "Відмовлено у видаленні поточної робочої директорії\n"
+
+msgid "Would refuse to remove current working directory\n"
+msgstr "Було б відмовлено у видаленні поточної робочої директорії\n"
+
+#, c-format
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing\n"
+msgstr ""
+"Підказка по опціям:\n"
+"1          - вибрати пронумерований елемент\n"
+"foo        - вибрати елемент за унікальним префіксом\n"
+"           - (порожньо) - не вибирати нічого\n"
+
+#, c-format
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting\n"
+msgstr ""
+"Підказка по опціям:\n"
+"1          - вибрати один елемент\n"
+"3-5        - вибрати діапазон елементів\n"
+"2-3,6-9    - вибрати кілька діапазонів\n"
+"foo        - вибрати елемент за унікальним префіксом\n"
+"-...       - скасувати вибір вказаних елементів\n"
+"*          - вибрати всі елементи\n"
+"           - (порожньо) завершити вибір\n"
+
+#, c-format
+msgid "Huh (%s)?\n"
+msgstr "Га (%s)?\n"
+
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr "Введіть шаблони ігнорування>> "
+
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr "ПОПЕРЕДЖЕННЯ: Не вдалося знайти елементи, що відповідають: %s"
+
+msgid "Select items to delete"
+msgstr "Виберіть елементи для видалення"
+
+#. TRANSLATORS: Make sure to keep [y/N] as is
+#, c-format
+msgid "Remove %s [y/N]? "
+msgstr "Видалити %s [y/N]? "
+
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+"clean               - розпочати прибирання\n"
+"filter by pattern   - виключити елементи з прибирання\n"
+"select by numbers   - вибрати елементи для прибирання за номерами\n"
+"ask each            - підтверджувати кожне видалення (як \"rm -i\")\n"
+"quit                - припинити прибирання\n"
+"help                - показати цей екран\n"
+"?                   - підказка для швидкого вибору"
+
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] "Видалить наступний елемент:"
+msgstr[1] "Видалить наступні елементи:"
+msgstr[2] "Видалить наступні елементи:"
+
+msgid "No more files to clean, exiting."
+msgstr "Більше немає файлів для прибирання, вихід."
+
+msgid "do not print names of files removed"
+msgstr "не виводити назви видалених файлів"
+
+msgid "force"
+msgstr "примусово"
+
+msgid "interactive cleaning"
+msgstr "інтерактивне прибирання"
+
+msgid "remove whole directories"
+msgstr "видаляти цілі директорії"
+
+msgid "pattern"
+msgstr "шаблон"
+
+msgid "add <pattern> to ignore rules"
+msgstr "додати <шаблон> в правила ігнорування"
+
+msgid "remove ignored files, too"
+msgstr "видалити також ігноровані файли"
+
+msgid "remove only ignored files"
+msgstr "видалити лише ігноровані файли"
+
+msgid ""
+"clean.requireForce set to true and neither -i, -n, nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce встановлено у true і не задано ні -i, ні -n, ні -f; "
+"відмовлено в прибиранні"
+
+msgid ""
+"clean.requireForce defaults to true and neither -i, -n, nor -f given; "
+"refusing to clean"
+msgstr ""
+"clean.requireForce встановлено у true за замовчуванням і не задано ні -i, ні "
+"-n, ні -f; відмовлено в прибиранні"
+
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<опції>] [--] <сховище> [<директорія>]"
+
+msgid "don't clone shallow repository"
+msgstr "не клонувати неглибоке сховище"
+
+msgid "don't create a checkout"
+msgstr "не переходити на гілку"
+
+msgid "create a bare repository"
+msgstr "створити порожнє сховище"
+
+msgid "create a mirror repository (implies bare)"
+msgstr "створити дзеркальне сховище (включає опцію bare)"
+
+msgid "to clone from a local repository"
+msgstr "клонувати з локального сховища"
+
+msgid "don't use local hardlinks, always copy"
+msgstr "не використовувати локальні жорсткі посилання, завжди копіювати"
+
+msgid "setup as shared repository"
+msgstr "налаштувати як спільне сховище"
+
+msgid "pathspec"
+msgstr "визначник шляху"
+
+msgid "initialize submodules in the clone"
+msgstr "ініціалізувати підмодулі в клоні"
+
+msgid "number of submodules cloned in parallel"
+msgstr "кількість підмодулів, що клонуються паралельно"
+
+msgid "template-directory"
+msgstr "директорія-шаблонів"
+
+msgid "directory from which templates will be used"
+msgstr "директорія, з якої брати шаблони"
+
+msgid "reference repository"
+msgstr "посилання на сховище"
+
+msgid "use --reference only while cloning"
+msgstr "використовуйте --reference тільки під час клонування"
+
+msgid "name"
+msgstr "назва"
+
+msgid "use <name> instead of 'origin' to track upstream"
+msgstr ""
+"використовуйте <назва> замість \"origin\" для відстежування першоджерельного "
+"сховища"
+
+msgid "checkout <branch> instead of the remote's HEAD"
+msgstr "перейти до <гілки> замість HEAD віддаленого сховища"
+
+msgid "path to git-upload-pack on the remote"
+msgstr "шлях до git-upload-pack на віддаленому сервері"
+
+msgid "depth"
+msgstr "глибина"
+
+msgid "create a shallow clone of that depth"
+msgstr "створити неглибокий клон вказаної глибини"
+
+msgid "create a shallow clone since a specific time"
+msgstr "створити неглибокий клон з певного часу"
+
+msgid "revision"
+msgstr "ревізія"
+
+msgid "deepen history of shallow clone, excluding rev"
+msgstr "поглибити історію неглибокого клону, за винятком ревізії"
+
+msgid "clone only one branch, HEAD or --branch"
+msgstr "клонувати лише одну гілку, HEAD або --branch"
+
+msgid "don't clone any tags, and make later fetches not to follow them"
+msgstr ""
+"не клонувати жодних тегів і не слідувати за ними під час отримувань пізніше"
+
+msgid "any cloned submodules will be shallow"
+msgstr "будь-які клоновані підмодулі будуть неглибокими"
+
+msgid "gitdir"
+msgstr "git директорія"
+
+msgid "separate git dir from working tree"
+msgstr "відокремити git-директорію від робочого дерева"
+
+msgid "specify the reference format to use"
+msgstr "вкажіть формат посилання, який потрібно використовувати"
+
+msgid "key=value"
+msgstr "ключ=значення"
+
+msgid "set config inside the new repository"
+msgstr "встановити конфігурацію всередині нового сховища"
+
+msgid "server-specific"
+msgstr "тільки для сервера"
+
+msgid "option to transmit"
+msgstr "опція для передачі"
+
+msgid "apply partial clone filters to submodules"
+msgstr "застосувати фільтри часткового клонування до підмодулів"
+
+msgid "any cloned submodules will use their remote-tracking branch"
+msgstr ""
+"будь-які клоновані підмодулі будуть використовувати свою віддалено "
+"відстежувану гілку"
+
+msgid "initialize sparse-checkout file to include only files at root"
+msgstr ""
+"ініціалізувати, щоб файл розрідженого переходу включав лише файли в корені"
+
+msgid "uri"
+msgstr "uri"
+
+msgid "a URI for downloading bundles before fetching from origin remote"
+msgstr "URI для завантаження пакунків перед отриманням з віддаленого джерела"
+
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "инфо: Не вдалося додати запозичений обʼєкт для \"%s\": %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "не вдалося виконати stat \"%s\""
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s існує і не є директорією"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "\"%s\" є символьним посиланням, відмовлено в клонуванні з --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "не вдалося запустити перебір для \"%s\""
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "символьне посилання \"%s\" існує, не можу клонувати з --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "не вдалося створити посилання \"%s\""
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "не вдалося скопіювати файл у \"%s\""
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "не вдалося перебрати \"%s\""
+
+#, c-format
+msgid "done.\n"
+msgstr "готово.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Клонування пройшло успішно, але не вдалося перейти на гілку.\n"
+"Ви можете перевірити, що було додано за допомогою 'git status'\n"
+"і повторити спробу за допомогою 'git restore --source=HEAD :/'\n"
+
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Не вдалося знайти віддалену гілку %s для клонування."
+
+msgid "remote did not send all necessary objects"
+msgstr "віддалене сховище не надіслало всі необхідні обʼєкти"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "не вдалося оновити %s"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "не вдалося ініціалізувати розріджений перехід"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "віддалений HEAD посилається на неіснуючого рефа, неможливо перейти"
+
+msgid "unable to checkout working tree"
+msgstr "не вдалося завантажити стан робочої директорії"
+
+msgid "unable to write parameters to config file"
+msgstr "не вдалося записати параметри до конфігураційного файлу"
+
+msgid "cannot repack to clean up"
+msgstr "неможливо перепакувати, щоб очистити"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "неможливо видалити тимчасовий файл запозичених обʼєктів"
+
+msgid "Too many arguments."
+msgstr "Забагато аргументів."
+
+msgid "You must specify a repository to clone."
+msgstr "Треба вказати сховище для клонування."
+
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "невідомий формат зберігання посилань \"%s\""
+
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "сховище \"%s\" не існує"
+
+#, c-format
+msgid "depth %s is not a positive number"
+msgstr "глибина %s не є додатнім числом"
+
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "шлях призначення \"%s\" вже існує і не є порожньою директорією."
+
+#, c-format
+msgid "repository path '%s' already exists and is not an empty directory."
+msgstr "шлях до сховища \"%s\" вже існує і не є порожньою директорією."
+
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "робоче дерево \"%s\" вже існує."
+
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "не вдалося створити провідні директорії для \"%s\""
+
+#, c-format
+msgid "could not create work tree dir '%s'"
+msgstr "не вдалося створити директорію робочого дерева \"%s\""
+
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Клонування у порожнє сховище \"%s\"...\n"
+
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Клонування в \"%s\"..\n"
+
+msgid ""
+"clone --recursive is not compatible with both --reference and --reference-if-"
+"able"
+msgstr ""
+"clone --recursive не сумісне з --reference та --reference-if-able одночасно"
+
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "\"%s\" не є дійсною назвою віддаленого сховища"
+
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+"--depth ігнорується у локальних клонах; використовуйте file:// замість цього."
+
+msgid "--shallow-since is ignored in local clones; use file:// instead."
+msgstr ""
+"--shallow-since ігнорується у локальних клонах; використовуйте file:// "
+"замість цього."
+
+msgid "--shallow-exclude is ignored in local clones; use file:// instead."
+msgstr ""
+"--shallow-exclude ігнорується у локальних клонах; використовуйте file:// "
+"замість цього."
+
+msgid "--filter is ignored in local clones; use file:// instead."
+msgstr ""
+"--filter ігнорується у локальних клонах; використовуйте file:// замість "
+"нього."
+
+msgid "source repository is shallow, reject to clone."
+msgstr "джерельне сховище є неглибоким, клонування відхилено."
+
+msgid "source repository is shallow, ignoring --local"
+msgstr "джерельне сховище є неглибоким, --local ігноровано"
+
+msgid "--local is ignored"
+msgstr "--local ігноровано"
+
+msgid "cannot clone from filtered bundle"
+msgstr "неможливо клонувати з відфільтрованого пакунка"
+
+msgid "failed to initialize the repo, skipping bundle URI"
+msgstr "не вдалося ініціалізувати сховище, URI пакунка пропущено"
+
+#, c-format
+msgid "failed to fetch objects from bundle URI '%s'"
+msgstr "не вдалося отримати обʼєкти з пакунка URI '%s'"
+
+msgid "failed to fetch advertised bundles"
+msgstr "не вдалося отримати обіцяні пакунки"
+
+msgid "remote transport reported error"
+msgstr "операція віддаленого отримання повідомила про помилку"
+
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "Віддалену гілку %s не знайдено у першоджерельному сховищі %s"
+
+msgid "You appear to have cloned an empty repository."
+msgstr "Здається, ви клонували порожнє сховище."
+
+msgid "git column [<options>]"
+msgstr "git column [<опції>]"
+
+msgid "lookup config vars"
+msgstr "пошук параметрів конфігурації"
+
+msgid "layout to use"
+msgstr "схема розташування"
+
+msgid "maximum width"
+msgstr "максимальна ширина"
+
+msgid "padding space on left border"
+msgstr "відступ по лівому краю"
+
+msgid "padding space on right border"
+msgstr "відступ по правому краю"
+
+msgid "padding space between columns"
+msgstr "відступ між стовпчиками"
+
+msgid "--command must be the first argument"
+msgstr "--command має бути першим аргументом"
+
+msgid ""
+"git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]"
+msgstr ""
+"git commit-graph verify [--object-dir <директорія>] [--shallow] [--"
+"[no-]progress]"
+
+msgid ""
+"git commit-graph write [--object-dir <dir>] [--append]\n"
+"                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
+"                       <split-options>"
+msgstr ""
+"git commit-graph write [--object-dir <директорія>] [--append] [--object-dir "
+"<директорія>] [--append] [--object-dir <директорія>] [--append\n"
+"                       [--split[=<стратегія>]] [--reachable | --stdin-packs "
+"| --stdin-commits]\n"
+"                       [--changed-paths] [--[no-]max-new-filters <число>] [--"
+"[no-]progress]\n"
+"                       <опції-розділення>"
+
+msgid "dir"
+msgstr "директорія"
+
+msgid "the object directory to store the graph"
+msgstr "директорія об’єктів для зберігання графу"
+
+msgid "if the commit-graph is split, only verify the tip file"
+msgstr "якщо коміт-граф розщеплено, перевіряти тільки файл підказок"
+
+#, c-format
+msgid "Could not open commit-graph '%s'"
+msgstr "Не вдалося відкрити коміт-граф \"%s\""
+
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "не вдалося відкрити ланцюжок коміт-графа \"%s\""
+
+#, c-format
+msgid "unrecognized --split argument, %s"
+msgstr "нерозпізнаний --split аргумент, %s"
+
+#, c-format
+msgid "unexpected non-hex object ID: %s"
+msgstr "неочікуваний не hex ідентифікатор обʼєкта: %s"
+
+#, c-format
+msgid "invalid object: %s"
+msgstr "неприпустимий об’єкт: %s"
+
+#, c-format
+msgid "option `%s' expects a numerical value"
+msgstr "опція \"%s\" очікує числове значення"
+
+msgid "start walk at all refs"
+msgstr "розпочати проходження по всім посиланням"
+
+msgid "scan pack-indexes listed by stdin for commits"
+msgstr "просканувати pack-indexes зазначені через stdin для комітів"
+
+msgid "start walk at commits listed by stdin"
+msgstr "розпочати проходження по всім комітам зазначеним через stdin"
+
+msgid "include all commits already in the commit-graph file"
+msgstr "включити всі коміти, які вже є у файлі коміт-графа"
+
+msgid "enable computation for changed paths"
+msgstr "увімкнути обчислення для змінених шляхів"
+
+msgid "allow writing an incremental commit-graph file"
+msgstr "дозволити запис інкрементного файлу коміт-графа"
+
+msgid "maximum number of commits in a non-base split commit-graph"
+msgstr "максимальна кількість комітів у безбазовому розщепленому коміт-графі"
+
+msgid "maximum ratio between two levels of a split commit-graph"
+msgstr "максимальне співвідношення між двома рівнями розщепленого коміт-графа"
+
+msgid "only expire files older than a given date-time"
+msgstr "видалити лише файли, старіші за вказану дату"
+
+msgid "maximum number of changed-path Bloom filters to compute"
+msgstr "максимальна кількість фільтрів Блума зміненого шляху для обчислення"
+
+msgid "use at most one of --reachable, --stdin-commits, or --stdin-packs"
+msgstr ""
+"використовувати щонайбільше один з --reachable, --stdin-commits або --stdin-"
+"packs"
+
+msgid "Collecting commits from input"
+msgstr "Збирання комітів з вхідних даних"
+
+msgid "git commit-tree <tree> [(-p <parent>)...]"
+msgstr "git commit-tree <дерево> [(-p <батько>)...]"
+
+msgid ""
+"git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n"
+"                [(-F <file>)...] <tree>"
+msgstr ""
+"git commit-tree [(-p <батько>)...] [-S[<ідентифікатор-ключа>]] [(-m "
+"<допис>)...]\n"
+"                [(-F <файл>)...] <дерево>"
+
+#, c-format
+msgid "duplicate parent %s ignored"
+msgstr "проігноровано дубльований батьківський %s"
+
+#, c-format
+msgid "not a valid object name %s"
+msgstr "невірне ім’я об’єкта %s"
+
+#, c-format
+msgid "git commit-tree: failed to read '%s'"
+msgstr "git commit-tree: не вдалося прочитати \"%s\""
+
+#, c-format
+msgid "git commit-tree: failed to close '%s'"
+msgstr "git commit-tree: не вдалося закрити \"%s\""
+
+msgid "parent"
+msgstr "батько"
+
+msgid "id of a parent commit object"
+msgstr "ідентифікатор обʼєкта батьківського коміту"
+
+msgid "message"
+msgstr "допис"
+
+msgid "commit message"
+msgstr "допис до коміту"
+
+msgid "read commit log message from file"
+msgstr "читати допис до коміту з файлу"
+
+msgid "GPG sign commit"
+msgstr "підписати коміт за допомогою GPG"
+
+msgid "must give exactly one tree"
+msgstr "має бути надано лишень одне дерево"
+
+msgid "git commit-tree: failed to read"
+msgstr "git commit-tree: не вдалося прочитати"
+
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+"reword):]<commit>)]\n"
+"           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"           [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"           [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+"           [--] [<pathspec>...]"
+msgstr ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u<режим>] [--amend]\n"
+"           [--dry-run] [(-c | -C | --squash) <коміт> | --fixup [(amend|"
+"reword):]<коміт>)]\n"
+"           [-F <файл> | -m <допис>] [--reset-author] [--allow-empty]\n"
+"           [--allow-empty-message] [--no-verify] [-e] [--author=<автор>]\n"
+"           [--date=<дата>] [--cleanup=<режим>] [--[no-]status]\n"
+"           [-i | -o] [--pathspec-from-file=<файл> [--pathspec-file-nul]]\n"
+"           [(--trailer <токен>[(=|:)<значення>])...] [-S[<ідентифікатор "
+"ключа>]]\n"
+"           [--] [<визначник шляху>...]"
+
+msgid "git status [<options>] [--] [<pathspec>...]"
+msgstr "git status [<опції>] [--] [<визначник шляху>...]"
+
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr ""
+"Ви просили внести зміни до останнього коміту, але це зробить його\n"
+"порожнім. Ви можете повторити команду, скориставшись --allow-empty, або\n"
+"повністю видалити коміт за допомогою \"git reset HEAD^\".\n"
+
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+msgstr ""
+"Попереднє висмикування зараз порожнє, можливо, через вирішення конфлікту.\n"
+"Якщо ви все одно бажаєте додати його, скористайтесь командою\n"
+"\n"
+"    git commit --allow-empty\n"
+
+msgid "Otherwise, please use 'git rebase --skip'\n"
+msgstr "В іншому випадку, будь ласка, скористайтесь \"git rebase --skip\"\n"
+
+msgid "Otherwise, please use 'git cherry-pick --skip'\n"
+msgstr ""
+"В іншому випадку, будь ласка, скористайтесь \"git cherry-pick --skip\"\n"
+
+msgid ""
+"and then use:\n"
+"\n"
+"    git cherry-pick --continue\n"
+"\n"
+"to resume cherry-picking the remaining commits.\n"
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git cherry-pick --skip\n"
+"\n"
+msgstr ""
+"а потім скористайтесь:\n"
+"\n"
+"    git cherry-pick --continue\n"
+"\n"
+"щоб продовжити висмикування решти комітів.\n"
+"Якщо ви хочете пропустити цей коміт, скористайтесь\n"
+"\n"
+"    git cherry-pick --skip\n"
+
+msgid "updating files failed"
+msgstr "не вдалося оновити файли"
+
+msgid "failed to unpack HEAD tree object"
+msgstr "не вдалося розпакувати HEAD обʼєкт дерева"
+
+msgid "No paths with --include/--only does not make sense."
+msgstr "Ніякі шляхи з --include/--only не мають сенсу."
+
+msgid "unable to create temporary index"
+msgstr "не вдалося створити тимчасовий індекс"
+
+msgid "interactive add failed"
+msgstr "інтерактивне додавання не вдалося"
+
+msgid "unable to update temporary index"
+msgstr "не вдалося оновити тимчасовий індекс"
+
+msgid "Failed to update main cache tree"
+msgstr "Не вдалося оновити головне дерево кешу"
+
+msgid "cannot do a partial commit during a merge."
+msgstr "неможливо зробити частковий коміт під час злиття."
+
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "неможливо зробити частковий коміт під час висмикування."
+
+msgid "cannot do a partial commit during a rebase."
+msgstr "неможливо зробити частковий коміт під час перебазування."
+
+msgid "cannot read the index"
+msgstr "неможливо прочитати індекс"
+
+msgid "unable to write temporary index file"
+msgstr "не вдалося записати тимчасовий файл індексу"
+
+#, c-format
+msgid "commit '%s' lacks author header"
+msgstr "у коміті \"%s\" немає заголовка автора"
+
+#, c-format
+msgid "commit '%s' has malformed author line"
+msgstr "у коміті \"%s\" невірно сформовано рядок автора"
+
+msgid "malformed --author parameter"
+msgstr "невірно сформований --author параметр"
+
+#, c-format
+msgid "invalid date format: %s"
+msgstr "неприпустимий формат дати: %s"
+
+msgid ""
+"unable to select a comment character that is not used\n"
+"in the current commit message"
+msgstr ""
+"не вдалося вибрати символ коментаря, який не використовується\n"
+"у поточному дописі до коміту"
+
+#, c-format
+msgid "could not lookup commit '%s'"
+msgstr "не вдалося знайти коміт \"%s\""
+
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(читання допису журналу зі стандартного вводу)\n"
+
+msgid "could not read log from standard input"
+msgstr "не вдалося прочитати допис зі стандартного вводу"
+
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "не вдалося прочитати допис з файлу \"%s\""
+
+#, c-format
+msgid "options '%s' and '%s:%s' cannot be used together"
+msgstr "опції \"%s\" та \"%s:%s\" не можна використовувати разом"
+
+msgid "could not read SQUASH_MSG"
+msgstr "не вдалося прочитати SQUASH_MSG"
+
+msgid "could not read MERGE_MSG"
+msgstr "не вдалося прочитати MERGE_MSG"
+
+#, c-format
+msgid "could not open '%s'"
+msgstr "не вдалося відкрити \"%s\""
+
+msgid "could not write commit template"
+msgstr "не вдалося записати шаблон комітів"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be ignored.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть проігноровані.\n"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть проігноровані, а порожній допис перерве коміт.\n"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be kept; you may remove them yourself if you want to.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть збережені; ви можете вилучити їх самостійно, якщо захочете.\n"
+
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту для ваших змін. Рядки, що починаються з\n"
+" \"%c\" будуть збережені; ви можете вилучити їх самостійно, якщо захочете.\n"
+"Порожній допис перериває коміт.\n"
+
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please run\n"
+"\tgit update-ref -d MERGE_HEAD\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Схоже, що ви робите злиття.\n"
+"Якщо це не так, виконайте команду\n"
+"\tgit update-ref -d MERGE_HEAD\n"
+"і спробуйте ще раз.\n"
+
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please run\n"
+"\tgit update-ref -d CHERRY_PICK_HEAD\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Схоже, що ви робите висмикування.\n"
+"Якщо це не так, виконайте\n"
+"\tgit update-ref -d CHERRY_PICK_HEAD\n"
+"і спробуйте ще раз.\n"
+
+#, c-format
+msgid "%sAuthor:    %.*s <%.*s>"
+msgstr "%sАвтор:    %.*s <%.*s>"
+
+#, c-format
+msgid "%sDate:      %s"
+msgstr "%sДата:      %s"
+
+#, c-format
+msgid "%sCommitter: %.*s <%.*s>"
+msgstr "%sКомітер: %.*s <%.*s>"
+
+msgid "Cannot read index"
+msgstr "Неможливо прочитати індекс"
+
+msgid "unable to pass trailers to --trailers"
+msgstr "не вдалося передати причепи до --trailers"
+
+msgid "Error building trees"
+msgstr "Помилка при побудові дерев"
+
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Будь ласка, надайте допис, використовуючи опцію -m або -F.\n"
+
+#, c-format
+msgid "--author '%s' is not 'Name <email>' and matches no existing author"
+msgstr ""
+"--author \"%s\" не в форматі \"Імʼя <адреса електронної пошти>\" і не "
+"відповідає жодному існуючому автору"
+
+#, c-format
+msgid "Invalid ignored mode '%s'"
+msgstr "Неприпустимий режим ігнорування \"%s\""
+
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Неприпустимий режим невідстежуваних файлів \"%s\""
+
+msgid "You are in the middle of a merge -- cannot reword."
+msgstr "Ви перебуваєте в процесі злиття -- неможливо перефразувати."
+
+msgid "You are in the middle of a cherry-pick -- cannot reword."
+msgstr "Ви перебуваєте в процесі висмикування -- неможливо перефразувати."
+
+#, c-format
+msgid "reword option of '%s' and path '%s' cannot be used together"
+msgstr ""
+"параметр перефразування \"%s\" і шлях \"%s\" не можуть бути використані разом"
+
+#, c-format
+msgid "reword option of '%s' and '%s' cannot be used together"
+msgstr "опцію перефразування \"%s\" і \"%s\" не можна використовувати разом"
+
+msgid "You have nothing to amend."
+msgstr "Вам немає до чого вносити зміни."
+
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "Ви перебуваєте в процесі злиття -- неможливо внести зміни."
+
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "Ви перебуваєте в процесі висмикування - неможливо внести зміни."
+
+msgid "You are in the middle of a rebase -- cannot amend."
+msgstr "Ви перебуваєте в процесі перебазуавння -- неможливо внести зміни."
+
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset-author можна використовувати лише з -C, -c або --amend."
+
+#, c-format
+msgid "unknown option: --fixup=%s:%s"
+msgstr "невідомий параметр: --fixup=%s:%s"
+
+#, c-format
+msgid "paths '%s ...' with -a does not make sense"
+msgstr "шляхи \"%s ...\" з -a не мають сенсу"
+
+msgid "show status concisely"
+msgstr "показувати статус стисло"
+
+msgid "show branch information"
+msgstr "показати інформацію про гілку"
+
+msgid "show stash information"
+msgstr "показати інформацію про схов"
+
+msgid "compute full ahead/behind values"
+msgstr "обчислювати повні попереду/позаду значення"
+
+msgid "version"
+msgstr "версія"
+
+msgid "machine-readable output"
+msgstr "машинозчитуваний вивід"
+
+msgid "show status in long format (default)"
+msgstr "показувати статус у повному форматі (за замовчуванням)"
+
+msgid "terminate entries with NUL"
+msgstr "завершувати записи символом NUL"
+
+msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
+msgstr ""
+"показувати невідстежувані файли, варіанти режимів: all, normal, no (за "
+"замовчуванням: all)"
+
+msgid ""
+"show ignored files, optional modes: traditional, matching, no. (Default: "
+"traditional)"
+msgstr ""
+"показувати ігноровані файли, варіанти режимів: traditional, matching, no (за "
+"замовчуванням: traditional)."
+
+msgid "when"
+msgstr "коли"
+
+msgid ""
+"ignore changes to submodules, optional when: all, dirty, untracked. "
+"(Default: all)"
+msgstr ""
+"ігнорувати зміни підмодулів, опціонально, якщо: all, dirty, untracked (за "
+"замовчуванням: all)."
+
+msgid "list untracked files in columns"
+msgstr "показати невідстежувані файли в стовпчиках"
+
+msgid "do not detect renames"
+msgstr "не виявляти перейменування"
+
+msgid "detect renames, optionally set similarity index"
+msgstr "виявляти перейменування, опціонально встановлювати індекс схожості"
+
+msgid "Unsupported combination of ignored and untracked-files arguments"
+msgstr ""
+"Непідтримувана комбінація аргументів для ігнорованих та невідстежуваних "
+"файлів"
+
+msgid "suppress summary after successful commit"
+msgstr "не показувати підсумок після успішного коміту"
+
+msgid "show diff in commit message template"
+msgstr "показувати різницю в шаблоні дописа до коміту"
+
+msgid "Commit message options"
+msgstr "Опції дописа до коміту"
+
+msgid "read message from file"
+msgstr "читати текст дописа з файлу"
+
+msgid "author"
+msgstr "автор"
+
+msgid "override author for commit"
+msgstr "перевизначити автора коміту"
+
+msgid "date"
+msgstr "дата"
+
+msgid "override date for commit"
+msgstr "перевизначити дату коміту"
+
+msgid "commit"
+msgstr "коміт"
+
+msgid "reuse and edit message from specified commit"
+msgstr "повторно використати та редагувати допис зі вказаного коміту"
+
+msgid "reuse message from specified commit"
+msgstr "повторно використати допис зі вказаного коміту"
+
+#. TRANSLATORS: Leave "[(amend|reword):]" as-is,
+#. and only translate <commit>.
+#.
+msgid "[(amend|reword):]commit"
+msgstr "[(amend|reword):]коміт"
+
+msgid ""
+"use autosquash formatted message to fixup or amend/reword specified commit"
+msgstr ""
+"використовувати допис у форматі autosquash для виправлення або зміни/"
+"редагування вказаного коміту"
+
+msgid "use autosquash formatted message to squash specified commit"
+msgstr ""
+"використовувати допис у форматі autosquash для зчавлювання вказаного коміту"
+
+msgid "the commit is authored by me now (used with -C/-c/--amend)"
+msgstr "автором коміту тепер є я (використовується з -C/-c/--amend)"
+
+msgid "trailer"
+msgstr "причіп"
+
+msgid "add custom trailer(s)"
+msgstr "додати нестандартний причіп"
+
+msgid "add a Signed-off-by trailer"
+msgstr "додати Signed-off-by причіп"
+
+msgid "use specified template file"
+msgstr "використати зазначений файл шаблону"
+
+msgid "force edit of commit"
+msgstr "редагувати коміт примусово"
+
+msgid "include status in commit message template"
+msgstr "включити статус у шаблон дописа до коміту"
+
+msgid "Commit contents options"
+msgstr "Опції вмісту коміту"
+
+msgid "commit all changed files"
+msgstr "закомітити всі змінені файли"
+
+msgid "add specified files to index for commit"
+msgstr "додати вказані файли до індексу для коміту"
+
+msgid "interactively add files"
+msgstr "додавати файли інтерактивно"
+
+msgid "interactively add changes"
+msgstr "додавати зміни інтерактивно"
+
+msgid "commit only specified files"
+msgstr "комітити лише вказані файли"
+
+msgid "bypass pre-commit and commit-msg hooks"
+msgstr "обходити pre-commit та commit-msg гачки"
+
+msgid "show what would be committed"
+msgstr "показати, що буде закомічено"
+
+msgid "amend previous commit"
+msgstr "внести зміни до попереднього коміту"
+
+msgid "bypass post-rewrite hook"
+msgstr "обійти post-rewrite гачок"
+
+msgid "ok to record an empty change"
+msgstr "дозволити записати порожню зміну"
+
+msgid "ok to record a change with an empty message"
+msgstr "дозволити записати зміну з порожнім дописом"
+
+msgid "could not parse HEAD commit"
+msgstr "не вдалося розібрати HEAD коміт"
+
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "Пошкоджений MERGE_HEAD (%s)"
+
+msgid "could not read MERGE_MODE"
+msgstr "не вдалося прочитати MERGE_MODE"
+
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "не вдалося прочитати допис до коміту: %s"
+
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Переривання коміту через порожній допис до коміту.\n"
+
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Переривання коміту; ви не відредагували допис.\n"
+
+#, c-format
+msgid "Aborting commit due to empty commit message body.\n"
+msgstr "Переривання коміту через порожнє тіло дописа до коміту.\n"
+
+msgid ""
+"repository has been updated, but unable to write\n"
+"new index file. Check that disk is not full and quota is\n"
+"not exceeded, and then \"git restore --staged :/\" to recover."
+msgstr ""
+"сховище було оновлено, але не вдалося записати\n"
+"новий файл індексу. Переконайтеся, що диск не переповнений і квота\n"
+"не перевищена, а потім виконайте \"git restore --staged :/\" для відновлення."
+
+msgid "git config [<options>]"
+msgstr "git config [<опції>]"
+
+#, c-format
+msgid "unrecognized --type argument, %s"
+msgstr "нерозпізнаний аргумент --type, %s"
+
+msgid "only one type at a time"
+msgstr "лише один тип за раз"
+
+msgid "Config file location"
+msgstr "Розташування файлу конфігурації"
+
+msgid "use global config file"
+msgstr "використовувати глобальний файл конфігурації"
+
+msgid "use system config file"
+msgstr "використовувати файл конфігурації системи"
+
+msgid "use repository config file"
+msgstr "використовувати файл конфігурації сховища"
+
+msgid "use per-worktree config file"
+msgstr "використовувати файл конфігурації робочого дерева"
+
+msgid "use given config file"
+msgstr "використовувати наданий файл конфігурації"
+
+msgid "blob-id"
+msgstr "blob-id"
+
+msgid "read config from given blob object"
+msgstr "прочитати конфігурацію з наданого blob-обʼєкту"
+
+msgid "Action"
+msgstr "Дія"
+
+msgid "get value: name [value-pattern]"
+msgstr "отримати значення: назва [шаблон-значення]"
+
+msgid "get all values: key [value-pattern]"
+msgstr "отримати всі значення: ключ [шаблон-значення]"
+
+msgid "get values for regexp: name-regex [value-pattern]"
+msgstr "отримати значення для регвиру: регвир-назви [шаблон-значення]"
+
+msgid "get value specific for the URL: section[.var] URL"
+msgstr "отримати значення для конкретної URL-адреси: розділ[.var] URL-адреса"
+
+msgid "replace all matching variables: name value [value-pattern]"
+msgstr "замінити всі відповідні змінні: назва значення [шаблон-значення]"
+
+msgid "add a new variable: name value"
+msgstr "додати нову змінну: назва значення"
+
+msgid "remove a variable: name [value-pattern]"
+msgstr "видалити змінну: назва [шаблон-значення]"
+
+msgid "remove all matches: name [value-pattern]"
+msgstr "видалити всі збіги: назва [шаблон-значення]"
+
+msgid "rename section: old-name new-name"
+msgstr "перейменувати розділ: стара-назва нова-назва"
+
+msgid "remove a section: name"
+msgstr "видалити розділ: назва"
+
+msgid "list all"
+msgstr "показати всі змінні"
+
+msgid "use string equality when comparing values to 'value-pattern'"
+msgstr ""
+"використовувати рівність строк при порівнянні значень з \"шаблон-значенням\""
+
+msgid "open an editor"
+msgstr "відкрити редактор"
+
+msgid "find the color configured: slot [default]"
+msgstr "знайти налаштований колір: слот [за замовчуванням]"
+
+msgid "find the color setting: slot [stdout-is-tty]"
+msgstr "знайти налаштування кольору: slot [stdout-is-tty]"
+
+msgid "Type"
+msgstr "Тип"
+
+msgid "type"
+msgstr "тип"
+
+msgid "value is given this type"
+msgstr "тип значення"
+
+msgid "value is \"true\" or \"false\""
+msgstr "значення \"true\" або \"false\""
+
+msgid "value is decimal number"
+msgstr "значення десяткове число"
+
+msgid "value is --bool or --int"
+msgstr "значення --bool або --int"
+
+msgid "value is --bool or string"
+msgstr "значення --bool або string"
+
+msgid "value is a path (file or directory name)"
+msgstr "значення шлях (файл або назва директорії)"
+
+msgid "value is an expiry date"
+msgstr "значення - дата закінчення терміну дії"
+
+msgid "Other"
+msgstr "Інше"
+
+msgid "terminate values with NUL byte"
+msgstr "завершити значення байтом NUL"
+
+msgid "show variable names only"
+msgstr "показувати тільки назви змінних"
+
+msgid "respect include directives on lookup"
+msgstr "дотримуватись директив включення при пошуку"
+
+msgid "show origin of config (file, standard input, blob, command line)"
+msgstr ""
+"показати походження конфігурації (файл, стандартний ввід, blob, командний "
+"рядок)"
+
+msgid "show scope of config (worktree, local, global, system, command)"
+msgstr ""
+"показати межі дії конфігурації (робоче дерево, локально, глобально, система, "
+"команда)"
+
+msgid "value"
+msgstr "значення"
+
+msgid "with --get, use default value when missing entry"
+msgstr ""
+"з --get використовувати значення за замовчуванням, якщо запис відсутній"
+
+#, c-format
+msgid "wrong number of arguments, should be %d"
+msgstr "невірна кількість аргументів, має бути %d"
+
+#, c-format
+msgid "wrong number of arguments, should be from %d to %d"
+msgstr "невірна кількість аргументів, має бути від %d до %d"
+
+#, c-format
+msgid "invalid key pattern: %s"
+msgstr "неприпустимий шаблон ключа: %s"
+
+#, c-format
+msgid "invalid pattern: %s"
+msgstr "неприпустимий шаблон: %s"
+
+#, c-format
+msgid "failed to format default config value: %s"
+msgstr "не вдалося відформатувати початкове значення конфігурації: %s"
+
+#, c-format
+msgid "cannot parse color '%s'"
+msgstr "не вдалося розібрати колір \"%s\""
+
+msgid "unable to parse default color value"
+msgstr "не вдалося розібрати початкове значення кольору"
+
+msgid "not in a git directory"
+msgstr "не в git директорії"
+
+msgid "writing to stdin is not supported"
+msgstr "запис до stdin не підтримується"
+
+msgid "writing config blobs is not supported"
+msgstr "запис blob конфігурації не підтримується"
+
+#, c-format
+msgid ""
+"# This is Git's per-user configuration file.\n"
+"[user]\n"
+"# Please adapt and uncomment the following lines:\n"
+"#\tname = %s\n"
+"#\temail = %s\n"
+msgstr ""
+"# Це файл конфігурації Git для кожного користувача.\n"
+"[user]\n"
+"# Будь ласка, адаптуйте та розкоментуйте наступні рядки:\n"
+"# name = %s\n"
+"# email = %s\n"
+
+msgid "only one config file at a time"
+msgstr "лише один конфігураційний файл за раз"
+
+msgid "--local can only be used inside a git repository"
+msgstr "--local можна використовувати лише всередині git сховища"
+
+msgid "--blob can only be used inside a git repository"
+msgstr "--blob можна використовувати лише всередині git сховища"
+
+msgid "--worktree can only be used inside a git repository"
+msgstr "--worktree можна використовувати лише всередині git сховища"
+
+msgid "$HOME not set"
+msgstr "$HOME не встановлено"
+
+msgid ""
+"--worktree cannot be used with multiple working trees unless the config\n"
+"extension worktreeConfig is enabled. Please read \"CONFIGURATION FILE\"\n"
+"section in \"git help worktree\" for details"
+msgstr ""
+"--worktree неможливо використовувати з кількома робочими деревами, якщо не "
+"увімкнено розширення\n"
+"конфігурації worktreeConfig. Будь ласка, прочитайте розділ \"КОНФІГУРАЦІЙНИЙ "
+"ФАЙЛ\"\n"
+"у \"git help worktree\" для більш детальної інформації"
+
+msgid "--get-color and variable type are incoherent"
+msgstr "--get-color і тип змінної не узгоджуються"
+
+msgid "only one action at a time"
+msgstr "лише одна дія за раз"
+
+msgid "--name-only is only applicable to --list or --get-regexp"
+msgstr "--name-only застосовується лише до --list або --get-regexp"
+
+msgid ""
+"--show-origin is only applicable to --get, --get-all, --get-regexp, and --"
+"list"
+msgstr ""
+"--show-origin застосовується лише до --get, --get-all, --get-regexp та --list"
+
+msgid "--default is only applicable to --get"
+msgstr "--default застосовується лише до --get"
+
+msgid "--fixed-value only applies with 'value-pattern'"
+msgstr "--fixed-value застосовується лише з \"шаблоном-значення\""
+
+#, c-format
+msgid "unable to read config file '%s'"
+msgstr "не вдалося прочитати файл конфігурації \"%s\""
+
+msgid "error processing config file(s)"
+msgstr "помилка при обробці файлу(ів) конфігурації"
+
+msgid "editing stdin is not supported"
+msgstr "редагування stdin не підтримується"
+
+msgid "editing blobs is not supported"
+msgstr "редагування blobs не підтримується"
+
+#, c-format
+msgid "cannot create configuration file %s"
+msgstr "неможливо створити конфігураційний файл %s"
+
+#, c-format
+msgid ""
+"cannot overwrite multiple values with a single value\n"
+"       Use a regexp, --add or --replace-all to change %s."
+msgstr ""
+"неможливо перезаписати кілька значень одним значенням\n"
+"       Використовуйте regexp, --add або --replace-all для зміни %s."
+
+#, c-format
+msgid "no such section: %s"
+msgstr "немає такого розділу: %s"
+
+msgid "print sizes in human readable format"
+msgstr "показувати розмір у зручному для читання форматі"
+
+#, c-format
+msgid ""
+"The permissions on your socket directory are too loose; other\n"
+"users may be able to read your cached credentials. Consider running:\n"
+"\n"
+"\tchmod 0700 %s"
+msgstr ""
+"Дозволи на вашу сокет директорію занадто вільні; інші\n"
+"користувачі можуть прочитати ваші кешовані облікові дані. Подумайте про "
+"запуск:\n"
+"\n"
+"\tchmod 0700 %s"
+
+msgid "print debugging messages to stderr"
+msgstr "виводити відлагоджувальні повідомлення в stderr"
+
+msgid "credential-cache--daemon unavailable; no unix socket support"
+msgstr "credential-cache--daemon недоступний; немає підтримки unix-сокетів"
+
+msgid "credential-cache unavailable; no unix socket support"
+msgstr "credential-cache недоступний; немає підтримки unix-сокетів"
+
+#, c-format
+msgid "unable to get credential storage lock in %d ms"
+msgstr "не вдалося отримати файл блокування сховища облікових даних за %d мс"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<н>] "
+"[<комітоподібне>...]"
+
+msgid ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"
+msgstr ""
+"git describe [--all] [--tags] [--contains] [--abbrev=<н>] --"
+"dirty[=<позначка>]"
+
+msgid "git describe <blob>"
+msgstr "git describe <blob>"
+
+msgid "head"
+msgstr "head"
+
+msgid "lightweight"
+msgstr "lightweight"
+
+msgid "annotated"
+msgstr "annotated"
+
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "анотований тег %s недоступний"
+
+#, c-format
+msgid "tag '%s' is externally known as '%s'"
+msgstr "тег \"%s\" відомий зовні як \"%s\""
+
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "жоден тег не збігається точно з \"%s\""
+
+#, c-format
+msgid "No exact match on refs or tags, searching to describe\n"
+msgstr "Немає точного збігу за посиланнями або тегами, пошук для опису\n"
+
+#, c-format
+msgid "finished search at %s\n"
+msgstr "пошук завершено на %s\n"
+
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+"Жоден анотований тег не може описати \"%s\".\n"
+"Однак існують неанотовані теги: спробуйте --tags."
+
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Жоден тег не може описати \"%s\".\n"
+"Спробуйте --always або створіть кілька тегів."
+
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "пройдено через %lu комітів\n"
+
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+"знайдено більше %i тегів; показані %i останніх\n"
+"припинено пошук на %s\n"
+
+#, c-format
+msgid "describe %s\n"
+msgstr "описати %s\n"
+
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "Неприпустиме ім’я об’єкта %s"
+
+#, c-format
+msgid "%s is neither a commit nor blob"
+msgstr "%s не є commit чи blob"
+
+msgid "find the tag that comes after the commit"
+msgstr "знайти тег, що йде після коміту"
+
+msgid "debug search strategy on stderr"
+msgstr "виводити відлагоджувальні повідомлення стратегії пошуку в stderr"
+
+msgid "use any ref"
+msgstr "використати будь-яке посилання"
+
+msgid "use any tag, even unannotated"
+msgstr "використати будь-який тег, навіть неанотований"
+
+msgid "always use long format"
+msgstr "завжди використовувати довгий формат"
+
+msgid "only follow first parent"
+msgstr "слідувати тільки за першим з батьків"
+
+msgid "only output exact matches"
+msgstr "виводити лише точні збіги"
+
+msgid "consider <n> most recent tags (default: 10)"
+msgstr "враховувати <н> останніх тегів (за замовчуванням: 10)"
+
+msgid "only consider tags matching <pattern>"
+msgstr "враховувати лише теги, що відповідають <шаблону>"
+
+msgid "do not consider tags matching <pattern>"
+msgstr "не враховувати теги, що відповідають <шаблону>"
+
+msgid "show abbreviated commit object as fallback"
+msgstr "показувати скорочений обʼєкт коміту як запасний варіант"
+
+msgid "mark"
+msgstr "позначка"
+
+msgid "append <mark> on dirty working tree (default: \"-dirty\")"
+msgstr ""
+"додати <позначку> до брудного робочого дерева (за замовчуванням: \\”-"
+"dirty\\”)"
+
+msgid "append <mark> on broken working tree (default: \"-broken\")"
+msgstr ""
+"додати <позначку> до пошкодженого робочого дерева (за замовчуванням: \"-"
+"broken\")"
+
+msgid "No names found, cannot describe anything."
+msgstr "Назв не знайдено, неможливо нічого описати."
+
+#, c-format
+msgid "option '%s' and commit-ishes cannot be used together"
+msgstr "опцію \"%s\" не можна використовувати разом з комітоподібними"
+
+msgid ""
+"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+"             [--mode=<mode>]"
+msgstr ""
+"git diagnose [(-o | --output-directory) <шлях>] [(-s | --suffix) <формат>]\n"
+"             [--mode=<режим>]"
+
+msgid "specify a destination for the diagnostics archive"
+msgstr "вказати місце призначення архіву діагностики"
+
+msgid "specify a strftime format suffix for the filename"
+msgstr "вказати суфікс формату strftime для назви файлу"
+
+msgid "specify the content of the diagnostic archive"
+msgstr "вказати вміст архіву діагностики"
+
+msgid "--merge-base only works with two commits"
+msgstr "--merge-base працює лише з двома комітами"
+
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "\"%s\": не звичайний файл або символьне посилання"
+
+msgid "no merge given, only parents."
+msgstr "злиття не надано, лише батьки."
+
+#, c-format
+msgid "invalid option: %s"
+msgstr "неприпустима опція %s"
+
+#, c-format
+msgid "%s...%s: no merge base"
+msgstr "%s...%s: немає бази злиття"
+
+msgid "Not a git repository"
+msgstr "Не є git сховищем"
+
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "надано неприпустимий об’єкт \"%s\"."
+
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "надано більше двох blob: \"%s\""
+
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "надано необроблений об’єкт \"%s\"."
+
+#, c-format
+msgid "%s...%s: multiple merge bases, using %s"
+msgstr "%s...%s: кілька баз злиття, використання %s"
+
+msgid "git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"
+msgstr "git difftool [<опції>] [<коміт> [<коміт>]] [--] [<шлях>...]"
+
+#, c-format
+msgid "could not read symlink %s"
+msgstr "не вдалося прочитати символьне посилання %s"
+
+#, c-format
+msgid "could not read symlink file %s"
+msgstr "не вдалося прочитати файл символьного посилання %s"
+
+#, c-format
+msgid "could not read object %s for symlink %s"
+msgstr "не вдалося прочитати об’єкт %s для символьного посилання %s"
+
+msgid ""
+"combined diff formats ('-c' and '--cc') are not supported in\n"
+"directory diff mode ('-d' and '--dir-diff')."
+msgstr ""
+"комбіновані формати diff (\"-c\" і \"--cc\") не підтримуються у\n"
+"режимі порівняння директорій (\"-d\" і \"--dir-diff\")."
+
+#, c-format
+msgid "both files modified: '%s' and '%s'."
+msgstr "обидва файли змінено: \"%s\" і \"%s\"."
+
+msgid "working tree file has been left."
+msgstr "файл робочого дерева було залишено."
+
+#, c-format
+msgid "could not copy '%s' to '%s'"
+msgstr "не вдалося скопіювати \"%s\" в \"%s\""
+
+#, c-format
+msgid "temporary files exist in '%s'."
+msgstr "тимчасові файли існують в \"%s\"."
+
+msgid "you may want to cleanup or recover these."
+msgstr "можливо, ви захочете очистити або відновити їх."
+
+#, c-format
+msgid "failed: %d"
+msgstr "завершилось невдало: %d"
+
+msgid "use `diff.guitool` instead of `diff.tool`"
+msgstr "використовувати \"diff.guitool\" замість \"diff.tool\""
+
+msgid "perform a full-directory diff"
+msgstr "виконати порівняння всіх директорій"
+
+msgid "do not prompt before launching a diff tool"
+msgstr "не запитувати перед запуском diff"
+
+msgid "use symlinks in dir-diff mode"
+msgstr "використовувати символьні посилання у режимі dir-diff"
+
+msgid "tool"
+msgstr "засіб"
+
+msgid "use the specified diff tool"
+msgstr "використовувати вказаний diff засіб"
+
+msgid "print a list of diff tools that may be used with `--tool`"
+msgstr "показати список diff засобів, які можна використовувати з \"--tool\""
+
+msgid ""
+"make 'git-difftool' exit when an invoked diff tool returns a non-zero exit "
+"code"
+msgstr ""
+"змусити \"git-difftool\" завершувати роботу, коли викликаний diff засіб "
+"повертає ненульовий код завершення"
+
+msgid "specify a custom command for viewing diffs"
+msgstr "вказати нестандартну команду для перегляду різниць"
+
+msgid "passed to `diff`"
+msgstr "передається до \"diff\""
+
+msgid "difftool requires worktree or --no-index"
+msgstr "difftool потребує worktree або --no-index"
+
+msgid "no <tool> given for --tool=<tool>"
+msgstr "не надано <засіб> для --tool=<засіб>"
+
+msgid "no <cmd> given for --extcmd=<cmd>"
+msgstr "не надана <команда> для --extcmd=<команда>"
+
+msgid "git fast-export [<rev-list-opts>]"
+msgstr "git fast-export [<rev-list-опції>]"
+
+msgid "Error: Cannot export nested tags unless --mark-tags is specified."
+msgstr ""
+"Помилка: неможливо експортувати вкладені теги, якщо не вказано --mark-tags."
+
+msgid "--anonymize-map token cannot be empty"
+msgstr "--anonymize-map токен не може бути порожнім"
+
+msgid "show progress after <n> objects"
+msgstr "показати прогрес після <н> обʼєктів"
+
+msgid "select handling of signed tags"
+msgstr "вибрати обробку підписаних тегів"
+
+msgid "select handling of tags that tag filtered objects"
+msgstr "вибрати обробку тегів, якими позначено відфільтровані обʼєкти"
+
+msgid "select handling of commit messages in an alternate encoding"
+msgstr "вибрати обробку дописів до комітів в іншому кодуванні"
+
+msgid "dump marks to this file"
+msgstr "експортувати позначки в цей файл"
+
+msgid "import marks from this file"
+msgstr "імпортувати позначки з цього файлу"
+
+msgid "import marks from this file if it exists"
+msgstr "імпортувати позначки з цього файлу, якщо він існує"
+
+msgid "fake a tagger when tags lack one"
+msgstr "підробляти теггера, коли його не вказано для тега"
+
+msgid "output full tree for each commit"
+msgstr "виводити повне дерево для кожного коміту"
+
+msgid "use the done feature to terminate the stream"
+msgstr "використовувати особливість done для завершення потоку"
+
+msgid "skip output of blob data"
+msgstr "пропускати виведення blob-даних"
+
+msgid "refspec"
+msgstr "визначник посилання"
+
+msgid "apply refspec to exported refs"
+msgstr "застосувати визначник посилання до експортованих посилань"
+
+msgid "anonymize output"
+msgstr "анонімізувати вивід"
+
+msgid "from:to"
+msgstr "від:до"
+
+msgid "convert <from> to <to> in anonymized output"
+msgstr "конвертувати <від> в <до> в анонімізованому виводі"
+
+msgid "reference parents which are not in fast-export stream by object id"
+msgstr ""
+"батьки посилання, яких не знайдено в fast-export потоці за ідентифікатором "
+"обʼєкта"
+
+msgid "show original object ids of blobs/commits"
+msgstr "показувати оригінальні ідентифікатори обʼєктів blob/комітів"
+
+msgid "label tags with mark ids"
+msgstr "позначати теги ідентифікаторами позначок"
+
+#, c-format
+msgid "Missing from marks for submodule '%s'"
+msgstr "Відсутні \"від\" позначки для підмодуля \"%s\""
+
+#, c-format
+msgid "Missing to marks for submodule '%s'"
+msgstr "Відсутні \"до\" позначки для підмодуля \"%s\""
+
+#, c-format
+msgid "Expected 'mark' command, got %s"
+msgstr "Очікувалась команда \"mark\", отримано %s"
+
+#, c-format
+msgid "Expected 'to' command, got %s"
+msgstr "Очікувалась команда \"to\", отримано %s"
+
+msgid "Expected format name:filename for submodule rewrite option"
+msgstr "Очікуваний формат назва:назва файлу для параметра перезапису підмодуля"
+
+#, c-format
+msgid "feature '%s' forbidden in input without --allow-unsafe-features"
+msgstr ""
+"особливість \"%s\" не можна використовувати без --allow-unsafe-features"
+
+#, c-format
+msgid "Lockfile created but not reported: %s"
+msgstr "Файл блокування створено, але не звітовано: %s"
+
+msgid "git fetch [<options>] [<repository> [<refspec>...]]"
+msgstr "git fetch [<опції>] [<сховище> [<визначник посилання>...]]"
+
+msgid "git fetch [<options>] <group>"
+msgstr "git fetch [<опції>] [<група>]"
+
+msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
+msgstr "git fetch --multiple [<опції>] [(<сховище> | <група>)...]"
+
+msgid "git fetch --all [<options>]"
+msgstr "git fetch --all [<опції>]"
+
+msgid "fetch.parallel cannot be negative"
+msgstr "fetch.parallel не може бути відʼємним"
+
+msgid "couldn't find remote ref HEAD"
+msgstr "не вдалося знайти посилання для віддаленого HEAD"
+
+#, c-format
+msgid "From %.*s\n"
+msgstr "Від %.*s\n"
+
+#, c-format
+msgid "object %s not found"
+msgstr "об’єкт %s не знайдено"
+
+msgid "[up to date]"
+msgstr "[в актуальному стані]"
+
+msgid "[rejected]"
+msgstr "[відхилено]"
+
+msgid "can't fetch into checked-out branch"
+msgstr "неможливо виконати отримання в активну гілку"
+
+msgid "[tag update]"
+msgstr "[оновлення тегу]"
+
+msgid "unable to update local ref"
+msgstr "не вдалося оновити локальне посилання"
+
+msgid "would clobber existing tag"
+msgstr "зруйнує існуючий тег"
+
+msgid "[new tag]"
+msgstr "[новий тег]"
+
+msgid "[new branch]"
+msgstr "[нова гілка]"
+
+msgid "[new ref]"
+msgstr "[нове посилання]"
+
+msgid "forced update"
+msgstr "примусове оновлення"
+
+msgid "non-fast-forward"
+msgstr "без перемотування вперед"
+
+#, c-format
+msgid "cannot open '%s'"
+msgstr "неможливо відкрити \"%s\""
+
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"fetch зазвичай показує, які гілки було примусово оновлено,\n"
+"але цю перевірку було вимкнено; щоб увімкнути її знову, скористайтесь \"--"
+"show-forced-updates\"\n"
+"або виконайте \"git config fetch.showForcedUpdates true\""
+
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"перевірка примусових оновлень зайняла %.2f секунд; ви можете скористатися\n"
+"\"--no-show-forced-updates\" або виконати \"git config fetch."
+"showForcedUpdates false\"\n"
+"щоб уникнути цієї перевірки\n"
+
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s не надіслав всіх необхідних обʼєктів\n"
+
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr "відхилено %s, оскільки неглибокі корені не можна оновлювати"
+
+#, c-format
+msgid ""
+"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\", щоб видалити всі старі конфліктні гілки"
+
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s стануть висячими)"
+
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s став висячим)"
+
+msgid "[deleted]"
+msgstr "[видалено]"
+
+msgid "(none)"
+msgstr "(нічого)"
+
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "відмовлено в отримані в гілку \"%s\", що знаходиться в \"%s\""
+
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "значення \"%s\" опції \"%s\" неприпустиме для %s"
+
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "опція \"%s\" ігнорується для %s\n"
+
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s не є припустимим об’єктом"
+
+#, c-format
+msgid "the object %s does not exist"
+msgstr "об’єкт %s не існує"
+
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "виявлено кілька гілок, несумісних з --set-upstream"
+
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"не вдалося встановити першоджерельне сховище для HEAD в \"%s\" з \"%s\", "
+"коли воно не вказує на жодну гілку."
+
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr ""
+"не встановлено першоджерельне сховище для віддалено відстежуваної гілки"
+
+msgid "not setting upstream for a remote tag"
+msgstr "не встановлено першоджерельне сховище для віддаленого тега"
+
+msgid "unknown branch type"
+msgstr "невідомий тип гілки"
+
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"джерельна гілка не знайдена;\n"
+"потрібно вказати лишень одну гілку з опцією --set-upstream"
+
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Отримання %s\n"
+
+#, c-format
+msgid "could not fetch %s"
+msgstr "не вдалося отримати %s"
+
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "не вдалося отримати \"%s\" (код завершення: %d)\n"
+
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"віддалене сховище не вказано; будь ласка, вкажіть або URL або\n"
+"назву віддаленого сховища, з якого слід отримувати нові ревізії"
+
+msgid "you need to specify a tag name"
+msgstr "потрібно вказати назву тегу"
+
+msgid "fetch from all remotes"
+msgstr "отримати з усіх віддалених призначень"
+
+msgid "set upstream for git pull/fetch"
+msgstr "встановити першоджерельне сховище для git pull/fetch"
+
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "додати до .git/FETCH_HEAD замість перезапису"
+
+msgid "use atomic transaction to update references"
+msgstr "використати атомарну транзакцію для оновлення посилань"
+
+msgid "path to upload pack on remote end"
+msgstr "шлях для завантаження пакунка на віддаленій стороні"
+
+msgid "force overwrite of local reference"
+msgstr "примусовий перезапис локального посилання"
+
+msgid "fetch from multiple remotes"
+msgstr "отримати з кількох віддалених призначень"
+
+msgid "fetch all tags and associated objects"
+msgstr "отримати всі теги і повʼязані з ними обʼєкти"
+
+msgid "do not fetch all tags (--no-tags)"
+msgstr "не отримувати всі теги (--no-tags)"
+
+msgid "number of submodules fetched in parallel"
+msgstr "кількість підмодулів, що завантажуються паралельно"
+
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr ""
+"змінити визначник посилання, щоб помістити всі посилання в refs/prefetch/"
+
+msgid "prune remote-tracking branches no longer on remote"
+msgstr ""
+"видалити віддалено відстежувані гілки, що більше не існують у віддаленому "
+"призначенні"
+
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr ""
+"видалити локальні теги, що більше не існують у віддаленому призначенні, і "
+"зруйнувати змінені теги"
+
+msgid "on-demand"
+msgstr "на вимогу"
+
+msgid "control recursive fetching of submodules"
+msgstr "контролювати рекурсивне отримання підмодулів"
+
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "записувати отримані посилання у файл FETCH_HEAD"
+
+msgid "keep downloaded pack"
+msgstr "зберегти завантажений пакунок"
+
+msgid "allow updating of HEAD ref"
+msgstr "дозволити оновлення HEAD посилання"
+
+msgid "deepen history of shallow clone"
+msgstr "поглибити історію неглибокого клону"
+
+msgid "deepen history of shallow repository based on time"
+msgstr "поглибити історію неглибокого клону залежно від часу"
+
+msgid "convert to a complete repository"
+msgstr "перетворити на повне сховище"
+
+msgid "re-fetch without negotiating common commits"
+msgstr "повторне отримання без узгодження спільних комітів"
+
+msgid "prepend this to submodule path output"
+msgstr "додавати це напочатку шляху до підмодуля при виведенні"
+
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr ""
+"поведінка за замовчуванням для рекурсивного отримання підмодулів (нижчий "
+"пріоритет, ніж у конфігураційних файлів)"
+
+msgid "accept refs that update .git/shallow"
+msgstr "приймати посилання, які оновлюють .git/shallow"
+
+msgid "refmap"
+msgstr "refmap"
+
+msgid "specify fetch refmap"
+msgstr "вказати мапу посилань для fetch"
+
+msgid "report that we have only objects reachable from this object"
+msgstr "звітувати, що у нас є тільки обʼєкти, доступні з цього обʼєкта"
+
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr ""
+"не отримувати файл пакунка; замість цього роздрукувати предків верхівок для "
+"узгодження"
+
+msgid "run 'maintenance --auto' after fetching"
+msgstr "виконати \"maintenance --auto\" після отримуваання"
+
+msgid "check for forced-updates on all updated branches"
+msgstr "перевірити на примусове оновлення для всіх оновлених гілок"
+
+msgid "write the commit-graph after fetching"
+msgstr "записати коміт-граф після отримання"
+
+msgid "accept refspecs from stdin"
+msgstr "приймати визначники посилань з stdin"
+
+msgid "--negotiate-only needs one or more --negotiation-tip=*"
+msgstr "--negotiate-only потребує одного або кількох --negotiation-tip=*"
+
+msgid "negative depth in --deepen is not supported"
+msgstr "відʼємна глибина в --deepen не підтримується"
+
+msgid "--unshallow on a complete repository does not make sense"
+msgstr "--unshallow на повному сховищі не має сенсу"
+
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "не вдалося отримати пакунки з \"%s\""
+
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all не приймає сховище як аргумент"
+
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all не має сенсу з визначниками посилань"
+
+#, c-format
+msgid "no such remote or remote group: %s"
+msgstr "немає такого віддаленого призначення або віддаленої групи: %s"
+
+msgid "fetching a group and specifying refspecs does not make sense"
+msgstr "отримання групи і вказівка визначників посилань не має сенсу"
+
+msgid "must supply remote when using --negotiate-only"
+msgstr ""
+"необхідно вказати віддалене призначення при використанні --negotiate-only"
+
+msgid "protocol does not support --negotiate-only, exiting"
+msgstr "протокол не підтримує --negotiate-only, вихід"
+
+msgid ""
+"--filter can only be used with the remote configured in extensions."
+"partialclone"
+msgstr ""
+"--filter можна використовувати лише з віддаленим призначенням, налаштованим "
+"у extensions.partialclone"
+
+msgid "--atomic can only be used when fetching from one remote"
+msgstr ""
+"--atomic може бути використано лише при отриманні з одного віддаленого "
+"джерела"
+
+msgid "--stdin can only be used when fetching from one remote"
+msgstr ""
+"--stdin можна використовувати лише при отриманні одного віддаленого джерела"
+
+msgid ""
+"git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
+msgstr ""
+"git fmt-merge-msg [-m <допис>] [--log[=<н>] | --no-log] [--file <файл>]"
+
+msgid "populate log with at most <n> entries from shortlog"
+msgstr "заповнити журнал не більше ніж <н> записами з shortlog"
+
+msgid "alias for --log (deprecated)"
+msgstr "псевдонім для --log (застарілий)"
+
+msgid "text"
+msgstr "текст"
+
+msgid "use <text> as start of message"
+msgstr "використовувати <текст> як початок допису"
+
+msgid "use <name> instead of the real target branch"
+msgstr "використовувати <назва> замість реальної цільової гілки"
+
+msgid "file to read from"
+msgstr "файл, з якого читати"
+
+msgid "git for-each-ref [<options>] [<pattern>]"
+msgstr "git for-each-ref [<опції>] [<шаблон>]"
+
+msgid "git for-each-ref [--points-at <object>]"
+msgstr "git for-each-ref [--points-at <обʼєкт>]"
+
+msgid "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"
+msgstr "git for-each-ref [--merged [<коміт>]] [--no-merged [<коміт>]]"
+
+msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"
+msgstr "git for-each-ref [--contains [<коміт>]] [--no-contains [<коміт>]]"
+
+msgid "quote placeholders suitably for shells"
+msgstr "заповнювачі лапок для shell"
+
+msgid "quote placeholders suitably for perl"
+msgstr "заповнювачі лапок для perl"
+
+msgid "quote placeholders suitably for python"
+msgstr "заповнювачі лапок для python"
+
+msgid "quote placeholders suitably for Tcl"
+msgstr "заповнювачі лапок для Tcl"
+
+msgid "show only <n> matched refs"
+msgstr "показати тільки <н> відповідних посилань"
+
+msgid "respect format colors"
+msgstr "дотримуватися кольорів формату"
+
+msgid "print only refs which points at the given object"
+msgstr "виводити тільки посилання, які вказують на заданий об’єкт"
+
+msgid "print only refs that are merged"
+msgstr "виводити тільки злиті посилання"
+
+msgid "print only refs that are not merged"
+msgstr "виводити тільки не злиті посилання"
+
+msgid "print only refs which contain the commit"
+msgstr "виводити тільки ті посилання, що містять коміт"
+
+msgid "print only refs which don't contain the commit"
+msgstr "виводити тільки ті посилання, що не містять коміту"
+
+msgid "read reference patterns from stdin"
+msgstr "читати шаблони посилань з stdin"
+
+msgid "unknown arguments supplied with --stdin"
+msgstr "невідомі аргументи надані через --stdin"
+
+msgid "git for-each-repo --config=<config> [--] <arguments>"
+msgstr "git for-each-repo --config=<конфіг> [--] <аргументи>"
+
+msgid "config"
+msgstr "конфіг"
+
+msgid "config key storing a list of repository paths"
+msgstr "ключ конфігурації, в якому зберігається список шляхів до сховищ"
+
+msgid "missing --config=<config>"
+msgstr "відсутній --config=<конфіг>"
+
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "невірно задано параметр --config=%s"
+
+msgid "unknown"
+msgstr "невідомо"
+
+#. TRANSLATORS: e.g. error in tree 01bfda: <more explanation>
+#, c-format
+msgid "error in %s %s: %s"
+msgstr "помилка в %s %s: %s"
+
+#. TRANSLATORS: e.g. warning in tree 01bfda: <more explanation>
+#, c-format
+msgid "warning in %s %s: %s"
+msgstr "попередження в %s %s: %s"
+
+#, c-format
+msgid "broken link from %7s %s"
+msgstr "пошкоджене посилання з %7s %s"
+
+msgid "wrong object type in link"
+msgstr "невірний тип об’єкта в посиланні"
+
+#, c-format
+msgid ""
+"broken link from %7s %s\n"
+"              to %7s %s"
+msgstr ""
+"пошкоджене посилання з %7s %s\n"
+"                    на %7s %s"
+
+msgid "Checking connectivity"
+msgstr "Перевірка підключення"
+
+#, c-format
+msgid "missing %s %s"
+msgstr "відсутній %s %s"
+
+#, c-format
+msgid "unreachable %s %s"
+msgstr "недосяжний %s %s"
+
+#, c-format
+msgid "dangling %s %s"
+msgstr "висячий %s %s"
+
+msgid "could not create lost-found"
+msgstr "не вдалося створити lost-found"
+
+#, c-format
+msgid "could not write '%s'"
+msgstr "не вдалося записати \"%s\""
+
+#, c-format
+msgid "could not finish '%s'"
+msgstr "не вдалося завершити \"%s\""
+
+#, c-format
+msgid "Checking %s"
+msgstr "Перевірка %s"
+
+#, c-format
+msgid "Checking connectivity (%d objects)"
+msgstr "Перевірка підключення (%d обʼєктів)"
+
+#, c-format
+msgid "Checking %s %s"
+msgstr "Перевірка %s %s"
+
+msgid "broken links"
+msgstr "пошкоджені посилання"
+
+#, c-format
+msgid "root %s"
+msgstr "корінь %s"
+
+#, c-format
+msgid "tagged %s %s (%s) in %s"
+msgstr "з тегом %s %s (%s) в %s"
+
+#, c-format
+msgid "%s: object corrupt or missing"
+msgstr "%s: об’єкт пошкоджений або відсутній"
+
+#, c-format
+msgid "%s: invalid reflog entry %s"
+msgstr "%s: неприпустимий запис журналу посилань %s"
+
+#, c-format
+msgid "Checking reflog %s->%s"
+msgstr "Перевірка журналу посилань %s->%s"
+
+#, c-format
+msgid "%s: invalid sha1 pointer %s"
+msgstr "%s: невірний вказівник sha1 %s"
+
+#, c-format
+msgid "%s: not a commit"
+msgstr "%s не є комітом"
+
+msgid "notice: No default references"
+msgstr "повідомлення: Немає посилань за замовчуванням"
+
+#, c-format
+msgid "%s: hash-path mismatch, found at: %s"
+msgstr "%s: невідповідність хеш/шлях знайдена в: %s"
+
+#, c-format
+msgid "%s: object corrupt or missing: %s"
+msgstr "%s: об’єкт пошкоджений або відсутній: %s"
+
+#, c-format
+msgid "%s: object is of unknown type '%s': %s"
+msgstr "%s: об’єкт невідомого типу \"%s\": %s"
+
+#, c-format
+msgid "%s: object could not be parsed: %s"
+msgstr "%s: неможливо розібрати об’єкт: %s"
+
+#, c-format
+msgid "bad sha1 file: %s"
+msgstr "невірний sha1 файл: %s"
+
+msgid "Checking object directory"
+msgstr "Перевірка директорії об’єкта"
+
+msgid "Checking object directories"
+msgstr "Перевірка директорій обʼєкта"
+
+#, c-format
+msgid "Checking %s link"
+msgstr "Перевірка %s посилання"
+
+#, c-format
+msgid "invalid %s"
+msgstr "неприпустимий %s"
+
+#, c-format
+msgid "%s points to something strange (%s)"
+msgstr "%s вказує на щось дивне (%s)"
+
+#, c-format
+msgid "%s: detached HEAD points at nothing"
+msgstr "%s: відокремлений HEAD вказує на ніщо"
+
+#, c-format
+msgid "notice: %s points to an unborn branch (%s)"
+msgstr "повідомлення: %s вказує на ненароджену гілку (%s)"
+
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "Перевірка дерева кеша для \"%s\""
+
+#, c-format
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s: невірний sha1 вказівник в cache-tree для %s"
+
+msgid "non-tree in cache-tree"
+msgstr "non-tree в cache-tree"
+
+#, c-format
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s: невірний sha1 вказівник в resolve-undo для %s"
+
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "не вдалося завантажити rev-index для пакунку \"%s\""
+
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "неприпустимий rev-index для \"%s\""
+
+msgid ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<object>...]"
+msgstr ""
+"git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
+"         [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
+"         [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
+"         [--[no-]name-objects] [<обʼєкт>...]"
+
+msgid "show unreachable objects"
+msgstr "показати недосяжні об’єкти"
+
+msgid "show dangling objects"
+msgstr "показати висячі об’єкти"
+
+msgid "report tags"
+msgstr "звітувати про теги"
+
+msgid "report root nodes"
+msgstr "звітувати про кореневі вузли"
+
+msgid "make index objects head nodes"
+msgstr "побудувати головні вузли об’єктів індексу"
+
+msgid "make reflogs head nodes (default)"
+msgstr "побудувати головні вузли журналу посилань (за замовчуванням)"
+
+msgid "also consider packs and alternate objects"
+msgstr "також розглядати пакунки та запозичені об’єкти"
+
+msgid "check only connectivity"
+msgstr "перевірити лише звʼязність"
+
+msgid "enable more strict checking"
+msgstr "увімкнути більш сувору перевірку"
+
+msgid "write dangling objects in .git/lost-found"
+msgstr "записувати висячі об’єкти в .git/lost-found"
+
+msgid "show progress"
+msgstr "показувати прогрес"
+
+msgid "show verbose names for reachable objects"
+msgstr "показувати докладні назви для доступних об’єктів"
+
+msgid "Checking objects"
+msgstr "Перевірка обʼєктів"
+
+#, c-format
+msgid "%s: object missing"
+msgstr "%s: об’єкт відсутній"
+
+#, c-format
+msgid "invalid parameter: expected sha1, got '%s'"
+msgstr "неприпустимий параметр: очікувалось sha1, надано \"%s\""
+
+msgid "git fsmonitor--daemon start [<options>]"
+msgstr "git fsmonitor--daemon start [<опції>]"
+
+msgid "git fsmonitor--daemon run [<options>]"
+msgstr "git fsmonitor--daemon run [<опції>]"
+
+#, c-format
+msgid "value of '%s' out of range: %d"
+msgstr "значення \"%s\" за межами діапазону: %d"
+
+#, c-format
+msgid "value of '%s' not bool or int: %d"
+msgstr "значення \"%s\" не є bool або int: %d"
+
+#, c-format
+msgid "fsmonitor-daemon is watching '%s'\n"
+msgstr "fsmonitor-daemon стежить за \"%s\"\n"
+
+#, c-format
+msgid "fsmonitor-daemon is not watching '%s'\n"
+msgstr "fsmonitor-daemon не стежить за \"%s\"\n"
+
+#, c-format
+msgid "could not create fsmonitor cookie '%s'"
+msgstr "не вдалося створити fsmonitor cookie \"%s\""
+
+#, c-format
+msgid "fsmonitor: cookie_result '%d' != SEEN"
+msgstr "fsmonitor: cookie_result \"%d\" != SEEN"
+
+#, c-format
+msgid "could not start IPC thread pool on '%s'"
+msgstr "не вдалося запустити пул потоків IPC на \"%s\""
+
+msgid "could not start fsmonitor listener thread"
+msgstr "не вдалося запустити потік слухача fsmonitor"
+
+msgid "could not start fsmonitor health thread"
+msgstr "не вдалося запустити потік стану fsmonitor"
+
+msgid "could not initialize listener thread"
+msgstr "не вдалося ініціалізувати потік слухача"
+
+msgid "could not initialize health thread"
+msgstr "не вдалося ініціалізувати потік стану"
+
+#, c-format
+msgid "could not cd home '%s'"
+msgstr "не вдалося виконати cd home \"%s\""
+
+#, c-format
+msgid "fsmonitor--daemon is already running '%s'"
+msgstr "fsmonitor--daemon вже запущений \"%s\""
+
+#, c-format
+msgid "running fsmonitor-daemon in '%s'\n"
+msgstr "запуск fsmonitor-daemon в \"%s\"\n"
+
+#, c-format
+msgid "starting fsmonitor-daemon in '%s'\n"
+msgstr "старт fsmonitor-daemon в \"%s\"\n"
+
+msgid "daemon failed to start"
+msgstr "не вдалося запустити демон"
+
+msgid "daemon not online yet"
+msgstr "демон ще не онлайн"
+
+msgid "daemon terminated"
+msgstr "роботу демона припинено"
+
+msgid "detach from console"
+msgstr "від’єднати від консолі"
+
+msgid "use <n> ipc worker threads"
+msgstr "використовувати <н> потоків IPC працівника"
+
+msgid "max seconds to wait for background daemon startup"
+msgstr "максимальна кількість секунд для очікування запуску фонового демона"
+
+#, c-format
+msgid "invalid 'ipc-threads' value (%d)"
+msgstr "невірне значення \"ipc-threads\" (%d)"
+
+#, c-format
+msgid "Unhandled subcommand '%s'"
+msgstr "Необроблена підкоманда \"%s\""
+
+msgid "fsmonitor--daemon not supported on this platform"
+msgstr "fsmonitor--daemon не підтримується на цій платформі"
+
+msgid "git gc [<options>]"
+msgstr "git gc [<опції>]"
+
+#, c-format
+msgid "Failed to fstat %s: %s"
+msgstr "Не вдалося виконати fstat %s: %s"
+
+#, c-format
+msgid "failed to parse '%s' value '%s'"
+msgstr "не вдалося розібрати \"%s\" значення \"%s\""
+
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "неможливо виконати stat \"%s\""
+
+#, c-format
+msgid ""
+"The last gc run reported the following. Please correct the root cause\n"
+"and remove %s\n"
+"Automatic cleanup will not be performed until the file is removed.\n"
+"\n"
+"%s"
+msgstr ""
+"Попередній запуск gc показав наступне. Будь ласка, виправте першопричину\n"
+"і видаліть %s\n"
+"Автоматичне очищення не буде виконано, доки файл не буде вилучено.\n"
+"\n"
+"%s"
+
+msgid "prune unreferenced objects"
+msgstr "видалити об’єкти, на які немає посилань"
+
+msgid "pack unreferenced objects separately"
+msgstr "пакувати об’єкти, на які немає посилань, окремо"
+
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "з --cruft, обмежити розмір нових марних пакунків"
+
+msgid "be more thorough (increased runtime)"
+msgstr "працювати ретельніше (збільшує час виконання)"
+
+msgid "enable auto-gc mode"
+msgstr "увімкнути режим автоматичного збору сміття"
+
+msgid "force running gc even if there may be another gc running"
+msgstr "примусово запускати збирач сміття, навіть якщо інший збирач вже працює"
+
+msgid "repack all other packs except the largest pack"
+msgstr "перепакувати всі пакунки, крім найбільшого"
+
+#, c-format
+msgid "failed to parse gc.logExpiry value %s"
+msgstr "не вдалося розібрати gc.logExpiry значення %s"
+
+#, c-format
+msgid "failed to parse prune expiry value %s"
+msgstr ""
+"не вдалося розібрати значення дати закінчення терміну дії для видалення %s"
+
+#, c-format
+msgid "Auto packing the repository in background for optimum performance.\n"
+msgstr ""
+"Автоматичне пакування сховища у фоновому режимі для оптимальної "
+"продуктивності.\n"
+
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr "Автоматичне пакування сховища для оптимальної продуктивності.\n"
+
+#, c-format
+msgid "See \"git help gc\" for manual housekeeping.\n"
+msgstr ""
+"Дивіться \"git help gc\" для отримання інформації про налагодження вручну.\n"
+
+#, c-format
+msgid ""
+"gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)"
+msgstr ""
+"збір сміття вже запущено на машині \"%s\" pid %<PRIuMAX> (скористайтесь --"
+"force, якщо ні)"
+
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+"Занадто багато недосяжних вільних об’єктів; запустіть \"git prune\", щоб "
+"вилучити їх."
+
+msgid ""
+"git maintenance run [--auto] [--[no-]quiet] [--task=<task>] [--schedule]"
+msgstr ""
+"git maintenance run [--auto] [--[no-]quiet] [--task=<завдання>] [--schedule]"
+
+msgid "--no-schedule is not allowed"
+msgstr "--no-schedule не дозволяється"
+
+#, c-format
+msgid "unrecognized --schedule argument '%s'"
+msgstr "нерозпізнаний аргумент --schedule \"%s\""
+
+msgid "failed to write commit-graph"
+msgstr "не вдалося записати граф комітів"
+
+msgid "failed to prefetch remotes"
+msgstr "не вдалося виконати попереднє отримання з віддалених сховищ"
+
+msgid "failed to start 'git pack-objects' process"
+msgstr "не вдалося запустити \"git pack-objects\" процес"
+
+msgid "failed to finish 'git pack-objects' process"
+msgstr "не вдалося завершити \"git pack-objects\" процес"
+
+msgid "failed to write multi-pack-index"
+msgstr "не вдалося записати multi-pack-index"
+
+msgid "'git multi-pack-index expire' failed"
+msgstr "\"git multi-pack-index expire\" завершився невдало"
+
+msgid "'git multi-pack-index repack' failed"
+msgstr "\"git multi-pack-index repack\" завершився невдало"
+
+msgid ""
+"skipping incremental-repack task because core.multiPackIndex is disabled"
+msgstr ""
+"пропуск incremental-repack завдання, оскільки core.multiPackIndex вимкнено"
+
+#, c-format
+msgid "lock file '%s' exists, skipping maintenance"
+msgstr "файл блокування \"%s\" існує, пропуск обслуговування"
+
+#, c-format
+msgid "task '%s' failed"
+msgstr "завдання \"%s\" завершилося невдало"
+
+#, c-format
+msgid "'%s' is not a valid task"
+msgstr "\"%s\" не є припустимим завданням"
+
+#, c-format
+msgid "task '%s' cannot be selected multiple times"
+msgstr "завдання \"%s\" не можна вибрати кілька разів"
+
+msgid "run tasks based on the state of the repository"
+msgstr "запускати завдання на основі стану сховища"
+
+msgid "frequency"
+msgstr "частота"
+
+msgid "run tasks based on frequency"
+msgstr "запускати задачі на основі частоти"
+
+msgid "do not report progress or other information over stderr"
+msgstr "не показувати хід виконання та іншу інформацію в stderr"
+
+msgid "task"
+msgstr "завдання"
+
+msgid "run a specific task"
+msgstr "запустити певне завдання"
+
+msgid "use at most one of --auto and --schedule=<frequency>"
+msgstr "використовуйте щонайбільше одну з опцій --auto та --schedule=<частота>"
+
+#, c-format
+msgid "unable to add '%s' value of '%s'"
+msgstr "не вдалося додати \"%s\" значення до \"%s\""
+
+msgid "return success even if repository was not registered"
+msgstr "повертати успіх, навіть якщо сховище не було зареєстровано"
+
+#, c-format
+msgid "unable to unset '%s' value of '%s'"
+msgstr "не вдалося скинути \"%s\" значення для \"%s\""
+
+#, c-format
+msgid "repository '%s' is not registered"
+msgstr "сховище \"%s\" не зареєстровано"
+
+#, c-format
+msgid "failed to expand path '%s'"
+msgstr "не вдалося розгорнути шлях \"%s\""
+
+msgid "failed to start launchctl"
+msgstr "не вдалося запустити launchctl"
+
+#, c-format
+msgid "failed to create directories for '%s'"
+msgstr "не вдалося створити директорії для \"%s\""
+
+#, c-format
+msgid "failed to bootstrap service %s"
+msgstr "не вдалося розгорнути службу %s"
+
+msgid "failed to create temp xml file"
+msgstr "не вдалося створити тимчасовий xml файл"
+
+msgid "failed to start schtasks"
+msgstr "не вдалося запустити schtasks"
+
+msgid "failed to run 'crontab -l'; your system might not support 'cron'"
+msgstr ""
+"не вдалося запустити \"crontab -l\"; можливо, ваша система не підтримує "
+"\"cron\""
+
+msgid "failed to create crontab temporary file"
+msgstr "не вдалося створити тимчасовий crontab файл"
+
+msgid "failed to open temporary file"
+msgstr "не вдалося відкрити тимчасовий файл"
+
+msgid "failed to run 'crontab'; your system might not support 'cron'"
+msgstr ""
+"не вдалося запустити \"crontab\"; можливо, ваша система не підтримує \"cron\""
+
+msgid "'crontab' died"
+msgstr "\"crontab\" завершився невдало"
+
+#, c-format
+msgid "failed to delete '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "failed to flush '%s'"
+msgstr "не вдалося очистити \"%s\""
+
+msgid "failed to start systemctl"
+msgstr "не вдалося стартувати systemctl"
+
+msgid "failed to run systemctl"
+msgstr "не вдалося запустити systemctl"
+
+#, c-format
+msgid "unrecognized --scheduler argument '%s'"
+msgstr "нерозпізнаний --scheduler аргумент \"%s\""
+
+msgid "neither systemd timers nor crontab are available"
+msgstr "недоступні ні systemd таймери, ні crontab"
+
+#, c-format
+msgid "%s scheduler is not available"
+msgstr "%s планувальник недоступний"
+
+msgid "another process is scheduling background maintenance"
+msgstr "ще один процес планує фонове обслуговування"
+
+msgid "git maintenance start [--scheduler=<scheduler>]"
+msgstr "git maintenance start [--scheduler=<планувальник>]"
+
+msgid "scheduler"
+msgstr "планувальник"
+
+msgid "scheduler to trigger git maintenance run"
+msgstr "планувальник для запуску обслуговування git"
+
+msgid "failed to set up maintenance schedule"
+msgstr "не вдалося встановити розклад обслуговування"
+
+msgid "failed to add repo to global config"
+msgstr "не вдалося додати сховище до глобальної конфігурації"
+
+msgid "git maintenance <subcommand> [<options>]"
+msgstr "git maintenance <підкоманда> [<опції>]"
+
+msgid "git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"
+msgstr "git grep [<опції>] [-e] <шаблон> [<ревізія>...] [[--] <шлях>...]"
+
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: не вдалося створити потік: %s"
+
+#, c-format
+msgid "invalid number of threads specified (%d) for %s"
+msgstr "невірно вказана кількість потоків (%d) для %s"
+
+#. #-#-#-#-#  grep.c.po  #-#-#-#-#
+#. TRANSLATORS: %s is the configuration
+#. variable for tweaking threads, currently
+#. grep.threads
+#.
+#, c-format
+msgid "no threads support, ignoring %s"
+msgstr "немає підтримки потоків, ігнорування %s"
+
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "не вдалося прочитати дерево (%s)"
+
+#, c-format
+msgid "unable to read tree %s"
+msgstr "не вдалося прочитати дерево %s"
+
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr "не вдалося виконати grep для об’єкта типу %s"
+
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "switch \"%c\" очікує числове значення"
+
+msgid "search in index instead of in the work tree"
+msgstr "шукати в індексі замість робочого дерева"
+
+msgid "find in contents not managed by git"
+msgstr "шукати у вмісті, не індексованому git"
+
+msgid "search in both tracked and untracked files"
+msgstr "шукати як у відстежуваних, так і в невідстежуваних файлах"
+
+msgid "ignore files specified via '.gitignore'"
+msgstr "ігнорувати файли, вказані через \".gitignore\""
+
+msgid "recursively search in each submodule"
+msgstr "шукати рекурсивно в кожному підмодулі"
+
+msgid "show non-matching lines"
+msgstr "показувати рядки, що не збігаються"
+
+msgid "case insensitive matching"
+msgstr "нечутливе до регістру зіставлення"
+
+msgid "match patterns only at word boundaries"
+msgstr "зіставляти шаблони тільки на межі слів"
+
+msgid "process binary files as text"
+msgstr "обробляти бінарні файли як текст"
+
+msgid "don't match patterns in binary files"
+msgstr "не зіставляти шаблони в бінарних файлах"
+
+msgid "process binary files with textconv filters"
+msgstr "обробляти бінарні файли за допомогою textconv фільтрів"
+
+msgid "search in subdirectories (default)"
+msgstr "шукати в піддиректоріях (за замовчуванням)"
+
+msgid "descend at most <n> levels"
+msgstr "спускатися не більше ніж на <н> рівнів"
+
+msgid "use extended POSIX regular expressions"
+msgstr "використовувати розширені POSIX регулярні вирази"
+
+msgid "use basic POSIX regular expressions (default)"
+msgstr "використовувати базові регулярні вирази POSIX (за замовчуванням)"
+
+msgid "interpret patterns as fixed strings"
+msgstr "інтерпретувати шаблони як фіксовані строки"
+
+msgid "use Perl-compatible regular expressions"
+msgstr "використовувати Perl-сумісні регулярні вирази"
+
+msgid "show line numbers"
+msgstr "показувати номери рядків"
+
+msgid "show column number of first match"
+msgstr "показувати номер стовпця першого збігу"
+
+msgid "don't show filenames"
+msgstr "не показувати назви файлів"
+
+msgid "show filenames"
+msgstr "показувати назви файлів"
+
+msgid "show filenames relative to top directory"
+msgstr "показувати назви файлів відносно верхнього каталогу"
+
+msgid "show only filenames instead of matching lines"
+msgstr "показувати лише назви файлів замість відповідних рядків"
+
+msgid "synonym for --files-with-matches"
+msgstr "синонім для --files-with-matches"
+
+msgid "show only the names of files without match"
+msgstr "показувати лише назви файлів без збігу"
+
+msgid "print NUL after filenames"
+msgstr "друкувати NUL після назв файлів"
+
+msgid "show only matching parts of a line"
+msgstr "показувати лише частини рядка, що збігаються"
+
+msgid "show the number of matches instead of matching lines"
+msgstr "показувати кількість збігів замість рядків, що збігаються"
+
+msgid "highlight matches"
+msgstr "виділяти збіги"
+
+msgid "print empty line between matches from different files"
+msgstr "друкувати порожній рядок між збігами з різних файлів"
+
+msgid "show filename only once above matches from same file"
+msgstr "показувати назву файлу лише один раз над збігами з того файлу"
+
+msgid "show <n> context lines before and after matches"
+msgstr "показувати <н> рядків до і після збігу"
+
+msgid "show <n> context lines before matches"
+msgstr "показувати <н> рядків до збігу"
+
+msgid "show <n> context lines after matches"
+msgstr "показувати <н> рядків після збігу"
+
+msgid "use <n> worker threads"
+msgstr "використати <н> робочих потоків"
+
+msgid "shortcut for -C NUM"
+msgstr "скорочення для -C номер"
+
+msgid "show a line with the function name before matches"
+msgstr "показувати рядок з назвою функції перед збігами"
+
+msgid "show the surrounding function"
+msgstr "показати навколишню функцію"
+
+msgid "read patterns from file"
+msgstr "зчитувати шаблони з файлу"
+
+msgid "match <pattern>"
+msgstr "зіставляти <шаблон>"
+
+msgid "combine patterns specified with -e"
+msgstr "об’єднати шаблони, вказані через -e"
+
+msgid "indicate hit with exit status without output"
+msgstr "позначати збіг кодом виходу без виводу"
+
+msgid "show only matches from files that match all patterns"
+msgstr "показувати збіги лише з файлів, які відповідають усім шаблонам"
+
+msgid "pager"
+msgstr "пейджер"
+
+msgid "show matching files in the pager"
+msgstr "показати відповідні файли в пейджері"
+
+msgid "allow calling of grep(1) (ignored by this build)"
+msgstr "дозволяти виклик grep(1) (ігнорується у цій збірці)"
+
+msgid "maximum number of results per file"
+msgstr "максимальна кількість результатів у файлі"
+
+msgid "no pattern given"
+msgstr "шаблон не надано"
+
+msgid "--no-index or --untracked cannot be used with revs"
+msgstr "--no-index або --untracked не можна використовувати з ревізіями"
+
+#, c-format
+msgid "unable to resolve revision: %s"
+msgstr "не вдалося розвʼязати ревізію: %s"
+
+msgid "--untracked not supported with --recurse-submodules"
+msgstr "--untracked не підтримується з --recurse-submodules"
+
+msgid "invalid option combination, ignoring --threads"
+msgstr "неприпустима комбінація опцій, ігнорування --threads"
+
+msgid "no threads support, ignoring --threads"
+msgstr "немає підтримки потоків, ігнорування --threads"
+
+#, c-format
+msgid "invalid number of threads specified (%d)"
+msgstr "вказано неприпустиму кількість потоків (%d)"
+
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager працює тільки в робочому дереві"
+
+msgid "--[no-]exclude-standard cannot be used for tracked contents"
+msgstr ""
+"--[no-]exclude-standard не можна використовувати для відстежуваного вмісту"
+
+msgid "both --cached and trees are given"
+msgstr "надані як --cached, так і дерева"
+
+msgid ""
+"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <file>..."
+msgstr ""
+"git hash-object [-t <тип>] [-w] [--path=<файл> | --no-filters]\n"
+"                [--stdin [--literally]] [--] <файл>..."
+
+msgid "git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"
+msgstr "git hash-object [-t <тип>] [-w] --stdin-paths [--no-filters]"
+
+msgid "object type"
+msgstr "тип обʼєкта"
+
+msgid "write the object into the object database"
+msgstr "записати об’єкт до бази даних об’єктів"
+
+msgid "read the object from stdin"
+msgstr "прочитати об’єкт з stdin"
+
+msgid "store file as is without filters"
+msgstr "зберегти файл як є без фільтрів"
+
+msgid ""
+"just hash any random garbage to create corrupt objects for debugging Git"
+msgstr ""
+"просто хешувати будь-який випадковий непотріб, щоб створити пошкоджені "
+"об’єкти для відлагодження Git"
+
+msgid "process file as it were from this path"
+msgstr "оброблювати файл, наче він з цього шляху"
+
+msgid "print all available commands"
+msgstr "показати всі доступні команди"
+
+msgid "show external commands in --all"
+msgstr "показати зовнішні команди в --all"
+
+msgid "show aliases in --all"
+msgstr "показати псевдоніми в --all"
+
+msgid "exclude guides"
+msgstr "виключити посібники"
+
+msgid "show man page"
+msgstr "показати сторінку керівництва користувача"
+
+msgid "show manual in web browser"
+msgstr "показати сторінку керівництва користувача в веб-браузері"
+
+msgid "show info page"
+msgstr "показати інформаційну сторінку"
+
+msgid "print command description"
+msgstr "показати опис команди"
+
+msgid "print list of useful guides"
+msgstr "показати список корисних посібників"
+
+msgid "print list of user-facing repository, command and file interfaces"
+msgstr "показати список інтерфейсів користувача для сховищ, команд та файлів"
+
+msgid "print list of file formats, protocols and other developer interfaces"
+msgstr ""
+"показати список форматів файлів, протоколів та інших інтерфейсів розробника"
+
+msgid "print all configuration variable names"
+msgstr "показати всі назви конфігураційних змінних"
+
+msgid "git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"
+msgstr "git help [[-i|--info] [-m|--man] [-w|--web]] [<команда>|<док>]"
+
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "нерозпізнаний формат довідки \"%s\""
+
+msgid "Failed to start emacsclient."
+msgstr "Не вдалося запустити emacsclient."
+
+msgid "Failed to parse emacsclient version."
+msgstr "Не вдалося розібрати версію emacsclient."
+
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr "версія emacsclient \"%d\" застаріла (< 22)."
+
+#, c-format
+msgid "failed to exec '%s'"
+msgstr "не вдалося виконати \"%s\""
+
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+"\"%s\": шлях до непідтримуваного переглядача керівництва користувача.\n"
+"Будь ласка, скористайтесь \"man.<tool>.cmd\" замість цього."
+
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+"\"%s\": команда для підтримуваного переглядача керівництва користувача.\n"
+"Будь ласка, скористайтесь \"man.<tool>.path\" замість цього."
+
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr "\"%s\": невідомий переглядач керівництва користувача."
+
+msgid "no man viewer handled the request"
+msgstr "жоден з переглядачів керівництва користувача не обробив запит"
+
+msgid "no info viewer handled the request"
+msgstr "жоден з переглядачів інформації не обробив запит"
+
+#, c-format
+msgid "'%s' is aliased to '%s'"
+msgstr "\"%s\" є псевдонімом для \"%s\""
+
+#, c-format
+msgid "bad alias.%s string: %s"
+msgstr "невірний псевдонім.%s рядок: %s"
+
+#, c-format
+msgid "the '%s' option doesn't take any non-option arguments"
+msgstr "опція \"%s\" не приймає жодних неопціональних аргументів"
+
+msgid ""
+"the '--no-[external-commands|aliases]' options can only be used with '--all'"
+msgstr ""
+"опції \"--no-[external-commands|aliases]\" можна використовувати лише з \"--"
+"all\""
+
+#, c-format
+msgid "usage: %s%s"
+msgstr "використання: %s%s"
+
+msgid "'git help config' for more information"
+msgstr "\"git help config\" для додаткової інформації"
+
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<шлях>] <назва-гачка> [-- "
+"<аргументи-гачка>]"
+
+msgid "silently ignore missing requested <hook-name>"
+msgstr "мовчки ігнорувати відсутній <назва-гачка>"
+
+msgid "file to read into hooks' stdin"
+msgstr "файл, з якого читати stdin хука"
+
+#, c-format
+msgid "object type mismatch at %s"
+msgstr "не співпадає тип обʼєкта для %s"
+
+#, c-format
+msgid "did not receive expected object %s"
+msgstr "очікуваних обʼєктів не отримано %s"
+
+#, c-format
+msgid "object %s: expected type %s, found %s"
+msgstr "обʼєкт %s: очікувався тип %s, знайдено %s"
+
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] "не вдається заповнити %d байт"
+msgstr[1] "не вдається заповнити %d байти"
+msgstr[2] "не вдається заповнити %d байтів"
+
+msgid "early EOF"
+msgstr "ранній EOF"
+
+msgid "read error on input"
+msgstr "помилка зчитування на вході"
+
+msgid "used more bytes than were available"
+msgstr "використано більше байтів, ніж було доступно"
+
+msgid "pack too large for current definition of off_t"
+msgstr "пакунок занадто великий для поточного визначення off_t"
+
+#, c-format
+msgid "pack exceeds maximum allowed size (%s)"
+msgstr "пакунок перевищує максимально дозволений розмір (%s)"
+
+msgid "pack signature mismatch"
+msgstr "не співпадає підпис пакунка"
+
+#, c-format
+msgid "pack version %<PRIu32> unsupported"
+msgstr "версія пакунка %<PRIu32> не підтримується"
+
+#, c-format
+msgid "pack has bad object at offset %<PRIuMAX>: %s"
+msgstr "пакунок має невірний обʼєкт зі зміщенням %<PRIuMAX>: %s"
+
+#, c-format
+msgid "inflate returned %d"
+msgstr "розпаковувач повернув %d"
+
+msgid "offset value overflow for delta base object"
+msgstr "переповнення значення зсуву для обʼєкту дельти з базою"
+
+msgid "delta base offset is out of bound"
+msgstr "зсув дельти з базою виходить за межі"
+
+#, c-format
+msgid "unknown object type %d"
+msgstr "невідомий тип обʼєкта %d"
+
+msgid "cannot pread pack file"
+msgstr "неможливо прочитати файл пакунка"
+
+#, c-format
+msgid "premature end of pack file, %<PRIuMAX> byte missing"
+msgid_plural "premature end of pack file, %<PRIuMAX> bytes missing"
+msgstr[0] "передчасний кінець файлу пакунка, %<PRIuMAX> байт відсутній"
+msgstr[1] "передчасний кінець файлу пакунка, %<PRIuMAX> байта відсутні"
+msgstr[2] "передчасний кінець файлу пакунка, %<PRIuMAX> байтів відсутні"
+
+msgid "serious inflate inconsistency"
+msgstr "серйозне неспівпадіння під час розпакування"
+
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr "ВИЯВЛЕНО SHA1 КОЛІЗІЮ З %s!"
+
+#, c-format
+msgid "cannot read existing object info %s"
+msgstr "неможливо прочитати інформацію про існуючий об’єкт %s"
+
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "неможливо прочитати існуючий об’єкт %s"
+
+#, c-format
+msgid "invalid blob object %s"
+msgstr "неприпустимий об’єкт blob %s"
+
+msgid "fsck error in packed object"
+msgstr "помилка fsck у запакованому обʼєкті"
+
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr "Не всі дочірні об’єкти %s доступні"
+
+msgid "failed to apply delta"
+msgstr "не вдалося застосувати дельту"
+
+msgid "Receiving objects"
+msgstr "Отримання об’єктів"
+
+msgid "Indexing objects"
+msgstr "Індексація об’єктів"
+
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr "пакунок пошкоджено (SHA1 не співпадає)"
+
+msgid "cannot fstat packfile"
+msgstr "неможливо зробити fstat файла пакунка"
+
+msgid "pack has junk at the end"
+msgstr "мотлох наприкінці пакунка"
+
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr "плутанина на межі пекельного борошна в parse_pack_objects()"
+
+msgid "Resolving deltas"
+msgstr "Розв’язання дельт"
+
+#, c-format
+msgid "unable to create thread: %s"
+msgstr "не вдалося створити потік: %s"
+
+msgid "confusion beyond insanity"
+msgstr "плутанина на межі пекельного борошна"
+
+#, c-format
+msgid "completed with %d local object"
+msgid_plural "completed with %d local objects"
+msgstr[0] "завершено з %d локальним об’єктом"
+msgstr[1] "завершено з %d локальними об’єктами"
+msgstr[2] "завершено з %d локальними об’єктами"
+
+#, c-format
+msgid "Unexpected tail checksum for %s (disk corruption?)"
+msgstr "Неочікувана контрольна сума наприкінці %s (пошкодження диска?)"
+
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] "пакунок має %d нерозвʼязану дельту"
+msgstr[1] "пакунок має %d нерозвʼязаних дельти"
+msgstr[2] "пакунок має %d нерозвʼязаних дельт"
+
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr "не вдалося запакувати доданий об’єкт (%d)"
+
+#, c-format
+msgid "local object %s is corrupt"
+msgstr "локальний обʼєкт %s пошкоджено"
+
+#, c-format
+msgid "packfile name '%s' does not end with '.%s'"
+msgstr "ім’я файла пакунка '%s' не закінчується на '.%s'"
+
+#, c-format
+msgid "cannot write %s file '%s'"
+msgstr "неможливо записати %s файл '%s'"
+
+#, c-format
+msgid "cannot close written %s file '%s'"
+msgstr "неможливо закрити записаний %s файл '%s'"
+
+#, c-format
+msgid "unable to rename temporary '*.%s' file to '%s'"
+msgstr "не вдається перейменувати тимчасовий файл '*.%s' на '%s'"
+
+msgid "error while closing pack file"
+msgstr "помилка під час закриття файлу пакунка"
+
+#, c-format
+msgid "bad pack.indexVersion=%<PRIu32>"
+msgstr "невірний pack.indexVersion=%<PRIu32>"
+
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Неможливо відкрити існуючий файл пакунка '%s"
+
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Неможливо відкрити існуючий індексний файл пакунка для '%s"
+
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] "не дельта: %d об’єкт"
+msgstr[1] "не дельта: %d об’єкта"
+msgstr[2] "не дельта: %d об’єктів"
+
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] "довжина ланцюжка = %d: %lu об’єкт"
+msgstr[1] "довжина ланцюжка = %d: %lu об’єкти"
+msgstr[2] "довжина ланцюжка = %d: %lu об’єктів"
+
+msgid "Cannot come back to cwd"
+msgstr "Неможливо повернутися до поточної робочої директорії"
+
+#, c-format
+msgid "bad %s"
+msgstr "невірний %s"
+
+#, c-format
+msgid "unknown hash algorithm '%s'"
+msgstr "невідомий хеш-алгоритм '%s'"
+
+msgid "--stdin requires a git repository"
+msgstr "--stdin потребує наявності git сховища"
+
+msgid "--verify with no packfile name given"
+msgstr "--verify без зазначення имені файла пакунка"
+
+msgid "fsck error in pack objects"
+msgstr "помилка fsck в об’єктах пакунка"
+
+msgid ""
+"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
+"         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
+"         [-b <branch-name> | --initial-branch=<branch-name>]\n"
+"         [--shared[=<permissions>]] [<directory>]"
+msgstr ""
+"git init [-q | --quiet] [--bare] [--template=<шаблон-директорія>]\n"
+"         [--separate-git-dir <git-директорія>] [--object-format=<формат>]\n"
+"         [--ref-format=<формат>]\n"
+"         [-b <назва-гілки> | --initial-branch=<назва-гілки>]\n"
+"         [--shared[=<дозволи>]] [<директорія>]"
+
+msgid "permissions"
+msgstr "дозволи"
+
+msgid "specify that the git repository is to be shared amongst several users"
+msgstr "вказати, що git сховище буде спільним для кількох користувачів"
+
+msgid "override the name of the initial branch"
+msgstr "перевизначити назву початкової гілки"
+
+msgid "hash"
+msgstr "хеш"
+
+msgid "specify the hash algorithm to use"
+msgstr "вказати, який алгоритм хешування використовувати"
+
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "не вдалося виконати mkdir %s"
+
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "не вдалося виконати chdir %s"
+
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"%s (або --work-tree=<директорія>) не дозволено без вказівки %s (або --git-"
+"dir=<каталог>)"
+
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "Неможливо отримати доступ до робочого дерева \"%s\""
+
+msgid "--separate-git-dir incompatible with bare repository"
+msgstr "--separate-git-dir несумісна з порожнім сховищем"
+
+msgid ""
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+"                       [--parse] [<file>...]"
+msgstr ""
+"git interpret-trailers [--in-place] [--trim-empty]\n"
+"                       [(--trailer <ключ>|"
+"<аліасКлюча>[(=|:)<значення>])...]\n"
+"                       [--parse] [<файл>...]"
+
+msgid "edit files in place"
+msgstr "редагувати файли на місцях"
+
+msgid "trim empty trailers"
+msgstr "обрізати порожні причепи"
+
+msgid "placement"
+msgstr "розміщення"
+
+msgid "where to place the new trailer"
+msgstr "де розмістити новий причіп"
+
+msgid "action if trailer already exists"
+msgstr "що робити, якщо причіп вже існує"
+
+msgid "action if trailer is missing"
+msgstr "що робити, якщо причіп відсутній"
+
+msgid "output only the trailers"
+msgstr "виводити лише причепи"
+
+msgid "do not apply trailer.* configuration variables"
+msgstr "не застосовувати конфігураційні змінні trailer.*"
+
+msgid "reformat multiline trailer values as single-line values"
+msgstr "переформатувати багаторядкові значення причепів в однорядкові"
+
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "аліас для --only-trailers --only-input --unfold"
+
+msgid "do not treat \"---\" as the end of input"
+msgstr "не обробляти \"---\" як кінець вводу"
+
+msgid "trailer(s) to add"
+msgstr "причіп(и) для додавання"
+
+msgid "--trailer with --only-input does not make sense"
+msgstr "--trailer з --only-input не має сенсу"
+
+msgid "no input file given for in-place editing"
+msgstr "не надано вхідного файлу для редагування на місці"
+
+msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
+msgstr "git log [<опції>] [<діапазон-ревізій>] [[--] <шлях>...]"
+
+msgid "git show [<options>] <object>..."
+msgstr "git show [<опції>] <обʼєкт>..."
+
+#, c-format
+msgid "invalid --decorate option: %s"
+msgstr "неприпустима --decorate опція: %s"
+
+msgid "suppress diff output"
+msgstr "приховати вивід diff"
+
+msgid "show source"
+msgstr "показати джерело"
+
+msgid "clear all previously-defined decoration filters"
+msgstr "очистити всі раніше визначені фільтри оздоблення"
+
+msgid "only decorate refs that match <pattern>"
+msgstr "оздоблювати лише посилання, що відповідають <шаблону>"
+
+msgid "do not decorate refs that match <pattern>"
+msgstr "не оздоблювати посилання, що відповідають <шаблону>"
+
+msgid "decorate options"
+msgstr "опції оздоблення"
+
+msgid ""
+"trace the evolution of line range <start>,<end> or function :<funcname> in "
+"<file>"
+msgstr ""
+"простежити еволюцію діапазону рядків <початок>,<кінець> або функції :<назва-"
+"функції> в <файлі>"
+
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "нерозпізнаний аргумент: %s"
+
+msgid "-L<range>:<file> cannot be used with pathspec"
+msgstr "-L<діапазон>:<файл> не можна використовувати з визначником шляху"
+
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "Кінцевий результат: %d %s\n"
+
+msgid "unable to create temporary object directory"
+msgstr "не вдалося створити тимчасову директорію об’єкта"
+
+#, c-format
+msgid "git show %s: bad file"
+msgstr "git show %s: невірний файл"
+
+#, c-format
+msgid "could not read object %s"
+msgstr "не вдалося прочитати об’єкт %s"
+
+#, c-format
+msgid "unknown type: %d"
+msgstr "невідомий тип: %d"
+
+#, c-format
+msgid "%s: invalid cover from description mode"
+msgstr "%s: невірна обкладинка з режиму опису"
+
+msgid "format.headers without value"
+msgstr "format.headers без значення"
+
+#, c-format
+msgid "cannot open patch file %s"
+msgstr "не вдається відкрити файл латки %s"
+
+msgid "need exactly one range"
+msgstr "потрібен лишень один діапазон"
+
+msgid "not a range"
+msgstr "не діапазон"
+
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "не вдалося прочитати файл опису гілки \"%s\""
+
+msgid "cover letter needs email format"
+msgstr "супровідний лист має бути у форматі електронної пошти"
+
+msgid "failed to create cover-letter file"
+msgstr "не вдалося створити файл супровідного листа"
+
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "неприпустимий in-reply-to: %s"
+
+msgid "git format-patch [<options>] [<since> | <revision-range>]"
+msgstr "git format-patch [<опції>] [<відколи> | <діапазон-ревізій>]"
+
+msgid "two output directories?"
+msgstr "дві вихідні директорії?"
+
+#, c-format
+msgid "unknown commit %s"
+msgstr "невідомий коміт %s"
+
+#, c-format
+msgid "failed to resolve '%s' as a valid ref"
+msgstr "не вдалося розпізнати \"%s\" як припустиме посилання"
+
+msgid "could not find exact merge base"
+msgstr "не вдалося знайти точну базу для злиття"
+
+msgid ""
+"failed to get upstream, if you want to record base commit automatically,\n"
+"please use git branch --set-upstream-to to track a remote branch.\n"
+"Or you could specify base commit by --base=<base-commit-id> manually"
+msgstr ""
+"не вдалося отримати першоджерельне сховище. Якщо ви хочете записати базовий "
+"коміт автоматично,\n"
+"будь ласка, скористайтесь git branch --set-upstream-to для відстежування "
+"віддаленої гілки.\n"
+"Або ви можете вказати базовий коміт вручну за допомогою --"
+"base=<ідентифікатор-базового-коміту>"
+
+msgid "failed to find exact merge base"
+msgstr "не вдалося знайти точну базу для злиття"
+
+msgid "base commit should be the ancestor of revision list"
+msgstr "базовий коміт має бути предком списку ревізій"
+
+msgid "base commit shouldn't be in revision list"
+msgstr "базового коміту не має бути в списку ревізій"
+
+msgid "cannot get patch id"
+msgstr "неможливо отримати ідентифікатор латки"
+
+msgid "failed to infer range-diff origin of current series"
+msgstr "не вдалося визначити походження різниці діапазонів поточного ряду"
+
+#, c-format
+msgid "using '%s' as range-diff origin of current series"
+msgstr "використання \"%s\" як походження різниці діапазонів поточного ряду"
+
+msgid "use [PATCH n/m] even with a single patch"
+msgstr "використовуйте [PATCH n/m] навіть з однією латкою"
+
+msgid "use [PATCH] even with multiple patches"
+msgstr "використовуйте [PATCH] навіть з кількома латками"
+
+msgid "print patches to standard out"
+msgstr "вивести латки на стандартний вивід"
+
+msgid "generate a cover letter"
+msgstr "скласти супровідний лист"
+
+msgid "use simple number sequence for output file names"
+msgstr "використати просту послідовність чисел для назв вихідних файлів"
+
+msgid "sfx"
+msgstr "суфікс"
+
+msgid "use <sfx> instead of '.patch'"
+msgstr "використати <суфікс> замість \".patch\""
+
+msgid "start numbering patches at <n> instead of 1"
+msgstr "почати нумерацію латок з <н> замість 1"
+
+msgid "reroll-count"
+msgstr "кількість перекидань"
+
+msgid "mark the series as Nth re-roll"
+msgstr "позначити ряд як N-не перекидання"
+
+msgid "max length of output filename"
+msgstr "максимальна довжина назви вихідного файлу"
+
+msgid "use [RFC PATCH] instead of [PATCH]"
+msgstr "використати [RFC PATCH] замість [PATCH]"
+
+msgid "cover-from-description-mode"
+msgstr "cover-from-description-mode"
+
+msgid "generate parts of a cover letter based on a branch's description"
+msgstr "скласти частини супровідного листа на основі опису гілки"
+
+msgid "use branch description from file"
+msgstr "використовувати опис гілки з файлу"
+
+msgid "use [<prefix>] instead of [PATCH]"
+msgstr "використати [<префікс>] замість [PATCH]"
+
+msgid "store resulting files in <dir>"
+msgstr "зберегти результуючі файли в <директорії>"
+
+msgid "don't strip/add [PATCH]"
+msgstr "не видаляти/додавати [PATCH]"
+
+msgid "don't output binary diffs"
+msgstr "не виводити бінарні різниці"
+
+msgid "output all-zero hash in From header"
+msgstr "вивести хеш з усіма нулями в заголовку From"
+
+msgid "don't include a patch matching a commit upstream"
+msgstr ""
+"не включати латки, які мають відповідні коміти в першоджерельному сховищі"
+
+msgid "show patch format instead of default (patch + stat)"
+msgstr "показати формат латки замість стандартного (латка + підсумок)"
+
+msgid "Messaging"
+msgstr "Повідомлення"
+
+msgid "header"
+msgstr "заголовок"
+
+msgid "add email header"
+msgstr "додати заголовок листа"
+
+msgid "email"
+msgstr "електронна адреса"
+
+msgid "add To: header"
+msgstr "додати To: заголовок"
+
+msgid "add Cc: header"
+msgstr "додати Cc: заголовок"
+
+msgid "ident"
+msgstr "особистість"
+
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr ""
+"встановити From адресу в <особистість> (або особистість комітера, якщо "
+"відсутня)"
+
+msgid "message-id"
+msgstr "ідентифікатор-повідомлення"
+
+msgid "make first mail a reply to <message-id>"
+msgstr "зробити перший лист відповіддю на <id-повідомлення>"
+
+msgid "boundary"
+msgstr "межа"
+
+msgid "attach the patch"
+msgstr "прикріпити латку"
+
+msgid "inline the patch"
+msgstr "вставити латку"
+
+msgid "enable message threading, styles: shallow, deep"
+msgstr "увімкнути потік повідомлень, стилі: дрібний, глибокий"
+
+msgid "signature"
+msgstr "підпис"
+
+msgid "add a signature"
+msgstr "додати підпис"
+
+msgid "base-commit"
+msgstr "базовий коміт"
+
+msgid "add prerequisite tree info to the patch series"
+msgstr "додати інформацію про дерево передумов до серії латок"
+
+msgid "add a signature from a file"
+msgstr "додати підпис з файлу"
+
+msgid "don't print the patch filenames"
+msgstr "не виводити назви файлів латок"
+
+msgid "show progress while generating patches"
+msgstr "показувати прогрес під час генерації патчів"
+
+msgid "show changes against <rev> in cover letter or single patch"
+msgstr ""
+"показувати зміни проти <ревізія> у супровідному листі або окремій латці"
+
+msgid "show changes against <refspec> in cover letter or single patch"
+msgstr ""
+"показувати зміни проти <визначник-посилання> у супровідному листі або "
+"окремій латці"
+
+msgid "percentage by which creation is weighted"
+msgstr "відсоток, за яким зважується створення"
+
+msgid "show in-body From: even if identical to the e-mail header"
+msgstr "показувати in-body From: навіть якщо він ідентичний заголовку листа"
+
+#, c-format
+msgid "invalid ident line: %s"
+msgstr "неприпустимий рядок особистості: %s"
+
+msgid "--name-only does not make sense"
+msgstr "--name-only не має сенсу"
+
+msgid "--name-status does not make sense"
+msgstr "--name-status не має сенсу"
+
+msgid "--check does not make sense"
+msgstr "--check не має сенсу"
+
+msgid "--remerge-diff does not make sense"
+msgstr "--remerge-diff не має сенсу"
+
+#, c-format
+msgid "could not create directory '%s'"
+msgstr "не вдалося створити директорію \"%s\""
+
+msgid "--interdiff requires --cover-letter or single patch"
+msgstr "--interdiff вимагає --cover-letter або окремої латки"
+
+msgid "Interdiff:"
+msgstr "Різниця з попередньою серією латок:"
+
+#, c-format
+msgid "Interdiff against v%d:"
+msgstr "Різниця з v%d:"
+
+msgid "--range-diff requires --cover-letter or single patch"
+msgstr "--range-diff потребує --cover-letter або окремої латки"
+
+msgid "Range-diff:"
+msgstr "Різниця діапазону з попередньою серією латок:"
+
+#, c-format
+msgid "Range-diff against v%d:"
+msgstr "Різниця діапазону з v%d:"
+
+#, c-format
+msgid "unable to read signature file '%s'"
+msgstr "не вдалося прочитати файл підпису \"%s\""
+
+msgid "Generating patches"
+msgstr "Створення латок"
+
+msgid "failed to create output files"
+msgstr "не вдалося створити вихідні файли"
+
+msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
+msgstr "git cherry [-v] [<першоджерельне-сховище> [<голова> [<ліміт>]]]]"
+
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+"Не вдалося знайти віддалено відстежувану гілку, будь ласка, вкажіть "
+"<першоджерельне-сховище> вручну.\n"
+
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "не вдалося отримати інформацію про обʼєкт \"%s\""
+
+#, c-format
+msgid "bad ls-files format: element '%s' does not start with '('"
+msgstr "невірний ls-files формат: елемент \"%s\" не починається з \"(\""
+
+#, c-format
+msgid "bad ls-files format: element '%s' does not end in ')'"
+msgstr "невірний ls-files формат: елемент \"%s\" не закінчується на \")\""
+
+#, c-format
+msgid "bad ls-files format: %%%.*s"
+msgstr "невірний ls-files формат: %%%.*s"
+
+msgid "git ls-files [<options>] [<file>...]"
+msgstr "git ls-files [<опції>] [<файл>...]"
+
+msgid "separate paths with the NUL character"
+msgstr "відокремити шляхи символом NUL"
+
+msgid "identify the file status with tags"
+msgstr "визначити стан файлу за допомогою тегів"
+
+msgid "use lowercase letters for 'assume unchanged' files"
+msgstr "використати малі літери для \"вважати незмінними\" файлів"
+
+msgid "use lowercase letters for 'fsmonitor clean' files"
+msgstr "використати малі літери для \"fsmonitor clean\" файлів"
+
+msgid "show cached files in the output (default)"
+msgstr "показати кешовані файли у виводі (за замовчуванням)"
+
+msgid "show deleted files in the output"
+msgstr "показати видалені файли у виводі"
+
+msgid "show modified files in the output"
+msgstr "показати змінені файли у виводі"
+
+msgid "show other files in the output"
+msgstr "показати інші файли у виводі"
+
+msgid "show ignored files in the output"
+msgstr "показати ігноровані файли у виводі"
+
+msgid "show staged contents' object name in the output"
+msgstr "показати назву обʼєкта індексованого вмісту у виводі"
+
+msgid "show files on the filesystem that need to be removed"
+msgstr "показати файли файлової системи, які потрібно видалити"
+
+msgid "show 'other' directories' names only"
+msgstr "показати тільки назви \"інших\" директорій"
+
+msgid "show line endings of files"
+msgstr "показати закінчення рядків файлів"
+
+msgid "don't show empty directories"
+msgstr "не показувати порожні директорії"
+
+msgid "show unmerged files in the output"
+msgstr "показати не злиті файли у виводі"
+
+msgid "show resolve-undo information"
+msgstr "показати resolve-undo інформацію"
+
+msgid "skip files matching pattern"
+msgstr "пропустити файли, які відповідають шаблону"
+
+msgid "read exclude patterns from <file>"
+msgstr "читати шаблони виключення з <файлу>"
+
+msgid "read additional per-directory exclude patterns in <file>"
+msgstr "читати додаткові шаблони виключення для кожної директорії з <файлу>"
+
+msgid "add the standard git exclusions"
+msgstr "додати стандартні git виключення"
+
+msgid "make the output relative to the project top directory"
+msgstr "зробити виведення відносно верхньої директорії проекту"
+
+msgid "if any <file> is not in the index, treat this as an error"
+msgstr "якщо якогось <файлу> немає в індексі, вважати це помилкою"
+
+msgid "tree-ish"
+msgstr "деревоподібне-джерело"
+
+msgid "pretend that paths removed since <tree-ish> are still present"
+msgstr ""
+"вдавати, що шляхи, видалені після <деревоподібного-джерела>, все ще присутні"
+
+msgid "show debugging data"
+msgstr "показати дані відлагодження"
+
+msgid "suppress duplicate entries"
+msgstr "не показувати дублікати записів"
+
+msgid "show sparse directories in the presence of a sparse index"
+msgstr "показувати розріджені директорії при наявності розрідженого індексу"
+
+msgid ""
+"--format cannot be used with -s, -o, -k, -t, --resolve-undo, --deduplicate, "
+"--eol"
+msgstr ""
+"--format не можна використовувати з -s, -o, -k, -t, --resolve-undo, --"
+"deduplicate, --eol"
+
+msgid ""
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
+"              [--symref] [<repository> [<patterns>...]]"
+msgstr ""
+"git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<виконавчий-файл>]\n"
+"              [-q | --quiet] [--exit-code] [--get-url] [--sort=<ключ>]\n"
+"              [--symref] [<сховище> [<шаблони>...]]"
+
+msgid "do not print remote URL"
+msgstr "не виводити віддалену URL-адресу"
+
+msgid "exec"
+msgstr "виконавчий файл"
+
+msgid "path of git-upload-pack on the remote host"
+msgstr "шлях до git-upload-pack на віддаленому сервері"
+
+msgid "limit to tags"
+msgstr "обмежити до тегів"
+
+msgid "limit to heads"
+msgstr "обмежити до голів"
+
+msgid "do not show peeled tags"
+msgstr "не показувати очищені теги"
+
+msgid "take url.<base>.insteadOf into account"
+msgstr "враховувати url.<база>.insteadOf"
+
+msgid "exit with exit code 2 if no matching refs are found"
+msgstr "вийти з кодом виходу 2, якщо не знайдено відповідних посилань"
+
+msgid "show underlying ref in addition to the object pointed by it"
+msgstr "показувати базове посилання на додаток до об’єкта, на який воно вказує"
+
+msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
+msgstr "git ls-tree [<опції>] <деревоподібне-джерело> [<шлях>...]"
+
+#, c-format
+msgid "bad ls-tree format: element '%s' does not start with '('"
+msgstr "невірний формат ls-tree: елемент \"%s\" не починається з \"(\""
+
+#, c-format
+msgid "bad ls-tree format: element '%s' does not end in ')'"
+msgstr "невірний формат ls-tree: елемент \"%s\" не закінчується на \")\""
+
+#, c-format
+msgid "bad ls-tree format: %%%.*s"
+msgstr "невірний формат ls-tree: %%%.*s"
+
+msgid "only show trees"
+msgstr "показувати тільки дерева"
+
+msgid "recurse into subtrees"
+msgstr "рекурсивно в піддеревах"
+
+msgid "show trees when recursing"
+msgstr "відображати дерева при рекурсії"
+
+msgid "terminate entries with NUL byte"
+msgstr "завершувати записи байтом NUL"
+
+msgid "include object size"
+msgstr "включити розмір об’єкта"
+
+msgid "list only filenames"
+msgstr "показувати лише назви файлів"
+
+msgid "list only objects"
+msgstr "показати лише обʼєкти"
+
+msgid "use full path names"
+msgstr "використовувати повні назви шляхів"
+
+msgid "list entire tree; not just current directory (implies --full-name)"
+msgstr ""
+"показувати все дерево, а не лише поточну директорію (мається на увазі --full-"
+"name)."
+
+msgid "--format can't be combined with other format-altering options"
+msgstr "--format не можна комбінувати з іншими опціями зміни формату"
+
+#. TRANSLATORS: keep <> in "<" mail ">" info.
+msgid "git mailinfo [<options>] <msg> <patch> < mail >info"
+msgstr "git mailinfo [<опції>] <допис> <латка> < mail >info"
+
+msgid "keep subject"
+msgstr "не змінювати тему"
+
+msgid "keep non patch brackets in subject"
+msgstr "зберігати дужки, що не стосуються латок, в темі"
+
+msgid "copy Message-ID to the end of commit message"
+msgstr "копіювати Message-ID в кінець допису до коміту"
+
+msgid "re-code metadata to i18n.commitEncoding"
+msgstr "перекодувати метадані в i18n.commitEncoding"
+
+msgid "disable charset re-coding of metadata"
+msgstr "вимкнути перекодування метаданих"
+
+msgid "encoding"
+msgstr "кодування"
+
+msgid "re-code metadata to this encoding"
+msgstr "перекодувати метадані в це кодування"
+
+msgid "use scissors"
+msgstr "використовувати ножиці"
+
+msgid "<action>"
+msgstr "<дія>"
+
+msgid "action when quoted CR is found"
+msgstr "дія при знаходженні цитованого CR"
+
+msgid "use headers in message's body"
+msgstr "використовувати заголовки в тілі повідомлення"
+
+msgid "reading patches from stdin/tty..."
+msgstr "читання латок з stdin/tty..."
+
+#, c-format
+msgid "empty mbox: '%s'"
+msgstr "порожній mbox: \"%s\""
+
+msgid "git merge-base [-a | --all] <commit> <commit>..."
+msgstr "git merge-base [-a | --all] <коміт> <коміт>..."
+
+msgid "git merge-base [-a | --all] --octopus <commit>..."
+msgstr "git merge-base [-a | --all] --octopus <коміт>..."
+
+msgid "git merge-base --is-ancestor <commit> <commit>"
+msgstr "git merge-base --is-ancestor <коміт> <коміт>"
+
+msgid "git merge-base --independent <commit>..."
+msgstr "git merge-base --independent <коміт>..."
+
+msgid "git merge-base --fork-point <ref> [<commit>]"
+msgstr "git merge-base --fork-point <посилання> [<коміт>]"
+
+msgid "output all common ancestors"
+msgstr "вивести всіх спільних предків"
+
+msgid "find ancestors for a single n-way merge"
+msgstr "знайти предків для одного n-стороннього злиття"
+
+msgid "list revs not reachable from others"
+msgstr "показати ревізії, недоступні з інших джерел"
+
+msgid "is the first one ancestor of the other?"
+msgstr "чи є перша з них предком другої?"
+
+msgid "find where <commit> forked from reflog of <ref>"
+msgstr "знайти, де <коміт> відгалузився від журналу посилань <посилання>"
+
+msgid ""
+"git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> "
+"<orig-file> <file2>"
+msgstr ""
+"git merge-file [<опції>] [-L <назва1> [-L <оріг> [-L <назва2>]]] <файл1> "
+"<оріг-файл> <файл2>"
+
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"опція diff-algorithm приймає значення \"myers\", \"minimal\", \"patience\" "
+"та \"histogram\""
+
+msgid "send results to standard output"
+msgstr "надсилати результати до стандартного виводу"
+
+msgid "use object IDs instead of filenames"
+msgstr "використовувати ідентифікатори обʼєктів замість назв файлів"
+
+msgid "use a diff3 based merge"
+msgstr "використовувати злиття на основі diff3"
+
+msgid "use a zealous diff3 based merge"
+msgstr "використовувати ретельне злиття на основі diff3"
+
+msgid "for conflicts, use our version"
+msgstr "у разі конфліктів використовувати нашу версію"
+
+msgid "for conflicts, use their version"
+msgstr "у разі конфліктів використовувати їхню версію"
+
+msgid "for conflicts, use a union version"
+msgstr "у разі конфліктів використовувати об’єднану версію"
+
+msgid "<algorithm>"
+msgstr "<алгоритм>"
+
+msgid "choose a diff algorithm"
+msgstr "вибрати алгоритм різниці"
+
+msgid "for conflicts, use this marker size"
+msgstr "у разі конфліктів використовувати цей розмір маркера"
+
+msgid "do not warn about conflicts"
+msgstr "не попереджати про конфлікти"
+
+msgid "set labels for file1/orig-file/file2"
+msgstr "встановити мітки для файл1/оріг-файл/файл2"
+
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "об’єкт \"%s\" не існує"
+
+msgid "Could not write object file"
+msgstr "Не вдалося записати файл обʼєкта"
+
+#, c-format
+msgid "unknown option %s"
+msgstr "невідома опція %s"
+
+#, c-format
+msgid "could not parse object '%s'"
+msgstr "не вдалося розібрати обʼєкт \"%s\""
+
+#, c-format
+msgid "cannot handle more than %d base. Ignoring %s."
+msgid_plural "cannot handle more than %d bases. Ignoring %s."
+msgstr[0] "неможливо обробити більш ніж %d базу. Ігнорування %s."
+msgstr[1] "неможливо обробити більш ніж %d бази. Ігнорування %s."
+msgstr[2] "неможливо обробити більш ніж %d баз. Ігнорування %s."
+
+msgid "not handling anything other than two heads merge."
+msgstr "не оброблюється нічого, окрім злиття двох верхівок."
+
+#, c-format
+msgid "could not resolve ref '%s'"
+msgstr "не вдалося розвʼязати посилання \"%s\""
+
+#, c-format
+msgid "Merging %s with %s\n"
+msgstr "Злиття %s з %s\n"
+
+msgid "not something we can merge"
+msgstr "не те, що ми в змозі об’єднати"
+
+msgid "refusing to merge unrelated histories"
+msgstr "відмовлено в об’єднанні непов’язаних історій"
+
+msgid "failure to merge"
+msgstr "не вдалося злити"
+
+msgid "git merge-tree [--write-tree] [<options>] <branch1> <branch2>"
+msgstr "git merge-tree [--write-tree] [<опції>] <гілка1> <гілка2>"
+
+msgid "git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"
+msgstr "git merge-tree [--trivial-merge] <базове-дерево> <гілка1> <гілка2>"
+
+msgid "do a real merge instead of a trivial merge"
+msgstr "зробити справжнє злиття замість тривіального злиття"
+
+msgid "do a trivial merge only"
+msgstr "зробити лише тривіальне злиття"
+
+msgid "also show informational/conflict messages"
+msgstr "також показувати інформаційні/конфліктні повідомлення"
+
+msgid "list filenames without modes/oids/stages"
+msgstr "вивести назви файлів без режимів/oid/стадій"
+
+msgid "allow merging unrelated histories"
+msgstr "дозволити злиття непов’язаних історій"
+
+msgid "perform multiple merges, one per line of input"
+msgstr "виконати кілька злиттів, по одному на кожен рядок вводу"
+
+msgid "specify a merge-base for the merge"
+msgstr "вказати базу для злиття"
+
+msgid "option=value"
+msgstr "опція=значення"
+
+msgid "option for selected merge strategy"
+msgstr "опція для обраної стратегії злиття"
+
+msgid "--trivial-merge is incompatible with all other options"
+msgstr "--trivial-merge несумісна з усіма іншими опціями"
+
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "невідомий варіант стратегії: -X%s"
+
+#, c-format
+msgid "malformed input line: '%s'."
+msgstr "невірно сформований рядок вводу: \"%s\"."
+
+#, c-format
+msgid "merging cannot continue; got unclean result of %d"
+msgstr "неможливо продовжити злиття; отримано брудний результат для %d"
+
+msgid "git merge [<options>] [<commit>...]"
+msgstr "git merge [<опції>] [<коміт>...]"
+
+msgid "switch `m' requires a value"
+msgstr "перемикач \"m\" потребує значення"
+
+#, c-format
+msgid "option `%s' requires a value"
+msgstr "опція \"%s\" потребує значення"
+
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Не вдалося знайти стратегію злиття \"%s\".\n"
+
+#, c-format
+msgid "Available strategies are:"
+msgstr "Доступні стратегії:"
+
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Доступні спеціальні стратегії:"
+
+msgid "do not show a diffstat at the end of the merge"
+msgstr "не показувати diffstat наприкінці злиття"
+
+msgid "show a diffstat at the end of the merge"
+msgstr "показувати diffstat наприкінці злиття"
+
+msgid "(synonym to --stat)"
+msgstr "(синонім до --stat)"
+
+msgid "add (at most <n>) entries from shortlog to merge commit message"
+msgstr ""
+"додати (не більше <н>) записів з короткого журналу у допис до коміту злиття"
+
+msgid "create a single commit instead of doing a merge"
+msgstr "створити єдиний коміт замість злиття"
+
+msgid "perform a commit if the merge succeeds (default)"
+msgstr "виконати коміт, якщо злиття пройшло успішно (за замовчуванням)"
+
+msgid "edit message before committing"
+msgstr "редагувати допис перед комітом"
+
+msgid "allow fast-forward (default)"
+msgstr "дозволити перемотування вперед (за замовчуванням)"
+
+msgid "abort if fast-forward is not possible"
+msgstr "перервати, якщо перемотування вперед неможливе"
+
+msgid "verify that the named commit has a valid GPG signature"
+msgstr "перевіряти, чи має коміт дійсний GPG-підпис"
+
+msgid "strategy"
+msgstr "стратегія"
+
+msgid "merge strategy to use"
+msgstr "яку стратегію злиття використовувати"
+
+msgid "merge commit message (for a non-fast-forward merge)"
+msgstr "допис до коміту злиття (для злиття без перемотування вперед)"
+
+msgid "use <name> instead of the real target"
+msgstr "використовувати <назву> замість реальної цілі"
+
+msgid "abort the current in-progress merge"
+msgstr "перервати поточне злиття"
+
+msgid "--abort but leave index and working tree alone"
+msgstr "--abort, але зберегти стан індексу і робочого дерева"
+
+msgid "continue the current in-progress merge"
+msgstr "продовжити поточний процес злиття"
+
+msgid "bypass pre-merge-commit and commit-msg hooks"
+msgstr "обходити pre-merge-commit та commit-msg гачки"
+
+msgid "could not run stash."
+msgstr "не вдалося виконати stash."
+
+msgid "stash failed"
+msgstr "не вдалося додати до схову"
+
+#, c-format
+msgid "not a valid object: %s"
+msgstr "не є припустимим обʼєктом: %s"
+
+msgid "read-tree failed"
+msgstr "read-tree завершився невдало"
+
+msgid "Already up to date. (nothing to squash)"
+msgstr "Вже в актуальному стані. (нічого зчавлювати)"
+
+msgid "Already up to date."
+msgstr "Вже в актуальному стані."
+
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "Коміт зчавлювання -- HEAD не оновлюється\n"
+
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr "Немає допису до злиття -- HEAD не оновлюється\n"
+
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "\"%s\" не вказує на коміт"
+
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Невірна branch.%s.mergeoptions строка: %s"
+
+msgid "Unable to write index."
+msgstr "Не вдалося записати індекс."
+
+msgid "Not handling anything other than two heads merge."
+msgstr "Не оброблюється нічого, окрім злиття двох верхівок."
+
+#, c-format
+msgid "unable to write %s"
+msgstr "не вдалося записати %s"
+
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "Не вдалося прочитати з \"%s\""
+
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+"Не додано коміт злиття; скористайтесь \"git commit\", щоб завершити злиття.\n"
+
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+msgstr ""
+"Будь ласка, введіть допис до коміту, щоб пояснити, чому це злиття є "
+"необхідним,\n"
+"особливо якщо воно об’єднує першоджерельні оновлення з тематичною гілкою.\n"
+
+msgid "An empty message aborts the commit.\n"
+msgstr "Порожній допис перерве процес коміту.\n"
+
+#, c-format
+msgid ""
+"Lines starting with '%c' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"Рядки, що починаються з \"%c\", будуть проігноровані, а порожній допис "
+"перерве\n"
+"процес коміту.\n"
+
+msgid "Empty commit message."
+msgstr "Порожній допис до коміту."
+
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Чудово.\n"
+
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+"Автоматичне злиття не вдалося; виправте конфлікти і потім зробіть коміт "
+"результату.\n"
+
+msgid "No current branch."
+msgstr "Немає поточної гілки."
+
+msgid "No remote for the current branch."
+msgstr "Немає віддаленого призначення для поточної гілки."
+
+msgid "No default upstream defined for the current branch."
+msgstr ""
+"Для поточної гілки не визначено першоджерельне сховище за замовчуванням."
+
+#, c-format
+msgid "No remote-tracking branch for %s from %s"
+msgstr "Немає віддалено відстежуваної гілки для %s з %s"
+
+#, c-format
+msgid "Bad value '%s' in environment '%s'"
+msgstr "Невірне значення \"%s\" в оточенні \"%s\""
+
+#, c-format
+msgid "could not close '%s'"
+msgstr "не вдалося закрити \"%s\""
+
+#, c-format
+msgid "not something we can merge in %s: %s"
+msgstr "не те, що можна злити в %s: %s"
+
+msgid "--abort expects no arguments"
+msgstr "--abort не очікує жодних аргументів"
+
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Неможливо перервати злиття (відсутній MERGE_HEAD)."
+
+msgid "--quit expects no arguments"
+msgstr "--quit не очікує жодних аргументів"
+
+msgid "--continue expects no arguments"
+msgstr "--continue не очікує жодних аргументів"
+
+msgid "There is no merge in progress (MERGE_HEAD missing)."
+msgstr "Злиття не виконується (відсутній MERGE_HEAD)."
+
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you merge."
+msgstr ""
+"Ви не завершили злиття (існує MERGE_HEAD).\n"
+"Будь ласка, зробіть коміт для ваших змін перед злиттям."
+
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you merge."
+msgstr ""
+"Ви не завершили висмикування (існує CHERRY_PICK_HEAD).\n"
+"Будь ласка, зробіть коміт для ваших змін перед злиттям."
+
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr "Ви не завершили висмикування (існує CHERRY_PICK_HEAD)."
+
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Не вказано коміт і не встановлено merge.defaultToUpstream."
+
+msgid "Squash commit into empty head not supported yet"
+msgstr "Поки що немає підтримки комітів зчавлювання в порожню верхівку"
+
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "Коміт без перемотування вперед у порожню верхівку не має сенсу"
+
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - не те, що можна злити"
+
+msgid "Can merge only exactly one commit into empty head"
+msgstr "Можна злити лишень один коміт у порожню верхівку"
+
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Оновлення %s..%s\n"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"  %s"
+msgstr ""
+"Ваші локальні зміни у наступних файлах буде замінено злиттям:\n"
+"  %s"
+
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "Спроба справді тривіального внутрішньо-індексного злиття...\n"
+
+#, c-format
+msgid "Nope.\n"
+msgstr "Ні.\n"
+
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "Приведення дерева до початкового стану...\n"
+
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Спроба стратегії злиття %s...\n"
+
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Жодна стратегія злиття не впоралася зі злиттям.\n"
+
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Злиття зі стратегією %s не вдалося.\n"
+
+#, c-format
+msgid "Using the %s strategy to prepare resolving by hand.\n"
+msgstr "Використання стратегії %s для підготовки вирішення вручну.\n"
+
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+"Автоматичне злиття пройшло добре; зупинка перед комітом, як було вказано\n"
+
+#, c-format
+msgid "When finished, apply stashed changes with `git stash pop`\n"
+msgstr ""
+"Закінчивши, застосуйте збережені зміни за допомогою \"git stash pop\"\n"
+
+#, c-format
+msgid "warning: tag input does not pass fsck: %s"
+msgstr "попередження: вхідний тег не пройшов fsck: %s"
+
+#, c-format
+msgid "error: tag input does not pass fsck: %s"
+msgstr "помилка: вхідний тег не пройшов fsck: %s"
+
+#, c-format
+msgid "%d (FSCK_IGNORE?) should never trigger this callback"
+msgstr "%d (FSCK_IGNORE?) ніколи не мав спричинити цей зворотній виклик"
+
+#, c-format
+msgid "could not read tagged object '%s'"
+msgstr "не вдалося прочитати тегований об’єкт \"%s\""
+
+#, c-format
+msgid "object '%s' tagged as '%s', but is a '%s' type"
+msgstr "об’єкт \"%s\", позначений як \"%s\", але має тип \"%s\""
+
+msgid "could not read from stdin"
+msgstr "не вдалося прочитати з stdin"
+
+msgid "tag on stdin did not pass our strict fsck check"
+msgstr "тег з stdin не пройшов нашу сувору перевірку fsck"
+
+msgid "tag on stdin did not refer to a valid object"
+msgstr "тег з stdin не посилався на дійсний об’єкт"
+
+msgid "unable to write tag file"
+msgstr "не вдалося записати файл тегів"
+
+msgid "input is NUL terminated"
+msgstr "ввід завершено символом NUL"
+
+msgid "allow missing objects"
+msgstr "дозволяти відсутні об’єкти"
+
+msgid "allow creation of more than one tree"
+msgstr "дозволяти створення більш ніж одного дерева"
+
+msgid ""
+"git multi-pack-index [<options>] write [--preferred-pack=<pack>][--refs-"
+"snapshot=<path>]"
+msgstr ""
+"git multi-pack-index [<опції>] write [--preferred-pack=<пакунок>] [--refs-"
+"snapshot=<шлях>]"
+
+msgid "git multi-pack-index [<options>] verify"
+msgstr "git multi-pack-index [<опції>] verify"
+
+msgid "git multi-pack-index [<options>] expire"
+msgstr "git multi-pack-index [<опції>] expire"
+
+msgid "git multi-pack-index [<options>] repack [--batch-size=<size>]"
+msgstr "git multi-pack-index [<опції>] repack [--batch-size=<розмір>]"
+
+msgid "directory"
+msgstr "директорія"
+
+msgid "object directory containing set of packfile and pack-index pairs"
+msgstr "директорія об’єктів, що містить набір packfile та pack-index пар"
+
+msgid "preferred-pack"
+msgstr "preferred-pack"
+
+msgid "pack for reuse when computing a multi-pack bitmap"
+msgstr ""
+"пакунок для повторного використання під час обчислення multi-pack bitmap"
+
+msgid "write multi-pack bitmap"
+msgstr "записати multi-pack bitmap"
+
+msgid "write multi-pack index containing only given indexes"
+msgstr "записати multi-pack індекс, що містить лише задані індекси"
+
+msgid "refs snapshot for selecting bitmap commits"
+msgstr "знімок посилань для вибору bitmap комітів"
+
+msgid ""
+"during repack, collect pack-files of smaller size into a batch that is "
+"larger than this size"
+msgstr ""
+"під час перепакування збирати пакувальні файли меншого розміру в партію, "
+"більшу за цей розмір"
+
+msgid "git mv [<options>] <source>... <destination>"
+msgstr "git mv [<опції>] <джерело>... <призначення>"
+
+#, c-format
+msgid "Directory %s is in index and no submodule?"
+msgstr "Директорія %s в індексі, але без підмодуля?"
+
+msgid "Please stage your changes to .gitmodules or stash them to proceed"
+msgstr ""
+"Будь ласка, додайте змінений .gitmodules до індексу або до схову, щоб "
+"продовжити"
+
+#, c-format
+msgid "%.*s is in index"
+msgstr "%.*s в індексі"
+
+msgid "force move/rename even if target exists"
+msgstr "примусово переміщувати/перейменовувати, навіть якщо ціль існує"
+
+msgid "skip move/rename errors"
+msgstr "пропускати помилки переміщення/перейменування"
+
+#, c-format
+msgid "destination '%s' is not a directory"
+msgstr "місце призначення \"%s\" не є директорією"
+
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "Перевірка перейменування \"%s\" на \"%s\"\n"
+
+msgid "bad source"
+msgstr "невірне джерело"
+
+msgid "destination exists"
+msgstr "призначення існує"
+
+msgid "can not move directory into itself"
+msgstr "неможливо перемістити директорію в саму себе"
+
+msgid "destination already exists"
+msgstr "призначення вже існує"
+
+msgid "source directory is empty"
+msgstr "директорія джерела порожня"
+
+msgid "not under version control"
+msgstr "не під контролем версій"
+
+msgid "conflicted"
+msgstr "конфлікт"
+
+#, c-format
+msgid "overwriting '%s'"
+msgstr "перезапис \"%s\""
+
+msgid "Cannot overwrite"
+msgstr "Неможливо перезаписати"
+
+msgid "multiple sources for the same target"
+msgstr "кілька джерел для однієї цілі"
+
+msgid "destination directory does not exist"
+msgstr "директорія призначення не існує"
+
+msgid "destination exists in the index"
+msgstr "призначення існує в індексі"
+
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, джерело=%s, призначення=%s"
+
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Перейменування %s на %s\n"
+
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "перейменування \"%s\" завершилося невдало"
+
+msgid "git name-rev [<options>] <commit>..."
+msgstr "git name-rev [<опції>] <коміт>..."
+
+msgid "git name-rev [<options>] --all"
+msgstr "git name-rev [<опції>] --all"
+
+msgid "git name-rev [<options>] --annotate-stdin"
+msgstr "git name-rev [<опції>] --annotate-stdin"
+
+msgid "print only ref-based names (no object names)"
+msgstr "виводити тільки назви на основі посилань (без назв об’єктів)"
+
+msgid "only use tags to name the commits"
+msgstr "використовувати теги лише для назв комітів"
+
+msgid "only use refs matching <pattern>"
+msgstr "використовувати лише посилання, що збігаються з <шаблоном>"
+
+msgid "ignore refs matching <pattern>"
+msgstr "ігнорувати посилання, що збігаються з <шаблоном>"
+
+msgid "list all commits reachable from all refs"
+msgstr "вивести всі коміти, доступні з усіх посилань"
+
+msgid "deprecated: use --annotate-stdin instead"
+msgstr "застаріле: замість цього використовуйте --annotate-stdin"
+
+msgid "annotate text from stdin"
+msgstr "анотувати текст зі stdin"
+
+msgid "allow to print `undefined` names (default)"
+msgstr "дозволити виводити \"невизначені\" назви (за замовчуванням)"
+
+msgid "dereference tags in the input (internal use)"
+msgstr "розіменувати теги на вході (внутрішнє використання)"
+
+msgid "git notes [--ref <notes-ref>] [list [<object>]]"
+msgstr "git notes [--ref <посилання-нотатки>] [list [<об’єкт>]]"
+
+msgid ""
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] add [-f] [--allow-empty] [--"
+"[no-]separator|--separator=<розділювач-абзаців>] [--[no-]stripspace] [-m "
+"<допис> | -F <файл> | (-c | -C) <обʼєкт>] [<обʼєкт>]"
+
+msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] copy [-f] <з-об’єкта> <до-об’єкта>"
+
+msgid ""
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] append [--allow-empty] [--"
+"[no-]separator|--separator=<розділювач-абзаців>] [--[no-]stripspace] [-m "
+"<допис> | -F <файл> | (-c | -C) <обʼєкт>] [<обʼєкт>]"
+
+msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
+msgstr "git notes [--ref <посилання-нотатки>] edit [--allow-empty] [<об’єкт>]"
+
+msgid "git notes [--ref <notes-ref>] show [<object>]"
+msgstr "git notes [--ref <посилання-нотатки>] show [<об’єкт>]"
+
+msgid ""
+"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"
+msgstr ""
+"git notes [--ref <посилання-нотатки>] merge [-v | -q] [-s <стратегія>] "
+"<посилання-нотатки>"
+
+msgid "git notes [--ref <notes-ref>] remove [<object>...]"
+msgstr "git notes [--ref <посилання-нотатки>] remove [<об’єкт>...]"
+
+msgid "git notes [--ref <notes-ref>] prune [-n] [-v]"
+msgstr "git notes [--ref <посилання-нотатки>] prune [-n] [-v]."
+
+msgid "git notes [--ref <notes-ref>] get-ref"
+msgstr "git notes [--ref <посилання-нотатки>] get-ref"
+
+msgid "git notes [list [<object>]]"
+msgstr "git notes [list [<об’єкт>]]"
+
+msgid "git notes add [<options>] [<object>]"
+msgstr "git notes add [<опції>] [<обʼєкт>]"
+
+msgid "git notes copy [<options>] <from-object> <to-object>"
+msgstr "git notes copy [<опції>] <з-об’єкта> <до-об’єкта"
+
+msgid "git notes copy --stdin [<from-object> <to-object>]..."
+msgstr "git notes copy --stdin [<з-об’єкта> <до-об’єкта>]..."
+
+msgid "git notes append [<options>] [<object>]"
+msgstr "git notes append [<опції>] [<обʼєкт>]"
+
+msgid "git notes edit [<object>]"
+msgstr "git notes edit [<об’єкт>]"
+
+msgid "git notes show [<object>]"
+msgstr "git notes show [<об’єкт>]"
+
+msgid "git notes merge [<options>] <notes-ref>"
+msgstr "git notes merge [<опції>] <посилання-нотатки>"
+
+msgid "git notes merge --commit [<options>]"
+msgstr "git notes merge --commit [<опції>]"
+
+msgid "git notes merge --abort [<options>]"
+msgstr "git notes merge --abort [<опції>]"
+
+msgid "git notes remove [<object>]"
+msgstr "git notes remove [<об’єкт>]"
+
+msgid "git notes prune [<options>]"
+msgstr "git notes prune [<опції>]"
+
+msgid "Write/edit the notes for the following object:"
+msgstr "Записати/відредагувати нотатки для наступного обʼєкта:"
+
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "не вдалося запустити \"show\" для об’єкта \"%s\""
+
+msgid "could not read 'show' output"
+msgstr "не вдалося прочитати вивід \"show\""
+
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "не вдалося завершити \"show\" для об’єкта \"%s\""
+
+msgid "please supply the note contents using either -m or -F option"
+msgstr "будь ласка, надайте зміст нотатки, використовуючи опцію -m або -F"
+
+msgid "unable to write note object"
+msgstr "не вдалося записати об’єкт нотатки"
+
+#, c-format
+msgid "the note contents have been left in %s"
+msgstr "вміст нотатки залишено в %s"
+
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "не вдалося відкрити або прочитати \"%s\""
+
+#, c-format
+msgid "failed to resolve '%s' as a valid ref."
+msgstr "не вдалося розпізнати \"%s\" як припустиме посилання."
+
+#, c-format
+msgid "failed to read object '%s'."
+msgstr "не вдалося прочитати обʼєкт \"%s\"."
+
+#, c-format
+msgid "cannot read note data from non-blob object '%s'."
+msgstr "неможливо прочитати дані нотатки з не-blob обʼєкту \"%s\"."
+
+#, c-format
+msgid "failed to copy notes from '%s' to '%s'"
+msgstr "не вдалося скопіювати нотатки з \"%s\" в \"%s\""
+
+#. TRANSLATORS: the first %s will be replaced by a git
+#. notes command: 'add', 'merge', 'remove', etc.
+#.
+#, c-format
+msgid "refusing to %s notes in %s (outside of refs/notes/)"
+msgstr "відмовлено в %s нотаток у %s (за межами refs/notes/)"
+
+#, c-format
+msgid "no note found for object %s."
+msgstr "для обʼєкта %s не знайдено жодної нотатки."
+
+msgid "note contents as a string"
+msgstr "вміст нотатки як строка"
+
+msgid "note contents in a file"
+msgstr "вміст нотатки у файлі"
+
+msgid "reuse and edit specified note object"
+msgstr "повторно використати та редагувати вказаний обʼєкт нотатки"
+
+msgid "reuse specified note object"
+msgstr "повторно використати вказаний обʼєкт нотатки"
+
+msgid "allow storing empty note"
+msgstr "дозволити збереження порожньої нотатки"
+
+msgid "replace existing notes"
+msgstr "замінити існуючі нотатки"
+
+msgid "<paragraph-break>"
+msgstr "<розділювач-абзаців>"
+
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "вставити <розділювач-абзаців> між абзацами"
+
+msgid "remove unnecessary whitespace"
+msgstr "видалити зайві пробіли"
+
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Неможливо додати нотатки. Знайдено існуючі нотатки для обʼєкта %s. "
+"Скористайтесь \"-f\" для перезапису існуючих нотаток"
+
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Перезапис існуючих нотаток для обʼєкта %s\n"
+
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Видалення нотатки для обʼєкта %s\n"
+
+msgid "read objects from stdin"
+msgstr "зчитати обʼєкти з stdin"
+
+msgid "load rewriting config for <command> (implies --stdin)"
+msgstr ""
+"завантажити перезапис конфігурації для <команда> (мається на увазі --stdin)"
+
+msgid "too few arguments"
+msgstr "замало аргументів"
+
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Неможливо скопіювати нотатки. Знайдено існуючі нотатки для обʼєкта %s. "
+"Скористайтесь \"-f\", щоб перезаписати існуючі нотатки"
+
+#, c-format
+msgid "missing notes on source object %s. Cannot copy."
+msgstr "нотатки джерельного обʼєкта %s відсутні. Неможливо скопіювати."
+
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+"Опції -m/-F/-c/-C застаріли для підкоманди \"edit\".\n"
+"Будь ласка, скористайтесь \"git notes add -f -m/-F/-c/-C\" замість цього.\n"
+
+msgid "failed to delete ref NOTES_MERGE_PARTIAL"
+msgstr "не вдалося видалити посилання NOTES_MERGE_PARTIAL"
+
+msgid "failed to delete ref NOTES_MERGE_REF"
+msgstr "не вдалося видалити посилання NOTES_MERGE_REF"
+
+msgid "failed to remove 'git notes merge' worktree"
+msgstr "не вдалося видалити \"git notes merge\" робоче дерево"
+
+msgid "failed to read ref NOTES_MERGE_PARTIAL"
+msgstr "не вдалося прочитати посилання NOTES_MERGE_PARTIAL"
+
+msgid "could not find commit from NOTES_MERGE_PARTIAL."
+msgstr "не вдалося знайти коміт з NOTES_MERGE_PARTIAL."
+
+msgid "could not parse commit from NOTES_MERGE_PARTIAL."
+msgstr "не вдалося розібрати коміт з NOTES_MERGE_PARTIAL."
+
+msgid "failed to resolve NOTES_MERGE_REF"
+msgstr "не вдалося розвʼязати NOTES_MERGE_REF"
+
+msgid "failed to finalize notes merge"
+msgstr "не вдалося завершити злиття нотаток"
+
+#, c-format
+msgid "unknown notes merge strategy %s"
+msgstr "невідома стратегія злиття нотаток %s"
+
+msgid "General options"
+msgstr "Основні опції"
+
+msgid "Merge options"
+msgstr "Опції злиття"
+
+msgid ""
+"resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
+"cat_sort_uniq)"
+msgstr ""
+"вирішити конфлікти нотаток, використовуючи задану стратегію (manual/our/"
+"their/union/cat_sort_uniq)"
+
+msgid "Committing unmerged notes"
+msgstr "Виконнання коміту не злитих нотаток"
+
+msgid "finalize notes merge by committing unmerged notes"
+msgstr "завершити злиття комітом не злитих нотаток"
+
+msgid "Aborting notes merge resolution"
+msgstr "Переривання злиття нотаток"
+
+msgid "abort notes merge"
+msgstr "перервати злиття нотаток"
+
+msgid "cannot mix --commit, --abort or -s/--strategy"
+msgstr "не можна змішувати --commit, --abort або -s/--strategy"
+
+msgid "must specify a notes ref to merge"
+msgstr "необхідно вказати посилання нотаток для злиття"
+
+#, c-format
+msgid "unknown -s/--strategy: %s"
+msgstr "невідома -s/--strategy: %s"
+
+#, c-format
+msgid "a notes merge into %s is already in-progress at %s"
+msgstr "злиття нотаток у %s вже виконується в %s"
+
+#, c-format
+msgid "failed to store link to current notes ref (%s)"
+msgstr "не вдалося зберегти ланку на поточні нотатки посилання (%s)"
+
+#, c-format
+msgid ""
+"Automatic notes merge failed. Fix conflicts in %s and commit the result with "
+"'git notes merge --commit', or abort the merge with 'git notes merge --"
+"abort'.\n"
+msgstr ""
+"Автоматичне злиття нотаток не вдалося. Виправте конфлікти у %s і зробіть "
+"коміт змін командою \"git notes merge --commit\" або перервіть злиття "
+"командою \"git notes merge --abort\".\n"
+
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "Не вдалося розвʼязати \"%s\" як припустиме посилання."
+
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "Обʼєкт %s не має нотатки\n"
+
+msgid "attempt to remove non-existent note is not an error"
+msgstr "спроба видалити неіснуючу нотатку не є помилкою"
+
+msgid "read object names from the standard input"
+msgstr "зчитати імена обʼєктів зі стандартного вводу"
+
+msgid "do not remove, show only"
+msgstr "не видаляти, тільки показувати"
+
+msgid "report pruned notes"
+msgstr "звітувати про видалені нотатки"
+
+msgid "notes-ref"
+msgstr "посилання-нотатки"
+
+msgid "use notes from <notes-ref>"
+msgstr "використовувати нотатки з <посилання-нотатки>"
+
+#, c-format
+msgid "unknown subcommand: `%s'"
+msgstr "невідома підкоманда: \"%s\""
+
+msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"
+msgstr ""
+"git pack-objects --stdout [<опції>] [< <список-посилань> | < <список-"
+"обʼєктів>]"
+
+msgid ""
+"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"
+msgstr ""
+"git pack-objects [<опції>] <базова-назва> [< <список-посилань> | < <список-"
+"обʼєктів>]"
+
+#, c-format
+msgid ""
+"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
+"pack %s"
+msgstr ""
+"write_reuse_object: не вдалося знайти %s, очікуваний зі зміщенням %<PRIuMAX> "
+"у пакунку %s"
+
+#, c-format
+msgid "bad packed object CRC for %s"
+msgstr "невірна CRC запакованого обʼєкта %s"
+
+#, c-format
+msgid "corrupt packed object for %s"
+msgstr "пошкоджений запакований обʼєкт для %s"
+
+#, c-format
+msgid "recursive delta detected for object %s"
+msgstr "виявлено рекурсивну дельту для обʼєкта %s"
+
+#, c-format
+msgid "ordered %u objects, expected %<PRIu32>"
+msgstr "замовлено %u обʼєктів, очікувалось %<PRIu32>"
+
+#, c-format
+msgid "expected object at offset %<PRIuMAX> in pack %s"
+msgstr "очікувався обʼєкт на зміщенні %<PRIuMAX> пакунка %s"
+
+msgid "disabling bitmap writing, packs are split due to pack.packSizeLimit"
+msgstr ""
+"вимкнення bitmap запису, пакунки розбиваються на частини через pack."
+"packSizeLimit"
+
+msgid "Writing objects"
+msgstr "Запис обʼєктів"
+
+#, c-format
+msgid "failed to stat %s"
+msgstr "не вдалося виконати stat %s"
+
+#, c-format
+msgid "failed utime() on %s"
+msgstr "не вдалося виконати utime() на %s"
+
+msgid "failed to write bitmap index"
+msgstr "не вдалося записати bitmap індекс"
+
+#, c-format
+msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
+msgstr "записано %<PRIu32> обʼєкти при очікуванні %<PRIu32>"
+
+msgid "disabling bitmap writing, as some objects are not being packed"
+msgstr "вимкнення bitmap запису, оскільки деякі обʼєкти не упаковуються"
+
+#, c-format
+msgid "delta base offset overflow in pack for %s"
+msgstr "переповнення зміщення дельти бази у пакунку для %s"
+
+#, c-format
+msgid "delta base offset out of bound for %s"
+msgstr "зміщення бази дельти виходить за межі для %s"
+
+msgid "Counting objects"
+msgstr "Підрахунок обʼєктів"
+
+#, c-format
+msgid "unable to get size of %s"
+msgstr "не вдалося отримати розмір %s"
+
+#, c-format
+msgid "unable to parse object header of %s"
+msgstr "не вдалося розібрати заголовок обʼєкта %s"
+
+#, c-format
+msgid "object %s cannot be read"
+msgstr "обʼєкт %s не може бути прочитаний"
+
+#, c-format
+msgid "object %s inconsistent object length (%<PRIuMAX> vs %<PRIuMAX>)"
+msgstr "обʼєкт %s має невідповідну довжину (%<PRIuMAX> проти %<PRIuMAX>)"
+
+msgid "suboptimal pack - out of memory"
+msgstr "неоптимальний пакунок - не вистачає памʼяті"
+
+#, c-format
+msgid "Delta compression using up to %d threads"
+msgstr "Дельта компресія з використанням до %d потоків"
+
+#, c-format
+msgid "unable to pack objects reachable from tag %s"
+msgstr "не вдалося запакувати обʼєкти, доступні з тегу %s"
+
+#, c-format
+msgid "unable to get type of object %s"
+msgstr "не вдалося отримати тип обʼєкта %s"
+
+msgid "Compressing objects"
+msgstr "Компресія обʼєктів"
+
+msgid "inconsistency with delta count"
+msgstr "неспівпадіння з підрахунком дельти"
+
+#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "неприпустиме значення pack.allowPackReuse: \"%s\""
+
+#, c-format
+msgid ""
+"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
+"hash> <uri>' (got '%s')"
+msgstr ""
+"значення uploadpack.blobpackfileuri повинно мати вигляд \"<хеш-обʼєкта> <хеш-"
+"пакунка> <uri>\" (отримано \"%s\")"
+
+#, c-format
+msgid ""
+"object already configured in another uploadpack.blobpackfileuri (got '%s')"
+msgstr ""
+"обʼєкт вже сконфігуровано в іншому uploadpack.blobpackfileuri (отримано "
+"\"%s\")"
+
+#, c-format
+msgid "could not get type of object %s in pack %s"
+msgstr "не вдалося отримати тип обʼєкта %s у пакунку %s"
+
+#, c-format
+msgid "could not find pack '%s'"
+msgstr "не вдалося знайти пакунок \"%s\""
+
+#, c-format
+msgid "packfile %s cannot be accessed"
+msgstr "неможливо отримати доступ до файлу пакунку %s"
+
+msgid "Enumerating cruft objects"
+msgstr "Перерахування марних обʼєктів"
+
+msgid "unable to add cruft objects"
+msgstr "не вдалося додати марні обʼєкти"
+
+msgid "Traversing cruft objects"
+msgstr "Проходження по марним обʼєктам"
+
+#, c-format
+msgid ""
+"expected edge object ID, got garbage:\n"
+" %s"
+msgstr ""
+"очікувався ідентифікатор реберного обʼєкту, отримано непотріб:\n"
+" %s"
+
+#, c-format
+msgid ""
+"expected object ID, got garbage:\n"
+" %s"
+msgstr ""
+"очікувався ідентифікатор обʼєкта, отримано непотріб:\n"
+" %s"
+
+msgid "could not load cruft pack .mtimes"
+msgstr "не вдалося завантажити марний пакунок .mtimes"
+
+msgid "cannot open pack index"
+msgstr "не вдалося відкрити індекс пакунка"
+
+#, c-format
+msgid "loose object at %s could not be examined"
+msgstr "не вдалося розглянути вільний обʼєкт у %s"
+
+msgid "unable to force loose object"
+msgstr "не вдалося примусово вивільнити обʼєкт"
+
+#, c-format
+msgid "not a rev '%s'"
+msgstr "не є ревізією \"%s\""
+
+#, c-format
+msgid "bad revision '%s'"
+msgstr "невірна ревізія \"%s\""
+
+msgid "unable to add recent objects"
+msgstr "не вдалося додати нещодавні обʼєкти"
+
+#, c-format
+msgid "unsupported index version %s"
+msgstr "непідтримувана версія індексу %s"
+
+#, c-format
+msgid "bad index version '%s'"
+msgstr "невірна версія індексу \"%s\""
+
+msgid "show progress meter during object writing phase"
+msgstr "показувати хід виконання під час фази запису обʼєкта"
+
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "подібно до --all-progress, коли показано хід виконання"
+
+msgid "<version>[,<offset>]"
+msgstr "<версія>[, <зміщення>]"
+
+msgid "write the pack index file in the specified idx format version"
+msgstr "записати індексний файл пакунка у вказаній версії idx формату"
+
+msgid "maximum size of each output pack file"
+msgstr "максимальний розмір кожного файлу вихідного пакунку"
+
+msgid "ignore borrowed objects from alternate object store"
+msgstr "ігнорувати запозичені обʼєкти з місця збереження запозичених об’єктів"
+
+msgid "ignore packed objects"
+msgstr "ігнорувати запаковані обʼєкти"
+
+msgid "limit pack window by objects"
+msgstr "обмежити вікно пакування за обʼєктами"
+
+msgid "limit pack window by memory in addition to object limit"
+msgstr "обмежити вікно пакування за памʼяттю на додаток до ліміту обʼєкта"
+
+msgid "maximum length of delta chain allowed in the resulting pack"
+msgstr "максимальна довжина дельта ланцюжка, дозволена в результуючому пакунку"
+
+msgid "reuse existing deltas"
+msgstr "повторно використати існуючі дельти"
+
+msgid "reuse existing objects"
+msgstr "повторно використати існуючі обʼєкти"
+
+msgid "use OFS_DELTA objects"
+msgstr "використати OFS_DELTA обʼєкти"
+
+msgid "use threads when searching for best delta matches"
+msgstr "використовувати потоки під час пошуку найкращих дельта збігів"
+
+msgid "do not create an empty pack output"
+msgstr "не створювати вивід порожнього пакунка"
+
+msgid "read revision arguments from standard input"
+msgstr "зчитувати аргументи ревізії зі стандартного вводу"
+
+msgid "limit the objects to those that are not yet packed"
+msgstr "обмежувати обʼєкти тільки тими, які ще не запаковані"
+
+msgid "include objects reachable from any reference"
+msgstr "включати обʼєкти, досяжні з будь-якого посилання"
+
+msgid "include objects referred by reflog entries"
+msgstr "включати обʼєкти, на які посилаються записи журналу посилань"
+
+msgid "include objects referred to by the index"
+msgstr "включати обʼєкти, на які посилається індекс"
+
+msgid "read packs from stdin"
+msgstr "прочитати пакунки з stdin"
+
+msgid "output pack to stdout"
+msgstr "вивести пакунок у stdout"
+
+msgid "include tag objects that refer to objects to be packed"
+msgstr ""
+"включати обʼєкти тегів, які посилаються на обʼєкти, що будуть запаковані"
+
+msgid "keep unreachable objects"
+msgstr "зберігати недосяжні обʼєкти"
+
+msgid "pack loose unreachable objects"
+msgstr "запакувати вільні недосяжні обʼєкти"
+
+msgid "unpack unreachable objects newer than <time>"
+msgstr "розпакувати недосяжні обʼєкти, новіші за <час>"
+
+msgid "create a cruft pack"
+msgstr "створити марний пакунок"
+
+msgid "expire cruft objects older than <time>"
+msgstr "видалити марні обʼєкти старіші за <час>"
+
+msgid "use the sparse reachability algorithm"
+msgstr "використовувати алгоритм розрідженої досяжності"
+
+msgid "create thin packs"
+msgstr "створити тонкі пакунки"
+
+msgid "create packs suitable for shallow fetches"
+msgstr "створювати пакунки, придатні для неглибокого отримання"
+
+msgid "ignore packs that have companion .keep file"
+msgstr "ігнорувати пакунки, які мають супровідний .keep файл"
+
+msgid "ignore this pack"
+msgstr "ігнорувати цей пакунок"
+
+msgid "pack compression level"
+msgstr "рівень стиснення пакунка"
+
+msgid "do not hide commits by grafts"
+msgstr "не приховувати коміти через прищепи"
+
+msgid "use a bitmap index if available to speed up counting objects"
+msgstr ""
+"використовувати bitmap індекс, якщо він доступний, для прискорення "
+"підрахунку обʼєктів"
+
+msgid "write a bitmap index together with the pack index"
+msgstr "записати bitmap індекс разом з індексом пакунка"
+
+msgid "write a bitmap index if possible"
+msgstr "записати bitmap індекс, якщо це можливо"
+
+msgid "handling for missing objects"
+msgstr "обробка для відсутніх обʼєктів"
+
+msgid "do not pack objects in promisor packfiles"
+msgstr "не пакувати обʼєкти у promisor пакунки"
+
+msgid "respect islands during delta compression"
+msgstr "поважати острови під час дельта компресії"
+
+msgid "protocol"
+msgstr "протокол"
+
+msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
+msgstr "вилучити всі налаштовані uploadpack.blobpackfileuri з цим протоколом"
+
+#, c-format
+msgid "delta chain depth %d is too deep, forcing %d"
+msgstr "глибина дельта ланцюжка %d занадто глибока, примусове %d"
+
+#, c-format
+msgid "pack.deltaCacheLimit is too high, forcing %d"
+msgstr "pack.deltaCacheLimit занадто великий, примусове %d"
+
+#, c-format
+msgid "bad pack compression level %d"
+msgstr "невірний рівень стиснення пакунка %d"
+
+msgid "--max-pack-size cannot be used to build a pack for transfer"
+msgstr ""
+"--max-pack-size не можна використовувати для створення пакунка для передачі"
+
+msgid "minimum pack size limit is 1 MiB"
+msgstr "мінімальний розмір пакунка - 1 МіБ"
+
+msgid "--thin cannot be used to build an indexable pack"
+msgstr "--thin не можна використовувати для створення індексованого пакунка"
+
+msgid "cannot use --filter with --stdin-packs"
+msgstr "неможливо використовувати --filter з --stdin-packs"
+
+msgid "cannot use internal rev list with --stdin-packs"
+msgstr "неможливо використовувати внутрішній список ревізій з --stdin-packs"
+
+msgid "cannot use internal rev list with --cruft"
+msgstr "неможливо використовувати внутрішній список ревізій з --cruft"
+
+msgid "cannot use --stdin-packs with --cruft"
+msgstr "неможливо використовувати --stdin-packs з --cruft"
+
+msgid "Enumerating objects"
+msgstr "Перерахування обʼєктів"
+
+#, c-format
+msgid ""
+"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
+"reused %<PRIu32> (from %<PRIuMAX>)"
+msgstr ""
+"Всього %<PRIu32> (дельта %<PRIu32>), повторно використано %<PRIu32> (дельта "
+"%<PRIu32>), повторно використано пакунків %<PRIu32> (з %<PRIuMAX>)"
+
+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 ""
+"Команду \"git pack-redundant\" номіновано на вилучення.\n"
+"Якщо ви все ще використовуєте цю команду, будь ласка, додайте додатковий "
+"параметр\n"
+"\"--i-still-use-this\" у командному рядку\n"
+"і повідомте нам, що ви все ще використовуєте її, надіславши листа\n"
+"на адресу <git@vger.kernel.org>.  Дякуємо.\n"
+
+msgid "refusing to run without --i-still-use-this"
+msgstr "відмовлено в запуску без --i-still-use-this"
+
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <шаблон>] [--exclude <шаблон>]"
+
+msgid "pack everything"
+msgstr "запакувати все"
+
+msgid "prune loose refs (default)"
+msgstr "видалити вивільнені посилання (за замовчуванням)"
+
+msgid "references to include"
+msgstr "посилання для включення"
+
+msgid "references to exclude"
+msgstr "посилання для виключення"
+
+msgid "git patch-id [--stable | --unstable | --verbatim]"
+msgstr "git patch-id [--stable | --unstable | --verbatim]"
+
+msgid "use the unstable patch-id algorithm"
+msgstr "використовувати нестабільний алгоритм ідентифікатора латки"
+
+msgid "use the stable patch-id algorithm"
+msgstr "використовувати стабільний алгоритм ідентифікатора латки"
+
+msgid "don't strip whitespace from the patch"
+msgstr "не прибирати пробіли з латки"
+
+msgid "git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"
+msgstr "git prune [-n] [-v] [--progress] [--expire <час>] [--] [<верхівка>...]"
+
+msgid "report pruned objects"
+msgstr "повідомляти про видалені обʼєкти"
+
+msgid "expire objects older than <time>"
+msgstr "видалити обʼєкти старіші за <час>"
+
+msgid "limit traversal to objects outside promisor packfiles"
+msgstr "обмежити обхід об’єктами за межами promisor пакунків"
+
+msgid "cannot prune in a precious-objects repo"
+msgstr "неможливо виконати видалення в precious-objects сховищі"
+
+msgid "git pull [<options>] [<repository> [<refspec>...]]"
+msgstr "git pull [<опції>] [<сховище> [<визначник посилання>...]]"
+
+msgid "control for recursive fetching of submodules"
+msgstr "управління рекурсивним отриманням підмодулів"
+
+msgid "Options related to merging"
+msgstr "Опції, повʼязані зі злиттям"
+
+msgid "incorporate changes by rebasing rather than merging"
+msgstr "вносити зміни через перебазування, а не злиття"
+
+msgid "allow fast-forward"
+msgstr "дозволити перемотування вперед"
+
+msgid "control use of pre-merge-commit and commit-msg hooks"
+msgstr "контролювати використання гачків pre-merge-commit та commit-msg"
+
+msgid "automatically stash/stash pop before and after"
+msgstr "автоматично додавати до та забирати зі схову перед і після"
+
+msgid "Options related to fetching"
+msgstr "Опції, пов’язані з отриманням"
+
+msgid "force overwrite of local branch"
+msgstr "примусовий перезапис локальної гілки"
+
+msgid "number of submodules pulled in parallel"
+msgstr "кількість підмодулів, що затягуються паралельно"
+
+msgid "use IPv4 addresses only"
+msgstr "використовувати тільки IPv4 адреси"
+
+msgid "use IPv6 addresses only"
+msgstr "використовувати тільки IPv6 адреси"
+
+msgid ""
+"There is no candidate for rebasing against among the refs that you just "
+"fetched."
+msgstr ""
+"Серед щойно отриманих посилань немає кандидата, відносно якого можна "
+"виконати перебазування."
+
+msgid ""
+"There are no candidates for merging among the refs that you just fetched."
+msgstr "Серед щойно отриманих посилань немає кандидатів на злиття."
+
+msgid ""
+"Generally this means that you provided a wildcard refspec which had no\n"
+"matches on the remote end."
+msgstr ""
+"Зазвичай це означає, що для визначника посилання, ви ввели символ "
+"підстановки, який не має збігів на віддаленому призначені."
+
+#, c-format
+msgid ""
+"You asked to pull from the remote '%s', but did not specify\n"
+"a branch. Because this is not the default configured remote\n"
+"for your current branch, you must specify a branch on the command line."
+msgstr ""
+"Ви попросили затягнути з віддаленого \"%s\", але не вказали\n"
+"гілку. Оскільки це не типове віддалене призначення\n"
+"для вашої поточної гілки, вам треба вказати гілку у командному рядку."
+
+msgid "You are not currently on a branch."
+msgstr "Наразі ви не на гілці."
+
+msgid "Please specify which branch you want to rebase against."
+msgstr ""
+"Будь ласка, вкажіть, відносно якої гілки ви хочете виконати перебазування."
+
+msgid "Please specify which branch you want to merge with."
+msgstr "Будь ласка, вкажіть, з якою гілкою ви хочете виконати злиття."
+
+msgid "See git-pull(1) for details."
+msgstr "Дивіться git-pull(1) для більш детальної інформації."
+
+msgid "<remote>"
+msgstr "<віддалене-призначення>"
+
+msgid "<branch>"
+msgstr "<гілка>"
+
+msgid "There is no tracking information for the current branch."
+msgstr "Немає інформації для відстежування поточної гілки."
+
+msgid ""
+"If you wish to set tracking information for this branch you can do so with:"
+msgstr ""
+"Якщо ви бажаєте встановити інформацію для відстежування цієї гілки, ви "
+"можете зробити це за допомогою:"
+
+#, c-format
+msgid ""
+"Your configuration specifies to merge with the ref '%s'\n"
+"from the remote, but no such ref was fetched."
+msgstr ""
+"У вашій конфігурації вказано робити злиття з посиланням \"%s\"\n"
+"з віддаленого призначення, але такого посилання не було отримано."
+
+#, c-format
+msgid "unable to access commit %s"
+msgstr "не вдалося отримати доступ до коміту %s"
+
+msgid "ignoring --verify-signatures for rebase"
+msgstr "ігнорування --verify-signatures для перебазування"
+
+msgid ""
+"You have divergent branches and need to specify how to reconcile them.\n"
+"You can do so by running one of the following commands sometime before\n"
+"your next pull:\n"
+"\n"
+"  git config pull.rebase false  # merge\n"
+"  git config pull.rebase true   # rebase\n"
+"  git config pull.ff only       # fast-forward only\n"
+"\n"
+"You can replace \"git config\" with \"git config --global\" to set a "
+"default\n"
+"preference for all repositories. You can also pass --rebase, --no-rebase,\n"
+"or --ff-only on the command line to override the configured default per\n"
+"invocation.\n"
+msgstr ""
+"У вас є розбіжні гілки, і вам потрібно вказати, як їх узгодити.\n"
+"Ви можете зробити це, виконавши одну з наступних команд \n"
+"до вашого наступного затягування:\n"
+"\n"
+"  git config pull.rebase false # merge\n"
+"  git config pull.rebase true # rebase\n"
+"  git config pull.ff only # fast-forward only\n"
+"\n"
+"Ви можете замінити \"git config\" на \"git config --global\", щоб встановити "
+"налаштування за замовчуванням\n"
+"для всіх сховищ. Ви також можете передати --rebase, --no-rebase,\n"
+"або --ff-only у командному рядку, щоб перевизначити налаштування за "
+"замовчуванням для кожного\n"
+"виклику.\n"
+
+msgid "Updating an unborn branch with changes added to the index."
+msgstr "Оновлення ненародженої гілки зі змінами, доданими до індексу."
+
+msgid "pull with rebase"
+msgstr "затягнути з перебазуванням"
+
+msgid "Please commit or stash them."
+msgstr "Будь ласка, зробіть коміт або додайте зміни до схову."
+
+#, c-format
+msgid ""
+"fetch updated the current branch head.\n"
+"fast-forwarding your working tree from\n"
+"commit %s."
+msgstr ""
+"отримання оновило верхівку поточної гілки.\n"
+"перемотування вперед вашого робочого дерева з\n"
+"коміту %s."
+
+#, c-format
+msgid ""
+"Cannot fast-forward your working tree.\n"
+"After making sure that you saved anything precious from\n"
+"$ git diff %s\n"
+"output, run\n"
+"$ git reset --hard\n"
+"to recover."
+msgstr ""
+"Неможливо перемотати вперед ваше робоче дерево.\n"
+"Переконавшись, що ви зберегли все цінне з\n"
+"$ git diff %s\n"
+"виводу, виконайте\n"
+"$ git reset --hard\n"
+"для відновлення."
+
+msgid "Cannot merge multiple branches into empty head."
+msgstr "Неможливо злити кілька гілок до порожньої верхівки."
+
+msgid "Cannot rebase onto multiple branches."
+msgstr "Неможливо перебазувати на кілька гілок."
+
+msgid "Cannot fast-forward to multiple branches."
+msgstr "Неможливо перемотати вперед кілька гілок."
+
+msgid "Need to specify how to reconcile divergent branches."
+msgstr "Потрібно вказати, як узгоджувати розбіжні гілки."
+
+msgid "cannot rebase with locally recorded submodule modifications"
+msgstr "неможливо перебазувати з локально записаними модифікаціями підмодуля"
+
+msgid "git push [<options>] [<repository> [<refspec>...]]"
+msgstr "git push [<опції>] [<сховище> [<визначник посилання>...]]"
+
+msgid "tag shorthand without <tag>"
+msgstr "скорочення тегу без <тег>"
+
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete приймає лише прості назви посилань"
+
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'.\n"
+msgstr ""
+"\n"
+"Щоб обрати будь-яку з опцій постійною, скористайтесь опцією push.default у "
+"'git help config'.\n"
+
+msgid ""
+"\n"
+"To avoid automatically configuring an upstream branch when its name\n"
+"won't match the local branch, see option 'simple' of branch.autoSetupMerge\n"
+"in 'git help config'.\n"
+msgstr ""
+"\n"
+"Щоб уникнути автоматичного налаштування висхідної гілки, якщо її назва\n"
+"не збігається з назвою локальної гілки, скористайтесь опцією 'simple' для "
+"branch.autoSetupMerge\n"
+"у “git help config”.\n"
+
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s HEAD\n"
+"%s%s"
+msgstr ""
+"Назва висхідної гілки для вашої поточної гілки не збігається з\n"
+"назвою вашої поточної гілки.  Щоб надіслати до висхідної гілки,\n"
+"скористайтесь командою\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"Щоб надіслати до однойменної гілки на віддаленому сервері скористайтесь\n"
+"\n"
+"    git push %s HEAD\n"
+"%s%s"
+
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+"Ви зараз не перебуваєте на гілці.\n"
+"Щоб надіслати історію, що веде до поточного (відокремленого HEAD)\n"
+"стану, скористайтесь командою\n"
+"\n"
+"    git push %s HEAD:<назва-віддаленої-гілки>\n"
+
+msgid ""
+"\n"
+"To have this happen automatically for branches without a tracking\n"
+"upstream, see 'push.autoSetupRemote' in 'git help config'.\n"
+msgstr ""
+"\n"
+"Щоб це відбувалося автоматично для невідстежуваних гілок\n"
+"першоджерельного сховища, дивіться 'push.autoSetupRemote' у 'git help "
+"config'.\n"
+
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+"%s"
+msgstr ""
+"Поточна гілка %s не має висхідної гілки.\n"
+"Щоб надіслати поточну гілку і встановити віддалене призначення "
+"першоджерельним сховищем, скористайтесь\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+"%s"
+
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr "Поточна гілка %s має кілька висхідних гілок, відмовлено в надсиланні."
+
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+"Ви не вказали жодних визначників посилань для надсилання, і push.default "
+"дорівнює \"nothing\"."
+
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+"Ви надсилаєте до віддаленої гілки \"%s\", яка не є першоджерелом\n"
+"вашої поточної гілки \"%s\", не кажучі мені, що надсилати\n"
+"і яку віддалену гілку оновлювати."
+
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки верхівка вашої поточної гілки знаходиться "
+"позаду\n"
+"її віддаленого аналога. Якщо ви хочете інтегрувати віддалені зміни,\n"
+"скористайтесь командою \"git pull\" перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки верхівка надісланої гілки знаходиться "
+"позаду\n"
+"її віддаленого аналога. Якщо ви хочете інтегрувати віддалені зміни, "
+"скористайтесь командою \"git pull\"\n"
+"перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+msgid ""
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки у віддаленому сховищі міститься робота, "
+"якої\n"
+"немає локально. Зазвичай це стається, коли інше сховище надсилає\n"
+"до того самого посилання. Якщо ви хочете інтегрувати віддалені зміни,\n"
+"скористайтесь командою \"git pull\" перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+msgid "Updates were rejected because the tag already exists in the remote."
+msgstr ""
+"Оновлення були відхилені, оскільки тег вже існує на віддаленому сховищі."
+
+msgid ""
+"You cannot update a remote ref that points at a non-commit object,\n"
+"or update a remote ref to make it point at a non-commit object,\n"
+"without using the '--force' option.\n"
+msgstr ""
+"Ви не можете оновити віддалене посилання, яке вказує на об’єкт, що не є "
+"об’єктом коміту,\n"
+"або оновити віддалене посилання так, щоб воно вказувало на об’єкт, що не є "
+"об’єктом коміту,\n"
+"без використання опції \"--force\".\n"
+
+msgid ""
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+"Оновлення були відхилені, оскільки верхівка віддалено відстежуваної гілки "
+"була\n"
+"оновлена з часу останньої синхронізації. Якщо ви хочете інтегрувати "
+"віддалені зміни,\n"
+"скористайтесь командою \"git pull\" перед повторним надсиланням.\n"
+"Докладні відомості наведено у розділі \"Зауваження щодо перемотування "
+"вперед\" команди \"git push --help\"."
+
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Надсилання до %s\n"
+
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "не вдалося надіслати деякі посилання до '%s'"
+
+msgid ""
+"recursing into submodule with push.recurseSubmodules=only; using on-demand "
+"instead"
+msgstr ""
+"рекурсія в підмодулі з push.recurseSubmodules=only; натомість використовую "
+"on-demand"
+
+#, c-format
+msgid "invalid value for '%s'"
+msgstr "неприпустиме значення для '%s'"
+
+msgid "repository"
+msgstr "сховище"
+
+msgid "push all branches"
+msgstr "надіслати всі гілки"
+
+msgid "mirror all refs"
+msgstr "віддзеркалити всі посилання"
+
+msgid "delete refs"
+msgstr "видалити посилання"
+
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr ""
+"надіслати теги (не можна використовувати з --all, --branches або --mirror)"
+
+msgid "force updates"
+msgstr "оновити примусово"
+
+msgid "<refname>:<expect>"
+msgstr "<refname>:<expect>"
+
+msgid "require old value of ref to be at this value"
+msgstr "вимагати, щоб старе значення посилання було рівним цьому значенню"
+
+msgid "require remote updates to be integrated locally"
+msgstr "вимагати локальної інтеграції віддалених оновлень"
+
+msgid "control recursive pushing of submodules"
+msgstr "контролювати рекурсивне надсилання підмодулів"
+
+msgid "use thin pack"
+msgstr "використовувати тонке пакування"
+
+msgid "receive pack program"
+msgstr "отримати пакетну програму"
+
+msgid "set upstream for git pull/status"
+msgstr "встановити першоджерельне сховище для git pull/status"
+
+msgid "prune locally removed refs"
+msgstr "обрізати локально видалені посилання"
+
+msgid "bypass pre-push hook"
+msgstr "обійти pre-push гачок"
+
+msgid "push missing but relevant tags"
+msgstr "надсилання відсутнє, але є релевантні теги"
+
+msgid "GPG sign the push"
+msgstr "підписати надсилання за допомогою GPG"
+
+msgid "request atomic transaction on remote side"
+msgstr "запросити атомарну транзакцію на віддаленій стороні"
+
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete не має сенсу без посилань"
+
+#, c-format
+msgid "bad repository '%s'"
+msgstr "невірне сховище \"%s\""
+
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+"Немає налаштованого місця призначення надсилання.\n"
+"Або зазначте URL-адресу з командного рядка, або налаштуйте віддалене сховище "
+"за допомогою\n"
+"\n"
+"    git remote add <назва> <адреса>\n"
+"\n"
+"а потім виконайте надсилання, використовуючи назву віддаленого сховища\n"
+"\n"
+"    git push <ім'я>\n"
+
+msgid "--all can't be combined with refspecs"
+msgstr "--all не можна комбінувати з визначниками посилань"
+
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror не можна комбінувати з визначниками посилань"
+
+msgid "push options must not have new line characters"
+msgstr "опції push не можуть містити символи нового рядка"
+
+msgid "git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"
+msgstr ""
+"git range-diff [<опції>] <стара-база>..<стара-верхівка> <нова-база>..<нова-"
+"верхівка>"
+
+msgid "git range-diff [<options>] <old-tip>...<new-tip>"
+msgstr "git range-diff [<опції>] <стара-верхівка>...<нова-верхівка>"
+
+msgid "git range-diff [<options>] <base> <old-tip> <new-tip>"
+msgstr "git range-diff [<опції>] <база> <стара-верхівка> <нова-верхівка>"
+
+msgid "use simple diff colors"
+msgstr "використовувати прості кольори diff"
+
+msgid "notes"
+msgstr "нотатки"
+
+msgid "passed to 'git log'"
+msgstr "передано до \"git log\""
+
+msgid "only emit output related to the first range"
+msgstr "видати тільки вивід, що відноситься до першого діапазону"
+
+msgid "only emit output related to the second range"
+msgstr "видати тільки вивід, що відноситься до другого діапазону"
+
+#, c-format
+msgid "not a revision: '%s'"
+msgstr "не є ревізією: \"%s\""
+
+#, c-format
+msgid "not a commit range: '%s'"
+msgstr "не діапазон комітів: \"%s\""
+
+#, c-format
+msgid "not a symmetric range: '%s'"
+msgstr "не симетричний діапазон: \"%s\""
+
+msgid "need two commit ranges"
+msgstr "потрібно два діапазони комітів"
+
+msgid ""
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<prefix>)\n"
+"              [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n"
+"              (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
+msgstr ""
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --"
+"prefix=<префікс>)\n"
+"              [-u | -i]] [--index-output=<файл>] [--no-sparse-checkout]\n"
+"              (--empty | <деревоподібне-джерело1> [<деревоподібне-джерело2> "
+"[<деревоподібне-джерело3>]])"
+
+msgid "write resulting index to <file>"
+msgstr "записати отриманий індекс у <файл>"
+
+msgid "only empty the index"
+msgstr "тільки очистити індекс"
+
+msgid "Merging"
+msgstr "Злиття"
+
+msgid "perform a merge in addition to a read"
+msgstr "виконати злиття на додачу до читання"
+
+msgid "3-way merge if no file level merging required"
+msgstr "3-стороннє злиття, при відсутності потреби в злитті на рівні файлів"
+
+msgid "3-way merge in presence of adds and removes"
+msgstr "3-стороннє злиття за наявності додавання та видалення"
+
+msgid "same as -m, but discard unmerged entries"
+msgstr "те саме, що й -m, але відкидає не злиті записи"
+
+msgid "<subdirectory>/"
+msgstr "<піддиректорія>/"
+
+msgid "read the tree into the index under <subdirectory>/"
+msgstr "зчитати дерево в індекс під <піддиректорію>/"
+
+msgid "update working tree with merge result"
+msgstr "оновити робоче дерево результатом злиття"
+
+msgid "gitignore"
+msgstr "gitignore"
+
+msgid "allow explicitly ignored files to be overwritten"
+msgstr "дозволити перезапис явно проігнорованих файлів"
+
+msgid "don't check the working tree after merging"
+msgstr "не перевіряти робоче дерево після злиття"
+
+msgid "don't update the index or the work tree"
+msgstr "не оновлювати індекс або робоче дерево"
+
+msgid "skip applying sparse checkout filter"
+msgstr "пропустити застосування sparse checkout фільтра"
+
+msgid "debug unpack-trees"
+msgstr "відлагодити unpack-trees"
+
+msgid "suppress feedback messages"
+msgstr "не показувати повідомлення зворотного звʼязку"
+
+msgid "You need to resolve your current index first"
+msgstr "Спочатку вам потрібно розвʼязати поточний індекс"
+
+msgid ""
+"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase> | --keep-base] "
+"[<upstream> [<branch>]]"
+msgstr ""
+"git rebase [-i] [опції] [--exec <команда>] [--onto <нова-база> | --keep-"
+"base] [<першоджерельне-сховище> [<гілка>]]"
+
+msgid ""
+"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]"
+msgstr ""
+"git rebase [-i] [опції] [--exec <команда>] [--onto <нова-база>] --root "
+"[<гілка>]"
+
+#, c-format
+msgid "could not read '%s'."
+msgstr "не вдалося прочитати \"%s\"."
+
+#, c-format
+msgid "could not create temporary %s"
+msgstr "не вдалося створити тимчасовий %s"
+
+msgid "could not mark as interactive"
+msgstr "не вдалося позначити як інтерактивний"
+
+msgid "could not generate todo list"
+msgstr "не вдалося створити список справ"
+
+msgid "a base commit must be provided with --upstream or --onto"
+msgstr "базовий коміт має бути наданий з --upstream або --onto"
+
+#, c-format
+msgid "%s requires the merge backend"
+msgstr "%s потребує обробника злиття"
+
+#, c-format
+msgid "invalid onto: '%s'"
+msgstr "неприпустимий onto: \"%s\""
+
+#, c-format
+msgid "invalid orig-head: '%s'"
+msgstr "неприпустимий orig-head: \"%s\""
+
+#, c-format
+msgid "ignoring invalid allow_rerere_autoupdate: '%s'"
+msgstr "ігнорування неприпустимого allow_rerere_autoupdate: \"%s\""
+
+#, c-format
+msgid "could not remove '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+msgid ""
+"Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run \"git rebase --"
+"abort\"."
+msgstr ""
+"Вирішіть усі конфлікти вручну, позначте їх як вирішені за допомогою\n"
+"\"git add/rm <конфліктні_файли>\", а потім виконайте \"git rebase --"
+"continue\".\n"
+"Замість цього ви можете пропустити цей коміт: виконайте \"git rebase --"
+"skip\".\n"
+"Щоб перервати процес і повернутися до стану перед \"git rebase\", виконайте "
+"\"git rebase --abort\"."
+
+#, c-format
+msgid ""
+"\n"
+"git encountered an error while preparing the patches to replay\n"
+"these revisions:\n"
+"\n"
+"    %s\n"
+"\n"
+"As a result, git cannot rebase them."
+msgstr ""
+"\n"
+"git зіткнувся з помилкою під час підготовки латок для відтворення\n"
+"цих ревізій:\n"
+"\n"
+"    %s\n"
+"\n"
+"Внаслідок цього git не може їх перебазувати."
+
+#, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "Невідомий режим перебазування-злиття: %s"
+
+#, c-format
+msgid "could not switch to %s"
+msgstr "не вдалося переключитись на %s"
+
+msgid "apply options and merge options cannot be used together"
+msgstr "apply опції не можна використовувати разом з merge опціями"
+
+#, c-format
+msgid ""
+"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
+"\"ask\"."
+msgstr ""
+"нерозпізнаний порожній тип \"%s\"; припустимими значеннями є \"drop\", "
+"\"keep\" та \"ask\"."
+
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"Команда --rebase-merges з порожнім строковим аргументом є застарілою і "
+"перестане працювати у наступній версії Git. Замість цього скористайтесь "
+"командою --rebase-merges без аргументу, який робить те саме."
+
+#, c-format
+msgid ""
+"%s\n"
+"Please specify which branch you want to rebase against.\n"
+"See git-rebase(1) for details.\n"
+"\n"
+"    git rebase '<branch>'\n"
+"\n"
+msgstr ""
+"%s\n"
+"Будь ласка, вкажіть гілку, відносно якої ви хочете виконати перебазування.\n"
+"Дивіться git-rebase(1) для детальної інформації.\n"
+"\n"
+"    git rebase \"<гілка>\"\n"
+"\n"
+"\n"
+
+#, c-format
+msgid ""
+"If you wish to set tracking information for this branch you can do so with:\n"
+"\n"
+"    git branch --set-upstream-to=%s/<branch> %s\n"
+"\n"
+msgstr ""
+"Якщо ви бажаєте налаштувати відстежування для цієї гілки, ви можете зробити "
+"це за допомогою:\n"
+"\n"
+"    git branch --set-upstream-to=%s/<гілка> %s\n"
+"\n"
+
+msgid "exec commands cannot contain newlines"
+msgstr "команди exec не можуть містити символи нового рядка"
+
+msgid "empty exec command"
+msgstr "порожня команда exec"
+
+msgid "rebase onto given branch instead of upstream"
+msgstr "перебазувати на задану гілку замість першоджерельного сховища"
+
+msgid "use the merge-base of upstream and branch as the current base"
+msgstr ""
+"використовувати базу злиття першоджерельного сховища та гілки як поточну базу"
+
+msgid "allow pre-rebase hook to run"
+msgstr "дозволити запуск pre-rebase гачка"
+
+msgid "be quiet. implies --no-stat"
+msgstr "працювати тихесенько. Мається на увазі --no-stat"
+
+msgid "display a diffstat of what changed upstream"
+msgstr "відображати diffstat того, що змінилося у першоджерельному сховищі"
+
+msgid "do not show diffstat of what changed upstream"
+msgstr "не відображати diffstat того, що змінилося у першоджерельному сховищі"
+
+msgid "add a Signed-off-by trailer to each commit"
+msgstr "додати Signed-off-by причіп до кожного коміту"
+
+msgid "make committer date match author date"
+msgstr "зробити так, щоб дата комітера збігалася з датою автора"
+
+msgid "ignore author date and use current date"
+msgstr "ігнорувати дату автора та використати поточну дату"
+
+msgid "synonym of --reset-author-date"
+msgstr "синонім --reset-author-date"
+
+msgid "passed to 'git apply'"
+msgstr "передано в \"git apply\""
+
+msgid "ignore changes in whitespace"
+msgstr "ігнорувати зміни в пробілах"
+
+msgid "cherry-pick all commits, even if unchanged"
+msgstr "висмикнути всі коміти, навіть якщо вони не змінені"
+
+msgid "continue"
+msgstr "продовжити"
+
+msgid "skip current patch and continue"
+msgstr "пропустити поточну латку і продовжити"
+
+msgid "abort and check out the original branch"
+msgstr "перервати і перейти до початкової гілки"
+
+msgid "abort but keep HEAD where it is"
+msgstr "перервати, але залишити HEAD на місці"
+
+msgid "edit the todo list during an interactive rebase"
+msgstr "редагувати список справ під час інтерактивного перебазування"
+
+msgid "show the patch file being applied or merged"
+msgstr "показати файл латки, який застосовується або зливається"
+
+msgid "use apply strategies to rebase"
+msgstr "використовувати стратегії застосування для перебазування"
+
+msgid "use merging strategies to rebase"
+msgstr "використовувати стратегії злиття для перебазування"
+
+msgid "let the user edit the list of commits to rebase"
+msgstr "дозволити користувачеві редагувати список комітів для перебазування"
+
+msgid "(REMOVED) was: try to recreate merges instead of ignoring them"
+msgstr ""
+"(ВИДАЛЕНО) було: спробувати відтворити злиття замість того, щоб ігнорувати їх"
+
+msgid "how to handle commits that become empty"
+msgstr "як обробляти порожні коміти"
+
+msgid "keep commits which start empty"
+msgstr "зберігати коміти, які починаються порожніми"
+
+msgid "move commits that begin with squash!/fixup! under -i"
+msgstr "перемістити коміти, які починаються з squash!/fixup! під -i"
+
+msgid "update branches that point to commits that are being rebased"
+msgstr ""
+"оновити гілки, які вказують на коміти, що знаходяться в стані перебазування"
+
+msgid "add exec lines after each commit of the editable list"
+msgstr "додати exec рядки після кожного коміту редагованого списку"
+
+msgid "allow rebasing commits with empty messages"
+msgstr "дозволити перебазування комітів з порожніми дописами"
+
+msgid "try to rebase merges instead of skipping them"
+msgstr "спробувати перебазувати злиття замість того, щоб пропускати їх"
+
+msgid "use 'merge-base --fork-point' to refine upstream"
+msgstr ""
+"скористайтесь \"merge-base --fork-point\", щоб зазначиити першоджерельне "
+"сховище"
+
+msgid "use the given merge strategy"
+msgstr "використати задану стратегію злиття"
+
+msgid "option"
+msgstr "опція"
+
+msgid "pass the argument through to the merge strategy"
+msgstr "передати аргумент до стратегії злиття"
+
+msgid "rebase all reachable commits up to the root(s)"
+msgstr "перебазувати всі доступні коміти до кореня(ів)."
+
+msgid "automatically re-schedule any `exec` that fails"
+msgstr "автоматично переносити будь-який \"exec\", який завершився невдало"
+
+msgid "apply all changes, even those already present upstream"
+msgstr ""
+"застосовувати всі зміни, навіть ті, що вже існують у першоджерельному сховищі"
+
+msgid "It looks like 'git am' is in progress. Cannot rebase."
+msgstr "Схоже, що виконується команда \"git am\". Неможливо перебазувати."
+
+msgid ""
+"`rebase --preserve-merges` (-p) is no longer supported.\n"
+"Use `git rebase --abort` to terminate current rebase.\n"
+"Or downgrade to v2.33, or earlier, to complete the rebase."
+msgstr ""
+"Команда \"rebase --preserve-merges\" (-p) більше не підтримується.\n"
+"Скористайтесь \"git rebase --abort\" для завершення поточного "
+"перебазування.\n"
+"Або поверніться до v2.33 або більш ранньої версії, щоб завершити "
+"перебазування."
+
+msgid ""
+"--preserve-merges was replaced by --rebase-merges\n"
+"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
+"which is no longer supported; use 'merges' instead"
+msgstr ""
+"--preserve-merges було замінено на --rebase-merges\n"
+"Примітка: У вашій конфігурації \"pull.rebase\" також може бути встановлено у "
+"значення \"preserve\",\n"
+"який більше не підтримується; натомість використовуйте \"merges\""
+
+msgid "No rebase in progress?"
+msgstr "Перебазування не відбувається?"
+
+msgid "The --edit-todo action can only be used during interactive rebase."
+msgstr ""
+"Дію --edit-todo можна використовувати лише під час інтерактивного "
+"перебазування."
+
+msgid "Cannot read HEAD"
+msgstr "Неможливо прочитати HEAD"
+
+msgid ""
+"You must edit all merge conflicts and then\n"
+"mark them as resolved using git add"
+msgstr ""
+"Ви повинні відредагувати всі конфлікти злиття, а потім\n"
+"позначити їх як вирішені за допомогою git add"
+
+msgid "could not discard worktree changes"
+msgstr "не вдалося відкинути зміни робочого дерева"
+
+#, c-format
+msgid "could not move back to %s"
+msgstr "не вдалося повернутися до %s"
+
+#, c-format
+msgid ""
+"It seems that there is already a %s directory, and\n"
+"I wonder if you are in the middle of another rebase.  If that is the\n"
+"case, please try\n"
+"\t%s\n"
+"If that is not the case, please\n"
+"\t%s\n"
+"and run me again.  I am stopping in case you still have something\n"
+"valuable there.\n"
+msgstr ""
+"Здається, що директорія %s вже існує, і\n"
+"можливо, ви перебуваєте у процесі іншого перебазування.  Якщо це так,\n"
+"будь ласка, спробуйте\n"
+"\t%s\n"
+"Якщо це не так, спробуйте\n"
+"\t%s\n"
+"і запустіть команду ще раз.  Зупинка на випадок, якщо там у вас все ще є "
+"щось\n"
+"цінне.\n"
+
+msgid "switch `C' expects a numerical value"
+msgstr "перемикач \"C\" очікує числове значення"
+
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr ""
+"apply опції несумісні з rebase.rebaseMerges.  Розгляньте можливість "
+"додавання --no-rebase-merges"
+
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr ""
+"apply опції несумісні з rebase.updateRefs.  Розгляньте можливість додавання "
+"--no-update-refs"
+
+#, c-format
+msgid "Unknown rebase backend: %s"
+msgstr "Невідомий обробник перебазування: %s"
+
+msgid "--reschedule-failed-exec requires --exec or --interactive"
+msgstr "--reschedule-failed-exec потребує --exec або --interactive"
+
+#, c-format
+msgid "invalid upstream '%s'"
+msgstr "неприпустиме першоджерельне сховище \"%s\""
+
+msgid "Could not create new root commit"
+msgstr "Не вдалося створити новий кореневий коміт"
+
+#, c-format
+msgid "no such branch/commit '%s'"
+msgstr "немає такої гілки/коміту \"%s\""
+
+#, c-format
+msgid "No such ref: %s"
+msgstr "Немає такого посилання: %s"
+
+msgid "Could not resolve HEAD to a commit"
+msgstr "Не вдалося розвʼязати HEAD у коміт"
+
+#, c-format
+msgid "'%s': need exactly one merge base with branch"
+msgstr "\"%s\": потрібна лишень одна база злиття з гілкою"
+
+#, c-format
+msgid "'%s': need exactly one merge base"
+msgstr "\"%s\": потрібна лишень одна база злиття"
+
+#, c-format
+msgid "Does not point to a valid commit '%s'"
+msgstr "Не вказує на припустимий коміт \"%s\""
+
+msgid "HEAD is up to date."
+msgstr "HEAD знаходиться в актуальному стані."
+
+#, c-format
+msgid "Current branch %s is up to date.\n"
+msgstr "Поточна гілка %s знаходиться в актуальному стані.\n"
+
+msgid "HEAD is up to date, rebase forced."
+msgstr "HEAD знаходиться в актуальному стані, примусове перебазування."
+
+#, c-format
+msgid "Current branch %s is up to date, rebase forced.\n"
+msgstr ""
+"Поточна гілка %s знаходиться в актуальному стані, перебазовуйте примусово.\n"
+
+msgid "The pre-rebase hook refused to rebase."
+msgstr "Гачок pre-rebase відмовився перебазувати."
+
+#, c-format
+msgid "Changes to %s:\n"
+msgstr "Зміни у %s:\n"
+
+#, c-format
+msgid "Changes from %s to %s:\n"
+msgstr "Зміна з %s на %s:\n"
+
+#, c-format
+msgid "First, rewinding head to replay your work on top of it...\n"
+msgstr "Спочатку перемотуємо HEAD, щоб відтворити вашу роботу поверх того...\n"
+
+msgid "Could not detach HEAD"
+msgstr "Не вдалося відʼєднати HEAD"
+
+#, c-format
+msgid "Fast-forwarded %s to %s.\n"
+msgstr "Перемотано вперед %s на %s.\n"
+
+msgid "git receive-pack <git-dir>"
+msgstr "git receive-pack <git-директорія>"
+
+msgid ""
+"By default, updating the current branch in a non-bare repository\n"
+"is denied, because it will make the index and work tree inconsistent\n"
+"with what you pushed, and will require 'git reset --hard' to match\n"
+"the work tree to HEAD.\n"
+"\n"
+"You can set the 'receive.denyCurrentBranch' configuration variable\n"
+"to 'ignore' or 'warn' in the remote repository to allow pushing into\n"
+"its current branch; however, this is not recommended unless you\n"
+"arranged to update its work tree to match what you pushed in some\n"
+"other way.\n"
+"\n"
+"To squelch this message and still keep the default behaviour, set\n"
+"'receive.denyCurrentBranch' configuration variable to 'refuse'."
+msgstr ""
+"За замовчуванням, оновлення поточної гілки у непустому сховищі\n"
+"заборонено, оскільки це зробить індекс і робоче дерево невідповідними\n"
+"з тим, що ви надіслали, і вимагатиме виконання команди \"git reset --hard\" "
+"щоб співставити робоче дерево і HEAD.\n"
+"\n"
+"Ви можете встановити конфігураційну змінну \"receive.denyCurrentBranch\"\n"
+"на \"ignore\" або \"warn\" у віддаленому сховищі, щоб дозволити надсилання "
+"до поточної гілки; однак, це не рекомендується, якщо ви не\n"
+"налагодили оновлення його робочого дерева відповідно до того, що ви "
+"надіслали якимось іншим способом.\n"
+"\n"
+"Щоб ігнорувати це повідомлення і зберегти поведінку за замовчуванням, "
+"встановіть параметр \"receive.denyCurrentBranch\" у значення \"refuse\"."
+
+msgid ""
+"By default, deleting the current branch is denied, because the next\n"
+"'git clone' won't result in any file checked out, causing confusion.\n"
+"\n"
+"You can set 'receive.denyDeleteCurrent' configuration variable to\n"
+"'warn' or 'ignore' in the remote repository to allow deleting the\n"
+"current branch, with or without a warning message.\n"
+"\n"
+"To squelch this message, you can set it to 'refuse'."
+msgstr ""
+"За замовчуванням, видалення поточної гілки заборонено, оскільки наступний\n"
+"\"git clone\" не призведе до переключення стану жодного файлу, що спричинить "
+"плутанину.\n"
+"\n"
+"Ви можете встановити конфігураційну змінну \"receive.denyDeleteCurrent\" у "
+"значення\n"
+"\"warn\" або \"ignore\" у віддаленому сховищі, щоб дозволити видалення\n"
+"поточної гілки, з попереджувальним повідомленням або без нього.\n"
+"\n"
+"Щоб ігнорувати це повідомлення, ви можете встановити значення \"refuse\"."
+
+msgid "quiet"
+msgstr "тихо"
+
+msgid "you must specify a directory"
+msgstr "необхідно вказати директорію"
+
+msgid "git reflog [show] [<log-options>] [<ref>]"
+msgstr "git reflog [show] [<лог-опції>] [<посилання>]"
+
+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=<час>] [--expire-unreachable=<час>]\n"
+"                  [--rewrite] [--updateref] [--stale-fix]\n"
+"                  [--dry-run | -n] [--verbose] [--all [--single-worktree] | "
+"<посилання>...]"
+
+msgid ""
+"git reflog delete [--rewrite] [--updateref]\n"
+"                  [--dry-run | -n] [--verbose] <ref>@{<specifier>}..."
+msgstr ""
+"git reflog delete [--rewrite] [--updateref]\n"
+"                  [--dry-run | -n] [--verbose] <посилання>@{<визначник>}..."
+
+msgid "git reflog exists <ref>"
+msgstr "git reflog exists <посилання>"
+
+#, c-format
+msgid "invalid timestamp '%s' given to '--%s'"
+msgstr "неприпустима позначка часу \"%s\" передана до \"--%s\""
+
+msgid "do not actually prune any entries"
+msgstr "насправді не видаляти жодного запису"
+
+msgid ""
+"rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"
+msgstr "перезаписати старий SHA1 на новий SHA1 запису, який тепер передує йому"
+
+msgid "update the reference to the value of the top reflog entry"
+msgstr "оновити посилання на значення верхнього запису журналу посилань"
+
+msgid "print extra information on screen"
+msgstr "вивести додаткову інформацію на екран"
+
+msgid "timestamp"
+msgstr "позначка часу"
+
+msgid "prune entries older than the specified time"
+msgstr "видалити записи, старіші за вказаний час"
+
+msgid ""
+"prune entries older than <time> that are not reachable from the current tip "
+"of the branch"
+msgstr ""
+"видалити записи, старіші за <час>, які недоступні з поточної верхівки гілки"
+
+msgid "prune any reflog entries that point to broken commits"
+msgstr ""
+"видалити всі записи журналу посилань, які вказують на пошкоджені коміти"
+
+msgid "process the reflogs of all references"
+msgstr "обробляти журнали посилань всіх посилань"
+
+msgid "limits processing to reflogs from the current worktree only"
+msgstr "обмежити обробку журналами посилань лише з поточного робочого дерева"
+
+#, c-format
+msgid "Marking reachable objects..."
+msgstr "Позначення досяжних обʼєктів..."
+
+#, c-format
+msgid "%s points nowhere!"
+msgstr "%s вказує в нікуди!"
+
+msgid "no reflog specified to delete"
+msgstr "не вказано журнал посилань для видалення"
+
+#, c-format
+msgid "invalid ref format: %s"
+msgstr "неприпустимий формат посилання: %s"
+
+msgid ""
+"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--"
+"mirror=<fetch|push>] <name> <url>"
+msgstr ""
+"git remote add [-t <гілка>] [-m <мастер>] [-f] [--tags | --no-tags] [--"
+"mirror=<fetch|push>] <назва> <URL-адреса>"
+
+msgid "git remote rename [--[no-]progress] <old> <new>"
+msgstr "git remote rename [--[no-]progress] <стара-назва> <нова-назва>"
+
+msgid "git remote remove <name>"
+msgstr "git remote remove <назвa>"
+
+msgid "git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"
+msgstr "git remote set-head <назва> (-a | --auto | -d | --delete | <гілка>)"
+
+msgid "git remote [-v | --verbose] show [-n] <name>"
+msgstr "git remote [-v | --verbose] show [-n] <назва>"
+
+msgid "git remote prune [-n | --dry-run] <name>"
+msgstr "git remote prune [-n | --dry-run] <назва>"
+
+msgid ""
+"git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
+msgstr ""
+"git remote [-v | --verbose] update [-p | --prune] [(<група> | <віддалене-"
+"призначення>)...]"
+
+msgid "git remote set-branches [--add] <name> <branch>..."
+msgstr "git remote set-branches [--add] <назва> <гілка>..."
+
+msgid "git remote get-url [--push] [--all] <name>"
+msgstr "git remote get-url [--push] [--all] <назва>"
+
+msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]"
+msgstr ""
+"git remote set-url [--push] <назва> <нова-url-адреса> [<стара-url-адреса>]"
+
+msgid "git remote set-url --add <name> <newurl>"
+msgstr "git remote set-url --add <назва> <нова-url-адреса>"
+
+msgid "git remote set-url --delete <name> <url>"
+msgstr "git remote set-url --delete <назва> <url-адреса>"
+
+msgid "git remote add [<options>] <name> <url>"
+msgstr "git remote add [<опції>] <назва> <url>"
+
+msgid "git remote set-branches <name> <branch>..."
+msgstr "git remote set-branches <назва> <гілка>..."
+
+msgid "git remote set-branches --add <name> <branch>..."
+msgstr "git remote set-branches --add <назва> <гілка>..."
+
+msgid "git remote show [<options>] <name>"
+msgstr "git remote show [<опції>] <назва>"
+
+msgid "git remote prune [<options>] <name>"
+msgstr "git remote prune [<опції>] <назва>"
+
+msgid "git remote update [<options>] [<group> | <remote>]..."
+msgstr "git remote update [<опції>] [<група> | <віддаленe-призначення>]..."
+
+#, c-format
+msgid "Updating %s"
+msgstr "Оновлення %s"
+
+#, c-format
+msgid "Could not fetch %s"
+msgstr "Не вдалося отримати %s"
+
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror небезпечний і застарілий; будь ласка, \n"
+"\t скористайтесь --mirror=fetch або --mirror=push"
+
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "невідомий аргумент дзеркала: %s"
+
+msgid "fetch the remote branches"
+msgstr "отримати віддалені гілки"
+
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"імпортувати всі теги та повʼязані з ними обʼєкти під час отримання\n"
+"або не отримувати жодного тегу (--no-tags)"
+
+msgid "branch(es) to track"
+msgstr "гілка(и) для відстежування"
+
+msgid "master branch"
+msgstr "master гілка"
+
+msgid "set up remote as a mirror to push to or fetch from"
+msgstr ""
+"налаштувати віддалене призначення як дзеркало, щоб надсилати до нього або "
+"отримувати з нього дані"
+
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "вказівка головної гілки не має сенсу з --mirror"
+
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+"вказівка гілок для відстежування має сенс тільки з дзеркалами отримання"
+
+#, c-format
+msgid "remote %s already exists."
+msgstr "віддалений %s вже існує."
+
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Не вдалося налаштувати master \"%s\""
+
+#, c-format
+msgid "more than one %s"
+msgstr "більше одного %s"
+
+#, c-format
+msgid "unhandled branch.%s.rebase=%s; assuming 'true'"
+msgstr "unhandled branch.%s.rebase=%s; припускаю \"true\""
+
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr "Не вдалося забрати карту отримання для визначника посилання %s"
+
+msgid "(matching)"
+msgstr "(збіг)"
+
+msgid "(delete)"
+msgstr "(видалити)"
+
+#, c-format
+msgid "could not set '%s'"
+msgstr "не вдалося встановити \"%s\""
+
+#, c-format
+msgid "could not unset '%s'"
+msgstr "не вдалося скинути \"%s\""
+
+#, c-format
+msgid ""
+"The %s configuration remote.pushDefault in:\n"
+"\t%s:%d\n"
+"now names the non-existent remote '%s'"
+msgstr ""
+"Конфігурація %s remote.pushDefault in:\n"
+"\t%s:%d\n"
+"тепер називає неіснуюче віддалене посилання \"%s\""
+
+#, c-format
+msgid "No such remote: '%s'"
+msgstr "Немає такого віддаленого призначення: \"%s\""
+
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Не вдалося перейменувати розділ конфігурації з \"%s\" на \"%s\""
+
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"Не оновлюється нестандартний визначник отримання посилань\n"
+"%s\n"
+"Будь ласка, за потреби оновіть конфігурацію вручну."
+
+msgid "Renaming remote references"
+msgstr "Перейменування віддалених посилань"
+
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "creating '%s' failed"
+msgstr "не вдалося створити \"%s\""
+
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Примітка: гілка поза межами refs/remotes/ ієрархії не була видалена;\n"
+"щоб видалити її, скористайтесь:"
+msgstr[1] ""
+"Примітка: деякі гілки поза межами refs/remotes/ ієрархії не були видалені;\n"
+"щоб видалити їх, скористайтесь:"
+msgstr[2] ""
+"Примітка: деякі гілки поза межами refs/remotes/ ієрархії не були видалені;\n"
+"щоб видалити їх, скористайтесь:"
+
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Не вдалося видалити секцію конфігурації \"%s\""
+
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " нова (наступне отримання зберігатиметься у віддалених remotes/%s)"
+
+msgid " tracked"
+msgstr " відстежується"
+
+msgid " skipped"
+msgstr " пропущена"
+
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " застаріла (скористайтесь \"git remote prune\", щоб видалити)"
+
+msgid " ???"
+msgstr " ???"
+
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "неприпустиме branch.%s.merge; неможливо перебазувати на > 1 гілку"
+
+#, c-format
+msgid "rebases interactively onto remote %s"
+msgstr "перебазувати інтерактивно на віддалене призначення %s"
+
+#, c-format
+msgid "rebases interactively (with merges) onto remote %s"
+msgstr "перебазувати інтерактивно (зі злиттям) на віддалене призначення %s"
+
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "перебазувати на віддалене призначення %s"
+
+#, c-format
+msgid " merges with remote %s"
+msgstr " зливається з віддаленим призначенням %s"
+
+#, c-format
+msgid "merges with remote %s"
+msgstr "зливається з віддаленим призначенням %s"
+
+#, c-format
+msgid "%-*s    and with remote %s\n"
+msgstr "%-*s    та з віддаленим призначенням %s\n"
+
+msgid "create"
+msgstr "створити"
+
+msgid "delete"
+msgstr "видалити"
+
+msgid "up to date"
+msgstr "в актуальному стані"
+
+msgid "fast-forwardable"
+msgstr "з можливістю перемотування вперед"
+
+msgid "local out of date"
+msgstr "локальне сховище застаріло"
+
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s примусово надіслати до %-*s (%s)"
+
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s надіслати до %-*s (%s)"
+
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s примусово надіслати до %s"
+
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s надіслати до %s"
+
+msgid "do not query remotes"
+msgstr "не запитувати віддалені призначення"
+
+#, c-format
+msgid "* remote %s"
+msgstr "* віддалене %s"
+
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  URL-адреса отримання: %s"
+
+msgid "(no URL)"
+msgstr "(без URL-адреси)"
+
+#. TRANSLATORS: the colon ':' should align
+#. with the one in " Fetch URL: %s"
+#. translation.
+#.
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  URL-адреса надсилання: %s"
+
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  HEAD гілка: %s"
+
+msgid "(not queried)"
+msgstr "(не запитувалось)"
+
+msgid "(unknown)"
+msgstr "(невідомо)"
+
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+"  HEAD гілка (віддалений HEAD неоднозначний, може бути одним з наступних):\n"
+
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] " Віддалена гілка:%s"
+msgstr[1] " Віддалені гілки:%s"
+msgstr[2] " Віддалених гілок:%s"
+
+msgid " (status not queried)"
+msgstr " (статус не запитувався)"
+
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Локальну гілку налаштовано на \"git pull\":"
+msgstr[1] "  Локальні гілки налаштовано на \"git pull\":"
+msgstr[2] "  Локальних гілок налаштовано на \"git pull\":"
+
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  Локальні посилання будуть віддзеркалені за допомогою \"git push\""
+
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Локальне посилання налаштовано для \"git push\"%s:"
+msgstr[1] "  Локальних посилання налаштовано для \"git push\"%s:"
+msgstr[2] "  Локальних посилань налаштовано для \"git push\"%s:"
+
+msgid "set refs/remotes/<name>/HEAD according to remote"
+msgstr ""
+"встановити refs/remotes/<назва>/HEAD відповідно до віддаленого призначення"
+
+msgid "delete refs/remotes/<name>/HEAD"
+msgstr "видалити refs/remotes/<назва>/HEAD"
+
+msgid "Cannot determine remote HEAD"
+msgstr "Не вдалося визначити віддалений HEAD"
+
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+"Існує кілька віддалених HEAD гілок. Будь ласка, виберіть одну з них, "
+"скориставшись:"
+
+#, c-format
+msgid "Could not delete %s"
+msgstr "Не вдалося видалити %s"
+
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "Не є припустимим посиланням: %s"
+
+#, c-format
+msgid "Could not setup %s"
+msgstr "Не вдалося налаштувати %s"
+
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s стануть висячими!"
+
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s став висячим!"
+
+#, c-format
+msgid "Pruning %s"
+msgstr "Видалення %s"
+
+#, c-format
+msgid "URL: %s"
+msgstr "URL-адреса: %s"
+
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [буде видалено] %s"
+
+#, c-format
+msgid " * [pruned] %s"
+msgstr " * [видалено] %s"
+
+msgid "prune remotes after fetching"
+msgstr "видалити віддалені призначення після отримання"
+
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Немає такого віддаленого призначення \"%s\""
+
+msgid "add branch"
+msgstr "додати гілку"
+
+msgid "no remote specified"
+msgstr "віддалене призначення не вказано"
+
+msgid "query push URLs rather than fetch URLs"
+msgstr "запитувати URL-адреси надсилань замість URL-адрес отримання"
+
+msgid "return all URLs"
+msgstr "повернути всі URL-адреси"
+
+#, c-format
+msgid "no URLs configured for remote '%s'"
+msgstr "не налаштовано URL-адреси для віддаленого \"%s\""
+
+msgid "manipulate push URLs"
+msgstr "маніпулювати URL-адресами надсилання"
+
+msgid "add URL"
+msgstr "додати URL-адресу"
+
+msgid "delete URLs"
+msgstr "видалити URL-адреси"
+
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete не має сенсу"
+
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "Неприпустимий шаблон старої URL-адреси: %s"
+
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Такої URL-адреси не знайдено: %s"
+
+msgid "Will not delete all non-push URLs"
+msgstr "Не видалятиме всі URL-адреси, що не є призначенням надсилань"
+
+msgid "be verbose; must be placed before a subcommand"
+msgstr "розгорнутий вивід; має стояти перед підкомандою"
+
+msgid "git repack [<options>]"
+msgstr "git repack [<опції>]"
+
+msgid ""
+"Incremental repacks are incompatible with bitmap indexes.  Use\n"
+"--no-write-bitmap-index or disable the pack.writeBitmaps configuration."
+msgstr ""
+"Поступові перепакування несумісні з bitmap індексами.  Скористайтесь "
+"параметром\n"
+"--no-write-bitmap-index або вимкніть конфігурацію pack.writeBitmaps."
+
+msgid "could not start pack-objects to repack promisor objects"
+msgstr "не вдалося розпочати pack-objects для перепакування promisor обʼєктів"
+
+msgid "repack: Expecting full hex object ID lines only from pack-objects."
+msgstr ""
+"перепакування: очікуються повні рядки hex ідентифікаторів обʼєктів тільки "
+"від pack-objects."
+
+msgid "could not finish pack-objects to repack promisor objects"
+msgstr "не вдалося завершити pack-objects для перепакування promisor обʼєктів"
+
+#, c-format
+msgid "cannot open index for %s"
+msgstr "неможливо відкрити індекс для %s"
+
+#, c-format
+msgid "pack %s too large to consider in geometric progression"
+msgstr "пакунок %s занадто великий, щоб враховувати в геометричній прогресії"
+
+#, c-format
+msgid "pack %s too large to roll up"
+msgstr "пакунок %s занадто великий для згортання"
+
+#, c-format
+msgid "could not open tempfile %s for writing"
+msgstr "не вдалося відкрити тимчасовий файл %s для запису"
+
+msgid "could not close refs snapshot tempfile"
+msgstr "не вдалося закрити тимчасовий файл знімка посилань"
+
+#, c-format
+msgid "could not remove stale bitmap: %s"
+msgstr "не вдалося видалити застарілий bitmap: %s"
+
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "префікс пакунку %s не починається з objdir %s"
+
+msgid "pack everything in a single pack"
+msgstr "запакувати все в один пакунок"
+
+msgid "same as -a, and turn unreachable objects loose"
+msgstr "те саме, що й -a, та звільняє недосяжні обʼєкти"
+
+msgid "same as -a, pack unreachable cruft objects separately"
+msgstr "те саме, що й -a, пакує недосяжні марні обʼєкти окремо"
+
+msgid "approxidate"
+msgstr "приблизна дата"
+
+msgid "with --cruft, expire objects older than this"
+msgstr "з --cruft видалити обʼєкти, старіші за цей термін"
+
+msgid "remove redundant packs, and run git-prune-packed"
+msgstr "видалити зайві пакунки і запустити git-prune-packed"
+
+msgid "pass --no-reuse-delta to git-pack-objects"
+msgstr "передати --no-reuse-delta до git-pack-objects"
+
+msgid "pass --no-reuse-object to git-pack-objects"
+msgstr "передати --no-reuse-object до git-pack-objects"
+
+msgid "do not run git-update-server-info"
+msgstr "не запускати git-update-server-info"
+
+msgid "pass --local to git-pack-objects"
+msgstr "передати --local до git-pack-objects"
+
+msgid "write bitmap index"
+msgstr "записати bitmap індекс"
+
+msgid "pass --delta-islands to git-pack-objects"
+msgstr "передати --delta-islands до git-pack-objects"
+
+msgid "with -A, do not loosen objects older than this"
+msgstr "з -A, не послабляти обʼєкти, старіші за це значення"
+
+msgid "with -a, repack unreachable objects"
+msgstr "з -a, перепакувати недоступні обʼєкти"
+
+msgid "size of the window used for delta compression"
+msgstr "розмір вікна, що використовується для дельта компресії"
+
+msgid "bytes"
+msgstr "байти"
+
+msgid "same as the above, but limit memory size instead of entries count"
+msgstr ""
+"те саме, що й вище, але обмежує розмір памʼяті замість кількості записів"
+
+msgid "limits the maximum delta depth"
+msgstr "обмежує максимальну глибину дельти"
+
+msgid "limits the maximum number of threads"
+msgstr "обмежує максимальну кількість потоків"
+
+msgid "maximum size of each packfile"
+msgstr "максимальний розмір кожного файла пакунка"
+
+msgid "repack objects in packs marked with .keep"
+msgstr "перепакувати обʼєкти в пакунках, позначених .keep"
+
+msgid "do not repack this pack"
+msgstr "не перепаковувати цей пакунок"
+
+msgid "find a geometric progression with factor <N>"
+msgstr "знайти геометричну прогресію з фактором <Н>"
+
+msgid "write a multi-pack index of the resulting packs"
+msgstr "записати multi-pack-index результуючих пакунків"
+
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "префікс для зберігання пакунка з обрізаними обʼєктами"
+
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "префікс для зберігання пакунка з відфільтрованими  обʼєктами"
+
+msgid "cannot delete packs in a precious-objects repo"
+msgstr "неможливо видалити пакунки в precious-objects сховищі"
+
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "опція \"%s\" може бути використана тільки разом з \"%s\""
+
+msgid "Nothing new to pack."
+msgstr "Немає нічого нового для пакування."
+
+#, c-format
+msgid "renaming pack to '%s' failed"
+msgstr "перейменування пакунка на \"%s\" завершилося невдало"
+
+#, c-format
+msgid "pack-objects did not write a '%s' file for pack %s-%s"
+msgstr "pack-objects не записав файл \"%s\" для пакунка %s-%s"
+
+#, c-format
+msgid "could not unlink: %s"
+msgstr "не вдалося видалити: %s"
+
+msgid "git replace [-f] <object> <replacement>"
+msgstr "git replace [-f] <обʼєкт> <заміна>"
+
+msgid "git replace [-f] --edit <object>"
+msgstr "git replace [-f] --edit <обʼєкт>"
+
+msgid "git replace [-f] --graft <commit> [<parent>...]"
+msgstr "git replace [-f] --graft <коміт> [<батько>...]"
+
+msgid "git replace -d <object>..."
+msgstr "git replace -d <обʼєкт>..."
+
+msgid "git replace [--format=<format>] [-l [<pattern>]]"
+msgstr "git replace [--format=<формат>] [-l [<шаблон>]]"
+
+#, c-format
+msgid ""
+"invalid replace format '%s'\n"
+"valid formats are 'short', 'medium' and 'long'"
+msgstr ""
+"невірний формат заміни \"%s\"\n"
+"допустимі формати \"short\", \"medium\" та \"long\""
+
+#, c-format
+msgid "replace ref '%s' not found"
+msgstr "заміна посилання \"%s\" не знайдена"
+
+#, c-format
+msgid "Deleted replace ref '%s'"
+msgstr "Видалена заміна посилання \"%s\""
+
+#, c-format
+msgid "'%s' is not a valid ref name"
+msgstr "\"%s\" не є припустимою назвою посилання"
+
+#, c-format
+msgid "replace ref '%s' already exists"
+msgstr "заміна посилання \"%s\" вже існує"
+
+#, c-format
+msgid ""
+"Objects must be of the same type.\n"
+"'%s' points to a replaced object of type '%s'\n"
+"while '%s' points to a replacement object of type '%s'."
+msgstr ""
+"Обʼєкти повинні бути одного типу.\n"
+"\"%s\" вказує на замінений обʼєкт типу \"%s\", \n"
+"тоді як \"%s\" вказує на обʼєкт заміни типу \"%s\"."
+
+#, c-format
+msgid "unable to open %s for writing"
+msgstr "не вдалося відкрити %s для запису"
+
+msgid "cat-file reported failure"
+msgstr "cat-file повідомила про збій"
+
+#, c-format
+msgid "unable to open %s for reading"
+msgstr "не вдалося відкрити %s для читання"
+
+msgid "unable to spawn mktree"
+msgstr "не вдалося породити mktree"
+
+msgid "unable to read from mktree"
+msgstr "не вдалося прочитати з mktree"
+
+msgid "mktree reported failure"
+msgstr "mktree повідомила про збій"
+
+msgid "mktree did not return an object name"
+msgstr "mktree не повернув назву обʼєкта"
+
+#, c-format
+msgid "unable to fstat %s"
+msgstr "не вдалося виконати fstat %s"
+
+msgid "unable to write object to database"
+msgstr "не вдалося записати обʼєкт до бази даних"
+
+#, c-format
+msgid "unable to get object type for %s"
+msgstr "не вдалося отримати тип обʼєкта для %s"
+
+msgid "editing object file failed"
+msgstr "редагування файла обʼєкта завершилося невдало"
+
+#, c-format
+msgid "new object is the same as the old one: '%s'"
+msgstr "новий обʼєкт такий самий, як і старий: \"%s\""
+
+#, c-format
+msgid "could not parse %s as a commit"
+msgstr "не вдалося розібрати %s як коміт"
+
+#, c-format
+msgid "bad mergetag in commit '%s'"
+msgstr "невірний mergetag в коміті \"%s\""
+
+#, c-format
+msgid "malformed mergetag in commit '%s'"
+msgstr "невірно сформований тег злиття у коміті \"%s\""
+
+#, c-format
+msgid ""
+"original commit '%s' contains mergetag '%s' that is discarded; use --edit "
+"instead of --graft"
+msgstr ""
+"початковий коміт \"%s\" містить тег злиття \"%s\", який було відкинуто; "
+"скористайтесь --edit замість --graft"
+
+#, c-format
+msgid "the original commit '%s' has a gpg signature"
+msgstr "оригінальний коміт \"%s\" має підпис gpg"
+
+msgid "the signature will be removed in the replacement commit!"
+msgstr "підпис буде видалено в коміті заміни!"
+
+#, c-format
+msgid "could not write replacement commit for: '%s'"
+msgstr "не вдалося записати коміт заміни для: \"%s\""
+
+#, c-format
+msgid "graft for '%s' unnecessary"
+msgstr "прищепа для \"%s\" не потрібна"
+
+#, c-format
+msgid "new commit is the same as the old one: '%s'"
+msgstr "новий коміт такий самий, як і старий: \"%s\""
+
+#, c-format
+msgid ""
+"could not convert the following graft(s):\n"
+"%s"
+msgstr ""
+"не вдалося конвертувати наступні прищепи:\n"
+"%s"
+
+msgid "list replace refs"
+msgstr "показати заміни посилань"
+
+msgid "delete replace refs"
+msgstr "видалити заміни посиланнь"
+
+msgid "edit existing object"
+msgstr "редагувати існуючий обʼєкт"
+
+msgid "change a commit's parents"
+msgstr "змінити батьків коміту"
+
+msgid "convert existing graft file"
+msgstr "конвертувати існуючий файл щеплення"
+
+msgid "replace the ref if it exists"
+msgstr "замінити посилання, якщо воно існує"
+
+msgid "do not pretty-print contents for --edit"
+msgstr "не прикрашати вивід вмісту для --edit"
+
+msgid "use this format"
+msgstr "використати цей формат"
+
+msgid "--format cannot be used when not listing"
+msgstr "--format не можна використовувати без list"
+
+msgid "-f only makes sense when writing a replacement"
+msgstr "-f має сенс тільки при записі заміни"
+
+msgid "--raw only makes sense with --edit"
+msgstr "--raw має сенс тільки з --edit"
+
+msgid "-d needs at least one argument"
+msgstr "-d потребує принаймні одного аргументу"
+
+msgid "bad number of arguments"
+msgstr "невірна кількість аргументів"
+
+msgid "-e needs exactly one argument"
+msgstr "-e потребує лишень один аргумент"
+
+msgid "-g needs at least one argument"
+msgstr "-g потребує принаймні одного аргументу"
+
+msgid "--convert-graft-file takes no argument"
+msgstr "--convert-graft-file не потребує аргументів"
+
+msgid "only one pattern can be given with -l"
+msgstr "тільки один шаблон може бути заданий з -l"
+
+msgid "need some commits to replay"
+msgstr "потрібні деякі комміти для відтворення"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto та --advance несумісні"
+
+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 ""
+"неможливо просунути посилання з декількома джерелами, тому що впорядкування "
+"буде нечітко визначеним"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr "неможливо неявно визначити, чи це операція --advance або --onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"неможливо просунути посилання з декількома джерельними гілками, тому що "
+"впорядкування буде нечітко визначеним"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "неможливо неявно визначити вірну базу для --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(ЕКСПЕРИМЕНТАЛЬНО!) git replay ([--contained] --onto <нова-база> | --advance "
+"<гілка>) <діапазон-ревізій>..."
+
+msgid "make replay advance given branch"
+msgstr "зробити відтворення з просуванням даної гілки"
+
+msgid "replay onto given commit"
+msgstr "відтворити на заданий коміт"
+
+msgid "advance all branches contained in revision-range"
+msgstr "просунути всі гілки, що містяться в діапазоні ревізій"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "опція --onto або --advance є обовʼязковою"
+
+#, 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\" буде примусово використано"
+
+msgid "error preparing revisions"
+msgstr "помилка при підготовці ревізій"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "відтворення до кореневого коміту поки що не підтримується!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "відтворення коммітів злиття поки що не підтримується!"
+
+msgid ""
+"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
+msgstr ""
+"git rerere [clear | forget <визначник-шляху>... | diff | status | remaining "
+"| gc]"
+
+msgid "register clean resolutions in index"
+msgstr "зареєструвати чисті вирішення в індексі"
+
+msgid "'git rerere forget' without paths is deprecated"
+msgstr "команда \"git rerere forget\" без шляхів застаріла"
+
+#, c-format
+msgid "unable to generate diff for '%s'"
+msgstr "не вдалося згенерувати diff для \"%s\""
+
+msgid ""
+"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
+msgstr ""
+"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
+
+msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..."
+msgstr "git reset [-q] [<деревоподібне-джерело>] [--] <визначник шляху>..."
+
+msgid ""
+"git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"
+msgstr ""
+"git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<деревоподібне-"
+"джерело>]"
+
+msgid "git reset --patch [<tree-ish>] [--] [<pathspec>...]"
+msgstr ""
+"git reset --patch [<деревоподібне-джерело>] [--] [<визначник шляху>...]"
+
+msgid "mixed"
+msgstr "змішане"
+
+msgid "soft"
+msgstr "м’яке"
+
+msgid "hard"
+msgstr "жорстке"
+
+msgid "merge"
+msgstr "злити"
+
+msgid "keep"
+msgstr "зберегти"
+
+msgid "You do not have a valid HEAD."
+msgstr "У вас немає дійсного HEAD."
+
+msgid "Failed to find tree of HEAD."
+msgstr "Не вдалося знайти дерево HEAD."
+
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Не вдалося знайти дерево %s."
+
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD зараз на %s"
+
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "Неможливо виконати %s скидання посеред злиття."
+
+msgid "be quiet, only report errors"
+msgstr "тихесенько, повідомляти лише про помилки"
+
+msgid "skip refreshing the index after reset"
+msgstr "пропустити оновлення індексу після скидання"
+
+msgid "reset HEAD and index"
+msgstr "скинути HEAD та індекс"
+
+msgid "reset only HEAD"
+msgstr "скинути тільки HEAD"
+
+msgid "reset HEAD, index and working tree"
+msgstr "скинути HEAD, індекс та робоче дерево"
+
+msgid "reset HEAD but keep local changes"
+msgstr "скинути HEAD, але зберегти локальні зміни"
+
+msgid "record only the fact that removed paths will be added later"
+msgstr "записати лише той факт, що вилучені шляхи будуть додані пізніше"
+
+#, c-format
+msgid "Failed to resolve '%s' as a valid revision."
+msgstr "Не вдалося розпізнати '%s' як припустиму ревізію."
+
+#, c-format
+msgid "Failed to resolve '%s' as a valid tree."
+msgstr "Не вдалося розпізнати '%s' як припустиме дерево."
+
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+"використання --mixed зі шляхами застаріло; використовуйте 'git reset -- "
+"<paths>' замість цього."
+
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Неможливо виконати %s скидання зі шляхами."
+
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "%s скидання не дозволяється у порожньому сховищі"
+
+msgid "Unstaged changes after reset:"
+msgstr "Неіндексовані зміни після скидання:"
+
+#, c-format
+msgid ""
+"It took %.2f seconds to refresh the index after reset.  You can use\n"
+"'--no-refresh' to avoid this."
+msgstr ""
+"Оновлення індексу після скидання зайняло %.2f секунд.  Ви можете "
+"скористатися параметром\n"
+"'--no-refresh', щоб уникнути цього."
+
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Не вдалося скинути індексний файл до ревізії '%s'."
+
+msgid "Could not write new index file."
+msgstr "Не вдалося записати новий індексний файл."
+
+#, c-format
+msgid "unable to get disk usage of %s"
+msgstr "не вдалося отримати дані про використання диска %s"
+
+#, c-format
+msgid "invalid value for '%s': '%s', the only allowed format is '%s'"
+msgstr ""
+"неприпустиме значення для \"%s\": \"%s\", єдиним допустимим форматом є \"%s\""
+
+msgid "rev-list does not support display of notes"
+msgstr "rev-list не підтримує відображення нотаток"
+
+#, c-format
+msgid "marked counting and '%s' cannot be used together"
+msgstr "позначений підрахунок та \"%s\" не можна використовувати разом"
+
+msgid "git rev-parse --parseopt [<options>] -- [<args>...]"
+msgstr "git rev-parse --parseopt [<опції>] -- [<аргументи>...]"
+
+msgid "keep the `--` passed as an arg"
+msgstr "залишити \"--\" переданим як аргумент"
+
+msgid "stop parsing after the first non-option argument"
+msgstr "зупинити розбір після першого неопціонального аргументу"
+
+msgid "output in stuck long form"
+msgstr "виведення в застряглій довгій формі"
+
+msgid "premature end of input"
+msgstr "передчасне закінчення вхідних даних"
+
+msgid "no usage string given before the `--' separator"
+msgstr "не вказана строка використання перед \"--\" розділювачем"
+
+msgid "missing opt-spec before option flags"
+msgstr "відсутній opt-spec перед прапорцями опції"
+
+msgid "Needed a single revision"
+msgstr "Необхідна одна ревізія"
+
+msgid ""
+"git rev-parse --parseopt [<options>] -- [<args>...]\n"
+"   or: git rev-parse --sq-quote [<arg>...]\n"
+"   or: git rev-parse [<options>] [<arg>...]\n"
+"\n"
+"Run \"git rev-parse --parseopt -h\" for more information on the first usage."
+msgstr ""
+"git rev-parse --parseopt [<опції>] -- [<аргументи>...]\n"
+"   або: git rev-parse --sq-quote [<аргумент>...]\n"
+"   або: git rev-parse [<опції>] [<аргумент>...]\n"
+"\n"
+"Запустіть \"git rev-parse --parseopt -h\" для отримання додаткової "
+"інформації про перше використання."
+
+msgid "--resolve-git-dir requires an argument"
+msgstr "--resolve-git-dir потребує аргументу"
+
+#, c-format
+msgid "not a gitdir '%s'"
+msgstr "не є git директорією \"%s\""
+
+msgid "--git-path requires an argument"
+msgstr "--git-path потребує аргументу"
+
+msgid "-n requires an argument"
+msgstr "-n потребує аргументу"
+
+msgid "--path-format requires an argument"
+msgstr "--path-format потребує аргументу"
+
+#, c-format
+msgid "unknown argument to --path-format: %s"
+msgstr "невідомий аргумент до --path-format: %s"
+
+msgid "--default requires an argument"
+msgstr "--default потребує аргументу"
+
+msgid "--prefix requires an argument"
+msgstr "--prefix потребує аргументу"
+
+#, c-format
+msgid "unknown mode for --abbrev-ref: %s"
+msgstr "невідомий режим для --abbrev-ref: %s"
+
+msgid "this operation must be run in a work tree"
+msgstr "цю операцію треба виконувати в робочому дереві"
+
+msgid "Could not read the index"
+msgstr "Не вдалося прочитати індекс"
+
+#, c-format
+msgid "unknown mode for --show-object-format: %s"
+msgstr "невідомий режим для --show-object-format: %s"
+
+msgid ""
+"git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] "
+"<commit>..."
+msgstr ""
+"git revert [--[no-]edit] [-n] [-m <номер-батька>] [-s] [-S[<ідентифікатор-"
+"ключа>]] <коміт>..."
+
+msgid "git revert (--continue | --skip | --abort | --quit)"
+msgstr "git revert (--continue | --skip | --abort | --quit)"
+
+msgid ""
+"git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n"
+"                [-S[<keyid>]] <commit>..."
+msgstr ""
+"git cherry-pick [--edit] [-n] [-m <номер-батька>] [-s] [-x] [--ff].\n"
+"                [-S[<ідентифікатор-ключа>]] <коміт>..."
+
+msgid "git cherry-pick (--continue | --skip | --abort | --quit)"
+msgstr "git cherry-pick (--continue | --skip | --abort | --quit)"
+
+#, c-format
+msgid "option `%s' expects a number greater than zero"
+msgstr "опція \"%s\" очікує число більше нуля"
+
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s неможливо використовувати з %s"
+
+msgid "end revert or cherry-pick sequence"
+msgstr "завершити процес вивертання або висмикування"
+
+msgid "resume revert or cherry-pick sequence"
+msgstr "відновити процес вивертання або висмикування"
+
+msgid "cancel revert or cherry-pick sequence"
+msgstr "скасувати процес вивертання або висмикування"
+
+msgid "skip current commit and continue"
+msgstr "пропустити поточний коміт і продовжити"
+
+msgid "don't automatically commit"
+msgstr "не комітити автоматично"
+
+msgid "edit the commit message"
+msgstr "редагувати допис до коміту"
+
+msgid "parent-number"
+msgstr "номер батька"
+
+msgid "select mainline parent"
+msgstr "вибрати основну батьківську лінію"
+
+msgid "merge strategy"
+msgstr "стратегія злиття"
+
+msgid "option for merge strategy"
+msgstr "опція для стратегії злиття"
+
+msgid "append commit name"
+msgstr "додати назву коміту"
+
+msgid "preserve initially empty commits"
+msgstr "зберігати первинно порожні коміти"
+
+msgid "allow commits with empty messages"
+msgstr "дозволити коміти з порожніми дописами"
+
+msgid "keep redundant, empty commits"
+msgstr "зберігати зайві порожні коміти"
+
+msgid "use the 'reference' format to refer to commits"
+msgstr "використовувати \"reference\" формат для посилань на коміти"
+
+msgid "revert failed"
+msgstr "вивертання не вдалося"
+
+msgid "cherry-pick failed"
+msgstr "висмикування не вдалося"
+
+msgid ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n"
+"       [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+"       [--] [<pathspec>...]"
+msgstr ""
+"git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--ignore-"
+"unmatch\n"
+"       [--quiet] [--pathspec-from-file=<файл> [--pathspec-file-nul]]\n"
+"       [--] [<визначник-шляху>...]"
+
+msgid ""
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] ""
+"наступний файл має доданий до індексу вміст, відмінний як від файла\n"
+"так й від HEAD:"
+msgstr[1] ""
+"наступні файли мають доданий до індексу вміст, відмінний як від файла\n"
+"так й від HEAD:"
+msgstr[2] ""
+"наступні файли мають доданий до індексу вміст, відмінний як від файла\n"
+"так й від HEAD:"
+
+msgid ""
+"\n"
+"(use -f to force removal)"
+msgstr ""
+"\n"
+"(використовуйте -f щоб видалити примусово)"
+
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] "у наступному файлі є зміни, додані до індексу:"
+msgstr[1] "у наступних файлах є зміни, додані до індексу:"
+msgstr[2] "у наступних файлах є зміни, додані до індексу:"
+
+msgid ""
+"\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"\n"
+"(використовуйте --cached, щоб зберегти файл, або -f, щоб видалити примусово)"
+
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] "наступний файл має локальні зміни:"
+msgstr[1] "наступні файли мають локальні зміни:"
+msgstr[2] "наступних файлів мають локальні зміни:"
+
+msgid "do not list removed files"
+msgstr "не показувати видалені файли"
+
+msgid "only remove from the index"
+msgstr "видалити тільки з індексу"
+
+msgid "override the up-to-date check"
+msgstr "перевизначити перевірку на актуальність"
+
+msgid "allow recursive removal"
+msgstr "дозволити рекурсивне видалення"
+
+msgid "exit with a zero status even if nothing matched"
+msgstr "виходити з нульовим статусом, навіть якщо нічого не збігається"
+
+msgid "No pathspec was given. Which files should I remove?"
+msgstr "Не було вказано визначник шляху. Які файли слід видалити?"
+
+msgid "please stage your changes to .gitmodules or stash them to proceed"
+msgstr ""
+"будь ласка, додайте ваші зміни до .gitmodules або схову, щоб продовжити"
+
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "не видалено \"%s\" рекурсивно без -r"
+
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: не вдалося видалити %s"
+
+msgid ""
+"git send-pack [--mirror] [--dry-run] [--force]\n"
+"              [--receive-pack=<git-receive-pack>]\n"
+"              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
+"              [<host>:]<directory> (--all | <ref>...)"
+msgstr ""
+"git send-pack [--mirror] [--dry-run] [--force]\n"
+"              [--receive-pack=<git-пакунок-отримання>]\n"
+"              [--verbose] [--thin] [--atomic]\n"
+"              [--[no-]signed | --signed=(true|false|if-asked)]\n"
+"              [<хост>:]<директорія> (--all | <посилання>...)"
+
+msgid "remote name"
+msgstr "віддалена назва"
+
+msgid "push all refs"
+msgstr "надіслати всі посилання"
+
+msgid "use stateless RPC protocol"
+msgstr "використовувати протокол RPC без збереження стану"
+
+msgid "read refs from stdin"
+msgstr "прочитати посилання з stdin"
+
+msgid "print status from remote helper"
+msgstr "вивести статус з віддаленого помічника"
+
+msgid "git shortlog [<options>] [<revision-range>] [[--] <path>...]"
+msgstr "git shortlog [<опції>] [<діапазон-ревізій>] [[--] <шлях>...]"
+
+msgid "git log --pretty=short | git shortlog [<options>]"
+msgstr "git log --pretty=short | git shortlog [<опції>]"
+
+msgid "using multiple --group options with stdin is not supported"
+msgstr "використання кількох --group опцій з stdin не підтримується"
+
+#, c-format
+msgid "using %s with stdin is not supported"
+msgstr "використання %s з stdin не підтримується"
+
+#, c-format
+msgid "unknown group type: %s"
+msgstr "невідомий тип групи: %s"
+
+msgid "group by committer rather than author"
+msgstr "групувати за комітером, а не за автором"
+
+msgid "sort output according to the number of commits per author"
+msgstr "сортувати виведення за кількістю комітів на автора"
+
+msgid "suppress commit descriptions, only provides commit count"
+msgstr "приховати описи комітів, показати лише кількість комітів"
+
+msgid "show the email address of each author"
+msgstr "показати адресу електронної пошти кожного автора"
+
+msgid "<w>[,<i1>[,<i2>]]"
+msgstr "<w>[,<i1>[,<i2>]]"
+
+msgid "linewrap output"
+msgstr "обгортати рядки виводу"
+
+msgid "field"
+msgstr "поле"
+
+msgid "group by field"
+msgstr "групувати за полем"
+
+msgid "too many arguments given outside repository"
+msgstr "занадто багато аргументів надано поза сховищем"
+
+msgid ""
+"git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
+"                [--current] [--color[=<when>] | --no-color] [--sparse]\n"
+"                [--more=<n> | --list | --independent | --merge-base]\n"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<rev> | <glob>)...]"
+msgstr ""
+"git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
+"                [--current] [--color[=<коли>] | --no-color] [--sparse]\n"
+"                [--more=<н> | --list | --independent | --merge-base]\n"
+"                [--no-name | --sha1-name] [--topics]\n"
+"                [(<ревізія> | <глоб>)...]"
+
+msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"
+msgstr "git show-branch (-g | --reflog)[=<н>[,<база>]] [--list] [<посилання>]"
+
+#, c-format
+msgid "ignoring %s; cannot handle more than %d ref"
+msgid_plural "ignoring %s; cannot handle more than %d refs"
+msgstr[0] "ігнорування %s; неможливо обробити більше %d посилання"
+msgstr[1] "ігнорування %s; неможливо обробити більше %d посилань"
+msgstr[2] "ігнорування %s; неможливо обробити більше %d посилань"
+
+#, c-format
+msgid "no matching refs with %s"
+msgstr "немає співпадаючих посилань з %s"
+
+msgid "show remote-tracking and local branches"
+msgstr "показати віддалено відстежувані та локальні гілки"
+
+msgid "show remote-tracking branches"
+msgstr "показати віддалено відстежувані гілки"
+
+msgid "color '*!+-' corresponding to the branch"
+msgstr "колір \"*!+-\" відповідно на гілку"
+
+msgid "show <n> more commits after the common ancestor"
+msgstr "показати ще <н> комітів після спільного предка"
+
+msgid "synonym to more=-1"
+msgstr "синонім до more=-1"
+
+msgid "suppress naming strings"
+msgstr "не показувати назву"
+
+msgid "include the current branch"
+msgstr "включити поточну гілку"
+
+msgid "name commits with their object names"
+msgstr "називати коміти за іменами обʼєктів"
+
+msgid "show possible merge bases"
+msgstr "показати можливі бази злиття"
+
+msgid "show refs unreachable from any other ref"
+msgstr "показати посилання, недосяжні з жодного іншого посилання"
+
+msgid "show commits in topological order"
+msgstr "показати коміти в топологічному порядку"
+
+msgid "show only commits not on the first branch"
+msgstr "показати лише коміти не з найпершої гілки"
+
+msgid "show merges reachable from only one tip"
+msgstr "показати злиття досяжні лише з однієї верхівки"
+
+msgid "topologically sort, maintaining date order where possible"
+msgstr "сортувати топологічно, зберігаючи порядок дат, якщо це можливо"
+
+msgid "<n>[,<base>]"
+msgstr "<н>[,<база>]"
+
+msgid "show <n> most recent ref-log entries starting at base"
+msgstr "показати <н> останніх записів лога посилань, починаючи з бази"
+
+msgid "no branches given, and HEAD is not valid"
+msgstr "не надано гілок, і HEAD не є дійсним"
+
+msgid "--reflog option needs one branch name"
+msgstr "--reflog потребує одну назву гілки"
+
+#, c-format
+msgid "only %d entry can be shown at one time."
+msgid_plural "only %d entries can be shown at one time."
+msgstr[0] "одночасно може бути показаний лише %d запис."
+msgstr[1] "одночасно можуть бути показані лише %d записи."
+msgstr[2] "одночасно можуть бути показані лише %d записів."
+
+#, c-format
+msgid "no such ref %s"
+msgstr "немає такого посилання %s"
+
+#, c-format
+msgid "cannot handle more than %d rev."
+msgid_plural "cannot handle more than %d revs."
+msgstr[0] "неможливо обробити більше %d ревізії."
+msgstr[1] "неможливо обробити більше %d ревізій."
+msgstr[2] "неможливо обробити більше %d ревізій."
+
+#, c-format
+msgid "'%s' is not a valid ref."
+msgstr "\"%s\" не є припустимим посиланням."
+
+#, c-format
+msgid "cannot find commit %s (%s)"
+msgstr "не вдалося знайти коміт %s (%s)"
+
+msgid "hash-algorithm"
+msgstr "хеш-алгоритм"
+
+msgid "Unknown hash algorithm"
+msgstr "Невідомий хеш-алгоритм"
+
+msgid ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
+"             [--heads] [--] [<pattern>...]"
+msgstr ""
+"git show-ref [--head] [-d | --dereference]\n"
+"             [-s | --hash[=<н>]] [--abbrev[=<н>]] [--tags]\n"
+"             [--heads] [--] [<шаблон>...]"
+
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<н>]] [--abbrev[=<н>]]\n"
+"             [--] [<посилання>...]"
+
+msgid "git show-ref --exclude-existing[=<pattern>]"
+msgstr "git show-ref --exclude-existing[=<шаблон>]"
+
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <посилання>"
+
+msgid "reference does not exist"
+msgstr "посилання не існує"
+
+msgid "failed to look up reference"
+msgstr "не вдалося знайти посилання"
+
+msgid "only show tags (can be combined with heads)"
+msgstr "показати тільки теги (можна комбінувати з верхівками)"
+
+msgid "only show heads (can be combined with tags)"
+msgstr "показати тільки верхівки (можна комбінувати з тегами)"
+
+msgid "check for reference existence without resolving"
+msgstr "перевіряти наявність посилання без розвʼязання"
+
+msgid "stricter reference checking, requires exact ref path"
+msgstr "більш сувора перевірка посилань, потребує точного шляху до посилання"
+
+msgid "show the HEAD reference, even if it would be filtered out"
+msgstr "показати HEAD посилання, навіть якщо воно було б відфільтроване"
+
+msgid "dereference tags into object IDs"
+msgstr "розіменувати теги в ідентифікатори обʼєктів"
+
+msgid "only show SHA1 hash using <n> digits"
+msgstr "показати тільки SHA1 хеш з використанням <н> цифр"
+
+msgid "do not print results to stdout (useful with --verify)"
+msgstr "не виводити результати у stdout (корисно з --verify)"
+
+msgid "show refs from stdin that aren't in local repository"
+msgstr "показати посилання з stdin, яких немає в локальному сховищі"
+
+msgid ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
+msgstr ""
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<опції>]"
+
+msgid "this worktree is not sparse"
+msgstr "це робоче дерево не є розрідженим"
+
+msgid "this worktree is not sparse (sparse-checkout file may not exist)"
+msgstr ""
+"це робоче дерево не є розрідженим (файл розрідженого переходу може не "
+"існувати)"
+
+#, c-format
+msgid ""
+"directory '%s' contains untracked files, but is not in the sparse-checkout "
+"cone"
+msgstr ""
+"директорія \"%s\" містить невідстежувані файли, але не входить до конуса "
+"розрідженого переходу"
+
+#, c-format
+msgid "failed to remove directory '%s'"
+msgstr "не вдалося видалити директорію \"%s\""
+
+msgid "failed to create directory for sparse-checkout file"
+msgstr "не вдалося створити директорію для файлу розрідженого переходу"
+
+msgid "failed to initialize worktree config"
+msgstr "не вдалося ініціалізувати конфігурацію робочого дерева"
+
+msgid "failed to modify sparse-index config"
+msgstr "не вдалося змінити sparse-index конфігурацію"
+
+msgid "initialize the sparse-checkout in cone mode"
+msgstr "ініціалізувати розріджений перехід в режимі конуса"
+
+msgid "toggle the use of a sparse index"
+msgstr "перемкнути використання розрідженого індексу"
+
+#, c-format
+msgid "unable to create leading directories of %s"
+msgstr "не вдалося створити провідні директорії %s"
+
+#, c-format
+msgid "failed to open '%s'"
+msgstr "не вдалося відкрити \"%s\""
+
+#, c-format
+msgid "could not normalize path %s"
+msgstr "не вдалося нормалізувати шлях %s"
+
+#, c-format
+msgid "unable to unquote C-style string '%s'"
+msgstr "неможливо прибрати лапки з C-style строки \"%s\""
+
+msgid "unable to load existing sparse-checkout patterns"
+msgstr "не вдалося завантажити існуючі шаблони розріджених переходів"
+
+msgid "existing sparse-checkout patterns do not use cone mode"
+msgstr "існуючі шаблони розрідженого переходу не використовують режим конуса"
+
+msgid "please run from the toplevel directory in non-cone mode"
+msgstr ""
+"будь ласка, запускайте з директорії верхнього рівня в не конусномі режимі"
+
+msgid "specify directories rather than patterns (no leading slash)"
+msgstr "вказати директорії замість шаблонів (без першого слешу)"
+
+msgid ""
+"specify directories rather than patterns.  If your directory starts with a "
+"'!', pass --skip-checks"
+msgstr ""
+"вказати директорії замість шаблонів.  Якщо ваша директорія починається з \"!"
+"\", додайте --skip-checks"
+
+msgid ""
+"specify directories rather than patterns.  If your directory really has any "
+"of '*?[]\\' in it, pass --skip-checks"
+msgstr ""
+"вказати директорії замість шаблонів.  Якщо в назві вашої директорії дійсно є "
+"символи \"*?[]\\\", додайте --skip-checks"
+
+#, c-format
+msgid ""
+"'%s' is not a directory; to treat it as a directory anyway, rerun with --"
+"skip-checks"
+msgstr ""
+"\"%s\" не є директорією; щоб вважати його директорією, повторіть запуск з --"
+"skip-checks"
+
+#, c-format
+msgid ""
+"pass a leading slash before paths such as '%s' if you want a single file "
+"(see NON-CONE PROBLEMS in the git-sparse-checkout manual)."
+msgstr ""
+"додайте перший слеш перед такими шляхами, як \"%s\", якщо вам потрібен один "
+"файл (див. розділ НЕ КОНУСНІ ПРОБЛЕМИ у посібнику з git-sparse-checkout)."
+
+msgid "git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"
+msgstr "git sparse-checkout add [--skip-checks] (--stdin | <шаблони>)"
+
+msgid ""
+"skip some sanity checks on the given paths that might give false positives"
+msgstr ""
+"пропустити деякі перевірки на заданих шляхах, які можуть давати хибні "
+"результати"
+
+msgid "read patterns from standard in"
+msgstr "читати шаблони зі стандартного вводу"
+
+msgid "no sparse-checkout to add to"
+msgstr "немає розрідженого переходу для додавання"
+
+msgid ""
+"git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] "
+"(--stdin | <patterns>)"
+msgstr ""
+"git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] "
+"(--stdin | <шаблони>)"
+
+msgid "must be in a sparse-checkout to reapply sparsity patterns"
+msgstr ""
+"має перебувати в розрідженому переході для повторного застосування шаблонів "
+"розрідженості"
+
+msgid "error while refreshing working directory"
+msgstr "помилка під час оновлення робочої директорії"
+
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <файл>]"
+
+msgid "terminate input and output files by a NUL character"
+msgstr "завершити вхідні та вихідні файли символом NUL"
+
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"при використанні з --rules-file інтерпретувати шаблони як шаблони конусного "
+"режиму"
+
+msgid "use patterns in <file> instead of the current ones."
+msgstr "використовувати шаблони з <file> замість поточних."
+
+msgid "git stash list [<log-options>]"
+msgstr "git stash list [<лог-опції>]"
+
+msgid ""
+"git stash show [-u | --include-untracked | --only-untracked] [<diff-"
+"options>] [<stash>]"
+msgstr ""
+"git stash show [-u | --include-untracked | --only-untracked] [<опції-"
+"різниці>] [<схов>]"
+
+msgid "git stash drop [-q | --quiet] [<stash>]"
+msgstr "git stash drop [-q | --quiet] [<схов>]"
+
+msgid "git stash pop [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash pop [--index] [-q | --quiet] [<схов>]"
+
+msgid "git stash apply [--index] [-q | --quiet] [<stash>]"
+msgstr "git stash apply [--index] [-q | --quiet] [<схов>]"
+
+msgid "git stash branch <branchname> [<stash>]"
+msgstr "git stash branch <назва-гілки> [<схов>]"
+
+msgid "git stash store [(-m | --message) <message>] [-q | --quiet] <commit>"
+msgstr "git stash store [(-m | --message) <допис>] [-q | --quiet] <коміт>"
+
+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"
+"          [--] [<визначник-шляху>...]]"
+
+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 | "
+"--quiet]\n"
+"          [-u | --include-untracked] [-a | --all] [<допис>]"
+
+msgid "git stash create [<message>]"
+msgstr "git stash create [<допис>]"
+
+#, c-format
+msgid "'%s' is not a stash-like commit"
+msgstr "\"%s\" не є сховоподібним комітом"
+
+#, c-format
+msgid "Too many revisions specified:%s"
+msgstr "Вказано забагато ревізій:%s"
+
+msgid "No stash entries found."
+msgstr "Записи схова не знайдені."
+
+#, c-format
+msgid "%s is not a valid reference"
+msgstr "%s не є припустимим посиланням"
+
+msgid "git stash clear with arguments is unimplemented"
+msgstr "git stash clear з аргументами не реалізовано"
+
+#, c-format
+msgid ""
+"WARNING: Untracked file in way of tracked file!  Renaming\n"
+"            %s -> %s\n"
+"         to make room.\n"
+msgstr ""
+"ПОПЕРЕДЖЕННЯ: Невідстежуваний файл на шляху відстежуваного!  Перейменування\n"
+"            %s -> %s\n"
+"         щоб звільнити місце.\n"
+
+msgid "cannot apply a stash in the middle of a merge"
+msgstr "неможливо застосувати схов посеред злиття"
+
+#, c-format
+msgid "could not generate diff %s^!."
+msgstr "не вдалося згенерувати різницю %s^!."
+
+msgid "conflicts in index. Try without --index."
+msgstr "конфлікти в індексі. Спробуйте без --index."
+
+msgid "could not save index tree"
+msgstr "не вдалося зберегти дерево індекса"
+
+#, c-format
+msgid "Merging %s with %s"
+msgstr "Злиття %s з %s"
+
+msgid "Index was not unstashed."
+msgstr "Індекс не було вилучено зі схову."
+
+msgid "could not restore untracked files from stash"
+msgstr "не вдалося відновити невідстежувані файли зі схову"
+
+msgid "attempt to recreate the index"
+msgstr "спроба відтворити індекс"
+
+#, c-format
+msgid "Dropped %s (%s)"
+msgstr "Скинуто %s (%s)"
+
+#, c-format
+msgid "%s: Could not drop stash entry"
+msgstr "%s: Не вдалося скинути запис схову"
+
+#, c-format
+msgid "'%s' is not a stash reference"
+msgstr "\"%s\" не є посиланням схова"
+
+msgid "The stash entry is kept in case you need it again."
+msgstr "Запис схову збережено на випадок, якщо він вам знову знадобиться."
+
+msgid "No branch name specified"
+msgstr "Не вказана назва гілки"
+
+msgid "failed to parse tree"
+msgstr "не вдалося розібрати дерево"
+
+msgid "failed to unpack trees"
+msgstr "не вдалося розпакувати дерева"
+
+msgid "include untracked files in the stash"
+msgstr "в тому числі невідстежувані файли схову"
+
+msgid "only show untracked files in the stash"
+msgstr "показувати тільки невідстежувані файли схову"
+
+#, c-format
+msgid "Cannot update %s with %s"
+msgstr "Неможливо оновити %s з %s"
+
+msgid "stash message"
+msgstr "допис до запису схова"
+
+msgid "\"git stash store\" requires one <commit> argument"
+msgstr "\"git stash store\" потребує одного <коміт> аргументу"
+
+msgid "No staged changes"
+msgstr "Немає індексованих змін"
+
+msgid "No changes selected"
+msgstr "Не обрано жодних змін"
+
+msgid "You do not have the initial commit yet"
+msgstr "У вас ще немає початкового коміту"
+
+msgid "Cannot save the current index state"
+msgstr "Неможливо зберегти поточний стан індексу"
+
+msgid "Cannot save the untracked files"
+msgstr "Неможливо зберегти невідстежувані файли"
+
+msgid "Cannot save the current worktree state"
+msgstr "Не вдалося зберегти поточний стан робочого дерева"
+
+msgid "Cannot save the current staged state"
+msgstr "Неможливо зберегти поточний індексований стан"
+
+msgid "Cannot record working tree state"
+msgstr "Неможливо записати стан робочого дерева"
+
+msgid "Can't use --patch and --include-untracked or --all at the same time"
+msgstr ""
+"Не можна використовувати --patch і --include-untracked або --all одночасно"
+
+msgid "Can't use --staged and --include-untracked or --all at the same time"
+msgstr ""
+"Не можна використовувати --staged і --include-untracked або --all одночасно"
+
+msgid "Did you forget to 'git add'?"
+msgstr "Ви забули \"git add\"?"
+
+msgid "No local changes to save"
+msgstr "Немає локальних змін для збереження"
+
+msgid "Cannot initialize stash"
+msgstr "Неможливо ініціалізувати схов"
+
+msgid "Cannot save the current status"
+msgstr "Неможливо зберегти поточний стан"
+
+#, c-format
+msgid "Saved working directory and index state %s"
+msgstr "Збережено робочу директорію та стан індексу %s"
+
+msgid "Cannot remove worktree changes"
+msgstr "Неможливо видалити зміни робочого дерева"
+
+msgid "keep index"
+msgstr "зберегти індекс"
+
+msgid "stash staged changes only"
+msgstr "додати до схову тільки індексовані зміни"
+
+msgid "stash in patch mode"
+msgstr "додати до схову у режимі латання"
+
+msgid "quiet mode"
+msgstr "тихий режим"
+
+msgid "include untracked files in stash"
+msgstr "в тому числі невідстежувані файли схову"
+
+msgid "include ignore files"
+msgstr "в тому числи файли ігнорування"
+
+msgid "skip and remove all lines starting with comment character"
+msgstr "пропустити та видалити всі рядки, що починаються з символу коментаря"
+
+msgid "prepend comment character and space to each line"
+msgstr "додати символ коментаря та пробіл до кожного рядка"
+
+#, c-format
+msgid "Expecting a full ref name, got %s"
+msgstr "Очікувалась повна назва посилання, отримано %s"
+
+#, c-format
+msgid "could not get a repository handle for submodule '%s'"
+msgstr "не вдалося отримати обʼєкт сховища для підмодуля \"%s\""
+
+#, c-format
+msgid ""
+"could not look up configuration '%s'. Assuming this repository is its own "
+"authoritative upstream."
+msgstr ""
+"не вдалося знайти конфігурацію \"%s\". Мабуть, це сховище має власне "
+"першоджерельне сховище."
+
+#, c-format
+msgid "No url found for submodule path '%s' in .gitmodules"
+msgstr "Не знайдено URL для шляху підмодуля \"%s\" у .gitmodules"
+
+#, c-format
+msgid "Entering '%s'\n"
+msgstr "Введення \"%s\"\n"
+
+#, c-format
+msgid ""
+"run_command returned non-zero status for %s\n"
+"."
+msgstr ""
+"run_command повернула ненульовий статус для %s\n"
+"."
+
+#, c-format
+msgid ""
+"run_command returned non-zero status while recursing in the nested "
+"submodules of %s\n"
+"."
+msgstr ""
+"run_command повернула ненульовий статус під час рекурсії у вкладених "
+"підмодулях %s\n"
+"."
+
+msgid "suppress output of entering each submodule command"
+msgstr "приховати вивід введення кожної команди підмодуля"
+
+msgid "recurse into nested submodules"
+msgstr "рекурсивно у вкладених підмодулях"
+
+msgid "git submodule foreach [--quiet] [--recursive] [--] <command>"
+msgstr "git submodule foreach [--quiet] [--recursive] [--] <команда>"
+
+#, c-format
+msgid "Failed to register url for submodule path '%s'"
+msgstr "Не вдалося зареєструвати url для підмодуля за шляхом \"%s\""
+
+#, c-format
+msgid "Submodule '%s' (%s) registered for path '%s'\n"
+msgstr "Підмодуль \"%s\" (%s) зареєстровано для шляху \"%s\"\n"
+
+#, c-format
+msgid "warning: command update mode suggested for submodule '%s'\n"
+msgstr ""
+"попередження: запропоновано режим оновлення команд для підмодуля \"%s\"\n"
+
+#, c-format
+msgid "Failed to register update mode for submodule path '%s'"
+msgstr ""
+"Не вдалося зареєструвати режим оновлення для підмодуля за шляхом \"%s\""
+
+msgid "suppress output for initializing a submodule"
+msgstr "приховати вивід для ініціалізації підмодуля"
+
+msgid "git submodule init [<options>] [<path>]"
+msgstr "git submodule init [<опції>] [<шлях>]"
+
+#, c-format
+msgid "no submodule mapping found in .gitmodules for path '%s'"
+msgstr ""
+"відповідне відображення підмодуля не знайдено у .gitmodules для шляху \"%s\""
+
+#, c-format
+msgid "could not resolve HEAD ref inside the submodule '%s'"
+msgstr "не вдалося розвʼязати HEAD посилання всередині підмодуля \"%s\""
+
+#, c-format
+msgid "failed to recurse into submodule '%s'"
+msgstr "не вдалося обробити рекурсивно підмодуль \"%s\""
+
+msgid "suppress submodule status output"
+msgstr "приховати вивід стану підмодуля"
+
+msgid ""
+"use commit stored in the index instead of the one stored in the submodule "
+"HEAD"
+msgstr ""
+"використати коміт, що зберігається в індексі, замість того, що зберігається "
+"в HEAD підмодуля"
+
+msgid "git submodule status [--quiet] [--cached] [--recursive] [<path>...]"
+msgstr "git submodule status [--quiet] [--cached] [--recursive] [<шлях>...]"
+
+#, c-format
+msgid "* %s %s(blob)->%s(submodule)"
+msgstr "* %s %s(blob)->%s(підмодуль)"
+
+#, c-format
+msgid "* %s %s(submodule)->%s(blob)"
+msgstr "* %s %s(підмодуль)->%s(blob)"
+
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#, c-format
+msgid "couldn't hash object from '%s'"
+msgstr "не вдалося хешувати обʼєкт з \"%s\""
+
+#, c-format
+msgid "unexpected mode %o\n"
+msgstr "неочікуваний режим %o\n"
+
+msgid "use the commit stored in the index instead of the submodule HEAD"
+msgstr "використати коміт, збережений в індексі, замість підмодуля HEAD"
+
+msgid "compare the commit in the index with that in the submodule HEAD"
+msgstr "порівняти коміт в індексі з комітом у HEAD підмодуля"
+
+msgid "skip submodules with 'ignore_config' value set to 'all'"
+msgstr ""
+"пропускати підмодулі зі значенням \"ignore_config\" встановленим у \"all\""
+
+msgid "limit the summary size"
+msgstr "обмежити розмір підсумку"
+
+msgid "git submodule summary [<options>] [<commit>] [--] [<path>]"
+msgstr "git summary submodule [<опції>] [<коміт>] [--] [<шлях>]"
+
+msgid "could not fetch a revision for HEAD"
+msgstr "не вдалося отримати ревізію для HEAD"
+
+#, c-format
+msgid "Synchronizing submodule url for '%s'\n"
+msgstr "Синхронізація url підмодуля для \"%s\"\n"
+
+#, c-format
+msgid "failed to register url for submodule path '%s'"
+msgstr "не вдалося зареєструвати url для підмодуля за шляхом \"%s\""
+
+#, c-format
+msgid "failed to update remote for submodule '%s'"
+msgstr "не вдалося оновити віддалене призначення для підмодуля \"%s\""
+
+msgid "suppress output of synchronizing submodule url"
+msgstr "приховати вивід процесу синхронізації url-адреси підмодуля"
+
+msgid "git submodule sync [--quiet] [--recursive] [<path>]"
+msgstr "git submodule sync [--quiet] [--recursive] [<шлях>]"
+
+#, c-format
+msgid ""
+"Submodule work tree '%s' contains a .git directory. This will be replaced "
+"with a .git file by using absorbgitdirs."
+msgstr ""
+"Робоче дерево підмодуля \"%s\" містить директорію .git. Її буде замінено на ."
+"git файл за допомогою absorbgitdirs."
+
+#, c-format
+msgid ""
+"Submodule work tree '%s' contains local modifications; use '-f' to discard "
+"them"
+msgstr ""
+"Робоче дерево підмодуля \"%s\" містить локальні модифікації; скористайтесь "
+"\"-f\", щоб скасувати їх"
+
+#, c-format
+msgid "Cleared directory '%s'\n"
+msgstr "Очищено директорію \"%s\"\n"
+
+#, c-format
+msgid "Could not remove submodule work tree '%s'\n"
+msgstr "Не вдалося видалити робоче дерево підмодуля \"%s\"\n"
+
+#, c-format
+msgid "could not create empty submodule directory %s"
+msgstr "не вдалося створити порожню директорію підмодуля %s"
+
+#, c-format
+msgid "Submodule '%s' (%s) unregistered for path '%s'\n"
+msgstr "Підмодуль \"%s\" (%s) не зареєстровано за шляхом \"%s\"\n"
+
+msgid "remove submodule working trees even if they contain local changes"
+msgstr ""
+"видалити робочі дерева підмодулів, навіть якщо вони містять локальні зміни"
+
+msgid "unregister all submodules"
+msgstr "скасувати реєстрацію всіх підмодулів"
+
+msgid ""
+"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"
+msgstr ""
+"git submodule deinit [--quiet] [-f | --force] [--all | [--] [<шлях>...]]"
+
+msgid "Use '--all' if you really want to deinitialize all submodules"
+msgstr ""
+"Скористайтесь \"--all\", якщо ви дійсно хочете деініціалізувати всі підмодулі"
+
+msgid ""
+"An alternate computed from a superproject's alternate is invalid.\n"
+"To allow Git to clone without an alternate in such a case, set\n"
+"submodule.alternateErrorStrategy to 'info' or, equivalently, clone with\n"
+"'--reference-if-able' instead of '--reference'."
+msgstr ""
+"Запозичений обʼєкт, обчислений з запозиченого обʼєкту батьківського проекту, "
+"не є дійсним.\n"
+"Щоб дозволити Git клонувати без запозиченого обʼєкту в такому випадку, "
+"встановіть\n"
+"submodule.alternateErrorStrategy на \"info\" або, що еквівалентно, клонуйте "
+"з\n"
+"\"--reference-if-able\" замість \"--reference\"."
+
+#, c-format
+msgid "could not get a repository handle for gitdir '%s'"
+msgstr "не вдалося отримати дескриптор сховища для git директорії \"%s\""
+
+#, c-format
+msgid "submodule '%s' cannot add alternate: %s"
+msgstr "підмодуль \"%s\" не може додати запозичених обʼєкт: %s"
+
+#, c-format
+msgid "Value '%s' for submodule.alternateErrorStrategy is not recognized"
+msgstr "Значення \"%s\" для submodule.alternateErrorStrategy не розпізнано"
+
+#, c-format
+msgid "Value '%s' for submodule.alternateLocation is not recognized"
+msgstr "Значення \"%s\" для submodule.alternateLocation не розпізнано"
+
+#, c-format
+msgid "refusing to create/use '%s' in another submodule's git dir"
+msgstr ""
+"відмовлено в створенні/використанні \"%s\" у git директорії іншого підмодуля"
+
+#, c-format
+msgid "clone of '%s' into submodule path '%s' failed"
+msgstr "не вдалося клонувати \"%s\" у шлях підмодуля \"%s\""
+
+#, c-format
+msgid "directory not empty: '%s'"
+msgstr "директорія не порожня: \"%s\""
+
+#, c-format
+msgid "could not get submodule directory for '%s'"
+msgstr "не вдалося отримати директорію підмодуля для \"%s\""
+
+msgid "alternative anchor for relative paths"
+msgstr "альтернативний якір для відносних шляхів"
+
+msgid "where the new submodule will be cloned to"
+msgstr "куди буде клоновано новий підмодуль"
+
+msgid "name of the new submodule"
+msgstr "назва нового підмодуля"
+
+msgid "url where to clone the submodule from"
+msgstr "url адреса, звідки клонувати підмодуль"
+
+msgid "depth for shallow clones"
+msgstr "глибина для неглибоких клонів"
+
+msgid "force cloning progress"
+msgstr "примусово звітувати прогрес клонування"
+
+msgid "disallow cloning into non-empty directory"
+msgstr "заборонити клонування у непорожню директорію"
+
+msgid ""
+"git submodule--helper clone [--prefix=<path>] [--quiet] [--reference "
+"<repository>] [--name <name>] [--depth <depth>] [--single-branch] [--filter "
+"<filter-spec>] --url <url> --path <path>"
+msgstr ""
+"git submodule--helper clone [--prefix=<шлях>] [--quiet] [--reference "
+"<сховище>] [--name <назва>] [--depth <глибина>] [--single-branch] [--filter "
+"<визначник-фільтру>] --url <url> --path <шлях>"
+
+#, c-format
+msgid "Invalid update mode '%s' configured for submodule path '%s'"
+msgstr ""
+"Неприпустимий режим оновлення \"%s\" налаштовано для підмодуля за шляхом "
+"\"%s\""
+
+#, c-format
+msgid "Submodule path '%s' not initialized"
+msgstr "Підмодуль за шляхом \"%s\" не ініціалізовано"
+
+msgid "Maybe you want to use 'update --init'?"
+msgstr "Можливо, ви хочете скористатись командою \"update --init\"?"
+
+#, c-format
+msgid "Skipping unmerged submodule %s"
+msgstr "Пропуск незлитого підмодуля %s"
+
+#, c-format
+msgid "Skipping submodule '%s'"
+msgstr "Пропуск підмодуля \"%s\""
+
+#, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "неможливо клонувати підмодуль \"%s\" без URL-адреси"
+
+#, c-format
+msgid "Failed to clone '%s'. Retry scheduled"
+msgstr "Не вдалося клонувати \"%s\". Запланована повторна спроба"
+
+#, c-format
+msgid "Failed to clone '%s' a second time, aborting"
+msgstr "Не вдалося клонувати \"%s\" вдруге, переривання"
+
+#, c-format
+msgid "Unable to checkout '%s' in submodule path '%s'"
+msgstr "Не вдалося переключитися на \"%s\" у підмодулі за шляхом \"%s\""
+
+#, c-format
+msgid "Unable to rebase '%s' in submodule path '%s'"
+msgstr "Не вдалося перебазувати \"%s\" в підмодулі за шляхом \"%s\""
+
+#, c-format
+msgid "Unable to merge '%s' in submodule path '%s'"
+msgstr "Не вдалося злити \"%s\" в підмодулі за шляхом \"%s\""
+
+#, c-format
+msgid "Execution of '%s %s' failed in submodule path '%s'"
+msgstr "Не вдалося виконати \"%s %s\" у шляху підмодуля \"%s\""
+
+#, c-format
+msgid "Submodule path '%s': checked out '%s'\n"
+msgstr "Шлях підмодуля \"%s\": переключено стан \"%s\"\n"
+
+#, c-format
+msgid "Submodule path '%s': rebased into '%s'\n"
+msgstr "Шлях підмодуля \"%s\": перебазовано в \"%s\"\n"
+
+#, c-format
+msgid "Submodule path '%s': merged in '%s'\n"
+msgstr "Шлях підмодуля \"%s\": злито в \"%s\"\n"
+
+#, c-format
+msgid "Submodule path '%s': '%s %s'\n"
+msgstr "Шлях підмодуля \"%s\": \"%s %s\"\n"
+
+#, c-format
+msgid "Unable to fetch in submodule path '%s'; trying to directly fetch %s:"
+msgstr ""
+"Неможливо виконати отримання шляху підмодуля \"%s\"; спроба отримати напряму "
+"%s:"
+
+#, c-format
+msgid ""
+"Fetched in submodule path '%s', but it did not contain %s. Direct fetching "
+"of that commit failed."
+msgstr ""
+"Отримано у шляху до підмодуля \"%s\", але він не містить %s. Пряме отримання "
+"цього коміту не вдалося."
+
+#, c-format
+msgid "could not initialize submodule at path '%s'"
+msgstr "не вдалося ініціалізувати підмодуль за шляхом \"%s\""
+
+#, c-format
+msgid ""
+"Submodule (%s) branch configured to inherit branch from superproject, but "
+"the superproject is not on any branch"
+msgstr ""
+"Гілку підмодуля (%s) налаштовано на успадкування гілки від суперпроекту, але "
+"суперпроект не знаходиться у жодній гілці"
+
+#, c-format
+msgid "Unable to find current revision in submodule path '%s'"
+msgstr "Не вдалося знайти поточну ревізію у шляху підмодуля \"%s\""
+
+#, c-format
+msgid "Unable to fetch in submodule path '%s'"
+msgstr "Не вдалося отримати шлях підмодуля \"%s\""
+
+#, c-format
+msgid "Unable to find %s revision in submodule path '%s'"
+msgstr "Не вдалося знайти ревізію %s у шляху підмодуля \"%s\""
+
+#, c-format
+msgid "Failed to recurse into submodule path '%s'"
+msgstr "Не вдалося обробити рекурсивно підмодуль за шляхом \"%s\""
+
+msgid "force checkout updates"
+msgstr "переключитися на оновлення примусово"
+
+msgid "initialize uninitialized submodules before update"
+msgstr "ініціалізувати неініціалізовані підмодулі перед оновленням"
+
+msgid "use SHA-1 of submodule's remote tracking branch"
+msgstr "використовувати SHA-1 гілки віддаленого відстежування підмодуля"
+
+msgid "traverse submodules recursively"
+msgstr "обходити підмодулі рекурсивно"
+
+msgid "don't fetch new objects from the remote site"
+msgstr "не отримувати нові обʼєкти з віддаленої сторони"
+
+msgid "use the 'checkout' update strategy (default)"
+msgstr "використати стратегію оновлення \"checkout \" (за замовчуванням)"
+
+msgid "use the 'merge' update strategy"
+msgstr "використати стратегію оновлення \"merge\""
+
+msgid "use the 'rebase' update strategy"
+msgstr "використати стратегію оновлення \"rebase\""
+
+msgid "create a shallow clone truncated to the specified number of revisions"
+msgstr "створити неглибокий клон, урізаний до вказаної кількості ревізій"
+
+msgid "parallel jobs"
+msgstr "паралельні потоки"
+
+msgid "whether the initial clone should follow the shallow recommendation"
+msgstr "чи повинен початковий клон слідувати неглибоким рекомендаціям"
+
+msgid "don't print cloning progress"
+msgstr "не виводити хід клонування"
+
+msgid "disallow cloning into non-empty directory, implies --init"
+msgstr "заборонити клонування у непорожній каталог, мається на увазі --init"
+
+msgid ""
+"git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] "
+"[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-"
+"shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
+"[--] [<path>...]"
+msgstr ""
+"git submodule [--quiet] update [--init [--filter=<визначник-фільтру>]] [--"
+"remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--"
+"[no-]recommend-shallow] [--reference <сховище>] [--recursive] [--[no-]single-"
+"branch] [--] [<шлях>...]"
+
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Не вдалося розвʼязати HEAD в дійсне посилання."
+
+msgid "git submodule absorbgitdirs [<options>] [<path>...]"
+msgstr "git submodule absorbgitdirs [<опції>] [<шлях>...]"
+
+msgid "suppress output for setting url of a submodule"
+msgstr "приховати вивід при встановленні url підмодуля"
+
+msgid "git submodule set-url [--quiet] <path> <newurl>"
+msgstr "git submodule set-url [--quiet] <шлях> <новий-url>"
+
+msgid "set the default tracking branch to master"
+msgstr "встановити гілку відстежування за замовчуванням на master"
+
+msgid "set the default tracking branch"
+msgstr "встановити гілку відстежування за замовчуванням"
+
+msgid "git submodule set-branch [-q|--quiet] (-d|--default) <path>"
+msgstr "git submodule set-branch [-q|--quiet] (-d|--default) <шлях>"
+
+msgid "git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"
+msgstr "git submodule set-branch [-q|--quiet] (-b|--branch) <гілка> <шлях>"
+
+msgid "--branch or --default required"
+msgstr "Потрібно вказати --branch або --default"
+
+msgid "print only error messages"
+msgstr "виводити тільки повідомлення про помилки"
+
+msgid "force creation"
+msgstr "примусове створення"
+
+msgid "show whether the branch would be created"
+msgstr "показати, чи буде створено гілку"
+
+msgid ""
+"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--"
+"quiet] [-t|--track] [-n|--dry-run] <name> <start-oid> <start-name>"
+msgstr ""
+"git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--"
+"quiet] [-t|--track] [-n|--dry-run] <назва> <початковий-oid> <початкова-назва>"
+
+#, c-format
+msgid "creating branch '%s'"
+msgstr "створення гілки \"%s\""
+
+#, c-format
+msgid "Adding existing repo at '%s' to the index\n"
+msgstr "Додавання існуючого сховища за адресою \"%s\" до індексу\n"
+
+#, c-format
+msgid "'%s' already exists and is not a valid git repo"
+msgstr "\"%s\" вже існує і не є припустимим git сховищем"
+
+#, c-format
+msgid "A git directory for '%s' is found locally with remote(s):\n"
+msgstr ""
+"Git директорію для \"%s\" знайдено локально з віддаленим(и) "
+"призначенням(и):\n"
+
+#, c-format
+msgid ""
+"If you want to reuse this local git directory instead of cloning again from\n"
+"  %s\n"
+"use the '--force' option. If the local git directory is not the correct "
+"repo\n"
+"or you are unsure what this means choose another name with the '--name' "
+"option."
+msgstr ""
+"Якщо ви хочете повторно використати цю локальну git директорію замість того, "
+"щоб знову клонувати з\n"
+"  %s\n"
+"скористайтесь опцією \"--force\". Якщо локальна директорія git не є "
+"правильним сховищем\n"
+"або ви не впевнені, що це означає, виберіть іншу назву за допомогою опції "
+"\"--name\"."
+
+#, c-format
+msgid "Reactivating local git directory for submodule '%s'\n"
+msgstr "Повторна активація локальної git директорії для підмодуля \"%s\"\n"
+
+#, c-format
+msgid "unable to checkout submodule '%s'"
+msgstr "не вдалося переключитись на підмодуль \"%s\""
+
+msgid "please make sure that the .gitmodules file is in the working tree"
+msgstr ""
+"будь ласка, переконайтеся, що файл .gitmodules знаходиться у робочому дереві"
+
+#, c-format
+msgid "Failed to add submodule '%s'"
+msgstr "Не вдалося додати підмодуль \"%s\""
+
+#, c-format
+msgid "Failed to register submodule '%s'"
+msgstr "Не вдалося зареєструвати підмодуль \"%s\""
+
+#, c-format
+msgid "'%s' already exists in the index"
+msgstr "\"%s\" вже існує в індексі"
+
+#, c-format
+msgid "'%s' already exists in the index and is not a submodule"
+msgstr "\"%s\" вже існує в індексі і не є підмодулем"
+
+#, c-format
+msgid "'%s' does not have a commit checked out"
+msgstr "\"%s\" не має активного коміту"
+
+msgid "branch of repository to add as submodule"
+msgstr "гілка сховища, яку потрібно додати як підмодуль"
+
+msgid "allow adding an otherwise ignored submodule path"
+msgstr "дозволити додавання шляху до підмодуля, який інакше ігнорується"
+
+msgid "borrow the objects from reference repositories"
+msgstr "запозичити обʼєкти з репозиторіїв посилань"
+
+msgid ""
+"sets the submodule's name to the given string instead of defaulting to its "
+"path"
+msgstr ""
+"встановлює назву підмодуля на вказане значення замість використання його "
+"шляху"
+
+msgid "git submodule add [<options>] [--] <repository> [<path>]"
+msgstr "git submodule add [<опції>] [--] <сховище> [<шлях>]"
+
+msgid "Relative path can only be used from the toplevel of the working tree"
+msgstr ""
+"Відносний шлях можна використовувати лише з верхнього рівня робочого дерева"
+
+#, c-format
+msgid "repo URL: '%s' must be absolute or begin with ./|../"
+msgstr "URL сховища: \"%s\" має бути абсолютним або починатися з ./|../."
+
+#, c-format
+msgid "'%s' is not a valid submodule name"
+msgstr "\"%s\" не є припустимою назвою підмодуля"
+
+msgid "git submodule--helper <command>"
+msgstr "git submodule--helper <команда>"
+
+msgid "git symbolic-ref [-m <reason>] <name> <ref>"
+msgstr "git symbolic-ref [-m <причина>] <назва> <посилання>"
+
+msgid "git symbolic-ref [-q] [--short] [--no-recurse] <name>"
+msgstr "git symbolic-ref [-q] [--short] [--no-recurse] <назва>"
+
+msgid "git symbolic-ref --delete [-q] <name>"
+msgstr "git symbolic-ref --delete [-q] <назва>"
+
+msgid "suppress error message for non-symbolic (detached) refs"
+msgstr ""
+"приховати повідомлення про помилку для несимвольних (відокремлених) посилань"
+
+msgid "delete symbolic ref"
+msgstr "видалити символьне посилання"
+
+msgid "shorten ref output"
+msgstr "скоротити вивід посилань"
+
+msgid "recursively dereference (default)"
+msgstr "рекурсивне розіменування (за замовчуванням)"
+
+msgid "reason"
+msgstr "причина"
+
+msgid "reason of the update"
+msgstr "причина оновлення"
+
+msgid ""
+"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+"        <tagname> [<commit> | <object>]"
+msgstr ""
+"git tag [-a | -s | -u <ідентифікатор-ключа>] [-f] [-m <допис> | -F <файл>] [-"
+"e]\n"
+"        <назва-тегу> [<коміт> | <об’єкт>]"
+
+msgid "git tag -d <tagname>..."
+msgstr "git tag -d <назва-тега>..."
+
+msgid ""
+"git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
+"        [--points-at <object>] [--column[=<options>] | --no-column]\n"
+"        [--create-reflog] [--sort=<key>] [--format=<format>]\n"
+"        [--merged <commit>] [--no-merged <commit>] [<pattern>...]"
+msgstr ""
+"git tag [-n[<число>]] -l [--contains <коміт>] [--no-contains <коміт>]\n"
+"        [--points-at <обʼєкт>] [--column[=<опціі>] | --no-column]\n"
+"        [--create-reflog] [--sort=<ключ>] [--format=<формат>]\n"
+"        [--merged <коміт>] [--no-merged <коміт>] [<шаблон>...]"
+
+msgid "git tag -v [--format=<format>] <tagname>..."
+msgstr "git tag -v [--format=<формат>] <назва-тега>..."
+
+#, c-format
+msgid "tag '%s' not found."
+msgstr "тег \"%s\" не знайдено."
+
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Видалено тег \"%s\" (було %s)\n"
+
+#, c-format
+msgid ""
+"\n"
+"Write a message for tag:\n"
+"  %s\n"
+"Lines starting with '%c' will be ignored.\n"
+msgstr ""
+"\n"
+"Напишіть допис до тегу:\n"
+"  %s\n"
+"Рядки, що починаються з \"%c\", будуть проігноровані.\n"
+
+#, c-format
+msgid ""
+"\n"
+"Write a message for tag:\n"
+"  %s\n"
+"Lines starting with '%c' will be kept; you may remove them yourself if you "
+"want to.\n"
+msgstr ""
+"\n"
+"Напишіть допис до тегу:\n"
+"  %s\n"
+"Рядки, що починаються з \"%c\", будуть збережені; ви можете вилучити їх "
+"самостійно, якщо захочете.\n"
+
+msgid "unable to sign the tag"
+msgstr "не вдалося підписати тег"
+
+#, c-format
+msgid ""
+"You have created a nested tag. The object referred to by your new tag is\n"
+"already a tag. If you meant to tag the object that it points to, use:\n"
+"\n"
+"\tgit tag -f %s %s^{}"
+msgstr ""
+"Ви створили вкладений тег. Обʼєкт, на який посилається ваш новий тег\n"
+"вже є тегом. Якщо ви хотіли позначити обʼєкт, на який він вказує, "
+"скористайтесь\n"
+"\n"
+"\tgit tag -f %s %s^{}"
+
+msgid "bad object type."
+msgstr "невірний тип обʼєкта."
+
+msgid "no tag message?"
+msgstr "немає допису до тегу?"
+
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Допис до тегу було залишено в %s\n"
+
+msgid "list tag names"
+msgstr "показати назви тегів"
+
+msgid "print <n> lines of each tag message"
+msgstr "вивести <н> рядків кожного допису до тегу"
+
+msgid "delete tags"
+msgstr "видалити теги"
+
+msgid "verify tags"
+msgstr "перевірити теги"
+
+msgid "Tag creation options"
+msgstr "Опції створення тегів"
+
+msgid "annotated tag, needs a message"
+msgstr "анотований тег потребує допису"
+
+msgid "tag message"
+msgstr "допис до тегу"
+
+msgid "force edit of tag message"
+msgstr "примусове редагування допису до тегу"
+
+msgid "annotated and GPG-signed tag"
+msgstr "анотований та підписаний GPG-підписом тег"
+
+msgid "use another key to sign the tag"
+msgstr "використати інший ключ для підпису тегу"
+
+msgid "replace the tag if exists"
+msgstr "замінити тег, якщо він існує"
+
+msgid "create a reflog"
+msgstr "створити журнал посилань"
+
+msgid "Tag listing options"
+msgstr "Опції виводу тегів"
+
+msgid "show tag list in columns"
+msgstr "показати список тегів в стовпчиках"
+
+msgid "print only tags that contain the commit"
+msgstr "вивести тільки теги, що містять коміти"
+
+msgid "print only tags that don't contain the commit"
+msgstr "вивести тільки теги, що не містять комітів"
+
+msgid "print only tags that are merged"
+msgstr "вивести тільки злиті теги"
+
+msgid "print only tags that are not merged"
+msgstr "вивести тільки не злиті теги"
+
+msgid "print only tags of the object"
+msgstr "вивести тільки теги обʼєкта"
+
+#, c-format
+msgid "the '%s' option is only allowed in list mode"
+msgstr "опція \"%s\" дозволена лише в режимі виводу"
+
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "\"%s\" не є припустимою назвою тега."
+
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "тег \"%s\" вже існує"
+
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "Неприпустимий режим очищення %s"
+
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Оновлено тег \"%s\" (було %s)\n"
+
+msgid "pack exceeds maximum allowed size"
+msgstr "пакунок перевищує максимально дозволений розмір"
+
+msgid "failed to write object in stream"
+msgstr "не вдалося записати обʼєкт потоку"
+
+#, c-format
+msgid "inflate returned (%d)"
+msgstr "розпаковувач повернув (%d)"
+
+msgid "invalid blob object from stream"
+msgstr "неприпустимий обʼєкт blob з потоку"
+
+msgid "Unpacking objects"
+msgstr "Розпакування обʼєктів"
+
+#, c-format
+msgid "failed to create directory %s"
+msgstr "не вдалося створити директорію %s"
+
+#, c-format
+msgid "failed to delete file %s"
+msgstr "не вдалося видалити файл %s"
+
+#, c-format
+msgid "failed to delete directory %s"
+msgstr "не вдалося видалити директорію %s"
+
+#, c-format
+msgid "Testing mtime in '%s' "
+msgstr "Тестування mtime в \"%s\" "
+
+msgid "directory stat info does not change after adding a new file"
+msgstr ""
+"статистична інформація директорії не змінилась після додання нового файла"
+
+msgid "directory stat info does not change after adding a new directory"
+msgstr ""
+"статистична інформація директорії не змінилась після додання нової директорії"
+
+msgid "directory stat info changes after updating a file"
+msgstr "статистична інформація директорії змінилась після оновлення файла"
+
+msgid "directory stat info changes after adding a file inside subdirectory"
+msgstr ""
+"статистична інформація директорії змінилась після додання файла до "
+"піддиректорії"
+
+msgid "directory stat info does not change after deleting a file"
+msgstr "статистична інформація директорії не змінилась після видалення файла"
+
+msgid "directory stat info does not change after deleting a directory"
+msgstr ""
+"статистична інформація директорії не змінилась після видалення директорії"
+
+msgid " OK"
+msgstr " OK"
+
+msgid "git update-index [<options>] [--] [<file>...]"
+msgstr "git update-index [<опції>] [--] [<файл>...]"
+
+msgid "continue refresh even when index needs update"
+msgstr "продовжити оновлення, навіть якщо індекс потребує змін"
+
+msgid "refresh: ignore submodules"
+msgstr "оновити: ігнорувати підмодулі"
+
+msgid "do not ignore new files"
+msgstr "не ігнорувати нові файли"
+
+msgid "let files replace directories and vice-versa"
+msgstr "дозволити файлам замінювати директорії і навпаки"
+
+msgid "notice files missing from worktree"
+msgstr "повідомляти про файли, відсутні в робочому дереві"
+
+msgid "refresh even if index contains unmerged entries"
+msgstr "оновити, навіть якщо індекс містить не злиті записи"
+
+msgid "refresh stat information"
+msgstr "оновити статистичну інформацію"
+
+msgid "like --refresh, but ignore assume-unchanged setting"
+msgstr "на кшталт --refresh, але ігнорує assume-unchanged опцію"
+
+msgid "<mode>,<object>,<path>"
+msgstr "<режим>,<обʼєкт>,<шлях>"
+
+msgid "add the specified entry to the index"
+msgstr "додати вказаний запис до індексу"
+
+msgid "mark files as \"not changing\""
+msgstr "позначити файли як \"не змінені\""
+
+msgid "clear assumed-unchanged bit"
+msgstr "очистити assumed-unchanged біт"
+
+msgid "mark files as \"index-only\""
+msgstr "позначити файли як \"тільки для індексу\""
+
+msgid "clear skip-worktree bit"
+msgstr "очистити skip-worktree біт"
+
+msgid "do not touch index-only entries"
+msgstr "ігнорувати файли, призначені тільки для індексу"
+
+msgid "add to index only; do not add content to object database"
+msgstr "додати лише до індексу; не додавати вміст до бази даних обʼєкта"
+
+msgid "remove named paths even if present in worktree"
+msgstr "видалити іменовані шляхи, навіть якщо вони присутні у робочому дереві"
+
+msgid "with --stdin: input lines are terminated by null bytes"
+msgstr "з --stdin: вхідні рядки завершуються нульовими байтами"
+
+msgid "read list of paths to be updated from standard input"
+msgstr "прочитати список шляхів для оновлення зі стандартного вводу"
+
+msgid "add entries from standard input to the index"
+msgstr "додати записи зі стандартного вводу до індексу"
+
+msgid "repopulate stages #2 and #3 for the listed paths"
+msgstr "перезаповнити етапи №2 і №3 для перелічених шляхів"
+
+msgid "only update entries that differ from HEAD"
+msgstr "оновити тільки ті записи, які відрізняються від HEAD"
+
+msgid "ignore files missing from worktree"
+msgstr "ігнорувати файли відсутні в робочому дереві"
+
+msgid "report actions to standard output"
+msgstr "звітувати про дії на стандартний вивід"
+
+msgid "(for porcelains) forget saved unresolved conflicts"
+msgstr "(для високорівневих команд) забути збережені невирішені конфлікти"
+
+msgid "write index in this format"
+msgstr "записати індекс у цьому форматі"
+
+msgid "report on-disk index format version"
+msgstr "звітувати про версію формату індексу на диску"
+
+msgid "enable or disable split index"
+msgstr "увімкнути або вимкнути розділений індекс"
+
+msgid "enable/disable untracked cache"
+msgstr "увімкнути/вимкнути невідстежуваний кеш"
+
+msgid "test if the filesystem supports untracked cache"
+msgstr "перевірити, чи підтримує файлова система невідстежуваний кеш"
+
+msgid "enable untracked cache without testing the filesystem"
+msgstr "увімкнути невідстежуваний кеш без тестування файлової системи"
+
+msgid "write out the index even if is not flagged as changed"
+msgstr "записати індекс, навіть якщо він не позначений як змінений"
+
+msgid "enable or disable file system monitor"
+msgstr "увімкнути або вимкнути монітор файлової системи"
+
+msgid "mark files as fsmonitor valid"
+msgstr "позначити файли придатними для fsmonitor"
+
+msgid "clear fsmonitor valid bit"
+msgstr "очистити біт придатності для fsmonitor"
+
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "версія індексу: була %d, стала %d"
+
+msgid ""
+"core.splitIndex is set to false; remove or change it, if you really want to "
+"enable split index"
+msgstr ""
+"core.splitIndex встановлено в false; видаліть або змініть його, якщо ви "
+"дійсно хочете увімкнути розділений індекс"
+
+msgid ""
+"core.splitIndex is set to true; remove or change it, if you really want to "
+"disable split index"
+msgstr ""
+"core.splitIndex встановлено в true; видаліть або змініть його, якщо ви "
+"дійсно хочете вимкнути розділений індекс"
+
+msgid ""
+"core.untrackedCache is set to true; remove or change it, if you really want "
+"to disable the untracked cache"
+msgstr ""
+"core.untrackedCache встановлено в true; видаліть або змініть його, якщо ви "
+"дійсно хочете вимкнути невідстежуваний кеш"
+
+msgid "Untracked cache disabled"
+msgstr "Невідстежуваний кеш вимкнено"
+
+msgid ""
+"core.untrackedCache is set to false; remove or change it, if you really want "
+"to enable the untracked cache"
+msgstr ""
+"core.untrackedCache встановлено в false; видаліть або змініть його, якщо ви "
+"дійсно хочете увімкнути невідстежуваний кеш"
+
+#, c-format
+msgid "Untracked cache enabled for '%s'"
+msgstr "Увімкнено невідстежуваний кеш для \"%s\""
+
+msgid "core.fsmonitor is unset; set it if you really want to enable fsmonitor"
+msgstr ""
+"core.fsmonitor не встановлено; встановіть його, якщо ви дійсно хочете "
+"увімкнути fsmonitor"
+
+msgid "fsmonitor enabled"
+msgstr "fsmonitor увімкнено"
+
+msgid ""
+"core.fsmonitor is set; remove it if you really want to disable fsmonitor"
+msgstr ""
+"core.fsmonitor встановлено; видаліть його, якщо ви дійсно хочете вимкнути "
+"fsmonitor"
+
+msgid "fsmonitor disabled"
+msgstr "fsmonitor вимкнено"
+
+msgid "git update-ref [<options>] -d <refname> [<old-val>]"
+msgstr "git update-ref [<опції>] -d <назва посилання> [<старе значення>]"
+
+msgid "git update-ref [<options>]    <refname> <new-val> [<old-val>]"
+msgstr ""
+"git update-ref [<опції>] <назва-посилання> <нове-значення> [<старе-значення>]"
+
+msgid "git update-ref [<options>] --stdin [-z]"
+msgstr "git update-ref [<опції>] --stdin [-z]"
+
+msgid "delete the reference"
+msgstr "видалити посилання"
+
+msgid "update <refname> not the one it points to"
+msgstr "оновити <назвау-посилання>, а не те, на яке воно вказує"
+
+msgid "stdin has NUL-terminated arguments"
+msgstr "stdin має аргументи, що закінчуються NUL"
+
+msgid "read updates from stdin"
+msgstr "читати оновлення з stdin"
+
+msgid "update the info files from scratch"
+msgstr "оновити інформаційні файли з чистого аркуша"
+
+msgid ""
+"git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
+"                [--advertise-refs] <directory>"
+msgstr ""
+"git-upload-pack [--[no-]strict] [--timeout=<н>] [--stateless-rpc]\n"
+"                [--advertise-refs] <директорія>"
+
+msgid "quit after a single request/response exchange"
+msgstr "припинити роботу після першого запит/відповідь обміну"
+
+msgid "serve up the info/refs for git-http-backend"
+msgstr "надати info/refs для git-http-backend"
+
+msgid "do not try <directory>/.git/ if <directory> is no Git directory"
+msgstr ""
+"не пробувати <директорія>/.git/, якщо <директорія> не є директорією Git"
+
+msgid "interrupt transfer after <n> seconds of inactivity"
+msgstr "перервати передачу після <н> секунд неактивності"
+
+msgid "git verify-commit [-v | --verbose] [--raw] <commit>..."
+msgstr "git verify-commit [-v | —verbose] [--raw] <коміт>..."
+
+msgid "print commit contents"
+msgstr "показати вміст коміту"
+
+msgid "print raw gpg status output"
+msgstr "показати необроблений вивід стану gpg"
+
+msgid "git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."
+msgstr ""
+"git verify-pack [-v | --verbose] [-s | --stat-only] [--] <пакунок>.idx..."
+
+msgid "verbose"
+msgstr "розгорнутий вивід"
+
+msgid "show statistics only"
+msgstr "показати тільки статистику"
+
+msgid "git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."
+msgstr "git verify-tag [-v | --verbose] [--format=<формат>] [--raw] <тег>..."
+
+msgid "print tag contents"
+msgstr "показати вміст тегу"
+
+msgid ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
+msgstr ""
+"git worktree add [-f] [--detach] [--checkout] [--lock [--reason <строка>]]\n"
+"                 [--orphan] [(-b | -B) <нова-гілка>] <шлях> [<комітоподібне>]"
+
+msgid "git worktree list [-v | --porcelain [-z]]"
+msgstr "git worktree list [-v | --porcelain [-z]]"
+
+msgid "git worktree lock [--reason <string>] <worktree>"
+msgstr "git worktree lock [--reason <строка>] <робоче дерево>"
+
+msgid "git worktree move <worktree> <new-path>"
+msgstr "git worktree move <робоче-дерево> <новий-шлях>"
+
+msgid "git worktree prune [-n] [-v] [--expire <expire>]"
+msgstr "git worktree prune [-n] [-v] [--expire <термін-дії>]"
+
+msgid "git worktree remove [-f] <worktree>"
+msgstr "git worktree remove [-f] <робоче дерево>"
+
+msgid "git worktree repair [<path>...]"
+msgstr "git worktree repair [<шлях>...]"
+
+msgid "git worktree unlock <worktree>"
+msgstr "git worktree unlock <робоче дерево>"
+
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "Немає можливої джерельної гілки, що означає \"--orphan\""
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"Якщо ви хочете створити робоче дерево, що містить нову ненароджену гілку\n"
+"(гілку без комітів) для цього сховища, ви можете зробити це\n"
+"за допомогою прапорця --orphan:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"Якщо ви хочете створити робоче дерево, що містить нову ненароджену гілку\n"
+"(гілку без комітів) для цього сховища, ви можете зробити це\n"
+"за допомогою прапорця --orphan:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
+#, c-format
+msgid "Removing %s/%s: %s"
+msgstr "Видалення %s/%s: %s"
+
+msgid "report pruned working trees"
+msgstr "звітувати про видалення робочих дерев"
+
+msgid "expire working trees older than <time>"
+msgstr "видалити робочі дерева, старіші за <час>"
+
+#, c-format
+msgid "'%s' already exists"
+msgstr "\"%s\" вже існує"
+
+#, c-format
+msgid "unusable worktree destination '%s'"
+msgstr "непридатне місце призначення робочого дерева \"%s\""
+
+#, c-format
+msgid ""
+"'%s' is a missing but locked worktree;\n"
+"use '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"
+msgstr ""
+"\"%s\" - є відсутнім, але заблокованим робочим деревом;\n"
+"скористайтесь командою \"%s -f -f\", щоб перевизначити або \"unlock\" і "
+"\"prune\" або \"remove\", щоб очистити"
+
+#, c-format
+msgid ""
+"'%s' is a missing but already registered worktree;\n"
+"use '%s -f' to override, or 'prune' or 'remove' to clear"
+msgstr ""
+"\"%s\" - є відсутнім, але вже зареєстрованим робочим деревом;\n"
+"скористайтесь командою \"%s -f\", щоб перевизначити, або \"prune\" чи "
+"\"remove\", щоб очистити"
+
+#, c-format
+msgid "failed to copy '%s' to '%s'; sparse-checkout may not work correctly"
+msgstr ""
+"не вдалося скопіювати \"%s\" в \"%s\"; розріджений перехід може працювати "
+"некоректно"
+
+#, c-format
+msgid "failed to copy worktree config from '%s' to '%s'"
+msgstr "не вдалося скопіювати конфігурацію робочого дерева з \"%s\" до \"%s\""
+
+#, c-format
+msgid "failed to unset '%s' in '%s'"
+msgstr "не вдалося скинути \"%s\" в \"%s\""
+
+#, c-format
+msgid "could not create directory of '%s'"
+msgstr "не вдалося створити директорію \"%s\""
+
+msgid "initializing"
+msgstr "ініціалізація"
+
+#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "не вдалося знайти створене робоче дерево \"%s\""
+
+#, c-format
+msgid "Preparing worktree (new branch '%s')"
+msgstr "Підготовка робочого дерева (нова гілка \"%s\")"
+
+#, c-format
+msgid "Preparing worktree (resetting branch '%s'; was at %s)"
+msgstr "Підготовка робочого дерева (скидання гілки \"%s\"; була на %s)"
+
+#, c-format
+msgid "Preparing worktree (checking out '%s')"
+msgstr "Підготовка робочого дерева (перехід до \"%s\")"
+
+#, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "недосяжне: неприпустиме посилання: %s"
+
+#, c-format
+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 шлях: \"%s\"\n"
+"HEAD вміст: \"%s\""
+
+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"
+"призначення, зупинка; скористайтесь \"add -f\", щоб перевизначити, або "
+"спочатку виконайте отримання з віддаленного сховища"
+
+msgid "checkout <branch> even if already checked out in other worktree"
+msgstr ""
+"перейти на <гілку>, навіть якщо вона вже активна в іншому робочому дереві"
+
+msgid "create a new branch"
+msgstr "створити нову гілку"
+
+msgid "create or reset a branch"
+msgstr "створити або скинути гілку"
+
+msgid "create unborn branch"
+msgstr "створити ненароджену гілку"
+
+msgid "populate the new working tree"
+msgstr "заповнити нове робоче дерево"
+
+msgid "keep the new working tree locked"
+msgstr "зберегти нове робоче дерево зафіксованим"
+
+msgid "reason for locking"
+msgstr "причина блокування"
+
+msgid "set up tracking mode (see git-branch(1))"
+msgstr "налаштувати режим відстежування (дивіться git-branch(1))"
+
+msgid "try to match the new branch name with a remote-tracking branch"
+msgstr ""
+"спробуйте співставити нову назву гілки з назвою віддалено відстежуваної гілки"
+
+#, c-format
+msgid "options '%s', '%s', and '%s' cannot be used together"
+msgstr "опції \"%s\", \"%s\" та \"%s\" не можна використовувати разом"
+
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "опцію \"%s\" не можна використовувати разом з комітоподібними"
+
+msgid "added with --lock"
+msgstr "додано з --lock"
+
+msgid "--[no-]track can only be used if a new branch is created"
+msgstr "--[no-]track можна використовувати лише при створенні нової гілки"
+
+msgid "show extended annotations and reasons, if available"
+msgstr "показувати розширені анотації та причини, якщо вони доступні"
+
+msgid "add 'prunable' annotation to worktrees older than <time>"
+msgstr "додати анотацію \"prunable\" до робочих дерев, старіших за <час>"
+
+msgid "terminate records with a NUL character"
+msgstr "завершувати записи символом NUL"
+
+#, c-format
+msgid "'%s' is not a working tree"
+msgstr "\"%s\" не є робочим деревом"
+
+msgid "The main working tree cannot be locked or unlocked"
+msgstr "Головне робоче дерево неможливо заблокувати або розблокувати"
+
+#, c-format
+msgid "'%s' is already locked, reason: %s"
+msgstr "\"%s\" вже заблоковано, причина: %s"
+
+#, c-format
+msgid "'%s' is already locked"
+msgstr "\"%s\" вже заблоковано"
+
+#, c-format
+msgid "'%s' is not locked"
+msgstr "\"%s\" не зафіксовано"
+
+msgid "working trees containing submodules cannot be moved or removed"
+msgstr ""
+"робочі дерева, що містять підмодулі, не можуть бути переміщені або видалені"
+
+msgid "force move even if worktree is dirty or locked"
+msgstr ""
+"перемістити примусово, навіть якщо робоче дерево забруднене або заблоковане"
+
+#, c-format
+msgid "'%s' is a main working tree"
+msgstr "\"%s\" є головним робочим деревом"
+
+#, c-format
+msgid "could not figure out destination name from '%s'"
+msgstr "не вдалося визначити назву призначення з \"%s\""
+
+#, c-format
+msgid ""
+"cannot move a locked working tree, lock reason: %s\n"
+"use 'move -f -f' to override or unlock first"
+msgstr ""
+"неможливо перемістити заблоковане робоче дерево, причина блокування: %s\n"
+"скористайтесь командою \"move -f -f\", щоб спочатку перевизначити або "
+"розблокувати його"
+
+msgid ""
+"cannot move a locked working tree;\n"
+"use 'move -f -f' to override or unlock first"
+msgstr ""
+"неможливо перемістити заблоковане робоче дерево;\n"
+"скористайтесь командою \"move -f -f\", щоб спочатку перевизначити або "
+"розблокувати його"
+
+#, c-format
+msgid "validation failed, cannot move working tree: %s"
+msgstr "валідація завершилася невдало, неможливо перемістити робоче дерево: %s"
+
+#, c-format
+msgid "failed to move '%s' to '%s'"
+msgstr "не вдалося перенести \"%s\" до \"%s\""
+
+#, c-format
+msgid "failed to run 'git status' on '%s'"
+msgstr "не вдалося виконати \"git status\" на \"%s\""
+
+#, c-format
+msgid "'%s' contains modified or untracked files, use --force to delete it"
+msgstr ""
+"\"%s\" містить змінені або невідстежувані файли, скористайтесь --force, щоб "
+"видалити їх"
+
+#, c-format
+msgid "failed to run 'git status' on '%s', code %d"
+msgstr "не вдалося виконати \"git status\" на \"%s\", код %d"
+
+msgid "force removal even if worktree is dirty or locked"
+msgstr ""
+"видалити примусово, навіть якщо робоче дерево забруднене або заблоковане"
+
+#, c-format
+msgid ""
+"cannot remove a locked working tree, lock reason: %s\n"
+"use 'remove -f -f' to override or unlock first"
+msgstr ""
+"неможливо видалити заблоковане робоче дерево, причина блокування: %s\n"
+"скористайтесь \"remove -f -f\", щоб перевизначити або розблокувати"
+
+msgid ""
+"cannot remove a locked working tree;\n"
+"use 'remove -f -f' to override or unlock first"
+msgstr ""
+"неможливо видалити заблоковане робоче дерево;\n"
+"скористайтесь \"remove -f -f\", щоб перевизначити або розблокувати"
+
+#, c-format
+msgid "validation failed, cannot remove working tree: %s"
+msgstr "валідація завершилася невдало, неможливо видалити робоче дерево: %s"
+
+#, c-format
+msgid "repair: %s: %s"
+msgstr "відремонтувати: %s: %s"
+
+#, c-format
+msgid "error: %s: %s"
+msgstr "помилка: %s: %s"
+
+msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
+msgstr "git write-tree [--missing-ok] [--prefix=<префікс>/]"
+
+msgid "<prefix>/"
+msgstr "<префікс>/"
+
+msgid "write tree object for a subdirectory <prefix>"
+msgstr "записати обʼєкт дерева для піддиректорії <префікс>"
+
+msgid "only useful for debugging"
+msgstr "корисно лише для відлагодження"
+
+msgid "core.fsyncMethod = batch is unsupported on this platform"
+msgstr "core.fsyncMethod = batch не підтримується на цій платформі"
+
+#, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "не вдалося розібрати ключ списку пакунків %s зі значенням \"%s\""
+
+#, c-format
+msgid "bundle list at '%s' has no mode"
+msgstr "список пакунків на \"%s\" не має режиму"
+
+msgid "failed to create temporary file"
+msgstr "не вдалося створити тимчасовий файл"
+
+msgid "insufficient capabilities"
+msgstr "недостатні здібності"
+
+#, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "файл, завантажений з \"%s\", не є пакунком"
+
+msgid "failed to store maximum creation token"
+msgstr "не вдалося зберегти токен максимального створення"
+
+#, c-format
+msgid "unrecognized bundle mode from URI '%s'"
+msgstr "нерозпізнаний режим пакунка з URI \"%s\""
+
+#, c-format
+msgid "exceeded bundle URI recursion limit (%d)"
+msgstr "перевищено ліміт рекурсії URI пакунка (%d)"
+
+#, c-format
+msgid "failed to download bundle from URI '%s'"
+msgstr "не вдалося завантажити пакунок з URI \"%s\""
+
+#, c-format
+msgid "file at URI '%s' is not a bundle or bundle list"
+msgstr "файл з URI \"%s\" не є пакунком або списком пакунків"
+
+#, c-format
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: неочікуваний аргумент: \"%s\""
+
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: очікувалось flush після аргументів"
+
+msgid "bundle-uri: got an empty line"
+msgstr "bundle-uri: отримано порожній рядок"
+
+msgid "bundle-uri: line is not of the form 'key=value'"
+msgstr "bundle-uri: рядок не відповідає формату \"key=value\""
+
+msgid "bundle-uri: line has empty key or value"
+msgstr "bundle-uri: рядок має порожній ключ або значення"
+
+#, c-format
+msgid "unrecognized bundle hash algorithm: %s"
+msgstr "нерозпізнаний хеш-алгоритм пакунка: %s"
+
+#, c-format
+msgid "unknown capability '%s'"
+msgstr "невідома властивість \"%s\""
+
+#, c-format
+msgid "'%s' does not look like a v2 or v3 bundle file"
+msgstr "\"%s\" не схожий на файл пакунка v2 або v3"
+
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "нерозпізнаний заголовок: %s%s (%d)"
+
+msgid "Repository lacks these prerequisite commits:"
+msgstr "У сховищі не вистачає обовʼязкових комітів:"
+
+msgid "need a repository to verify a bundle"
+msgstr "потрібне сховище для перевірки пакунка"
+
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr ""
+"деякі попередні коміти існують у місці збереження обʼєктів, але не повʼязані "
+"з історією сховища"
+
+#, c-format
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %<PRIuMAX> refs:"
+msgstr[0] "Пакунок містить це %<PRIuMAX> посилання:"
+msgstr[1] "Пакунок містить ці %<PRIuMAX> посилання:"
+msgstr[2] "Пакунок містить ці %<PRIuMAX> посилань:"
+
+msgid "The bundle records a complete history."
+msgstr "Пакунок записує повну історію."
+
+#, c-format
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %<PRIuMAX> refs:"
+msgstr[0] "Пакунок потребує це %<PRIuMAX> посилання:"
+msgstr[1] "Пакунок потребує ці %<PRIuMAX> посилання:"
+msgstr[2] "Пакунок потребує ці %<PRIuMAX> посилань:"
+
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "Пакунок використовує такий алгоритм хешування: %s"
+
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "Пакунок використовує такий фільтр: %s"
+
+msgid "unable to dup bundle descriptor"
+msgstr "не вдалося продублювати дескриптор пакунка"
+
+msgid "Could not spawn pack-objects"
+msgstr "Не вдалося розмножити об’єкти пакунків"
+
+msgid "pack-objects died"
+msgstr "pack-objects завершився невдало"
+
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "посилання \"%s\" виключено опціями rev-list"
+
+#, c-format
+msgid "unsupported bundle version %d"
+msgstr "непідтримувана версія пакунка %d"
+
+#, c-format
+msgid "cannot write bundle version %d with algorithm %s"
+msgstr "неможливо записати версію пакунка %d з алгоритмом %s"
+
+msgid "Refusing to create empty bundle."
+msgstr "Відмовлено в створенні порожнього пакунка."
+
+#, c-format
+msgid "cannot create '%s'"
+msgstr "неможливо створити \"%s\""
+
+msgid "index-pack died"
+msgstr "index-pack завершився невдало"
+
+msgid "terminating chunk id appears earlier than expected"
+msgstr "ідентифікатор завершення фрагмента зʼявився раніше, ніж очікувалось"
+
+#, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "шматок id %<PRIx32> не є %d-byte впорядкованим"
+
+#, c-format
+msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
+msgstr "невірне зміщення шматка(ів) %<PRIx64> та %<PRIx64>"
+
+#, c-format
+msgid "duplicate chunk ID %<PRIx32> found"
+msgstr "знайдено дубльований шматок з ідентифікатором %<PRIx32>"
+
+#, c-format
+msgid "final chunk has non-zero id %<PRIx32>"
+msgstr "заключний шматок має ненульовий ідентифікатор %<PRIx32>"
+
+msgid "invalid hash version"
+msgstr "неприпустима версія хешу"
+
+#, c-format
+msgid "invalid color value: %.*s"
+msgstr "неприпустиме значення кольору: %.*s"
+
+msgid "Add file contents to the index"
+msgstr "Додати вміст файлу до індексу"
+
+msgid "Apply a series of patches from a mailbox"
+msgstr "Застосувати серію латок з поштової скриньки"
+
+msgid "Annotate file lines with commit information"
+msgstr "Анотувати рядки файлів з інформацією про коміти"
+
+msgid "Apply a patch to files and/or to the index"
+msgstr "Застосувати латку до файлів та/або до індексу"
+
+msgid "Import a GNU Arch repository into Git"
+msgstr "Імпортувати GNU Arch сховище до Git"
+
+msgid "Create an archive of files from a named tree"
+msgstr "Створити архів файлів з названого дерева"
+
+msgid "Use binary search to find the commit that introduced a bug"
+msgstr "Використати бінарний пошук, щоб знайти коміт, який вніс помилку"
+
+msgid "Show what revision and author last modified each line of a file"
+msgstr "Показати, яка ревізія та автор востаннє змінювали кожен рядок файлу"
+
+msgid "List, create, or delete branches"
+msgstr "Показати, створити або видалити гілки"
+
+msgid "Collect information for user to file a bug report"
+msgstr "Зібрати інформацію, щоб користувач міг подати звіт про помилку"
+
+msgid "Move objects and refs by archive"
+msgstr "Перенести архів обʼєктів та посилань"
+
+msgid "Provide contents or details of repository objects"
+msgstr "Надавати вміст або деталі обʼєктів сховища"
+
+msgid "Display gitattributes information"
+msgstr "Відобразити інформацію про gitattributes"
+
+msgid "Debug gitignore / exclude files"
+msgstr "Відлагодити gitignore / вилучити файли"
+
+msgid "Show canonical names and email addresses of contacts"
+msgstr "Показати канонічні імена та адреси електронної пошти контактів"
+
+msgid "Ensures that a reference name is well formed"
+msgstr "Забезпечує правильне формування назви посилання"
+
+msgid "Switch branches or restore working tree files"
+msgstr "Перемкнути гілки або відновити файли робочого дерева"
+
+msgid "Copy files from the index to the working tree"
+msgstr "Скопіювати файли з індексу в робоче дерево"
+
+msgid "Find commits yet to be applied to upstream"
+msgstr "Знайти коміти, які ще не були застосовані до першоджерельного сховища"
+
+msgid "Apply the changes introduced by some existing commits"
+msgstr "Застосувати зміни, внесені деякими існуючими комітами"
+
+msgid "Graphical alternative to git-commit"
+msgstr "Графічна альтернатива git-commit"
+
+msgid "Remove untracked files from the working tree"
+msgstr "Видалити невідстежувані файли з робочого дерева"
+
+msgid "Clone a repository into a new directory"
+msgstr "Клонувати сховище в нову директорію"
+
+msgid "Display data in columns"
+msgstr "Відобразити дані в стовпчиках"
+
+msgid "Record changes to the repository"
+msgstr "Записати зміни у сховище"
+
+msgid "Write and verify Git commit-graph files"
+msgstr "Записати та перевірити файли коміт-графа Git"
+
+msgid "Create a new commit object"
+msgstr "Створити новий обʼєкт коміту"
+
+msgid "Get and set repository or global options"
+msgstr "Отримати та встановити параметри сховища або глобальні параметри"
+
+msgid "Count unpacked number of objects and their disk consumption"
+msgstr "Підрахувати кількість розпакованих обʼєктів та їхнє використання диска"
+
+msgid "Retrieve and store user credentials"
+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 стану"
+
+msgid "Salvage your data out of another SCM people love to hate"
+msgstr "Врятувати свої дані з іншої SCM, яку всі так неполюбляють"
+
+msgid "A CVS server emulator for Git"
+msgstr "Емулятор CVS сервера для Git"
+
+msgid "A really simple server for Git repositories"
+msgstr "Дуже простий сервер для Git сховищ"
+
+msgid "Give an object a human readable name based on an available ref"
+msgstr ""
+"Надати обʼєкту читабельну для людини назву на основі наявного посилання"
+
+msgid "Generate a zip archive of diagnostic information"
+msgstr "Згенерувати zip-архів з діагностичною інформацією"
+
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "Показати зміни між комітами, комітом та робочим деревом, тощо"
+
+msgid "Compares files in the working tree and the index"
+msgstr "Порівнює файли в робочому дереві та індексі"
+
+msgid "Compare a tree to the working tree or index"
+msgstr "Порівняти дерево з робочим деревом або індексом"
+
+msgid "Compares the content and mode of blobs found via two tree objects"
+msgstr ""
+"Порівнює вміст та режим blob обʼєктів, знайдених у двох обʼєктах дерева"
+
+msgid "Show changes using common diff tools"
+msgstr "Показати зміни за допомогою звичайних diff засобів"
+
+msgid "Git data exporter"
+msgstr "Експортер даних Git"
+
+msgid "Backend for fast Git data importers"
+msgstr "Обробник для швидких імпортерів даних Git"
+
+msgid "Download objects and refs from another repository"
+msgstr "Завантажити обʼєкти та посилання з іншого сховища"
+
+msgid "Receive missing objects from another repository"
+msgstr "Отримати відсутні обʼєкти з іншого сховища"
+
+msgid "Rewrite branches"
+msgstr "Перезаписати гілки"
+
+msgid "Produce a merge commit message"
+msgstr "Створити допис до коміту злиття"
+
+msgid "Output information on each ref"
+msgstr "Вивести інформацію про кожне посилання"
+
+msgid "Run a Git command on a list of repositories"
+msgstr "Запустити команду Git на списку сховищ"
+
+msgid "Prepare patches for e-mail submission"
+msgstr "Підготувати латки для надсилання електронною поштою"
+
+msgid "Verifies the connectivity and validity of the objects in the database"
+msgstr "Перевіряє звʼязність та валідність обʼєктів у базі даних"
+
+msgid "Cleanup unnecessary files and optimize the local repository"
+msgstr "Видалити непотрібні файли та оптимізувати локальне сховище"
+
+msgid "Extract commit ID from an archive created using git-archive"
+msgstr ""
+"Витягти ідентифікатор коміту з архіву, створеного за допомогою git-archive"
+
+msgid "Print lines matching a pattern"
+msgstr "Вивести рядки, що відповідають шаблону"
+
+msgid "A portable graphical interface to Git"
+msgstr "Портативний графічний інтерфейс до Git"
+
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "Обчислити ідентифікатор обʼєкта та за бажанням створити обʼєкт з файлу"
+
+msgid "Display help information about Git"
+msgstr "Відобразити довідкову інформацію про Git"
+
+msgid "Run git hooks"
+msgstr "Запустити git гачки"
+
+msgid "Server side implementation of Git over HTTP"
+msgstr "Серверна реалізація Git через HTTP"
+
+msgid "Download from a remote Git repository via HTTP"
+msgstr "Завантажити з віддаленого Git сховища через HTTP"
+
+msgid "Push objects over HTTP/DAV to another repository"
+msgstr "Надіслати обʼєкти через HTTP/DAV до іншого сховища"
+
+msgid "Send a collection of patches from stdin to an IMAP folder"
+msgstr "Надіслати збірку латок з stdin до теки IMAP"
+
+msgid "Build pack index file for an existing packed archive"
+msgstr "Побудувати індексний файл пакунка для існуючого запакованого архіву"
+
+msgid "Create an empty Git repository or reinitialize an existing one"
+msgstr "Створити порожнє Git сховище або переініціалізувати існуюче"
+
+msgid "Instantly browse your working repository in gitweb"
+msgstr "Миттєво переглянути своє робоче сховище в gitweb"
+
+msgid "Add or parse structured information in commit messages"
+msgstr "Додати або розібрати структуровану інформацію в дописах до комітів"
+
+msgid "Show commit logs"
+msgstr "Показати журнал коміту"
+
+msgid "Show information about files in the index and the working tree"
+msgstr "Показати інформацію про файли в індексі та робочому дереві"
+
+msgid "List references in a remote repository"
+msgstr "Показати посилання у віддаленому сховищі"
+
+msgid "List the contents of a tree object"
+msgstr "Показати вміст обʼєкта дерева"
+
+msgid "Extracts patch and authorship from a single e-mail message"
+msgstr "Витягує латку та авторство з одного повідомлення електронної пошти"
+
+msgid "Simple UNIX mbox splitter program"
+msgstr "Проста програма-розщеплювач UNIX mbox"
+
+msgid "Run tasks to optimize Git repository data"
+msgstr "Запустити завдання для оптимізації даних Git сховища"
+
+msgid "Join two or more development histories together"
+msgstr "Обʼєднати дві або більше історій розробки"
+
+msgid "Find as good common ancestors as possible for a merge"
+msgstr "Знайти якомога кращого спільного предка для злиття"
+
+msgid "Run a three-way file merge"
+msgstr "Запустити тристороннє злиття файлів"
+
+msgid "Run a merge for files needing merging"
+msgstr "Запустити злиття для файлів, які потрібно обʼєднати"
+
+msgid "The standard helper program to use with git-merge-index"
+msgstr "Стандартна допоміжна програма для використання з git-merge-index"
+
+msgid "Perform merge without touching index or working tree"
+msgstr "Виконати злиття, не торкаючись індексу або робочого дерева"
+
+msgid "Run merge conflict resolution tools to resolve merge conflicts"
+msgstr ""
+"Запустити інструменти вирішення конфліктів, щоб вирішити конфлікти злиття"
+
+msgid "Creates a tag object with extra validation"
+msgstr "Створює обʼєкт тегу з додатковою перевіркою"
+
+msgid "Build a tree-object from ls-tree formatted text"
+msgstr "Побудувати обʼєкт дерева з ls-tree форматованого тексту"
+
+msgid "Write and verify multi-pack-indexes"
+msgstr "Записати і перевірити multi-pack-indexes"
+
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "Перемістити або перейменувати файл, директорію або символьне посилання"
+
+msgid "Find symbolic names for given revs"
+msgstr "Знайти символьни назви для заданих ревізій"
+
+msgid "Add or inspect object notes"
+msgstr "Додати або перевірити нотатки до обʼєктів"
+
+msgid "Import from and submit to Perforce repositories"
+msgstr "Імпортувати з та надсилати до Perforce сховищ"
+
+msgid "Create a packed archive of objects"
+msgstr "Створити запакований архів обʼєктів"
+
+msgid "Find redundant pack files"
+msgstr "Знайти зайві файли пакунків"
+
+msgid "Pack heads and tags for efficient repository access"
+msgstr "Запакувати верхівки та теги для ефективного доступу до сховища"
+
+msgid "Compute unique ID for a patch"
+msgstr "Обчислити унікальний ідентифікатор для патчу"
+
+msgid "Prune all unreachable objects from the object database"
+msgstr "Видалити всі недосяжні обʼєкти з бази даних обʼєктів"
+
+msgid "Remove extra objects that are already in pack files"
+msgstr "Видалити зайві обʼєкти, які вже є у файлах пакунків"
+
+msgid "Fetch from and integrate with another repository or a local branch"
+msgstr "Отримати та інтегрувати з іншим сховищем або локальною гілкою"
+
+msgid "Update remote refs along with associated objects"
+msgstr "Оновити віддалені посилання разом з асоційованими обʼєктами"
+
+msgid "Applies a quilt patchset onto the current branch"
+msgstr "Застосовує набір латок до поточної гілки"
+
+msgid "Compare two commit ranges (e.g. two versions of a branch)"
+msgstr "Порівняти два діапазони комітів (наприклад, дві версії гілки)"
+
+msgid "Reads tree information into the index"
+msgstr "Зчитує інформацію про дерево в індекс"
+
+msgid "Reapply commits on top of another base tip"
+msgstr "Застосувати коміти поверх іншої верхівки бази"
+
+msgid "Receive what is pushed into the repository"
+msgstr "Отримати те, що надсилається до сховища"
+
+msgid "Manage reflog information"
+msgstr "Керування інформацією журналу посилань"
+
+msgid "Manage set of tracked repositories"
+msgstr "Керувати набором відстежуваних сховищ"
+
+msgid "Pack unpacked objects in a repository"
+msgstr "Запакувати розпаковані обʼєкти у сховищі"
+
+msgid "Create, list, delete refs to replace objects"
+msgstr "Створити, показати, видалити посилання для об’єктів заміни"
+
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"ЕКСПЕРИМЕНТАЛЬНО: Відтворення коммітів на новій базі також працює з "
+"порожніми сховищами"
+
+msgid "Generates a summary of pending changes"
+msgstr "Створює підсумок змін для розгляду"
+
+msgid "Reuse recorded resolution of conflicted merges"
+msgstr "Використати повторно записане розв’язання конфліктних злиттів"
+
+msgid "Reset current HEAD to the specified state"
+msgstr "Скинути поточний HEAD до вказаного стану"
+
+msgid "Restore working tree files"
+msgstr "Відновити файли робочого дерева"
+
+msgid "Lists commit objects in reverse chronological order"
+msgstr "Показувати обʼєкти коміту у зворотному хронологічному порядку"
+
+msgid "Pick out and massage parameters"
+msgstr "Визначення та обробка параметрів"
+
+msgid "Revert some existing commits"
+msgstr "Вивернути деякі існуючі коміти"
+
+msgid "Remove files from the working tree and from the index"
+msgstr "Видалити файли з робочого дерева та з індексу"
+
+msgid "Send a collection of patches as emails"
+msgstr "Надіслати колекцію латок у вигляді електронних листів"
+
+msgid "Push objects over Git protocol to another repository"
+msgstr "Надіслати обʼєкти за протоколом Git до іншого сховища"
+
+msgid "Git's i18n setup code for shell scripts"
+msgstr "Код налаштування i18n у Git для скриптів оболонки"
+
+msgid "Common Git shell script setup code"
+msgstr "Загальний код налаштування скриптів оболонки Git"
+
+msgid "Restricted login shell for Git-only SSH access"
+msgstr "Обмежений логін оболонки для доступу по SSH лише для Git"
+
+msgid "Summarize 'git log' output"
+msgstr "Підсумувати вивід \"git log\""
+
+msgid "Show various types of objects"
+msgstr "Показати різні типи об’єктів"
+
+msgid "Show branches and their commits"
+msgstr "Показати гілки та їхні коміти"
+
+msgid "Show packed archive index"
+msgstr "Показати індекс запакованого архіву"
+
+msgid "List references in a local repository"
+msgstr "Показати посилання у локальному сховищі"
+
+msgid "Reduce your working tree to a subset of tracked files"
+msgstr "Скоротити робоче дерево до підмножини відстежуваних файлів"
+
+msgid "Add file contents to the staging area"
+msgstr "Додати вміст файлу до області індексації"
+
+msgid "Stash the changes in a dirty working directory away"
+msgstr "Перенести зміни в брудній робочій директорії до схову"
+
+msgid "Show the working tree status"
+msgstr "Показати стан робочого дерева"
+
+msgid "Remove unnecessary whitespace"
+msgstr "Видалити зайві пробіли"
+
+msgid "Initialize, update or inspect submodules"
+msgstr "Ініціалізувати, оновити або перевірити підмодулі"
+
+msgid "Bidirectional operation between a Subversion repository and Git"
+msgstr "Двонаправлена операція між Subversion сховищем та Git"
+
+msgid "Switch branches"
+msgstr "Переключити гілки"
+
+msgid "Read, modify and delete symbolic refs"
+msgstr "Прочитати, змінити та видалити символьні посилання"
+
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr ""
+"Створити, показати, видалити або перевірити обʼєкт тегу, підписаний GPG"
+
+msgid "Creates a temporary file with a blob's contents"
+msgstr "Створює тимчасовий файл із вмістом blob"
+
+msgid "Unpack objects from a packed archive"
+msgstr "Розпакувати обʼєкти із запакованого архіву"
+
+msgid "Register file contents in the working tree to the index"
+msgstr "Зареєструвати вміст файлу робочого дерева в індексі"
+
+msgid "Update the object name stored in a ref safely"
+msgstr "Безпечно оновити назву обʼєкта, що зберігається в посиланні"
+
+msgid "Update auxiliary info file to help dumb servers"
+msgstr "Оновити додатковий інформаційний файл для допомоги \"тупим\" серверам"
+
+msgid "Send archive back to git-archive"
+msgstr "Надіслати архів назад до git-archive"
+
+msgid "Send objects packed back to git-fetch-pack"
+msgstr "Надіслати запаковані обʼєкти назад до git-fetch-pack"
+
+msgid "Show a Git logical variable"
+msgstr "Показати логічну змінну Git"
+
+msgid "Check the GPG signature of commits"
+msgstr "Перевірити GPG-підпис комітів"
+
+msgid "Validate packed Git archive files"
+msgstr "Перевірити запаковані файли Git архіву"
+
+msgid "Check the GPG signature of tags"
+msgstr "Перевірити GPG-підпис тегів"
+
+msgid "Display version information about Git"
+msgstr "Показати інформацію про версію Git"
+
+msgid "Show logs with differences each commit introduces"
+msgstr "Показати журнал з різницями, які вносить кожен коміт"
+
+msgid "Manage multiple working trees"
+msgstr "Керувати кількома робочими деревами"
+
+msgid "Create a tree object from the current index"
+msgstr "Створити обʼєкт дерева з поточного індексу"
+
+msgid "Defining attributes per path"
+msgstr "Визначення атрибутів для кожного шляху"
+
+msgid "Git command-line interface and conventions"
+msgstr "Інтерфейс та конвенції командного рядка Git"
+
+msgid "A Git core tutorial for developers"
+msgstr "Інструкція по ядру Git для розробників"
+
+msgid "Providing usernames and passwords to Git"
+msgstr "Надання імен користувачів та паролів до Git"
+
+msgid "Git for CVS users"
+msgstr "Git для користувачів CVS"
+
+msgid "Tweaking diff output"
+msgstr "Налаштування виводу різниці"
+
+msgid "A useful minimum set of commands for Everyday Git"
+msgstr "Корисний мінімальний набір команд для повсякденного використання Git"
+
+msgid "Frequently asked questions about using Git"
+msgstr "Поширені запитання про використання Git"
+
+msgid "The bundle file format"
+msgstr "Формат файлу пакунка"
+
+msgid "Chunk-based file formats"
+msgstr "Файлові формати на основі шматків"
+
+msgid "Git commit-graph format"
+msgstr "Формат Git коміт-графа"
+
+msgid "Git index format"
+msgstr "Формат Git індекса"
+
+msgid "Git pack format"
+msgstr "Формат Git пакунка"
+
+msgid "Git cryptographic signature formats"
+msgstr "Формати криптографічного підпису Git"
+
+msgid "A Git Glossary"
+msgstr "Git словник"
+
+msgid "Hooks used by Git"
+msgstr "Гачки, що використовує Git"
+
+msgid "Specifies intentionally untracked files to ignore"
+msgstr "Вказує навмисно невідстежувані файли для ігнорування"
+
+msgid "The Git repository browser"
+msgstr "Браузер сховища Git"
+
+msgid "Map author/committer names and/or E-Mail addresses"
+msgstr "Зробити мапу імен авторів/комітерів та/або адрес електронної пошти"
+
+msgid "Defining submodule properties"
+msgstr "Визначення властивостей підмодулів"
+
+msgid "Git namespaces"
+msgstr "Простори імен Git"
+
+msgid "Protocol v0 and v1 capabilities"
+msgstr "Здібності протоколів v0 та v1"
+
+msgid "Things common to various protocols"
+msgstr "Спільні риси різних протоколів"
+
+msgid "Git HTTP-based protocols"
+msgstr "Git протоколи на основі HTTP"
+
+msgid "How packs are transferred over-the-wire"
+msgstr "Як пакунки передаються по дроту"
+
+msgid "Git Wire Protocol, Version 2"
+msgstr "Протокол Git Wire, версія 2"
+
+msgid "Helper programs to interact with remote repositories"
+msgstr "Програми-помічники для взаємодії з віддаленими сховищами"
+
+msgid "Git Repository Layout"
+msgstr "Схема Git сховища"
+
+msgid "Specifying revisions and ranges for Git"
+msgstr "Визначення ревізій та діапазонів для Git"
+
+msgid "Mounting one repository inside another"
+msgstr "Монтування одного сховища всередині іншого"
+
+msgid "A tutorial introduction to Git"
+msgstr "Навчальний вступ до Git"
+
+msgid "A tutorial introduction to Git: part two"
+msgstr "Навчальний вступ до Git: частина друга"
+
+msgid "Git web interface (web frontend to Git repositories)"
+msgstr "Веб-інтерфейс Git (веб-фронтенд до сховищ Git)"
+
+msgid "An overview of recommended workflows with Git"
+msgstr "Огляд рекомендованих робочих процесів з Git"
+
+msgid "A tool for managing large Git repositories"
+msgstr "Інструмент для керування великими сховищами Git"
+
+msgid "commit-graph file is too small"
+msgstr "файл коміт-графа занадто малий"
+
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "шматок oid fanout коміт-графа має невірний розмір"
+
+msgid "commit-graph fanout values out of order"
+msgstr "значення fanout коміт-графа впорядковані невірно"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "шматок OID lookup коміт-графа має невірний розмір"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "шматок commit data коміт-графа має невірний розмір"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "шматок generations коміт-графа має невірний розмір"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "шматок changed-path index коміт-графа має невірний розмір"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"ігнорування занадто малого шматка changed-path (%<PRIuMAX> < %<PRIuMAX>) "
+"файла коміт-графа"
+
+#, c-format
+msgid "commit-graph signature %X does not match signature %X"
+msgstr "підпис коміт-графа %X не збігається з підписом %X"
+
+#, c-format
+msgid "commit-graph version %X does not match version %X"
+msgstr "версія коміт-графа %X не збігається з версією %X"
+
+#, c-format
+msgid "commit-graph hash version %X does not match version %X"
+msgstr "хеш версія коміт-графа %X не збігається з версією %X"
+
+#, c-format
+msgid "commit-graph file is too small to hold %u chunks"
+msgstr "файл коміт-графа занадто малий, щоб вмістити %u шматків"
+
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "необхідний шматок OID fanout коміт-графа відсутній або пошкоджений"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "необхідний шматок OID lookup коміт-графа відсутній або пошкоджений"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "необхідний шматок commit data коміт-графа відсутній або пошкоджений"
+
+msgid "commit-graph has no base graphs chunk"
+msgstr "коміт-граф не має шматка базових графів"
+
+msgid "commit-graph base graphs chunk is too small"
+msgstr "шматок base graphs коміт-графа занадто малий"
+
+msgid "commit-graph chain does not match"
+msgstr "ланцюжок коміт-графа не співпадає"
+
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "кількість комітів у базовому графі занадто велика: %<PRIuMAX>"
+
+msgid "commit-graph chain file too small"
+msgstr "файл ланцюжка коміт-графа занадто малий"
+
+#, c-format
+msgid "invalid commit-graph chain: line '%s' not a hash"
+msgstr "неприпустимий ланцюжок коміт-графа: рядок \"%s\" не є хешем"
+
+msgid "unable to find all commit-graph files"
+msgstr "не вдалося знайти всі файли коміт-графа"
+
+msgid "invalid commit position. commit-graph is likely corrupt"
+msgstr "неприпустима позиція коміту, ймовірно, коміт-граф пошкоджено"
+
+#, c-format
+msgid "could not find commit %s"
+msgstr "не вдалося знайти коміт %s"
+
+msgid "commit-graph requires overflow generation data but has none"
+msgstr "коміт-граф потребує даних генерації переповнення, але не має їх"
+
+msgid "commit-graph overflow generation data is too small"
+msgstr "занадто мало даних генерації переповнення коміт-графа"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "extra-edges pointer коміт-графа виходить за межі"
+
+msgid "Loading known commits in commit graph"
+msgstr "Завантаження відомих комітів у коміт-графі"
+
+msgid "Expanding reachable commits in commit graph"
+msgstr "Розширення досяжних комітів у коміт-графі"
+
+msgid "Clearing commit marks in commit graph"
+msgstr "Очищення позначок комітів у коміт-графі"
+
+msgid "Computing commit graph topological levels"
+msgstr "Обчислення топологічних рівнів коміт-графа"
+
+msgid "Computing commit graph generation numbers"
+msgstr "Обчислення номерів генерації коміт-графа"
+
+msgid "Computing commit changed paths Bloom filters"
+msgstr "Обчислення фільтрів Блума для шляхів, змінених комітом"
+
+msgid "Collecting referenced commits"
+msgstr "Збір посилань на коміти"
+
+#, c-format
+msgid "Finding commits for commit graph in %<PRIuMAX> pack"
+msgid_plural "Finding commits for commit graph in %<PRIuMAX> packs"
+msgstr[0] "Пошук комітів для коміт-графа у %<PRIuMAX> пакунку"
+msgstr[1] "Пошук комітів для коміт-графа у %<PRIuMAX> пакунках"
+msgstr[2] "Пошук комітів для коміт-графа у %<PRIuMAX> пакунках"
+
+#, c-format
+msgid "error adding pack %s"
+msgstr "помилка додавання пакету %s"
+
+#, c-format
+msgid "error opening index for %s"
+msgstr "помилка відкриття індексу для %s"
+
+msgid "Finding commits for commit graph among packed objects"
+msgstr "Пошук комітів для коміт-графа серед запакованих обʼєктів"
+
+msgid "Finding extra edges in commit graph"
+msgstr "Пошук додаткових ребер у коміт-графі"
+
+msgid "failed to write correct number of base graph ids"
+msgstr "не вдалося записати правильну кількість ідентифікаторів базових графів"
+
+msgid "unable to create temporary graph layer"
+msgstr "не вдалося створити тимчасовий шар графа"
+
+#, c-format
+msgid "unable to adjust shared permissions for '%s'"
+msgstr "не вдалося налаштувати спільні дозволи для \"%s\""
+
+#, c-format
+msgid "Writing out commit graph in %d pass"
+msgid_plural "Writing out commit graph in %d passes"
+msgstr[0] "Виведення коміт-графа за %d прохід"
+msgstr[1] "Виведення коміт-графа за %d проходи"
+msgstr[2] "Виведення коміт-графа за %d проходів"
+
+msgid "unable to open commit-graph chain file"
+msgstr "не вдалося відкрити ланцюжковий файл коміт-графа"
+
+msgid "failed to rename base commit-graph file"
+msgstr "не вдалося перейменувати базовий файл коміт-графа"
+
+msgid "failed to rename temporary commit-graph file"
+msgstr "не вдалося перейменувати тимчасовий файл коміт-графа"
+
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "неможливо обʼєднати графи з %<PRIuMAX>, %<PRIuMAX> комітів"
+
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "неможливо обʼєднати граф %s, занадто багато комітів: %<PRIuMAX>"
+
+msgid "Scanning merged commits"
+msgstr "Сканування злитих комітів"
+
+msgid "Merging commit-graph"
+msgstr "Злиття коміт-графа"
+
+msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
+msgstr "спроба записати коміт-граф, але \"core.commitGraph\" відключено"
+
+msgid "too many commits to write graph"
+msgstr "занадто багато комітів, щоб записати граф"
+
+msgid "the commit-graph file has incorrect checksum and is likely corrupt"
+msgstr "файл коміт-графа має невірну контрольну суму і, ймовірно, пошкоджений"
+
+#, c-format
+msgid "commit-graph has incorrect OID order: %s then %s"
+msgstr "коміт-граф має невірну OID послідовність: %s потім %s"
+
+#, c-format
+msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
+msgstr "коміт-граф має невірне fanout значення: fanout[%d] = %u != %u"
+
+#, c-format
+msgid "failed to parse commit %s from commit-graph"
+msgstr "не вдалося розібрати коміт %s з графа комітв"
+
+#, c-format
+msgid "failed to parse commit %s from object database for commit-graph"
+msgstr "не вдалося розібрати коміт %s з бази даних обʼєктів для коміт-графа"
+
+#, c-format
+msgid "root tree OID for commit %s in commit-graph is %s != %s"
+msgstr "OID кореневого дерева для коміту %s у коміт-графі є %s != %s"
+
+#, c-format
+msgid "commit-graph parent list for commit %s is too long"
+msgstr "список батьків для коміту %s коміт-графа занадто довгий"
+
+#, c-format
+msgid "commit-graph parent for %s is %s != %s"
+msgstr "батько для %s коміт-графа є %s != %s"
+
+#, c-format
+msgid "commit-graph parent list for commit %s terminates early"
+msgstr "список батьків коміт-графа для коміту %s завершився передчасно"
+
+#, c-format
+msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
+msgstr "генерація коміт-графа для коміту %s є %<PRIuMAX> < %<PRIuMAX>"
+
+#, c-format
+msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
+msgstr "дата коміту для коміту %s у коміт-графі є %<PRIuMAX> != %<PRIuMAX>"
+
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr ""
+"коміт-граф має як нульові, так і ненульові генерації (наприклад, коміти "
+"\"%s\" і \"%s\")"
+
+msgid "Verifying commits in commit graph"
+msgstr "Перевірка комітів у коміт-графі"
+
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s не є комітом!"
+
+msgid ""
+"Support for <GIT_DIR>/info/grafts is deprecated\n"
+"and will be removed in a future Git version.\n"
+"\n"
+"Please use \"git replace --convert-graft-file\"\n"
+"to convert the grafts into replace refs.\n"
+"\n"
+"Turn this message off by running\n"
+"\"git config advice.graftFileDeprecated false\""
+msgstr ""
+"Підтримка <GIT_DIR>/info/grafts застаріла\n"
+"і буде вилучена в одній з наступних версій Git.\n"
+"\n"
+"Будь ласка, скористайтесь \"git replace --convert-graft-file\"\n"
+"щоб перетворити щепи на заміни посилань.\n"
+"\n"
+"Щоб вимкнути це повідомлення, виконайте\n"
+"\"git config advice.graftFileDeprecated false\""
+
+#, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "коміт %s існує в коміт-графі, але не в базі даних обʼєктів"
+
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr "Коміт %s має недостовірний GPG-підпис, нібито від %s."
+
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr "Коміт %s має невірний GPG-підпис, нібито від %s."
+
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "Коміт %s не має GPG підпису."
+
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "Коміт %s має добрий GPG-підпис від %s\n"
+
+msgid ""
+"Warning: commit message did not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitEncoding to the encoding your project uses.\n"
+msgstr ""
+"Попередження: допис до коміту не відповідає кодуванню UTF-8.\n"
+"Можливо, ви захочете змінити його після виправлення допису, або встановити "
+"змінну конфігурації\n"
+"i18n.commitEncoding у кодування, яке використовується у вашому проекті.\n"
+
+msgid "no compiler information available\n"
+msgstr "немає інформації про компілятор\n"
+
+msgid "no libc information available\n"
+msgstr "немає інформації про libc\n"
+
+#, c-format
+msgid "could not determine free disk size for '%s'"
+msgstr "не вдалося визначити вільний розмір диска для \"%s\""
+
+#, c-format
+msgid "could not get info for '%s'"
+msgstr "не вдалося отримати інформацію для \"%s\""
+
+#, c-format
+msgid "[GLE %ld] health thread could not open '%ls'"
+msgstr "[GLE %ld] потік стану не зміг відкрити \"%ls\""
+
+#, c-format
+msgid "[GLE %ld] health thread getting BHFI for '%ls'"
+msgstr "[GLE %ld] потік стану отримує BHFI для \"%ls\""
+
+#, c-format
+msgid "could not convert to wide characters: '%s'"
+msgstr "не вдалося перетворити в широкі символи: \"%s\""
+
+#, c-format
+msgid "BHFI changed '%ls'"
+msgstr "BHFI змінено \"%ls\""
+
+#, c-format
+msgid "unhandled case in 'has_worktree_moved': %d"
+msgstr "необроблений випадок у \"has_worktree_moved\": %d"
+
+#, c-format
+msgid "health thread wait failed [GLE %ld]"
+msgstr "очікування потоку стану завершилося невдало [GLE %ld]"
+
+#, c-format
+msgid "Invalid path: %s"
+msgstr "Неприпустимий шлях: %s"
+
+msgid "Unable to create FSEventStream."
+msgstr "Не вдалося створити FSEventStream."
+
+msgid "Failed to start the FSEventStream"
+msgstr "Не вдалося запустити потік FSEventStream"
+
+#, c-format
+msgid "[GLE %ld] could not convert path to UTF-8: '%.*ls'"
+msgstr "[GLE %ld] не вдалося конвертувати шлях у UTF-8: \"%.*ls\""
+
+#, c-format
+msgid "[GLE %ld] could not watch '%s'"
+msgstr "[GLE %ld] стеження завершилось невдало \"%s\""
+
+#, c-format
+msgid "[GLE %ld] could not get longname of '%s'"
+msgstr "[GLE %ld] не вдалося отримати довгу назву \"%s\""
+
+#, c-format
+msgid "ReadDirectoryChangedW failed on '%s' [GLE %ld]"
+msgstr "ReadDirectoryChangedW завершилось невдало на \"%s\" [GLE %ld]"
+
+#, c-format
+msgid "GetOverlappedResult failed on '%s' [GLE %ld]"
+msgstr "GetOverlappedResult завершилось невдало на \"%s\" [GLE %ld]"
+
+#, c-format
+msgid "could not read directory changes [GLE %ld]"
+msgstr "не вдалося прочитати зміни директорії [GLE %ld]"
+
+#, c-format
+msgid "opendir('%s') failed"
+msgstr "opendir(\"%s\") завершився невдало"
+
+#, c-format
+msgid "lstat('%s') failed"
+msgstr "lstat(\"%s\") завершився невдало"
+
+#, c-format
+msgid "strbuf_readlink('%s') failed"
+msgstr "strbuf_readlink(\"%s\") завершився невдало"
+
+#, c-format
+msgid "closedir('%s') failed"
+msgstr "closedir(\"%s\") завершився невдало"
+
+#, c-format
+msgid "[GLE %ld] unable to open for read '%ls'"
+msgstr "[GLE %ld] не вдалося відкрити для читання \"%ls\""
+
+#, c-format
+msgid "[GLE %ld] unable to get protocol information for '%ls'"
+msgstr "[GLE %ld] не вдалося отримати інформацію про протокол для \"%ls\""
+
+#, c-format
+msgid "failed to copy SID (%ld)"
+msgstr "не вдалося скопіювати SID (%ld)"
+
+#, c-format
+msgid "failed to get owner for '%s' (%ld)"
+msgstr "не вдалося отримати власника для \"%s\" (%ld)"
+
+msgid "memory exhausted"
+msgstr "памʼять вичерпано"
+
+msgid "Success"
+msgstr "Успіх"
+
+msgid "No match"
+msgstr "Немає збігів"
+
+msgid "Invalid regular expression"
+msgstr "Неприпустимий регулярний вираз"
+
+msgid "Invalid collation character"
+msgstr "Неприпустимий символ зведення"
+
+msgid "Invalid character class name"
+msgstr "Неприпустима назва класу символів"
+
+msgid "Trailing backslash"
+msgstr "Кінцева зворотна коса риска"
+
+msgid "Invalid back reference"
+msgstr "Неприпустиме зворотнє посилання"
+
+msgid "Unmatched [ or [^"
+msgstr "Непарна [ або [^"
+
+msgid "Unmatched ( or \\("
+msgstr "Непарна ( або \\("
+
+msgid "Unmatched \\{"
+msgstr "Непарна \\{"
+
+msgid "Invalid content of \\{\\}"
+msgstr "Неприпустимий вміст для \\{\\}"
+
+msgid "Invalid range end"
+msgstr "Неприпустимий кінець діапазону"
+
+msgid "Memory exhausted"
+msgstr "Памʼять вичерпано"
+
+msgid "Invalid preceding regular expression"
+msgstr "Неприпустимий попередній регулярний вираз"
+
+msgid "Premature end of regular expression"
+msgstr "Передчасне завершення регулярного виразу"
+
+msgid "Regular expression too big"
+msgstr "Занадто великий регулярний вираз"
+
+msgid "Unmatched ) or \\)"
+msgstr "Непарна ) або \\)"
+
+msgid "No previous regular expression"
+msgstr "Немає попереднього регулярного виразу"
+
+msgid "could not send IPC command"
+msgstr "не вдалося відправити IPC команду"
+
+msgid "could not read IPC response"
+msgstr "не вдалося прочитати IPC відповідь"
+
+#, c-format
+msgid "could not start accept_thread '%s'"
+msgstr "не вдалося розпочати accept_thread \"%s\""
+
+#, c-format
+msgid "could not start worker[0] for '%s'"
+msgstr "не вдалося запустити worker[0] для \"%s\""
+
+#, c-format
+msgid "ConnectNamedPipe failed for '%s' (%lu)"
+msgstr "ConnectNamedPipe завершився невдало для \"%s\" (%lu)"
+
+#, c-format
+msgid "could not create fd from pipe for '%s'"
+msgstr "не вдалося створити fd з каналу для \"%s\""
+
+#, c-format
+msgid "could not start thread[0] for '%s'"
+msgstr "не вдалося запустити thread[0] для \"%s\""
+
+#, c-format
+msgid "wait for hEvent failed for '%s'"
+msgstr "очікування на hEvent не вдалося для \"%s\""
+
+msgid "cannot resume in the background, please use 'fg' to resume"
+msgstr ""
+"неможливо поновити роботу у фоновому режимі; будь ласка, скористайтесь "
+"\"fg\" для поновлення"
+
+msgid "cannot restore terminal settings"
+msgstr "не вдалося відновити налаштування термінала"
+
+#, c-format
+msgid ""
+"exceeded maximum include depth (%d) while including\n"
+"\t%s\n"
+"from\n"
+"\t%s\n"
+"This might be due to circular includes."
+msgstr ""
+"перевищено максимальну глибину (%d) під час включення\n"
+"\t%s\n"
+"від\n"
+"\t%s\n"
+"Це може бути повʼязано з циклічним включенням."
+
+#, c-format
+msgid "could not expand include path '%s'"
+msgstr "не вдалося визначити include path \"%s\""
+
+msgid "relative config includes must come from files"
+msgstr "відносні конфігураційні включення мають надходити з файлів"
+
+msgid "relative config include conditionals must come from files"
+msgstr ""
+"відносні умовні позначення конфігураційних включень мають надходити з файлів"
+
+msgid ""
+"remote URLs cannot be configured in file directly or indirectly included by "
+"includeIf.hasconfig:remote.*.url"
+msgstr ""
+"віддалені URL-адреси не можуть бути налаштовані у файлі, прямо чи "
+"опосередковано включеному за допомогою includeIf.hasconfig:remote.*.url"
+
+#, c-format
+msgid "invalid config format: %s"
+msgstr "неприпустимий формат конфігурації: %s"
+
+#, c-format
+msgid "missing environment variable name for configuration '%.*s'"
+msgstr "відсутня назва змінної оточення для конфігурації \"%.*s\""
+
+#, c-format
+msgid "missing environment variable '%s' for configuration '%.*s'"
+msgstr "відсутня змінна оточення \"%s\" для конфігурації \"%.*s\""
+
+#, c-format
+msgid "key does not contain a section: %s"
+msgstr "ключ не містить розділу: %s"
+
+#, c-format
+msgid "key does not contain variable name: %s"
+msgstr "ключ не містить назви змінної: %s"
+
+#, c-format
+msgid "invalid key: %s"
+msgstr "неприпустимий ключ: %s"
+
+#, c-format
+msgid "invalid key (newline): %s"
+msgstr "неприпустимий ключ (новий рядок): %s"
+
+msgid "empty config key"
+msgstr "порожній ключ конфігурації"
+
+#, c-format
+msgid "bogus config parameter: %s"
+msgstr "хибний параметр конфігурації: %s"
+
+#, c-format
+msgid "bogus format in %s"
+msgstr "хибний формат у %s"
+
+#, c-format
+msgid "bogus count in %s"
+msgstr "хибна кількість у %s"
+
+#, c-format
+msgid "too many entries in %s"
+msgstr "забагато записів у %s"
+
+#, c-format
+msgid "missing config key %s"
+msgstr "відсутній ключ конфігурації %s"
+
+#, c-format
+msgid "missing config value %s"
+msgstr "відсутнє значення конфігурації %s"
+
+#, c-format
+msgid "bad config line %d in blob %s"
+msgstr "невірний конфігураційний рядок %d у blob %s"
+
+#, c-format
+msgid "bad config line %d in file %s"
+msgstr "невірний конфігураційний рядок %d у файлі %s"
+
+#, c-format
+msgid "bad config line %d in standard input"
+msgstr "невірний конфігураційний рядок %d у стандартному вводі"
+
+#, c-format
+msgid "bad config line %d in submodule-blob %s"
+msgstr "невірний конфігураційний рядок %d у підмодулі-blob %s"
+
+#, c-format
+msgid "bad config line %d in command line %s"
+msgstr "невірний конфігураційний рядок %d у командному рядку %s"
+
+#, c-format
+msgid "bad config line %d in %s"
+msgstr "невірний конфігураційний рядок %d в %s"
+
+msgid "out of range"
+msgstr "поза межами діапазону"
+
+msgid "invalid unit"
+msgstr "неприпустима одиниця виміру"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s': %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\": %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in blob %s: %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\" у blob %s: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in file %s: %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\" у файлі %s: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in standard input: %s"
+msgstr ""
+"невірне числове значення конфігурації \"%s\" для \"%s\" у стандартному "
+"вводі: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in submodule-blob %s: %s"
+msgstr ""
+"невірне числове значення конфігурації \"%s\" для \"%s\" у підмодулі-blob %s: "
+"%s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in command line %s: %s"
+msgstr ""
+"невірне числове значення конфігурації \"%s\" для \"%s\" у командному рядку "
+"%s: %s"
+
+#, c-format
+msgid "bad numeric config value '%s' for '%s' in %s: %s"
+msgstr "невірне числове значення конфігурації \"%s\" для \"%s\" у \"%s\": %s"
+
+#, c-format
+msgid "invalid value for variable %s"
+msgstr "неприпустиме значення для змінної %s"
+
+#, c-format
+msgid "ignoring unknown core.fsync component '%s'"
+msgstr "ігнорування невідомого компонента core.fsync \"%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\""
+
+#, c-format
+msgid "'%s' for '%s' is not a valid timestamp"
+msgstr "\"%s\" для \"%s\" не є припустимою міткою часу"
+
+#, c-format
+msgid "abbrev length out of range: %d"
+msgstr "довжина скорочення поза діапазоном: %d"
+
+#, c-format
+msgid "bad zlib compression level %d"
+msgstr "невірний рівень zlib компресії %d"
+
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar має бути лише одним символом ASCII"
+
+#, c-format
+msgid "ignoring unknown core.fsyncMethod value '%s'"
+msgstr "ігнорування невідомого значення core.fsyncMethod \"%s\""
+
+msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead"
+msgstr "core.fsyncObjectFiles застаріла; натомість використовуйте core.fsync"
+
+#, c-format
+msgid "invalid mode for object creation: %s"
+msgstr "неприпустимий режим створення обʼєкта: %s"
+
+#, c-format
+msgid "malformed value for %s"
+msgstr "невірно сформоване значення для %s"
+
+#, c-format
+msgid "malformed value for %s: %s"
+msgstr "невірно сформоване значення для %s: %s"
+
+msgid "must be one of nothing, matching, simple, upstream or current"
+msgstr "має бути одним з nothing, matching, simple, upstream або current"
+
+#, c-format
+msgid "unable to load config blob object '%s'"
+msgstr "не вдалося завантажити config blob обʼєкт \"%s\""
+
+#, c-format
+msgid "reference '%s' does not point to a blob"
+msgstr "посилання \"%s\" не вказує на blob"
+
+#, c-format
+msgid "unable to resolve config blob '%s'"
+msgstr "не вдалося розпізнати config blob \"%s\""
+
+msgid "unable to parse command-line config"
+msgstr "не вдалося розібрати конфігурацію командного рядка"
+
+msgid "unknown error occurred while reading the configuration files"
+msgstr "невідома помилка виникла під час читання конфігураційних файлів"
+
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "Неприпустимий %s: \"%s\""
+
+#, c-format
+msgid "splitIndex.maxPercentChange value '%d' should be between 0 and 100"
+msgstr ""
+"значення splitIndex.maxPercentChange \"%d\" має бути в діапазоні від 0 до 100"
+
+#, c-format
+msgid "unable to parse '%s' from command-line config"
+msgstr "не вдалося розібрати \"%s\" з конфігурації командного рядка"
+
+#, c-format
+msgid "bad config variable '%s' in file '%s' at line %d"
+msgstr "невірна конфігураційна змінна \"%s\" у файлі \"%s\", рядок \"%d\""
+
+#, c-format
+msgid "invalid section name '%s'"
+msgstr "неприпустима назва секції \"%s\""
+
+#, c-format
+msgid "%s has multiple values"
+msgstr "%s має кілька значень"
+
+#, c-format
+msgid "failed to write new configuration file %s"
+msgstr "не вдалося записати новий конфігураційний файл %s"
+
+#, c-format
+msgid "could not lock config file %s"
+msgstr "не вдалося зафіксувати файл конфігурації %s"
+
+#, c-format
+msgid "opening %s"
+msgstr "відкриття %s"
+
+#, c-format
+msgid "invalid config file %s"
+msgstr "неприпустимий конфігураційний файл %s"
+
+#, c-format
+msgid "fstat on %s failed"
+msgstr "не вдалося виконати fstat на %s"
+
+#, c-format
+msgid "unable to mmap '%s'%s"
+msgstr "не вдалося виконати mmap \"%s\"%s"
+
+#, c-format
+msgid "chmod on %s failed"
+msgstr "не вдалося виконати chmod на %s"
+
+#, c-format
+msgid "could not write config file %s"
+msgstr "не вдалося записати конфігураційний файл %s"
+
+#, c-format
+msgid "could not set '%s' to '%s'"
+msgstr "не вдалося встановити \"%s\" в \"%s\""
+
+#, c-format
+msgid "invalid section name: %s"
+msgstr "неприпустима назва секції: %s"
+
+#, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr ""
+"відмовлено в роботі з занадто довгим рядком у \"%s\", рядок  %<PRIuMAX>"
+
+#, c-format
+msgid "missing value for '%s'"
+msgstr "відсутнє значення для \"%s\""
+
+msgid "the remote end hung up upon initial contact"
+msgstr "віддалене призначеня відключилося при першому контакті"
+
+msgid ""
+"Could not read from remote repository.\n"
+"\n"
+"Please make sure you have the correct access rights\n"
+"and the repository exists."
+msgstr ""
+"Не вдалося прочитати з віддаленого сховища.\n"
+"\n"
+"Будь ласка, переконайтеся, що у вас є правильні права доступу\n"
+"і що сховище існує."
+
+#, c-format
+msgid "server doesn't support '%s'"
+msgstr "сервер не підтримує \"%s\""
+
+#, c-format
+msgid "server doesn't support feature '%s'"
+msgstr "сервер не підтримує особливість \"%s\""
+
+msgid "expected flush after capabilities"
+msgstr "очікувалось flush після здібностей"
+
+#, c-format
+msgid "ignoring capabilities after first line '%s'"
+msgstr "ігнорування здібностей після першого рядка \"%s\""
+
+msgid "protocol error: unexpected capabilities^{}"
+msgstr "помилка протоколу: неочікувані здібності^{}"
+
+#, c-format
+msgid "protocol error: expected shallow sha-1, got '%s'"
+msgstr "помилка протоколу: очікувалось неглибоке sha-1, отримано \"%s\""
+
+msgid "repository on the other end cannot be shallow"
+msgstr "сховище на іншому кінці не може бути неглибоким"
+
+msgid "invalid packet"
+msgstr "неприпустимий пакет"
+
+#, c-format
+msgid "protocol error: unexpected '%s'"
+msgstr "помилка протоколу: неочікуване \"%s\""
+
+#, c-format
+msgid "unknown object format '%s' specified by server"
+msgstr "невідомий формат обʼєкта \"%s\", вказаний сервером"
+
+#, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "помилка у рядку відповіді bundle-uri %d: %s"
+
+msgid "expected flush after bundle-uri listing"
+msgstr "очікувалось flush після виводу bundle-uri"
+
+msgid "expected response end packet after ref listing"
+msgstr "очікувалось response end пакет після виводу посилань"
+
+#, c-format
+msgid "invalid ls-refs response: %s"
+msgstr "неприпустима ls-refs відповідь: %s"
+
+msgid "expected flush after ref listing"
+msgstr "очікувалось flush після виводу посилань"
+
+#, c-format
+msgid "protocol '%s' is not supported"
+msgstr "протокол \"%s\" не підтримується"
+
+msgid "unable to set SO_KEEPALIVE on socket"
+msgstr "не вдалося встановити SO_KEEPALIVE сокету"
+
+#, c-format
+msgid "Looking up %s ... "
+msgstr "Пошук %s ... "
+
+#, c-format
+msgid "unable to look up %s (port %s) (%s)"
+msgstr "не вдалося знайти %s (порт %s) (%s)"
+
+#. TRANSLATORS: this is the end of "Looking up %s ... "
+#, c-format
+msgid ""
+"done.\n"
+"Connecting to %s (port %s) ... "
+msgstr ""
+"готово.\n"
+"Підключення до %s (порт %s) ... "
+
+#, c-format
+msgid ""
+"unable to connect to %s:\n"
+"%s"
+msgstr ""
+"не вдалося приєднатися до %s:\n"
+"%s"
+
+#. TRANSLATORS: this is the end of "Connecting to %s (port %s) ... "
+msgid "done."
+msgstr "готово."
+
+#, c-format
+msgid "unable to look up %s (%s)"
+msgstr "не вдалося знайти %s (%s)"
+
+#, c-format
+msgid "unknown port %s"
+msgstr "невідомий порт %s"
+
+#, c-format
+msgid "strange hostname '%s' blocked"
+msgstr "дивне імʼя хоста \"%s\" заблоковано"
+
+#, c-format
+msgid "strange port '%s' blocked"
+msgstr "дивний порт \"%s\" заблоковано"
+
+#, c-format
+msgid "cannot start proxy %s"
+msgstr "неможливо запустити проксі %s"
+
+msgid "no path specified; see 'git help pull' for valid url syntax"
+msgstr "не вказано шлях; дивіться \"git help pull\" для чинного url синтаксису"
+
+msgid "newline is forbidden in git:// hosts and repo paths"
+msgstr "новий рядок у хостах git:// та шляхах репозиторіїв заборонено"
+
+msgid "ssh variant 'simple' does not support -4"
+msgstr "ssh опція \"simple\" не підтримує -4"
+
+msgid "ssh variant 'simple' does not support -6"
+msgstr "ssh опція \"simple\" не підтримує -6"
+
+msgid "ssh variant 'simple' does not support setting port"
+msgstr "ssh опція \"simple\" не підтримує встановлення порту"
+
+#, c-format
+msgid "strange pathname '%s' blocked"
+msgstr "дивне імʼя шляху \"%s\" заблоковано"
+
+msgid "unable to fork"
+msgstr "неможливо розгалужити"
+
+msgid "Could not run 'git rev-list'"
+msgstr "Не вдалося запустити \"git rev-list\""
+
+msgid "failed write to rev-list"
+msgstr "не вдалося записати до rev-list"
+
+msgid "failed to close rev-list's stdin"
+msgstr "не вдалося закрити stdin для rev-list"
+
+#, c-format
+msgid "illegal crlf_action %d"
+msgstr "неприпустиме crlf_action %d"
+
+#, c-format
+msgid "CRLF would be replaced by LF in %s"
+msgstr "CRLF буде замінено на LF у %s"
+
+#, c-format
+msgid ""
+"in the working copy of '%s', CRLF will be replaced by LF the next time Git "
+"touches it"
+msgstr ""
+"у робочій копії \"%s\" CRLF буде замінено на LF наступного разу, коли Git "
+"доторкнеться до неї"
+
+#, c-format
+msgid "LF would be replaced by CRLF in %s"
+msgstr "LF буде замінено на CRLF у %s"
+
+#, c-format
+msgid ""
+"in the working copy of '%s', LF will be replaced by CRLF the next time Git "
+"touches it"
+msgstr ""
+"у робочій копії \"%s\" LF буде замінено на CRLF наступного разу, коли Git "
+"доторкнеться до неї"
+
+#, c-format
+msgid "BOM is prohibited in '%s' if encoded as %s"
+msgstr "BOM заборонено в \"%s\", якщо закодовано як %s"
+
+#, c-format
+msgid ""
+"The file '%s' contains a byte order mark (BOM). Please use UTF-%.*s as "
+"working-tree-encoding."
+msgstr ""
+"Файл \"%s\" містить позначку порядку байтів (BOM). Будь ласка, "
+"використовуйте UTF-%.*s як кодування робочого дерева."
+
+#, c-format
+msgid "BOM is required in '%s' if encoded as %s"
+msgstr "BOM необхідний в \"%s\", якщо закодовано як %s"
+
+#, c-format
+msgid ""
+"The file '%s' is missing a byte order mark (BOM). Please use UTF-%sBE or UTF-"
+"%sLE (depending on the byte order) as working-tree-encoding."
+msgstr ""
+"У файлі \"%s\" відсутня позначка послідовності байтів (BOM). Будь ласка, "
+"використовуйте UTF-%sBE або UTF-%sLE (залежно від послідовності байтів) як "
+"кодування робочого дерева."
+
+#, c-format
+msgid "failed to encode '%s' from %s to %s"
+msgstr "не вдалося закодувати \"%s\" з %s в %s"
+
+#, c-format
+msgid "encoding '%s' from %s to %s and back is not the same"
+msgstr "кодування \"%s\" з %s в %s і назад не однакове"
+
+#, c-format
+msgid "cannot fork to run external filter '%s'"
+msgstr "неможливо розгалужити для запуску зовнішнього фільтра \"%s\""
+
+#, c-format
+msgid "cannot feed the input to external filter '%s'"
+msgstr "неможливо подати вхідні дані на зовнішній фільтр \"%s\""
+
+#, c-format
+msgid "external filter '%s' failed %d"
+msgstr "помилка зовнішнього фільтра \"%s\", код: %d"
+
+#, c-format
+msgid "read from external filter '%s' failed"
+msgstr "не вдалося прочитати з зовнішнього фільтра \"%s\""
+
+#, c-format
+msgid "external filter '%s' failed"
+msgstr "помилка зовнішнього фільтра \"%s\""
+
+msgid "unexpected filter type"
+msgstr "несподіваний тип фільтра"
+
+msgid "path name too long for external filter"
+msgstr "назва шляху занадто довга для зовнішнього фільтра"
+
+#, c-format
+msgid ""
+"external filter '%s' is not available anymore although not all paths have "
+"been filtered"
+msgstr ""
+"зовнішній фільтр \"%s\" більше не доступний, хоча не всі шляхи були "
+"відфільтровані"
+
+msgid "true/false are no valid working-tree-encodings"
+msgstr "true/false не є допустимими кодуваннями робочого дерева"
+
+#, c-format
+msgid "%s: clean filter '%s' failed"
+msgstr "%s: помилка фільтра очистки \"%s\""
+
+#, c-format
+msgid "%s: smudge filter %s failed"
+msgstr "%s: помилка фільтра розмиття %s"
+
+#, c-format
+msgid "skipping credential lookup for key: credential.%s"
+msgstr "пропуск пошуку облікових даних для ключа: credential.%s"
+
+msgid "refusing to work with credential missing host field"
+msgstr "відмовлено в роботі з відсутнім полем хоста в облікових даних"
+
+msgid "refusing to work with credential missing protocol field"
+msgstr "відмовлено в роботі з відсутнім полем протоколу облікових даних"
+
+#, c-format
+msgid "url contains a newline in its %s component: %s"
+msgstr "url містить новий рядок у %s компоненті: %s"
+
+#, c-format
+msgid "url has no scheme: %s"
+msgstr "url не має схеми: %s"
+
+#, c-format
+msgid "credential url cannot be parsed: %s"
+msgstr "неможливо розібрати url облікових даних: %s"
+
+msgid "in the future"
+msgstr "у майбутньому"
+
+#, c-format
+msgid "%<PRIuMAX> second ago"
+msgid_plural "%<PRIuMAX> seconds ago"
+msgstr[0] "%<PRIuMAX> секунду тому"
+msgstr[1] "%<PRIuMAX> секунди тому"
+msgstr[2] "%<PRIuMAX> секунд тому"
+
+#, c-format
+msgid "%<PRIuMAX> minute ago"
+msgid_plural "%<PRIuMAX> minutes ago"
+msgstr[0] "%<PRIuMAX> хвилину тому"
+msgstr[1] "%<PRIuMAX> хвилини тому"
+msgstr[2] "%<PRIuMAX> хвилин тому"
+
+#, c-format
+msgid "%<PRIuMAX> hour ago"
+msgid_plural "%<PRIuMAX> hours ago"
+msgstr[0] "%<PRIuMAX> годину тому"
+msgstr[1] "%<PRIuMAX> години тому"
+msgstr[2] "%<PRIuMAX> годин тому"
+
+#, c-format
+msgid "%<PRIuMAX> day ago"
+msgid_plural "%<PRIuMAX> days ago"
+msgstr[0] "%<PRIuMAX> день тому"
+msgstr[1] "%<PRIuMAX> дні тому"
+msgstr[2] "%<PRIuMAX> днів тому"
+
+#, c-format
+msgid "%<PRIuMAX> week ago"
+msgid_plural "%<PRIuMAX> weeks ago"
+msgstr[0] "%<PRIuMAX> тиждень тому"
+msgstr[1] "%<PRIuMAX> тижні тому"
+msgstr[2] "%<PRIuMAX> тижнів тому"
+
+#, c-format
+msgid "%<PRIuMAX> month ago"
+msgid_plural "%<PRIuMAX> months ago"
+msgstr[0] "%<PRIuMAX> місяць тому"
+msgstr[1] "%<PRIuMAX> місяці тому"
+msgstr[2] "%<PRIuMAX> місяців тому"
+
+#, c-format
+msgid "%<PRIuMAX> year"
+msgid_plural "%<PRIuMAX> years"
+msgstr[0] "%<PRIuMAX> рік"
+msgstr[1] "%<PRIuMAX> роки"
+msgstr[2] "%<PRIuMAX> років"
+
+#. TRANSLATORS: "%s" is "<n> years"
+#, c-format
+msgid "%s, %<PRIuMAX> month ago"
+msgid_plural "%s, %<PRIuMAX> months ago"
+msgstr[0] "%s, %<PRIuMAX> місяць тому"
+msgstr[1] "%s, %<PRIuMAX> місяці тому"
+msgstr[2] "%s, %<PRIuMAX> місяців тому"
+
+#, c-format
+msgid "%<PRIuMAX> year ago"
+msgid_plural "%<PRIuMAX> years ago"
+msgstr[0] "%<PRIuMAX> рік тому"
+msgstr[1] "%<PRIuMAX> роки тому"
+msgstr[2] "%<PRIuMAX> років тому"
+
+msgid "Propagating island marks"
+msgstr "Розповсюдження острівних позначок"
+
+#, c-format
+msgid "bad tree object %s"
+msgstr "невірний обʼєкт дерева %s"
+
+#, c-format
+msgid "failed to load island regex for '%s': %s"
+msgstr "не вдалося завантажити island регвир для \"%s\": %s"
+
+#, c-format
+msgid "island regex from config has too many capture groups (max=%d)"
+msgstr "регвир острова з конфігурації має забагато груп захоплення (max=%d)"
+
+#, c-format
+msgid "Marked %d islands, done.\n"
+msgstr "Позначено %d островів, готово.\n"
+
+#, c-format
+msgid "invalid --%s value '%s'"
+msgstr "неприпустиме --%s значення \"%s\""
+
+#, c-format
+msgid "could not archive missing directory '%s'"
+msgstr "не вдалося заархівувати відсутню директорію \"%s\""
+
+#, c-format
+msgid "could not open directory '%s'"
+msgstr "не вдалося відкрити директорію \"%s\""
+
+#, c-format
+msgid "skipping '%s', which is neither file nor directory"
+msgstr "пропускання \"%s\", який не є ні файлом, ні директорією"
+
+msgid "could not duplicate stdout"
+msgstr "не вдалося продублювати stdout"
+
+#, c-format
+msgid "could not add directory '%s' to archiver"
+msgstr "не вдалося додати директорію \"%s\" до архіватора"
+
+msgid "failed to write archive"
+msgstr "не вдалося записати архів"
+
+msgid "--merge-base does not work with ranges"
+msgstr "--merge-base не працює з діапазонами"
+
+msgid "unable to get HEAD"
+msgstr "не вдалося отримати HEAD"
+
+msgid "no merge base found"
+msgstr "базу злиття не знайдено"
+
+msgid "multiple merge bases found"
+msgstr "знайдено кілька баз злиття"
+
+msgid "cannot compare stdin to a directory"
+msgstr "неможливо порівняти stdin з директорією"
+
+msgid "cannot compare a named pipe to a directory"
+msgstr "неможливо порівняти іменований канал з директорією"
+
+msgid "git diff --no-index [<options>] <path> <path>"
+msgstr "git diff --no-index [<опції>] <шлях> <шлях>"
+
+msgid ""
+"Not a git repository. Use --no-index to compare two paths outside a working "
+"tree"
+msgstr ""
+"Це не git сховище. Скористайтесь --no-index для порівняння двох шляхів поза "
+"робочим деревом"
+
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%s'\n"
+msgstr " Не вдалося розібрати відсоток відсікання dirstat \"%s\"\n"
+
+#, c-format
+msgid "  Unknown dirstat parameter '%s'\n"
+msgstr " Невідомий параметр dirstat \"%s\"\n"
+
+msgid ""
+"color moved setting must be one of 'no', 'default', 'blocks', 'zebra', "
+"'dimmed-zebra', 'plain'"
+msgstr ""
+"значення параметра кольору перенесення має бути одним з \"no\", \"default\", "
+"\"blocks\", \"zebra\", \"dimmed-zebra\", \"plain\""
+
+#, c-format
+msgid ""
+"unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', "
+"'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"
+msgstr ""
+"невідомий режим color-moved-ws \"%s\", можливі значення \"ignore-space-"
+"change\", \"ignore-space-at-eol\", \"ignore-all-space\", \"allow-indentation-"
+"change\""
+
+msgid ""
+"color-moved-ws: allow-indentation-change cannot be combined with other "
+"whitespace modes"
+msgstr ""
+"color-moved-ws: allow-indentation-change не можна комбінувати з іншими "
+"режимами відображення пробілів"
+
+#, c-format
+msgid "Unknown value for 'diff.submodule' config variable: '%s'"
+msgstr "Невідоме значення конфігураційної змінної \"diff.submodule\": \"%s\""
+
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "невідоме значення для конфігурації \"%s\": %s"
+
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Знайдено помилки у конфігураційній змінній \"diff.dirstat\":\n"
+"%s"
+
+#, c-format
+msgid "external diff died, stopping at %s"
+msgstr "зовнішній diff завершився невдало, зупинившись на %s"
+
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow потрібен лишень один визначник шляху"
+
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "магічне значення визначника шляху не підтримується --follow: %s"
+
+#, c-format
+msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
+msgstr "опції \"%s\", \"%s\", \"%s\" та \"%s\" неможливо використовувати разом"
+
+#, c-format
+msgid "options '%s' and '%s' cannot be used together, use '%s' with '%s'"
+msgstr ""
+"опції \"%s\" і \"%s\" неможливо використати разом, використовуйте \"%s\" з "
+"\"%s\""
+
+#, c-format
+msgid ""
+"options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"
+msgstr ""
+"опції \"%s\" і \"%s\" неможливо використати разом, використовуйте \"%s\" з "
+"\"%s\" та \"%s\""
+
+#, c-format
+msgid "invalid --stat value: %s"
+msgstr "неприпустиме --stat значення: %s"
+
+#, c-format
+msgid "%s expects a numerical value"
+msgstr "%s очікує числове значення"
+
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Не вдалося розібрати параметр опції --dirstat/-X:\n"
+"%s"
+
+#, c-format
+msgid "unknown change class '%c' in --diff-filter=%s"
+msgstr "невідома зміна класу \"%c\" у --diff-filter=%s"
+
+#, c-format
+msgid "unknown value after ws-error-highlight=%.*s"
+msgstr "невідоме значення після ws-error-highlight=%.*s"
+
+#, c-format
+msgid "unable to resolve '%s'"
+msgstr "не вдалося розпізнати \"%s\""
+
+#, c-format
+msgid "%s expects <n>/<m> form"
+msgstr "%s очікує <n>/<m> форму"
+
+#, c-format
+msgid "%s expects a character, got '%s'"
+msgstr "%s очікує символ, отримано \"%s\""
+
+#, c-format
+msgid "bad --color-moved argument: %s"
+msgstr "невірний --color-moved аргумент: %s"
+
+#, c-format
+msgid "invalid mode '%s' in --color-moved-ws"
+msgstr "неприпустимий режим \"%s\" у --color-moved-ws"
+
+#, c-format
+msgid "invalid argument to %s"
+msgstr "неприпустимий аргумент до %s"
+
+#, c-format
+msgid "invalid regex given to -I: '%s'"
+msgstr "неприпустимий regex, переданий до -I: \"%s\""
+
+#, c-format
+msgid "failed to parse --submodule option parameter: '%s'"
+msgstr "не вдалося розібрати параметр опції --submodule: \"%s\""
+
+#, c-format
+msgid "bad --word-diff argument: %s"
+msgstr "невірний --word-diff аргумент: %s"
+
+msgid "Diff output format options"
+msgstr "Варіанти формату виводу різниці"
+
+msgid "generate patch"
+msgstr "згенерувати латку"
+
+msgid "<n>"
+msgstr "<n>"
+
+msgid "generate diffs with <n> lines context"
+msgstr "згенерувати різниці з контекстом в <n> рядків"
+
+msgid "generate the diff in raw format"
+msgstr "згенерувати різницю у форматі raw"
+
+msgid "synonym for '-p --raw'"
+msgstr "синонім для \"-p --raw\""
+
+msgid "synonym for '-p --stat'"
+msgstr "синонім для \"-p --stat\""
+
+msgid "machine friendly --stat"
+msgstr "машинний вивід --stat"
+
+msgid "output only the last line of --stat"
+msgstr "вивести лише останній рядок --stat"
+
+msgid "<param1>,<param2>..."
+msgstr "<параметр1>,<параметр2>..."
+
+msgid ""
+"output the distribution of relative amount of changes for each sub-directory"
+msgstr "вивести розподіл відносної кількості змін для кожної піддиректорії"
+
+msgid "synonym for --dirstat=cumulative"
+msgstr "синонім для --dirstat=cumulative"
+
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "синонім для --dirstat=files,<параметр1>,<параметр2>..."
+
+msgid "warn if changes introduce conflict markers or whitespace errors"
+msgstr ""
+"попереджати, якщо зміни призводять до появи конфліктних маркерів або помилок "
+"пробільних символів"
+
+msgid "condensed summary such as creations, renames and mode changes"
+msgstr "стислі підсумки, такі як створення, перейменування та зміни режимів"
+
+msgid "show only names of changed files"
+msgstr "показувати тільки назви змінених файлів"
+
+msgid "show only names and status of changed files"
+msgstr "показувати тільки назви та статус змінених файлів"
+
+msgid "<width>[,<name-width>[,<count>]]"
+msgstr "<ширина>[,<ширина-назви>[,<кількість>]]"
+
+msgid "generate diffstat"
+msgstr "згенерувати diffstat"
+
+msgid "<width>"
+msgstr "<ширина>"
+
+msgid "generate diffstat with a given width"
+msgstr "згенерувати diffstat із заданою шириною"
+
+msgid "generate diffstat with a given name width"
+msgstr "згенерувати diffstat із заданою шириною назви"
+
+msgid "generate diffstat with a given graph width"
+msgstr "згенерувати diffstat із заданою шириною графіка"
+
+msgid "<count>"
+msgstr "<кількість>"
+
+msgid "generate diffstat with limited lines"
+msgstr "згенерувати diffstat з обмеженою кількістю рядків"
+
+msgid "generate compact summary in diffstat"
+msgstr "згенерувати компактний підсумок у diffstat"
+
+msgid "output a binary diff that can be applied"
+msgstr "вивести бінарну різницю, яку можна застосувати"
+
+msgid "show full pre- and post-image object names on the \"index\" lines"
+msgstr ""
+"показувати повні назви обʼєктів до та після зображення в \"index\" рядках"
+
+msgid "show colored diff"
+msgstr "показати кольорову різницю"
+
+msgid "<kind>"
+msgstr "<тип>"
+
+msgid ""
+"highlight whitespace errors in the 'context', 'old' or 'new' lines in the "
+"diff"
+msgstr ""
+"виділяти помилки пробільних символів у \"context\", \"old\" або \"new\" "
+"рядках різниці"
+
+msgid ""
+"do not munge pathnames and use NULs as output field terminators in --raw or "
+"--numstat"
+msgstr ""
+"не змінювати імена шляхів і використовувати NUL як термінатори полів виводу "
+"в --raw або --numstat"
+
+msgid "<prefix>"
+msgstr "<префікс>"
+
+msgid "show the given source prefix instead of \"a/\""
+msgstr "показувати заданий префікс джерела замість \"a/\""
+
+msgid "show the given destination prefix instead of \"b/\""
+msgstr "показувати заданий префікс призначення замість \"b/\""
+
+msgid "prepend an additional prefix to every line of output"
+msgstr "додавати префікс до кожного рядка виводу"
+
+msgid "do not show any source or destination prefix"
+msgstr "не показувати жодного префікса джерела або призначення"
+
+msgid "use default prefixes a/ and b/"
+msgstr "використовувати префікси за замовчуванням a/ та b/"
+
+msgid "show context between diff hunks up to the specified number of lines"
+msgstr "показувати контекст між шматками різниці до вказаної кількості рядків"
+
+msgid "<char>"
+msgstr "<символ>"
+
+msgid "specify the character to indicate a new line instead of '+'"
+msgstr "вкажіть символ для позначення нового рядка замість \"+\""
+
+msgid "specify the character to indicate an old line instead of '-'"
+msgstr "вкажіть символ для позначення старого рядка замість \"-\""
+
+msgid "specify the character to indicate a context instead of ' '"
+msgstr "вкажіть символ для позначення контексту замість \" \""
+
+msgid "Diff rename options"
+msgstr "Варіанти перейменування різниці"
+
+msgid "<n>[/<m>]"
+msgstr "<n>[/<m>]"
+
+msgid "break complete rewrite changes into pairs of delete and create"
+msgstr "розбити повний перезапис змін на пари delete та create"
+
+msgid "detect renames"
+msgstr "виявляти перейменування"
+
+msgid "omit the preimage for deletes"
+msgstr "пропускати попереднє зображення для видалень"
+
+msgid "detect copies"
+msgstr "виявляти копії"
+
+msgid "use unmodified files as source to find copies"
+msgstr "використовувати незмінені файли як джерело для пошуку копій"
+
+msgid "disable rename detection"
+msgstr "вимкнути виявлення перейменування"
+
+msgid "use empty blobs as rename source"
+msgstr "використовувати порожні blobs як джерело перейменування"
+
+msgid "continue listing the history of a file beyond renames"
+msgstr "продовжити виведення історії файлу після перейменування"
+
+msgid ""
+"prevent rename/copy detection if the number of rename/copy targets exceeds "
+"given limit"
+msgstr ""
+"запобігати виявленню перейменування/копіювання, якщо кількість обʼєктів "
+"перейменування/копіювання перевищує заданий ліміт"
+
+msgid "Diff algorithm options"
+msgstr "Варіанти алгоритмів різниці"
+
+msgid "produce the smallest possible diff"
+msgstr "створювати найменшу можливу різницю"
+
+msgid "ignore whitespace when comparing lines"
+msgstr "ігнорувати пробіли при порівнянні рядків"
+
+msgid "ignore changes in amount of whitespace"
+msgstr "ігнорувати зміни кількості пробілів"
+
+msgid "ignore changes in whitespace at EOL"
+msgstr "ігнорувати зміни пробілів у EOL"
+
+msgid "ignore carrier-return at the end of line"
+msgstr "ігнорувати повернення каретки в кінці рядка"
+
+msgid "ignore changes whose lines are all blank"
+msgstr "ігнорувати зміни, всі рядки яких порожні"
+
+msgid "<regex>"
+msgstr "<регвир>"
+
+msgid "ignore changes whose all lines match <regex>"
+msgstr "ігнорувати зміни, всі рядки яких збігаються з <регвир>"
+
+msgid "heuristic to shift diff hunk boundaries for easy reading"
+msgstr "евристичне визначення зміщення меж шматка різниці для зручного читання"
+
+msgid "generate diff using the \"patience diff\" algorithm"
+msgstr "згенерувати різницю за алгоритмом \"patience diff\""
+
+msgid "generate diff using the \"histogram diff\" algorithm"
+msgstr "згенерувати різницю за алгоритмом \"histogram diff\""
+
+msgid "<text>"
+msgstr "<текст>"
+
+msgid "generate diff using the \"anchored diff\" algorithm"
+msgstr "згенерувати різницю за алгоритмом \"anchored diff\""
+
+msgid "<mode>"
+msgstr "<режим>"
+
+msgid "show word diff, using <mode> to delimit changed words"
+msgstr ""
+"показати різницю по словам, використовуючи <режим> для відокремлення "
+"змінених слів"
+
+msgid "use <regex> to decide what a word is"
+msgstr "скористайтесь <регвир>, щоб визначити, що це за слово"
+
+msgid "equivalent to --word-diff=color --word-diff-regex=<regex>"
+msgstr "еквівалентно --word-diff=color --word-diff-regex=<регвир>"
+
+msgid "moved lines of code are colored differently"
+msgstr "переміщені рядки коду мають інший колір"
+
+msgid "how white spaces are ignored in --color-moved"
+msgstr "як ігноруються пробіли в --color-moved"
+
+msgid "Other diff options"
+msgstr "Інші опції різниці"
+
+msgid "when run from subdir, exclude changes outside and show relative paths"
+msgstr ""
+"при запуску з піддиректорії не враховувати зміни ззовні та показувати "
+"відносні шляхи"
+
+msgid "treat all files as text"
+msgstr "обробляти всі файли як текстові"
+
+msgid "swap two inputs, reverse the diff"
+msgstr "поміняти місцями два вводи, змінити різницю на протилежну"
+
+msgid "exit with 1 if there were differences, 0 otherwise"
+msgstr ""
+"завершити виконання з кодом 1, якщо були різниці, або з кодом 0 в іншому "
+"випадку"
+
+msgid "disable all output of the program"
+msgstr "вимкнути весь вивід програми"
+
+msgid "allow an external diff helper to be executed"
+msgstr "дозволити виконання зовнішнього помічника різниці"
+
+msgid "run external text conversion filters when comparing binary files"
+msgstr ""
+"запускати зовнішні фільтри конвертації тексту під час порівняння бінарних "
+"файлів"
+
+msgid "<when>"
+msgstr "<коли>"
+
+msgid "ignore changes to submodules in the diff generation"
+msgstr "ігнорувати зміни підмодулів у генерації різниці"
+
+msgid "<format>"
+msgstr "<формат>"
+
+msgid "specify how differences in submodules are shown"
+msgstr "вказати як відображати різниці в підмодулях"
+
+msgid "hide 'git add -N' entries from the index"
+msgstr "приховати записи \"git add -N\" з індексу"
+
+msgid "treat 'git add -N' entries as real in the index"
+msgstr "обробляти \"git add -N\" записи як дійсні в індексі"
+
+msgid "<string>"
+msgstr "<строка>"
+
+msgid ""
+"look for differences that change the number of occurrences of the specified "
+"string"
+msgstr "шукати різниці, які змінюють кількість входжень вказаного рядка"
+
+msgid ""
+"look for differences that change the number of occurrences of the specified "
+"regex"
+msgstr "шукати різниці, які змінюють кількість входжень вказаного регвиру"
+
+msgid "show all changes in the changeset with -S or -G"
+msgstr "показати всі зміни у змінному наборі з -S або -G"
+
+msgid "treat <string> in -S as extended POSIX regular expression"
+msgstr "обробляти <рядок> у -S як розширений POSIX регулярний вираз"
+
+msgid "control the order in which files appear in the output"
+msgstr "керувати порядком, у якому файли зʼявляються у виводі"
+
+msgid "<path>"
+msgstr "<шлях>"
+
+msgid "show the change in the specified path first"
+msgstr "спочатку показати зміну для зазначеного шляху"
+
+msgid "skip the output to the specified path"
+msgstr "пропустити вивід для зазначеного шляху"
+
+msgid "<object-id>"
+msgstr "<id-обʼєкта>"
+
+msgid ""
+"look for differences that change the number of occurrences of the specified "
+"object"
+msgstr "шукати різниці, які змінюють кількість входжень вказаного обʼєкта"
+
+msgid "[(A|C|D|M|R|T|U|X|B)...[*]]"
+msgstr "[(A|C|D|M|R|T|U|X|B)...[*]]"
+
+msgid "select files by diff type"
+msgstr "вибрати файли за типом різниці"
+
+msgid "<file>"
+msgstr "<файл>"
+
+msgid "output to a specific file"
+msgstr "виводити до певного файла"
+
+msgid "exhaustive rename detection was skipped due to too many files."
+msgstr ""
+"вичерпне виявлення перейменування було пропущено через занадто велику "
+"кількість файлів."
+
+msgid "only found copies from modified paths due to too many files."
+msgstr ""
+"знайдено лише копії зі змінених шляхів через занадто велику кількість файлів."
+
+#, c-format
+msgid ""
+"you may want to set your %s variable to at least %d and retry the command."
+msgstr ""
+"можливо, вам слід встановити значення змінної %s принаймні у %d і повторити "
+"команду."
+
+#, c-format
+msgid "failed to read orderfile '%s'"
+msgstr "не вдалося прочитати orderfile \"%s\""
+
+msgid "Performing inexact rename detection"
+msgstr "Виявлення неточного перейменування"
+
+#, c-format
+msgid "No such path '%s' in the diff"
+msgstr "Немає такого шляху \"%s\" у різниці"
+
+#, c-format
+msgid "pathspec '%s' did not match any file(s) known to git"
+msgstr "визначник шляху \"%s\" не збігається з жодним файлом, відомим git"
+
+#, c-format
+msgid "unrecognized pattern: '%s'"
+msgstr "нерозпізнаний шаблон: \"%s\""
+
+#, c-format
+msgid "unrecognized negative pattern: '%s'"
+msgstr "нерозпізнаний негативний шаблон: \"%s\""
+
+#, c-format
+msgid "your sparse-checkout file may have issues: pattern '%s' is repeated"
+msgstr ""
+"ваш файл розрідженого переходу може мати певні проблеми: шаблон \"%s\" "
+"повторюється"
+
+msgid "disabling cone pattern matching"
+msgstr "вимкнення зіставлення шаблону конуса"
+
+#, c-format
+msgid "cannot use %s as an exclude file"
+msgstr "неможливо використовувати %s як файл виключення"
+
+msgid "failed to get kernel name and information"
+msgstr "не вдалося отримати назву та інформацію про ядро"
+
+msgid "untracked cache is disabled on this system or location"
+msgstr "невідстежуваний кеш вимкнено на цій системі або в цьому місці"
+
+msgid ""
+"No directory name could be guessed.\n"
+"Please specify a directory on the command line"
+msgstr ""
+"Hе вдалося вгадати назву директорії.\n"
+"Будь ласка, вкажіть директорію в командному рядку"
+
+#, c-format
+msgid "index file corrupt in repo %s"
+msgstr "пошкоджено індексний файл у сховищі %s"
+
+#, c-format
+msgid "could not create directories for %s"
+msgstr "не вдалося створити директорії для %s"
+
+#, c-format
+msgid "could not migrate git directory from '%s' to '%s'"
+msgstr "не вдалося перенести git директорію з \"%s\" до \"%s\""
+
+#, c-format
+msgid "hint: Waiting for your editor to close the file...%c"
+msgstr "підказка: чекаємо, поки редактор закриє файл...%c"
+
+#, c-format
+msgid "could not write to '%s'"
+msgstr "не вдалося записати в \"%s\""
+
+#, c-format
+msgid "could not edit '%s'"
+msgstr "не вдалося відредагувати \"%s\""
+
+msgid "Filtering content"
+msgstr "Фільтрація вмісту"
+
+#, c-format
+msgid "could not stat file '%s'"
+msgstr "не вдалося виконати stat для %s"
+
+#, c-format
+msgid "bad git namespace path \"%s\""
+msgstr "невірний шлях до простору імен git \"%s\""
+
+#, c-format
+msgid "too many args to run %s"
+msgstr "забагато аргументів для запуску %s"
+
+msgid "git fetch-pack: expected shallow list"
+msgstr "git fetch-pack: очікувався неглибокий список"
+
+msgid "git fetch-pack: expected a flush packet after shallow list"
+msgstr "git fetch-pack: очікувалось flush пакет після неглибокого списку"
+
+msgid "git fetch-pack: expected ACK/NAK, got a flush packet"
+msgstr "git fetch-pack: очікувалось ACK/NAK, отримано flush пакет"
+
+#, c-format
+msgid "git fetch-pack: expected ACK/NAK, got '%s'"
+msgstr "git fetch-pack: очікувалось ACK/NAK, отримано \"%s\""
+
+msgid "unable to write to remote"
+msgstr "не вдалося записати до віддаленого сховища"
+
+msgid "Server supports filter"
+msgstr "Сервер підтримує фільтр"
+
+#, c-format
+msgid "invalid shallow line: %s"
+msgstr "неприпустимий shallow рядок: %s"
+
+#, c-format
+msgid "invalid unshallow line: %s"
+msgstr "неприпустимий unshallow рядок: %s"
+
+#, c-format
+msgid "object not found: %s"
+msgstr "обʼєкт не знайдено: %s"
+
+#, c-format
+msgid "error in object: %s"
+msgstr "помилка в обʼєкті: %s"
+
+#, c-format
+msgid "no shallow found: %s"
+msgstr "не знайдено неглибоких: %s"
+
+#, c-format
+msgid "expected shallow/unshallow, got %s"
+msgstr "очікувалось shallow/unshallow, отримано %s"
+
+#, c-format
+msgid "got %s %d %s"
+msgstr "отримано %s %d %s"
+
+#, c-format
+msgid "invalid commit %s"
+msgstr "неприпустимий коміт %s"
+
+msgid "giving up"
+msgstr "здаюся"
+
+msgid "done"
+msgstr "готово"
+
+#, c-format
+msgid "got %s (%d) %s"
+msgstr "отримано %s (%d) %s"
+
+#, c-format
+msgid "Marking %s as complete"
+msgstr "Позначення %s як завершеного"
+
+#, c-format
+msgid "already have %s (%s)"
+msgstr "вже є %s (%s)"
+
+msgid "fetch-pack: unable to fork off sideband demultiplexer"
+msgstr "fetch-pack: не вдалося розгалужити sideband demultiplexer"
+
+msgid "protocol error: bad pack header"
+msgstr "помилка протоколу: невірний заголовок пакунка"
+
+#, c-format
+msgid "fetch-pack: unable to fork off %s"
+msgstr "fetch-pack: не вдалося розгалужити %s"
+
+msgid "fetch-pack: invalid index-pack output"
+msgstr "fetch-pack: неприпустиме виведення індексного пакунка"
+
+#, c-format
+msgid "%s failed"
+msgstr "%s завершився невдало"
+
+msgid "error in sideband demultiplexer"
+msgstr "помилка в sideband demultiplexer"
+
+#, c-format
+msgid "Server version is %.*s"
+msgstr "Версія сервера %.*s"
+
+#, c-format
+msgid "Server supports %s"
+msgstr "Сервер підтримує %s"
+
+msgid "Server does not support shallow clients"
+msgstr "Сервер не підтримує неглибоких клієнтів"
+
+msgid "Server does not support --shallow-since"
+msgstr "Сервер не підтримує параметр --shallow-since"
+
+msgid "Server does not support --shallow-exclude"
+msgstr "Сервер не підтримує параметр --shallow-exclude"
+
+msgid "Server does not support --deepen"
+msgstr "Сервер не підтримує параметр --deepen"
+
+msgid "Server does not support this repository's object format"
+msgstr "Сервер не підтримує формат об’єктів цього сховища"
+
+msgid "no common commits"
+msgstr "немає спільних комітів"
+
+msgid "git fetch-pack: fetch failed."
+msgstr "git fetch-pack: помилка отримання."
+
+#, c-format
+msgid "mismatched algorithms: client %s; server %s"
+msgstr "невідповідність алгоритмів: клієнт %s; сервер %s"
+
+#, c-format
+msgid "the server does not support algorithm '%s'"
+msgstr "сервер не підтримує алгоритм \"%s\""
+
+msgid "Server does not support shallow requests"
+msgstr "Сервер не підтримує неглибокі запити"
+
+msgid "unable to write request to remote"
+msgstr "не вдалося записати запит до віддаленого сховища"
+
+#, c-format
+msgid "expected '%s', received '%s'"
+msgstr "очікувалось \"%s\", отримано \"%s\""
+
+#, c-format
+msgid "expected '%s'"
+msgstr "очікувалось \"%s\""
+
+#, c-format
+msgid "unexpected acknowledgment line: '%s'"
+msgstr "несподіваний рядок підтвердження: \"%s\""
+
+#, c-format
+msgid "error processing acks: %d"
+msgstr "помилка при обробці підтверджень: %d"
+
+#. TRANSLATORS: The parameter will be 'ready', a protocol
+#. keyword.
+#.
+#, c-format
+msgid "expected packfile to be sent after '%s'"
+msgstr "очікувалось надсилання файла пакунка після \"%s\""
+
+#. TRANSLATORS: The parameter will be 'ready', a protocol
+#. keyword.
+#.
+#, c-format
+msgid "expected no other sections to be sent after no '%s'"
+msgstr "не очікувалось надсилання жодної секції для статусу не \"%s\""
+
+#, c-format
+msgid "error processing shallow info: %d"
+msgstr "помилка при обробці неглибокої інформації: %d"
+
+#, c-format
+msgid "expected wanted-ref, got '%s'"
+msgstr "очікувалось wanted-ref, отримано \"%s\""
+
+#, c-format
+msgid "unexpected wanted-ref: '%s'"
+msgstr "несподіване запитуване посилання: \"%s\""
+
+#, c-format
+msgid "error processing wanted refs: %d"
+msgstr "помилка під час обробки запитуваних посилань: %d"
+
+msgid "git fetch-pack: expected response end packet"
+msgstr "git fetch-pack: очікувався response end пакет"
+
+msgid "no matching remote head"
+msgstr "немає відповідного віддаленого head"
+
+msgid "unexpected 'ready' from remote"
+msgstr "несподіване \"ready\" з віддаленого призначення"
+
+#, c-format
+msgid "no such remote ref %s"
+msgstr "немає такого віддаленого посилання %s"
+
+#, c-format
+msgid "Server does not allow request for unadvertised object %s"
+msgstr "Сервер забороняє запити до неоголошених обʼєктів %s"
+
+#, c-format
+msgid "fsmonitor_ipc__send_query: invalid path '%s'"
+msgstr "fsmonitor_ipc__send_query: неприпустимий шлях \"%s\""
+
+#, c-format
+msgid "fsmonitor_ipc__send_query: unspecified error on '%s'"
+msgstr "fsmonitor_ipc__send_query: невизначена помилка на \"%s\""
+
+msgid "fsmonitor--daemon is not running"
+msgstr "fsmonitor--daemon не запущено"
+
+#, c-format
+msgid "could not send '%s' command to fsmonitor--daemon"
+msgstr "не вдалося відправити команду \"%s\" до fsmonitor--daemon"
+
+#, c-format
+msgid "bare repository '%s' is incompatible with fsmonitor"
+msgstr "порожнє сховище \"%s\" несумісне з fsmonitor"
+
+#, c-format
+msgid "repository '%s' is incompatible with fsmonitor due to errors"
+msgstr "сховище \"%s\" несумісне з fsmonitor через помилки"
+
+#, c-format
+msgid "remote repository '%s' is incompatible with fsmonitor"
+msgstr "віддалене сховище \"%s\" несумісне з fsmonitor"
+
+#, c-format
+msgid "virtual repository '%s' is incompatible with fsmonitor"
+msgstr "віртуальне сховище \"%s\" несумісне з fsmonitor"
+
+#, c-format
+msgid ""
+"socket directory '%s' is incompatible with fsmonitor due to lack of Unix "
+"sockets support"
+msgstr ""
+"директорія сокетів \"%s\" несумісна з fsmonitor через відсутність підтримки "
+"сокетів Unix"
+
+msgid ""
+"git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
+"           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
+"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
+"bare]\n"
+"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
+msgstr ""
+"git [-v | --version] [-h | --help] [-C <шлях>] [-c <назва>=<значення>]\n"
+"           [--exec-path[=<шлях>]] [--html-path] [--man-path] [--info-path]\n"
+"           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
+"bare]\n"
+"           [--git-dir=<шлях>] [--work-tree=<шлях>] [--namespace=<назва>]\n"
+"[--config-env=<назва>=<змінна-оточення>] <команда> [<аргументи>]"
+
+msgid ""
+"'git help -a' and 'git help -g' list available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept.\n"
+"See 'git help git' for an overview of the system."
+msgstr ""
+"\"git help -a\" і \"git help -g\" виводять доступні підкоманди та деякі\n"
+"посібники з понять. Дивіться \"git help <команда>\" або \"git help "
+"<поняття>\n"
+"щоб прочитати про конкретну підкоманду або поняття.\n"
+"Дивіться \"git help git\" для ознайомлення з системою."
+
+#, c-format
+msgid "unsupported command listing type '%s'"
+msgstr "непідтримуваний тип виводу команд \"%s\""
+
+#, c-format
+msgid "no directory given for '%s' option\n"
+msgstr "не вказано директорію для \"%s\" опціі\n"
+
+#, c-format
+msgid "no namespace given for --namespace\n"
+msgstr "не вказано простір імен для --namespace\n"
+
+#, c-format
+msgid "-c expects a configuration string\n"
+msgstr "-c очікує на рядок конфігурації\n"
+
+#, c-format
+msgid "no config key given for --config-env\n"
+msgstr "не вказано ключ конфігурації для --config-env\n"
+
+#, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "для --attr-source не вказано джерело атрибутів\n"
+
+#, c-format
+msgid "unknown option: %s\n"
+msgstr "невідома опція: %s\n"
+
+#, c-format
+msgid "while expanding alias '%s': '%s'"
+msgstr "під час розгортання псевдоніма \"%s\": \"%s\""
+
+#, c-format
+msgid ""
+"alias '%s' changes environment variables.\n"
+"You can use '!git' in the alias to do this"
+msgstr ""
+"псевдонім \"%s\" перемінює змінні оточення.\n"
+"Ви можете використовувати \"!git\" в псевдонімі для цього"
+
+#, c-format
+msgid "empty alias for %s"
+msgstr "порожній псевдонім для %s"
+
+#, c-format
+msgid "recursive alias: %s"
+msgstr "рекурсивний псевдонім: %s"
+
+msgid "write failure on standard output"
+msgstr "помилка запису в стандартний вивід"
+
+msgid "unknown write failure on standard output"
+msgstr "невідома помилка запису в стандартний вивід"
+
+msgid "close failed on standard output"
+msgstr "не вдалося закрити стандартний вивід"
+
+#, c-format
+msgid "alias loop detected: expansion of '%s' does not terminate:%s"
+msgstr "виявлено цикл псевдонімів: розширення \"%s\" не припиняє:%s"
+
+#, c-format
+msgid "cannot handle %s as a builtin"
+msgstr "неможливо обробити %s як вбудовану команду"
+
+#, c-format
+msgid ""
+"usage: %s\n"
+"\n"
+msgstr "використання: %s\n"
+
+#, c-format
+msgid "expansion of alias '%s' failed; '%s' is not a git command\n"
+msgstr "не вдалося розширити псевдонім \"%s\"; \"%s\" не є командою git\n"
+
+#, c-format
+msgid "failed to run command '%s': %s\n"
+msgstr "не вдалося виконати команду \"%s\": %s\n"
+
+msgid "could not create temporary file"
+msgstr "не вдалося створити тимчасовий файл"
+
+#, c-format
+msgid "failed writing detached signature to '%s'"
+msgstr "не вдалося записати відʼєднаний підпис до \"%s\""
+
+msgid ""
+"gpg.ssh.allowedSignersFile needs to be configured and exist for ssh "
+"signature verification"
+msgstr ""
+"gpg.ssh.allowedSignersFile має бути налаштований та існувати для перевірки "
+"ssh підпису"
+
+msgid ""
+"ssh-keygen -Y find-principals/verify is needed for ssh signature "
+"verification (available in openssh version 8.2p1+)"
+msgstr ""
+"ssh-keygen -Y find-principals/verify необхідно для перевірки підпису ssh "
+"(доступно у версії openssh 8.2p1+)"
+
+#, c-format
+msgid "ssh signing revocation file configured but not found: %s"
+msgstr "файл скасування підпису ssh налаштовано, але не знайдено: %s"
+
+#, c-format
+msgid "bad/incompatible signature '%s'"
+msgstr "невірний/несумісний підпис \"%s\""
+
+#, c-format
+msgid "failed to get the ssh fingerprint for key '%s'"
+msgstr "не вдалося отримати ssh відбиток для ключа \"%s\""
+
+msgid ""
+"either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"
+msgstr ""
+"потрібно налаштувати або user.signingkey, або gpg.ssh.defaultKeyCommand"
+
+#, c-format
+msgid "gpg.ssh.defaultKeyCommand succeeded but returned no keys: %s %s"
+msgstr ""
+"gpg.ssh.defaultKeyCommand команда виконалася успішно, але не повернула "
+"жодного ключа: %s %s"
+
+#, c-format
+msgid "gpg.ssh.defaultKeyCommand failed: %s %s"
+msgstr "gpg.ssh.defaultKeyCommand завершився невдало: %s %s"
+
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg не вдалося підписати дані:\n"
+"%s"
+
+msgid "user.signingKey needs to be set for ssh signing"
+msgstr "user.signingKey має бути налаштований для ssh підписання"
+
+#, c-format
+msgid "failed writing ssh signing key to '%s'"
+msgstr "не вдалося записати ssh ключ підпису до \"%s\""
+
+#, c-format
+msgid "failed writing ssh signing key buffer to '%s'"
+msgstr "не вдалося записати буфер ssh ключа підпису в \"%s\""
+
+msgid ""
+"ssh-keygen -Y sign is needed for ssh signing (available in openssh version "
+"8.2p1+)"
+msgstr ""
+"ssh-keygen -Y знак потрібен для ssh підписання (доступно у openssh версії "
+"8.2p1+)"
+
+#, c-format
+msgid "failed reading ssh signing data buffer from '%s'"
+msgstr "не вдалося прочитати буфер даних ssh підписання з \"%s\""
+
+#, c-format
+msgid "ignored invalid color '%.*s' in log.graphColors"
+msgstr "проігноровано неприпустимий колір \"%.*s\" у log.graphColors"
+
+msgid ""
+"given pattern contains NULL byte (via -f <file>). This is only supported "
+"with -P under PCRE v2"
+msgstr ""
+"заданий шаблон містить NULL байт (через -f <файл>). Це підтримується лише з "
+"опцією -P у PCRE v2"
+
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "\"%s\": не вдалося прочитати %s"
+
+#, c-format
+msgid "'%s': short read"
+msgstr "\"%s\": помилка считування"
+
+msgid "start a working area (see also: git help tutorial)"
+msgstr "запустити робочу область (дивіться також: git help tutorial)"
+
+msgid "work on the current change (see also: git help everyday)"
+msgstr "працювати над поточними змінами (дивіться також: git help everyday)"
+
+msgid "examine the history and state (see also: git help revisions)"
+msgstr "переглянути історію та стан (дивіться також: git help revisions)"
+
+msgid "grow, mark and tweak your common history"
+msgstr "розвивати, позначати та підправляти вашу спільну історію"
+
+msgid "collaborate (see also: git help workflows)"
+msgstr "співпрацювати (дивіться також: git help workflows)"
+
+msgid "Main Porcelain Commands"
+msgstr "Основні високорівневі команди"
+
+msgid "Ancillary Commands / Manipulators"
+msgstr "Допоміжні команди / Маніпулятори"
+
+msgid "Ancillary Commands / Interrogators"
+msgstr "Допоміжні команди / Допитувачі"
+
+msgid "Interacting with Others"
+msgstr "Взаємодія з іншими"
+
+msgid "Low-level Commands / Manipulators"
+msgstr "Низькорівневі команди / Маніпулятори"
+
+msgid "Low-level Commands / Interrogators"
+msgstr "Низькорівневі команди / Допитувачі"
+
+msgid "Low-level Commands / Syncing Repositories"
+msgstr "Низькорівневі команди / Сховища синхронізації"
+
+msgid "Low-level Commands / Internal Helpers"
+msgstr "Низькорівневі команди / Внутрішні помічники"
+
+msgid "User-facing repository, command and file interfaces"
+msgstr "Інтерфейси користувача для сховищ, команд та файлів"
+
+msgid "Developer-facing file formats, protocols and other interfaces"
+msgstr "Формати файлів, протоколи та інші інтерфейси розробника"
+
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "доступні команди git в \"%s\""
+
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "команди git доступні з будь-якого місця на вашому $PATH"
+
+msgid "These are common Git commands used in various situations:"
+msgstr "Це загальні команди Git, які використовуються в різних ситуаціях:"
+
+msgid "The Git concept guides are:"
+msgstr "Посібники з понять Git:"
+
+msgid "User-facing repository, command and file interfaces:"
+msgstr "Інтерфейси користувача для сховищ, команд та файлів:"
+
+msgid "File formats, protocols and other developer interfaces:"
+msgstr "Формати файлів, протоколи та інші інтерфейси розробника:"
+
+msgid "External commands"
+msgstr "Зовнішні команди"
+
+msgid "Command aliases"
+msgstr "Псевдоніми команд"
+
+msgid "See 'git help <command>' to read about a specific subcommand"
+msgstr ""
+"Дивіться \"git help <команда>\", щоб прочитати про конкретну підкоманду"
+
+#, c-format
+msgid ""
+"'%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"
+"виконати її. Можливо, git-%s не працює?"
+
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git: \"%s\" не є командою git. Дивітья git --help."
+
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Ой-ой. Ваша система повідомляє про повну відсутність Git команд."
+
+#, c-format
+msgid "WARNING: You called a Git command named '%s', which does not exist."
+msgstr "ПОПЕРЕДЖЕННЯ: Ви викликали Git команду \"%s\", якої не існує."
+
+#, c-format
+msgid "Continuing under the assumption that you meant '%s'."
+msgstr "Продовжую, припускаючи, що ви мали на увазі \"%s\"."
+
+#, c-format
+msgid "Run '%s' instead [y/N]? "
+msgstr "Запустити натомість \"%s\" [y/N]? "
+
+#, c-format
+msgid "Continuing in %0.1f seconds, assuming that you meant '%s'."
+msgstr "Продовжую через %0.1f секунд, припускаючи, що ви мали на увазі \"%s\"."
+
+msgid ""
+"\n"
+"The most similar command is"
+msgid_plural ""
+"\n"
+"The most similar commands are"
+msgstr[0] ""
+"\n"
+"Найбільш схожою командою є"
+msgstr[1] ""
+"\n"
+"Найбільш схожими командами є"
+msgstr[2] ""
+"\n"
+"Найбільш схожими командами є"
+
+msgid "git version [--build-options]"
+msgstr "git версія [--build-options]"
+
+#, c-format
+msgid "%s: %s - %s"
+msgstr "%s: %s - %s"
+
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+"\n"
+"Ви це мали на увазі?"
+msgstr[1] ""
+"\n"
+"Ви мали на увазі одну з цих?"
+msgstr[2] ""
+"\n"
+"Ви мали на увазі одну з цих?"
+
+#, c-format
+msgid ""
+"The '%s' hook was ignored because it's not set as executable.\n"
+"You can disable this warning with `git config advice.ignoredHook false`."
+msgstr ""
+"Гачок \"%s\" було проігноровано, оскільки він не визначений як виконуваний.\n"
+"Ви можете вимкнути це попередження за допомогою \"git config advice."
+"ignoredHook false\"."
+
+#, c-format
+msgid "argument to --packfile must be a valid hash (got '%s')"
+msgstr "аргумент до --packfile має бути коректним хешем (отримано \"%s\")"
+
+msgid "not a git repository"
+msgstr "не є git сховищем"
+
+#, c-format
+msgid "negative value for http.postBuffer; defaulting to %d"
+msgstr ""
+"відʼємне значення для http.postBuffer; зміна до значеня за замовчуванням %d"
+
+msgid "Delegation control is not supported with cURL < 7.22.0"
+msgstr "Контроль делегування не підтримується з cURL < 7.22.0"
+
+msgid "Public key pinning not supported with cURL < 7.39.0"
+msgstr "Закріплення відкритих ключів не підтримується з cURL < 7.39.0"
+
+msgid "CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"
+msgstr "CURLSSLOPT_NO_REVOKE не підтримується з cURL < 7.44.0"
+
+#, c-format
+msgid "Unsupported SSL backend '%s'. Supported SSL backends:"
+msgstr "Непідтримуваний SSL обробник \"%s\". Підтримувані SSL обробники:"
+
+#, c-format
+msgid "Could not set SSL backend to '%s': cURL was built without SSL backends"
+msgstr ""
+"Не вдалося встановити SSL обробник в \"%s\": cURL було зібрано без підтримки "
+"SSL обробників"
+
+#, c-format
+msgid "Could not set SSL backend to '%s': already set"
+msgstr "Не вдалося встановити SSL обробник в \"%s\": вже встановлено"
+
+#, c-format
+msgid ""
+"unable to update url base from redirection:\n"
+"  asked for: %s\n"
+"   redirect: %s"
+msgstr ""
+"не вдалося оновити базу url з перенаправлення:\n"
+"  запитували: %s\n"
+"   перенаправлення: %s"
+
+msgid "Author identity unknown\n"
+msgstr "Особистість автора невідома\n"
+
+msgid "Committer identity unknown\n"
+msgstr "Особистість комітера невідома\n"
+
+msgid ""
+"\n"
+"*** Please tell me who you are.\n"
+"\n"
+"Run\n"
+"\n"
+"  git config --global user.email \"you@example.com\"\n"
+"  git config --global user.name \"Your Name\"\n"
+"\n"
+"to set your account's default identity.\n"
+"Omit --global to set the identity only in this repository.\n"
+"\n"
+msgstr ""
+"\n"
+"*** Будь ласка, скажіть мені, хто ви.\n"
+"\n"
+"Виконайте\n"
+"\n"
+"  git config --global user.email \"you@example.com\"\n"
+"  git config --global user.name \"Ваше Імʼя\"\n"
+"\n"
+"щоб встановити особистість акаунту за замовчуванням.\n"
+"Пропустіть --global, щоб встановити особистість лише для цього сховища.\n"
+
+msgid "no email was given and auto-detection is disabled"
+msgstr "не вказана адреса електронної пошти і автоматичне визначення вимкнено"
+
+#, c-format
+msgid "unable to auto-detect email address (got '%s')"
+msgstr ""
+"не вдалося автоматично визначити адресу електронної пошти (отримано \"%s\")"
+
+msgid "no name was given and auto-detection is disabled"
+msgstr "імʼя не вказано і автоматичне визначення вимкнено"
+
+#, c-format
+msgid "unable to auto-detect name (got '%s')"
+msgstr "не вдалося автоматично визначити назву (отримано \"%s\")"
+
+#, c-format
+msgid "empty ident name (for <%s>) not allowed"
+msgstr "порожнє імʼя особистості (для <%s>) заборонено"
+
+#, c-format
+msgid "name consists only of disallowed characters: %s"
+msgstr "імʼя складається лише з заборонених символів: %s"
+
+msgid "expected 'tree:<depth>'"
+msgstr "очікувалось \"tree:<глибина>\""
+
+msgid "sparse:path filters support has been dropped"
+msgstr "підтримку фільтрів sparse:path було припинено"
+
+#, c-format
+msgid "'%s' for 'object:type=<type>' is not a valid object type"
+msgstr "\"%s\" для \"object:type=<type>\" не є припустимим типом обʼєкта"
+
+#, c-format
+msgid "invalid filter-spec '%s'"
+msgstr "неприпустимий визначник фільтра \"%s\""
+
+#, c-format
+msgid "must escape char in sub-filter-spec: '%c'"
+msgstr "необхідно екранувати символ у sub-filter-spec: \"%c\""
+
+msgid "expected something after combine:"
+msgstr "щоcь очікувалось після комбінування:"
+
+msgid "multiple filter-specs cannot be combined"
+msgstr "не можна комбінувати кілька визначників фільтра"
+
+msgid "unable to upgrade repository format to support partial clone"
+msgstr "не вдалося оновити формат сховища для підтримки часткового клонування"
+
+msgid "args"
+msgstr "аргументи"
+
+msgid "object filtering"
+msgstr "фільтрація обʼєктів"
+
+#, c-format
+msgid "unable to access sparse blob in '%s'"
+msgstr "не вдалося отримати доступ до розрідженого blob в \"%s\""
+
+#, c-format
+msgid "unable to parse sparse filter data in %s"
+msgstr "не вдалося розібрати дані розрідженого фільтра в %s"
+
+#, c-format
+msgid "entry '%s' in tree %s has tree mode, but is not a tree"
+msgstr "запис \"%s\" у дереві %s має режим tree, але не є деревом"
+
+#, c-format
+msgid "entry '%s' in tree %s has blob mode, but is not a blob"
+msgstr "запис \"%s\" у дереві %s має режим blob, але не є blob"
+
+#, c-format
+msgid "unable to load root tree for commit %s"
+msgstr "не вдалося завантажити кореневе дерево для коміту %s"
+
+#, c-format
+msgid ""
+"Unable to create '%s.lock': %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"
+"\n"
+"Схоже, у цьому сховищі запущено інший git процес, наприклад\n"
+"редактор, відкритий командою \"git commit\". Будь ласка, переконайтеся, що "
+"всі процеси\n"
+"завершено, а потім спробуйте ще раз. Якщо все одно не вдасться, можливо, "
+"раніше \n"
+"у цьому сховищі аварійно завершився git процес:\n"
+"видаліть файл вручну, щоб продовжити."
+
+#, c-format
+msgid "Unable to create '%s.lock': %s"
+msgstr "Не вдалося створити \"%s.lock\": %s"
+
+#, c-format
+msgid "unexpected line: '%s'"
+msgstr "неочікуваний рядок: \"%s\""
+
+msgid "expected flush after ls-refs arguments"
+msgstr "очікувався flush після ls-refs аргументів"
+
+msgid "quoted CRLF detected"
+msgstr "виявлено цитований CRLF"
+
+#, c-format
+msgid "Failed to merge submodule %s (not checked out)"
+msgstr "Не вдалося обʼєднати підмодуль %s (не активне)"
+
+#, c-format
+msgid "Failed to merge submodule %s (no merge base)"
+msgstr "Не вдалося злити підмодуль %s (немає бази злиття)"
+
+#, c-format
+msgid "Failed to merge submodule %s (commits not present)"
+msgstr "Не вдалося злити підмодуль %s (відсутні коміти)"
+
+#, c-format
+msgid "Failed to merge submodule %s (commits don't follow merge-base)"
+msgstr "Не вдалося злити підмодуль %s (коміти не відповідають базі)"
+
+#, c-format
+msgid "Note: Fast-forwarding submodule %s to %s"
+msgstr "Примітка: перемотування підмодуля %s вперед до %s"
+
+#, c-format
+msgid "Failed to merge submodule %s"
+msgstr "Не вдалося злити підмодуль %s"
+
+#, c-format
+msgid ""
+"Failed to merge submodule %s, but a possible merge resolution exists: %s"
+msgstr "Не вдалося злити підмодуль %s, але існує можливе вирішення злиття: %s"
+
+#, c-format
+msgid ""
+"Failed to merge submodule %s, but multiple possible merges exist:\n"
+"%s"
+msgstr ""
+"Не вдалося злити підмодуль %s, але існує кілька можливих варіантів злиття:\n"
+"%s"
+
+msgid "failed to execute internal merge"
+msgstr "не вдалося виконати внутрішнє злиття"
+
+#, c-format
+msgid "unable to add %s to database"
+msgstr "не вдалося додати %s до бази даних"
+
+#, c-format
+msgid "Auto-merging %s"
+msgstr "Автоматичне злиття %s"
+
+#, c-format
+msgid ""
+"CONFLICT (implicit dir rename): Existing file/dir at %s in the way of "
+"implicit directory rename(s) putting the following path(s) there: %s."
+msgstr ""
+"КОНФЛІКТ (неявне перейменування каталогу): існуючий файл/директорія в %s "
+"заважає неявному перейменуванню директорії, додавши наступні шляхи до: %s."
+
+#, c-format
+msgid ""
+"CONFLICT (implicit dir rename): Cannot map more than one path to %s; "
+"implicit directory renames tried to put these paths there: %s"
+msgstr ""
+"КОНФЛІКТ (неявне перейменування директорії): Неможливо зіставити більше "
+"одного шляху з %s; неявне перейменування директорій намагалося помістити "
+"туди ці шляхи: %s"
+
+#, c-format
+msgid ""
+"CONFLICT (directory rename split): Unclear where to rename %s to; it was "
+"renamed to multiple other directories, with no destination getting a "
+"majority of the files."
+msgstr ""
+"КОНФЛІКТ (розбіжність під час перейменування директорії): Неясно, як "
+"перейменувати директорію %s; її було перейменовано в кілька інших "
+"директорій, жодна з яких не отримала більшості файлів."
+
+#, c-format
+msgid ""
+"WARNING: Avoiding applying %s -> %s rename to %s, because %s itself was "
+"renamed."
+msgstr ""
+"ПОПЕРЕДЖЕННЯ: уникнення застосування %s -> %s перейменування на %s, оскільки "
+"%s було перейменовано."
+
+#, c-format
+msgid ""
+"Path updated: %s added in %s inside a directory that was renamed in %s; "
+"moving it to %s."
+msgstr ""
+"Шлях було оновлено: %s додано до %s всередині директорії, яку було "
+"перейменовано в %s; переміщення до %s."
+
+#, c-format
+msgid ""
+"Path updated: %s renamed to %s in %s, inside a directory that was renamed in "
+"%s; moving it to %s."
+msgstr ""
+"Шлях було оновлено: %s перейменовано на %s в %s, всередині директорії, яку "
+"було перейменовано в %s; переміщення до %s."
+
+#, c-format
+msgid ""
+"CONFLICT (file location): %s added in %s inside a directory that was renamed "
+"in %s, suggesting it should perhaps be moved to %s."
+msgstr ""
+"КОНФЛІКТ (розташування файлу): %s додано до %s всередині директорії, яку "
+"було перейменовано в %s, що свідчить про те, що його слід перемістити до %s."
+
+#, c-format
+msgid ""
+"CONFLICT (file location): %s renamed to %s in %s, inside a directory that "
+"was renamed in %s, suggesting it should perhaps be moved to %s."
+msgstr ""
+"КОНФЛІКТ (розташування файлу): %s перейменовано на %s в %s всередині "
+"директорії, яку було перейменовано в %s, що свідчить про те, що його слід "
+"перемістити до %s."
+
+#, c-format
+msgid "CONFLICT (rename/rename): %s renamed to %s in %s and to %s in %s."
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): %s перейменовано на %s в %s та на %s "
+"в %s."
+
+#, c-format
+msgid ""
+"CONFLICT (rename involved in collision): rename of %s -> %s has content "
+"conflicts AND collides with another path; this may result in nested conflict "
+"markers."
+msgstr ""
+"КОНФЛІКТ (перейменування призвело до колізії): перейменування %s -> %s має "
+"конфлікт вмісту та зіткається з іншим шляхом; це може призвести до появи "
+"вкладених маркерів конфлікту."
+
+#, c-format
+msgid "CONFLICT (rename/delete): %s renamed to %s in %s, but deleted in %s."
+msgstr ""
+"КОНФЛІКТ (перейменовано/видалено): %s перейменовано на %s в %s, але видалено "
+"в %s."
+
+#, c-format
+msgid "cannot read object %s"
+msgstr "неможливо прочитати обʼєкт %s"
+
+#, c-format
+msgid "object %s is not a blob"
+msgstr "обʼєкт %s не є blob"
+
+#, c-format
+msgid ""
+"CONFLICT (file/directory): directory in the way of %s from %s; moving it to "
+"%s instead."
+msgstr ""
+"КОНФЛІКТ (файл/директорія): директорія на шляху %s від %s; переміщення її до "
+"%s замість цього."
+
+#, c-format
+msgid ""
+"CONFLICT (distinct types): %s had different types on each side; renamed both "
+"of them so each can be recorded somewhere."
+msgstr ""
+"КОНФЛІКТ (різні типи): %s мали різні типи з обох боків; перейменовано "
+"обидва, щоб кожен можна було десь записати."
+
+#, c-format
+msgid ""
+"CONFLICT (distinct types): %s had different types on each side; renamed one "
+"of them so each can be recorded somewhere."
+msgstr ""
+"КОНФЛІКТ (різні типи): %s мали різні типи з обох боків; перейменовано один з "
+"них, щоб кожен можна було десь записати."
+
+msgid "content"
+msgstr "вміст"
+
+msgid "add/add"
+msgstr "додано/додано"
+
+msgid "submodule"
+msgstr "підмодуль"
+
+#, c-format
+msgid "CONFLICT (%s): Merge conflict in %s"
+msgstr "КОНФЛІКТ (%s): конфлікт злиття в %s"
+
+#, c-format
+msgid ""
+"CONFLICT (modify/delete): %s deleted in %s and modified in %s.  Version %s "
+"of %s left in tree."
+msgstr ""
+"КОНФЛІКТ (змінено/видалено): %s видалено в %s та змінено в %s.  Версію %s з "
+"%s залишено у дереві."
+
+#. TRANSLATORS: This is a line of advice to resolve a merge
+#. conflict in a submodule. The first argument is the submodule
+#. name, and the second argument is the abbreviated id of the
+#. commit that needs to be merged.  For example:
+#.  - go to submodule (mysubmodule), and either merge commit abc1234"
+#.
+#, c-format
+msgid ""
+" - go to submodule (%s), and either merge commit %s\n"
+"   or update to an existing commit which has merged those changes\n"
+msgstr ""
+" - перейдіть до підмодуля (%s), щоб або злити коміт %s\n"
+"   або оновитись до існуючого коміту, в якому ці зміни вже злиті\n"
+
+#, c-format
+msgid ""
+"Recursive merging with submodules currently only supports trivial cases.\n"
+"Please manually handle the merging of each conflicted submodule.\n"
+"This can be accomplished with the following steps:\n"
+"%s - come back to superproject and run:\n"
+"\n"
+"      git add %s\n"
+"\n"
+"   to record the above merge or update\n"
+" - resolve any other conflicts in the superproject\n"
+" - commit the resulting index in the superproject\n"
+msgstr ""
+"Рекурсивне злиття з підмодулями наразі підтримує лише тривіальні випадки.\n"
+"Будь ласка, обробіть злиття кожного конфліктного підмодуля вручну.\n"
+"Це можна зробити за допомогою наступних кроків:\n"
+"%s - поверніться до суперпроекту і виконайте:\n"
+"\n"
+"      git add %s\n"
+"\n"
+"   щоб записати вищезгадане злиття або оновлення\n"
+"- розвʼяжіть будь-які інші конфлікти в суперпроекті\n"
+"- зробіть коміт підсумкового індекса в суперпроекті\n"
+
+#. TRANSLATORS: The %s arguments are: 1) tree hash of a merge
+#. base, and 2-3) the trees for the two trees we're merging.
+#.
+#, c-format
+msgid "collecting merge info failed for trees %s, %s, %s"
+msgstr "збирання інформації про злиття не вдалося для дерев %s, %s, %s"
+
+msgid "(bad commit)\n"
+msgstr "(невірний коміт)\n"
+
+#, c-format
+msgid "add_cacheinfo failed for path '%s'; merge aborting."
+msgstr "невдала спроба add_cacheinfo для шляху \"%s\"; переривання злиття."
+
+#, c-format
+msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting."
+msgstr ""
+"процесу add_cacheinfo не вдалося зробити оновлення для шляху \"%s\"; "
+"переривання злиття."
+
+#, c-format
+msgid "failed to create path '%s'%s"
+msgstr "не вдалося створити шлях \"%s\"%s"
+
+#, c-format
+msgid "Removing %s to make room for subdirectory\n"
+msgstr "Видалення %s, щоб звільнити місце для піддиректорії\n"
+
+msgid ": perhaps a D/F conflict?"
+msgstr ": можливо, D/F конфлікт?"
+
+#, c-format
+msgid "refusing to lose untracked file at '%s'"
+msgstr "відмовлено у втраті невідстежуваного файла \"%s\""
+
+#, c-format
+msgid "blob expected for %s '%s'"
+msgstr "blob очікується для %s \"%s\""
+
+#, c-format
+msgid "failed to open '%s': %s"
+msgstr "не вдалося відкрити \"%s\": %s"
+
+#, c-format
+msgid "failed to symlink '%s': %s"
+msgstr "не вдалося зробити символьне посилання \"%s\": %s"
+
+#, c-format
+msgid "do not know what to do with %06o %s '%s'"
+msgstr "не знаю, що робити з %06o %s \"%s\""
+
+#, c-format
+msgid "Fast-forwarding submodule %s to the following commit:"
+msgstr "Перемотування підмодуля %s вперед до наступного коміту:"
+
+#, c-format
+msgid "Fast-forwarding submodule %s"
+msgstr "Перемотування підмодуля %s вперед"
+
+#, c-format
+msgid "Failed to merge submodule %s (merge following commits not found)"
+msgstr "Не вдалося злити підмодуль %s (злиття наступних комітів не знайдено)"
+
+#, c-format
+msgid "Failed to merge submodule %s (not fast-forward)"
+msgstr "Не вдалося злити підмодуль %s (не вдалося перемотати вперед)"
+
+msgid "Found a possible merge resolution for the submodule:\n"
+msgstr "Знайдено можливе вирішення злиття для підмодуля:\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 ""
+"Якщо все вірно, просто додайте це до індексу, наприклад,\n"
+"використавши:\n"
+"\n"
+"  git update-index --cacheinfo 160000 %s \"%s\"\n"
+"\n"
+"який прийме цю пропозицію.\n"
+
+#, c-format
+msgid "Failed to merge submodule %s (multiple merges found)"
+msgstr "Не вдалося злити підмодуль %s (знайдено більше одного злиття)"
+
+#, c-format
+msgid "Error: Refusing to lose untracked file at %s; writing to %s instead."
+msgstr ""
+"Помилка: відмовлено у втраті невідстежуваного файла %s; натомість записуємо "
+"до %s."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
+"in tree."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s та %s в %s. Версію %s з %s залишено "
+"в дереві."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s "
+"left in tree."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s і %s в %s в %s. Версію %s з %s "
+"залишено у дереві."
+
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
+"in tree at %s."
+msgstr ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s і %s в %s. Версію %s з %s залишено "
+"у дереві в %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 ""
+"КОНФЛІКТ (%s/видалено): %s видалено в %s і %s в %s в %s. Версію %s з %s "
+"залишено в дереві в %s."
+
+msgid "rename"
+msgstr "перейменувати"
+
+msgid "renamed"
+msgstr "перейменовано"
+
+#, c-format
+msgid "Refusing to lose dirty file at %s"
+msgstr "Відмовлено у втраті брудного файла %s"
+
+#, c-format
+msgid "Refusing to lose untracked file at %s, even though it's in the way."
+msgstr ""
+"Відмовлено у втраті невідстежуваного файла %s, не дивлячись на те, що він "
+"знаходиться на шляху."
+
+#, c-format
+msgid "CONFLICT (rename/add): Rename %s->%s in %s.  Added %s in %s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/додано): перейменовано %s->%s в %s.  Додано %s в %s"
+
+#, c-format
+msgid "%s is a directory in %s adding as %s instead"
+msgstr "%s є директорією в %s, натомість додаємо як %s"
+
+#, c-format
+msgid "Refusing to lose untracked file at %s; adding as %s instead"
+msgstr "Відмовлено у втраті невідстежуваного файла %s; натомість додаємо як %s"
+
+#, c-format
+msgid ""
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename "
+"\"%s\"->\"%s\" in \"%s\"%s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): перейменовано \"%s\"->\"%s\" у гілці "
+"\"%s\" перейменовано \"%s\"->\"%s\" у \"%s\"%s"
+
+msgid " (left unresolved)"
+msgstr " (залишилося нерозвʼязаним)"
+
+#, c-format
+msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): перейменовано %s->%s в %s. "
+"Перейменовано %s->%s в %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 ""
+"КОНФЛІКТ (розбіжність під час перейменування директорії): Неясно, куди "
+"помістити %s, оскільки директорію %s було перейменовано в кілька інших "
+"директорій,  жодна з яких не отримала більшості файлів."
+
+#, c-format
+msgid ""
+"CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-"
+">%s in %s"
+msgstr ""
+"КОНФЛІКТ (перейменовано/перейменовано): перейменовано директорію %s->%s в "
+"%s. Перейменовано директорію %s->%s в %s"
+
+msgid "modify"
+msgstr "змінити"
+
+msgid "modified"
+msgstr "змінено"
+
+#, c-format
+msgid "Skipped %s (merged same as existing)"
+msgstr "Пропущено %s (злите - те саме, що й існуюче)"
+
+#, c-format
+msgid "Adding as %s instead"
+msgstr "Додавання як %s замість цього"
+
+#, c-format
+msgid "Removing %s"
+msgstr "Видалення %s"
+
+msgid "file/directory"
+msgstr "файл/директорія"
+
+msgid "directory/file"
+msgstr "директорія/файл"
+
+#, c-format
+msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
+msgstr "КОНФЛІКТ (%s): в %s існує директорія з іменем %s. Додавання %s як %s"
+
+#, c-format
+msgid "Adding %s"
+msgstr "Додавання %s"
+
+#, c-format
+msgid "CONFLICT (add/add): Merge conflict in %s"
+msgstr "КОНФЛІКТ (додано/додано): Конфлікт злиття у %s"
+
+#, c-format
+msgid "merging of trees %s and %s failed"
+msgstr "не вдалося злити дерева %s та %s"
+
+msgid "Merging:"
+msgstr "Злиття:"
+
+#, c-format
+msgid "found %u common ancestor:"
+msgid_plural "found %u common ancestors:"
+msgstr[0] "знайшли %u спільного предка:"
+msgstr[1] "знайшли %u спільних предків:"
+msgstr[2] "знайшли %u спільних предків:"
+
+msgid "merge returned no commit"
+msgstr "злиття не повернуло коміт"
+
+#, c-format
+msgid "Could not parse object '%s'"
+msgstr "Не вдалося розібрати обʼєкт \"%s\""
+
+msgid "failed to read the cache"
+msgstr "не вдалося прочитати кеш"
+
+msgid "multi-pack-index OID fanout is of the wrong size"
+msgstr "multi-pack-index OID розсіювання має невірний розмір"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"невірна послідовність oid fanout: fanout[%d] = %<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "multi-pack-index OID lookup шматок має невірний розмір"
+
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "multi-pack-index object offset шматок має невірний розмір"
+
+#, c-format
+msgid "multi-pack-index file %s is too small"
+msgstr "multi-pack-index файл %s занадто малий"
+
+#, c-format
+msgid "multi-pack-index signature 0x%08x does not match signature 0x%08x"
+msgstr "multi-pack-index підпис 0x%08x не збігається з підписом 0x%08x"
+
+#, c-format
+msgid "multi-pack-index version %d not recognized"
+msgstr "multi-pack-index версія %d не розпізнана"
+
+#, c-format
+msgid "multi-pack-index hash version %u does not match version %u"
+msgstr "версія хешу multi-pack-index %u не збігається з версією %u"
+
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "необхідний шматок pack-name multi-pack-index відсутній або пошкоджений"
+
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr ""
+"необхідний шматок OID fanout multi-pack-index відсутній або пошкоджений"
+
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr ""
+"необхідний шматок OID lookup multi-pack-index відсутній або пошкоджений"
+
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr ""
+"необхідний шматок object offsets multi-pack-index відсутній або пошкоджений"
+
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "шматок pack-name multi-pack-index занадто малий"
+
+#, c-format
+msgid "multi-pack-index pack names out of order: '%s' before '%s'"
+msgstr ""
+"multi-pack-index назви пакунків знаходяться у невірній послідовності: \"%s\" "
+"перед \"%s\""
+
+#, c-format
+msgid "bad pack-int-id: %u (%u total packs)"
+msgstr "невірний pack-int-id: %u (%u всього пакунків)"
+
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX не містить шматок BTMP"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "не вдалося завантажити бітмаповий пакунок %<PRIu32>"
+
+msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
+msgstr ""
+"multi-pack-index зберігає 64-бітне зміщення, але значення off_t занадто мале"
+
+msgid "multi-pack-index large offset out of bounds"
+msgstr "large offset multi-pack-index виходить за межі"
+
+#, c-format
+msgid "failed to add packfile '%s'"
+msgstr "не вдалося додати packfile \"%s\""
+
+#, c-format
+msgid "failed to open pack-index '%s'"
+msgstr "не вдалося відкрити pack-index \"%s\""
+
+#, c-format
+msgid "failed to locate object %d in packfile"
+msgstr "не вдалося знайти обʼєкт %d у файлі пакунка"
+
+msgid "cannot store reverse index file"
+msgstr "неможливо зберегти файл зворотнього індексу"
+
+#, c-format
+msgid "could not parse line: %s"
+msgstr "не вдалося розібрати рядок: %s"
+
+#, c-format
+msgid "malformed line: %s"
+msgstr "невірно сформований рядок: %s"
+
+msgid "ignoring existing multi-pack-index; checksum mismatch"
+msgstr ""
+"ігнорування існуючого multi-pack-index; невідповідність контрольних сум"
+
+msgid "could not load pack"
+msgstr "не вдалося завантажити пакет"
+
+#, c-format
+msgid "could not open index for %s"
+msgstr "не вдалося відкрити індекс для %s"
+
+msgid "Adding packfiles to multi-pack-index"
+msgstr "Додавання пакунків до multi-pack-index"
+
+#, c-format
+msgid "unknown preferred pack: '%s'"
+msgstr "невідомий бажаний пакунок: \"%s\""
+
+#, c-format
+msgid "cannot select preferred pack %s with no objects"
+msgstr "неможливо вибрати бажаний пакунок %s за відсутності об’єктів"
+
+#, c-format
+msgid "did not see pack-file %s to drop"
+msgstr "не побачив файл пакунка %s, який потрібно скинути"
+
+#, c-format
+msgid "preferred pack '%s' is expired"
+msgstr "у бажаного пакета \"%s\" закінчився термін дії"
+
+msgid "no pack files to index."
+msgstr "немає файлів пакунків для індексації."
+
+msgid "refusing to write multi-pack .bitmap without any objects"
+msgstr "відмовлено в записі мультіпакункового .bitmap без обʼєктів"
+
+msgid "could not write multi-pack bitmap"
+msgstr "не вдалося записати мультіпакунковий bitmap"
+
+msgid "could not write multi-pack-index"
+msgstr "не вдалося записати multi-pack-index"
+
+#, c-format
+msgid "failed to clear multi-pack-index at %s"
+msgstr "не вдалося очистити multi-pack-index при %s"
+
+msgid "multi-pack-index file exists, but failed to parse"
+msgstr "multi-pack-index файл існує, але його не вдалося розібрати"
+
+msgid "incorrect checksum"
+msgstr "невірна контрольна сума"
+
+msgid "Looking for referenced packfiles"
+msgstr "Пошук файлів пакунків, на які є посилання"
+
+msgid "the midx contains no oid"
+msgstr "midx не містить oid"
+
+msgid "Verifying OID order in multi-pack-index"
+msgstr "Перевірка OID послідовності в multi-pack-index"
+
+#, c-format
+msgid "oid lookup out of order: oid[%d] = %s >= %s = oid[%d]"
+msgstr "невірна послідовність oid lookup: oid[%d] = %s >= %s = oid[%d]"
+
+msgid "Sorting objects by packfile"
+msgstr "Сортування обʼєктів за файлом пакунка"
+
+msgid "Verifying object offsets"
+msgstr "Перевірка зміщень обʼєкта"
+
+#, c-format
+msgid "failed to load pack entry for oid[%d] = %s"
+msgstr "не вдалося завантажити запис пакунка для oid[%d] = %s"
+
+#, c-format
+msgid "failed to load pack-index for packfile %s"
+msgstr "не вдалося завантажити індекс файла пакунка %s"
+
+#, c-format
+msgid "incorrect object offset for oid[%d] = %s: %<PRIx64> != %<PRIx64>"
+msgstr "невірне зміщення обʼєкта для oid[%d] = %s: %<PRIx64> != %<PRIx64>"
+
+msgid "Counting referenced objects"
+msgstr "Підрахунок обʼєктів, на які є посилання"
+
+msgid "Finding and deleting unreferenced packfiles"
+msgstr "Пошук і видалення файлів пакунків без посилань"
+
+msgid "could not start pack-objects"
+msgstr "не вдалося розпочати pack-objects"
+
+msgid "could not finish pack-objects"
+msgstr "не вдалося завершити pack-objects"
+
+#, c-format
+msgid "unable to create lazy_dir thread: %s"
+msgstr "не вдалося створити lazy_dir потік: %s"
+
+#, c-format
+msgid "unable to create lazy_name thread: %s"
+msgstr "не вдалося створити lazy_name потік: %s"
+
+#, c-format
+msgid "unable to join lazy_name thread: %s"
+msgstr "не вдалося приєднатися до lazy_name потоку: %s"
+
+#, c-format
+msgid ""
+"You have not concluded your previous notes merge (%s exists).\n"
+"Please, use 'git notes merge --commit' or 'git notes merge --abort' to "
+"commit/abort the previous merge before you start a new notes merge."
+msgstr ""
+"Ви не завершили попереднє злиття нотаток (існує %s).\n"
+"Будь ласка, скористайтесь командами \"git notes merge --commit\" або \"git "
+"notes merge --abort\", щоб зробити коміт/скасувати попереднє злиття, перш "
+"ніж розпочати нове злиття нотаток."
+
+#, c-format
+msgid "You have not concluded your notes merge (%s exists)."
+msgstr "Ви не завершили злиття ваших нотаток (%s існує)."
+
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Неможливо зробити коміт неініціалізованого/непосиланого дерева нотаток"
+
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Невірнe notes.rewriteMode значення: \"%s\""
+
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Відмовлено в перезаписі нотаток у %s (за межами refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of
+#. the environment variable, the second %s is
+#. its value.
+#.
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Невірне %s значення: \"%s\""
+
+#, c-format
+msgid "object directory %s does not exist; check .git/objects/info/alternates"
+msgstr "директорія об’єкта %s не існує; перевірте .git/objects/info/alternates"
+
+#, c-format
+msgid "unable to normalize alternate object path: %s"
+msgstr "не вдалося нормалізувати шлях запозиченого обʼєкта: %s"
+
+#, c-format
+msgid "%s: ignoring alternate object stores, nesting too deep"
+msgstr ""
+"%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\" не існує"
+
+#, c-format
+msgid "reference repository '%s' as a linked checkout is not supported yet."
+msgstr "посилання на сховище \"%s\" як повʼязане поки що не підтримується."
+
+#, c-format
+msgid "reference repository '%s' is not a local repository."
+msgstr "сховище посилання \"%s\" не є локальним сховищем."
+
+#, c-format
+msgid "reference repository '%s' is shallow"
+msgstr "сховище посилання \"%s\" є неглибоким"
+
+#, c-format
+msgid "reference repository '%s' is grafted"
+msgstr "сховище посилання \"%s\" прищеплено"
+
+#, c-format
+msgid "could not find object directory matching %s"
+msgstr "не вдалося знайти директорію обʼєктів, що відповідає %s"
+
+#, c-format
+msgid "invalid line while parsing alternate refs: %s"
+msgstr "неприпустимий рядок при розбиранні посилань запозичених обʼєктів: %s"
+
+#, c-format
+msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>"
+msgstr "спроба виконати mmap %<PRIuMAX> за межею %<PRIuMAX>"
+
+#, c-format
+msgid "mmap failed%s"
+msgstr "операція mmap не вдалася%s"
+
+#, c-format
+msgid "object file %s is empty"
+msgstr "обʼєктний файл %s порожній"
+
+#, c-format
+msgid "corrupt loose object '%s'"
+msgstr "пошкоджено вільний обʼєкт \"%s\""
+
+#, c-format
+msgid "garbage at end of loose object '%s'"
+msgstr "непотріб в кінці вільного обʼєкта \"%s\""
+
+#, c-format
+msgid "unable to open loose object %s"
+msgstr "не вдалося відкрити вільний обʼєкт %s"
+
+#, c-format
+msgid "unable to parse %s header"
+msgstr "не вдалося розібрати заголовок %s"
+
+msgid "invalid object type"
+msgstr "неприпустимий тип обʼєкта"
+
+#, c-format
+msgid "unable to unpack %s header"
+msgstr "не вдалося розпакувати %s заголовок"
+
+#, c-format
+msgid "header for %s too long, exceeds %d bytes"
+msgstr "заголовок для %s занадто довгий, перевищує %d байт"
+
+#, c-format
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "вільний обʼєкт %s (що зберігається у %s) пошкоджено"
+
+#, c-format
+msgid "replacement %s not found for %s"
+msgstr "заміна %s не знайдена для %s"
+
+#, c-format
+msgid "packed object %s (stored in %s) is corrupt"
+msgstr "упакований обʼєкт %s (що зберігається у %s) пошкоджено"
+
+#, c-format
+msgid "unable to write file %s"
+msgstr "не вдалося записати файл %s"
+
+#, c-format
+msgid "unable to set permission to '%s'"
+msgstr "не вдалося встановити дозволи для \"%s\""
+
+msgid "error when closing loose object file"
+msgstr "помилка при закритті файла вільного об’єкта"
+
+#, c-format
+msgid "insufficient permission for adding an object to repository database %s"
+msgstr "недостатньо дозволу для додавання обʼєкта до бази даних сховища %s"
+
+msgid "unable to create temporary file"
+msgstr "не вдалося створити тимчасовий файл"
+
+msgid "unable to write loose object file"
+msgstr "не вдалося записати файл вільного об’єкта"
+
+#, c-format
+msgid "unable to deflate new object %s (%d)"
+msgstr "не вдалося запакувати новий обʼєкт %s (%d)"
+
+#, c-format
+msgid "deflateEnd on object %s failed (%d)"
+msgstr "операція deflateEnd на обʼєкті %s завершилася невдало (%d)"
+
+#, c-format
+msgid "confused by unstable object source data for %s"
+msgstr "збентежений нестабільними початковими даними обʼєкта для %s"
+
+#, c-format
+msgid "write stream object %ld != %<PRIuMAX>"
+msgstr "запис обʼєкта потока %ld != %<PRIuMAX>"
+
+#, c-format
+msgid "unable to stream deflate new object (%d)"
+msgstr "не вдалося запакувати новий обʼєкт (%d) при передачі"
+
+#, c-format
+msgid "deflateEnd on stream object failed (%d)"
+msgstr "операція deflateEnd на обʼєкті потоку завершилася невдало (%d)"
+
+#, c-format
+msgid "unable to create directory %s"
+msgstr "не вдалося створити директорію %s"
+
+#, c-format
+msgid "cannot read object for %s"
+msgstr "неможливо прочитати обʼєкт для %s"
+
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "об’єкт не пройшов перевірку fsck: %s"
+
+msgid "refusing to create malformed object"
+msgstr "відмовлено в створенні невірно сформованого обʼєкта"
+
+#, c-format
+msgid "read error while indexing %s"
+msgstr "помилка считування при індексаціі %s"
+
+#, c-format
+msgid "short read while indexing %s"
+msgstr "помилка считування замалого вмісту при індексаціі %s"
+
+#, c-format
+msgid "%s: failed to insert into database"
+msgstr "%s: не вдалося внести до бази даних"
+
+#, c-format
+msgid "%s: unsupported file type"
+msgstr "%s: непідтримуваний тип файлу"
+
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s не є допустимим \"%s\" обʼєктом"
+
+#, c-format
+msgid "unable to open %s"
+msgstr "не вдалося відкрити %s"
+
+#, c-format
+msgid "hash mismatch for %s (expected %s)"
+msgstr "невідповідність хешу для %s (очікувалось %s)"
+
+#, c-format
+msgid "unable to mmap %s"
+msgstr "не вдалося виконати mmap %s"
+
+#, c-format
+msgid "unable to unpack header of %s"
+msgstr "не вдалося розпакувати заголовок %s"
+
+#, c-format
+msgid "unable to parse header of %s"
+msgstr "не вдалося розібрати заголовок %s"
+
+#, c-format
+msgid "unable to unpack contents of %s"
+msgstr "не вдалося розпакувати вміст %s"
+
+#. TRANSLATORS: This is a line of ambiguous object
+#. output shown when we cannot look up or parse the
+#. object in question. E.g. "deadbeef [bad object]".
+#.
+#, c-format
+msgid "%s [bad object]"
+msgstr "%s [невірний обʼект]"
+
+#. TRANSLATORS: This is a line of ambiguous commit
+#. object output. E.g.:
+#. *
+#.    "deadbeef commit 2021-01-01 - Some Commit Message"
+#.
+#, c-format
+msgid "%s commit %s - %s"
+msgstr "%s коміт %s - %s"
+
+#. TRANSLATORS: This is a line of ambiguous
+#. tag object output. E.g.:
+#. *
+#.    "deadbeef tag 2022-01-01 - Some Tag Message"
+#. *
+#. The second argument is the YYYY-MM-DD found
+#. in the tag.
+#. *
+#. The third argument is the "tag" string
+#. from object.c.
+#.
+#, c-format
+msgid "%s tag %s - %s"
+msgstr "%s тег %s - %s"
+
+#. TRANSLATORS: This is a line of ambiguous
+#. tag object output where we couldn't parse
+#. the tag itself. E.g.:
+#. *
+#.    "deadbeef [bad tag, could not parse it]"
+#.
+#, c-format
+msgid "%s [bad tag, could not parse it]"
+msgstr "%s [невірний тег, не вдалося розібрати]"
+
+#. TRANSLATORS: This is a line of ambiguous <type>
+#. object output. E.g. "deadbeef tree".
+#.
+#, c-format
+msgid "%s tree"
+msgstr "%s дерево"
+
+#. TRANSLATORS: This is a line of ambiguous <type>
+#. object output. E.g. "deadbeef blob".
+#.
+#, c-format
+msgid "%s blob"
+msgstr "%s blob"
+
+#, c-format
+msgid "short object ID %s is ambiguous"
+msgstr "короткий ідентифікатор обʼєкта %s неоднозначний"
+
+#. TRANSLATORS: The argument is the list of ambiguous
+#. objects composed in show_ambiguous_object(). See
+#. its "TRANSLATORS" comments for details.
+#.
+#, c-format
+msgid ""
+"The candidates are:\n"
+"%s"
+msgstr ""
+"Кандидати такі:\n"
+"%s"
+
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git switch -c $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+"Зазвичай Git ніколи не створює посилання, яке закінчується 40-hex "
+"символами,\n"
+"тому що воно буде проігноровано, якщо ви просто вкажете 40-hex. Такі "
+"посилання\n"
+"можуть бути створені помилково. Наприклад\n"
+"\n"
+"  git switch -c $br $(git rev-parse ...)\n"
+"\n"
+"де \"$br\" якимось чином порожнє, створює 40-hex посилання. Будь ласка\n"
+"перевірте ці посилання і, можливо, видаліть їх. Щоб вимкнути це "
+"повідомлення, виконайте\n"
+"\"git config advice.objectNameWarning false\""
+
+#, c-format
+msgid "log for '%.*s' only goes back to %s"
+msgstr "лог для \"%.*s\" містить записи тільки до %s"
+
+#, c-format
+msgid "log for '%.*s' only has %d entries"
+msgstr "лог для \"%.*s\" містить лише %d записів"
+
+#, c-format
+msgid "path '%s' exists on disk, but not in '%.*s'"
+msgstr "шлях \"%s\" існує на диску, але не в \"%.*s\""
+
+#, c-format
+msgid ""
+"path '%s' exists, but not '%s'\n"
+"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?"
+msgstr ""
+"шлях \"%s\" існує, але не \"%s\"\n"
+"підказка: ви мали на увазі \"%.*s:%s\" або \"%.*s:./%s\"?"
+
+#, c-format
+msgid "path '%s' does not exist in '%.*s'"
+msgstr "шлях \"%s\" не існує в \"%.*s\""
+
+#, c-format
+msgid ""
+"path '%s' is in the index, but not at stage %d\n"
+"hint: Did you mean ':%d:%s'?"
+msgstr ""
+"шлях \"%s\" є в індексі, але відсутній на стадії %d\n"
+"підказка: ви мали на увазі \":%d:%s\"?"
+
+#, c-format
+msgid ""
+"path '%s' is in the index, but not '%s'\n"
+"hint: Did you mean ':%d:%s' aka ':%d:./%s'?"
+msgstr ""
+"шлях \"%s\" є в індексі, але не \"%s\"\n"
+"підказка: ви мали на увазі \"%d:%s\" або \"%d:./%s\"?"
+
+#, c-format
+msgid "path '%s' exists on disk, but not in the index"
+msgstr "шлях \"%s\" існує на диску, але відсутній в індексі"
+
+#, c-format
+msgid "path '%s' does not exist (neither on disk nor in the index)"
+msgstr "шлях \"%s\" не існує (ні на диску, ні в індексі)"
+
+msgid "relative path syntax can't be used outside working tree"
+msgstr ""
+"синтаксис відносного шляху не можна використовувати поза робочим деревом"
+
+#, c-format
+msgid "<object>:<path> required, only <object> '%s' given"
+msgstr "потрібно <обʼєкт>:<шлях>, надано лише <обʼєкт> \"%s\""
+
+#, c-format
+msgid "invalid object name '%.*s'."
+msgstr "неприпустима назва обʼєкта \"%.*s\"."
+
+#, c-format
+msgid "invalid object type \"%s\""
+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"
+
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "не вдалося розібрати обʼєкт: %s"
+
+#, c-format
+msgid "hash mismatch %s"
+msgstr "невідповідність хешу %s"
+
+msgid "trying to write commit not in index"
+msgstr "спроба записати коміт, якого немає в індексі"
+
+msgid "failed to load bitmap index (corrupted?)"
+msgstr "не вдалося завантажити bitmap індекс (пошкоджено?)"
+
+msgid "corrupted bitmap index (too small)"
+msgstr "пошкоджений bitmap індекс (занадто малий)"
+
+msgid "corrupted bitmap index file (wrong header)"
+msgstr "пошкоджений файл bitmap індексу (занадто малий)"
+
+#, c-format
+msgid "unsupported version '%d' for bitmap index file"
+msgstr "непідтримувана версія \"%d\" для файлу bitmap індексу"
+
+msgid "corrupted bitmap index file (too short to fit hash cache)"
+msgstr ""
+"пошкоджений файл bitmap індексу (занадто короткий щоб умістити кеш хеша)"
+
+msgid "corrupted bitmap index file (too short to fit lookup table)"
+msgstr ""
+"пошкоджений файл bitmap індексу (занадто короткий, щоб вмістити таблицю "
+"пошуку)"
+
+#, c-format
+msgid "duplicate entry in bitmap index: '%s'"
+msgstr "дубльований запис в bitmap індексі: \"%s\""
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for entry %d"
+msgstr "пошкоджений ewah bitmap: урізаний заголовок для запису %d"
+
+#, c-format
+msgid "corrupt ewah bitmap: commit index %u out of range"
+msgstr "пошкоджений ewah bitmap: індекс коміту %u поза межами діапазону"
+
+msgid "corrupted bitmap pack index"
+msgstr "пошкоджений bitmap індекс пакунка"
+
+msgid "invalid XOR offset in bitmap pack index"
+msgstr "невірне XOR зміщення в bitmap індексі пакунка"
+
+msgid "cannot fstat bitmap file"
+msgstr "неможливо виконати fstat для bitmap файла"
+
+msgid "checksum doesn't match in MIDX and bitmap"
+msgstr "контрольна сума не збігається в MIDX і bitmap"
+
+msgid "multi-pack bitmap is missing required reverse index"
+msgstr "у мультіпакунковому bitmap відсутній необхідний зворотний індекс"
+
+#, c-format
+msgid "could not open pack %s"
+msgstr "не вдалося відкрити пакунок %s"
+
+msgid "could not determine MIDX preferred pack"
+msgstr "не вдалося визначити бажаний пакунок MIDX"
+
+#, c-format
+msgid "preferred pack (%s) is invalid"
+msgstr "бажаний пакунок (%s) є неприпустимим"
+
+msgid "corrupt bitmap lookup table: triplet position out of index"
+msgstr "пошкоджена bitmap таблиця пошуку: триплетна позиція поза індексом"
+
+msgid "corrupt bitmap lookup table: xor chain exceeds entry count"
+msgstr ""
+"пошкоджена bitmap таблиця пошуку: xor ланцюжок перевищує кількість записів"
+
+#, c-format
+msgid "corrupt bitmap lookup table: commit index %u out of range"
+msgstr ""
+"пошкоджена bitmap таблиця пошуку: індекс коміту %u поза межами діапазону"
+
+#, c-format
+msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
+msgstr "пошкоджений ewah bitmap: урізаний заголовок для bitmap коміту \"%s\""
+
+#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"не вдалося завантажити пакунок: \"%s\", вимкнення повторного використання "
+"пакунків"
+
+#, c-format
+msgid "object '%s' not found in type bitmaps"
+msgstr "обʼєкт \"%s\" не знайдено в типах bitmap"
+
+#, c-format
+msgid "object '%s' does not have a unique type"
+msgstr "обʼєкт \"%s\" не має унікального типу"
+
+#, c-format
+msgid "object '%s': real type '%s', expected: '%s'"
+msgstr "обʼєкт \"%s\": дійсний тип \"%s\", очікуваний: \"%s\""
+
+#, c-format
+msgid "object not in bitmap: '%s'"
+msgstr "обʼєкт не у bitmap: \"%s\""
+
+msgid "failed to load bitmap indexes"
+msgstr "не вдалося завантажити bitmap індекси"
+
+msgid "you must specify exactly one commit to test"
+msgstr "ви маєте вказати лишень один коміт для тестування"
+
+#, c-format
+msgid "commit '%s' doesn't have an indexed bitmap"
+msgstr "коміт \"%s\" не має індексованого bitmap"
+
+msgid "mismatch in bitmap results"
+msgstr "розбіжність в bitmap результатах"
+
+#, c-format
+msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
+msgstr "не вдалося знайти \"%s\" у пакунку \"%s\" зі зміщенням %<PRIuMAX>"
+
+#, c-format
+msgid "unable to get disk usage of '%s'"
+msgstr ""
+"не вдалося отримати дані про використання дискового простору для \"%s\""
+
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "bitmap файл \"%s\" має невірну контрольну суму"
+
+#, c-format
+msgid "mtimes file %s is too small"
+msgstr "mtimes файл %s занадто малий"
+
+#, c-format
+msgid "mtimes file %s has unknown signature"
+msgstr "mtimes файл %s має невідомий підпис"
+
+#, c-format
+msgid "mtimes file %s has unsupported version %<PRIu32>"
+msgstr "mtimes файл %s має непідтримувану версію %<PRIu32>"
+
+#, c-format
+msgid "mtimes file %s has unsupported hash id %<PRIu32>"
+msgstr "mtimes файл %s має непідтримуваний хеш-ідентифікатор %<PRIu32>"
+
+#, c-format
+msgid "mtimes file %s is corrupt"
+msgstr "mtimes файл %s пошкоджено"
+
+#, c-format
+msgid "reverse-index file %s is too small"
+msgstr "файл зворотного індексу %s занадто малий"
+
+#, c-format
+msgid "reverse-index file %s is corrupt"
+msgstr "файл зворотнього індексу %s пошкоджено"
+
+#, c-format
+msgid "reverse-index file %s has unknown signature"
+msgstr "файл зворотного індексу %s має невідомий підпис"
+
+#, c-format
+msgid "reverse-index file %s has unsupported version %<PRIu32>"
+msgstr "файл зворотного індексу %s має непідтримувану версію %<PRIu32>"
+
+#, c-format
+msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
+msgstr ""
+"файл зворотного індексу %s має непідтримуваний хеш-ідентифікатор %<PRIu32>"
+
+msgid "invalid checksum"
+msgstr "неприпустима контрольна сума"
+
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr ""
+"невірна позиція зворортного індексу у %<PRIu64>: %<PRIu32> != %<PRIu32>"
+
+msgid "multi-pack-index reverse-index chunk is the wrong size"
+msgstr "multi-pack-index reverse-index шматок має невірний розмір"
+
+msgid "could not determine preferred pack"
+msgstr "не вдалося визначити бажаний пакунок"
+
+msgid "cannot both write and verify reverse index"
+msgstr "неможливо одночасно записувати та звіряти зворотний індекс"
+
+#, c-format
+msgid "could not stat: %s"
+msgstr "не вдалося виконати stat: %s"
+
+#, c-format
+msgid "failed to make %s readable"
+msgstr "не вдалося зробити %s читабельним"
+
+#, c-format
+msgid "could not write '%s' promisor file"
+msgstr "не вдалося записати promisor файл \"%s\""
+
+msgid "offset before end of packfile (broken .idx?)"
+msgstr "зміщення перед кінцем файла пакунка (пошкоджений .idx?)"
+
+#, c-format
+msgid "packfile %s cannot be mapped%s"
+msgstr "файл пакунка %s не може бути mapped%s"
+
+#, c-format
+msgid "offset before start of pack index for %s (corrupt index?)"
+msgstr "зміщення перед початком індекса пакунка для %s (пошкоджений індекс?)"
+
+#, c-format
+msgid "offset beyond end of pack index for %s (truncated index?)"
+msgstr "зміщення за межою кінця індекса пакунка для %s (зрізаний індекс?)"
+
+#, c-format
+msgid "malformed expiration date '%s'"
+msgstr "невірно сформована дата закінчення терміну дії \"%s\""
+
+#, c-format
+msgid "option `%s' expects \"always\", \"auto\", or \"never\""
+msgstr "опція \"%s\" очікує \"always\", \"auto\" або \"never\""
+
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "невірно сформована назва обʼєкта \"%s\""
+
+#, c-format
+msgid "option `%s' expects \"%s\" or \"%s\""
+msgstr "опція \"%s\" очікує \"%s\" або \"%s\""
+
+#, c-format
+msgid "%s requires a value"
+msgstr "%s потребує значення"
+
+#, c-format
+msgid "%s takes no value"
+msgstr "%s не приймає значення"
+
+#, c-format
+msgid "%s isn't available"
+msgstr "%s недоступний"
+
+#, c-format
+msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
+msgstr "%s очікує невід'ємне ціле значення з опціональним суфіксом k/m/g"
+
+#, c-format
+msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
+msgstr "неоднозначна опція: %s (може бути --%s%s або --%s%s)"
+
+#, c-format
+msgid "did you mean `--%s` (with two dashes)?"
+msgstr "ви мали на увазі \"--%s\" (з двома рисками)?"
+
+#, c-format
+msgid "alias of --%s"
+msgstr "псевдонім для --%s"
+
+msgid "need a subcommand"
+msgstr "потрібна підкоманда"
+
+#, c-format
+msgid "unknown option `%s'"
+msgstr "невідома опція \"%s\""
+
+#, c-format
+msgid "unknown switch `%c'"
+msgstr "невідомий перемикач \"%c\""
+
+#, c-format
+msgid "unknown non-ascii option in string: `%s'"
+msgstr "невідомий non-ascii параметр у рядку: \"%s\""
+
+msgid "..."
+msgstr "..."
+
+#, c-format
+msgid "usage: %s"
+msgstr "використання: %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation.
+#.
+#, c-format
+msgid "   or: %s"
+msgstr "         або: %s"
+
+#. TRANSLATORS: You should only need to translate this format
+#. string if your language is a RTL language (e.g. Arabic,
+#. Hebrew etc.), not if it's a LTR language (e.g. German,
+#. Russian, Chinese etc.).
+#. *
+#. When a translated usage string has an embedded "\n" it's
+#. because options have wrapped to the next line. The line
+#. after the "\n" will then be padded to align with the
+#. command name, such as N_("git cmd [opt]\n<8
+#. spaces>[opt2]"), where the 8 spaces are the same length as
+#. "git cmd ".
+#. *
+#. This format string prints out that already-translated
+#. line. The "%*s" is whitespace padding to account for the
+#. padding at the start of the line that we add in this
+#. function. The "%s" is a line in the (hopefully already
+#. translated) N_() usage string, which contained embedded
+#. newlines before we split it up.
+#.
+#, c-format
+msgid "%*s%s"
+msgstr "%*s%s"
+
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+msgid "-NUM"
+msgstr "-NUM"
+
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "протилежне --no-%s"
+
+msgid "expiry-date"
+msgstr "закінчення строку дії"
+
+msgid "no-op (backward compatibility)"
+msgstr "не працює (зворотна сумісність)"
+
+msgid "be more verbose"
+msgstr "надавати більш розгорнутий вивід"
+
+msgid "be more quiet"
+msgstr "надавати менш розгорнутий вивід"
+
+msgid "use <n> digits to display object names"
+msgstr "використовувати <n> цифр для відображення назв обʼєктів"
+
+msgid "prefixed path to initial superproject"
+msgstr "префіксний шлях до початкового суперпроекту"
+
+msgid "how to strip spaces and #comments from message"
+msgstr "як прибирати пробіли та #коментарі з допису"
+
+msgid "read pathspec from file"
+msgstr "прочитати визначник шляху з файлу"
+
+msgid ""
+"with --pathspec-from-file, pathspec elements are separated with NUL character"
+msgstr ""
+"за допомогою --pathspec-from-file елементи визначника шляху відокремлюються "
+"символом NUL"
+
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "невірне булеве значення оточення \"%s\" для \"%s\""
+
+#, c-format
+msgid "failed to parse %s"
+msgstr "не вдалося розібрати %s"
+
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Не вдалося зробити %s доступним для запису групою"
+
+msgid "Escape character '\\' not allowed as last character in attr value"
+msgstr ""
+"Символ екранування \"\\\" не дозволяється як останній символ у значенні "
+"атрибута"
+
+msgid "Only one 'attr:' specification is allowed."
+msgstr "Дозволений лише один визначник \"attr:\"."
+
+msgid "attr spec must not be empty"
+msgstr "визначник атрибута повинен бути не порожнім"
+
+#, c-format
+msgid "invalid attribute name %s"
+msgstr "невірна назва атрибуту %s"
+
+msgid "global 'glob' and 'noglob' pathspec settings are incompatible"
+msgstr "глобальні параметри визначника шляху \"glob\" та \"noglob\" несумісні"
+
+msgid ""
+"global 'literal' pathspec setting is incompatible with all other global "
+"pathspec settings"
+msgstr ""
+"глобальний параметр \"literal\" визначника шляху несумісний з усіма іншими "
+"глобальними параметрами визначника шляху"
+
+msgid "invalid parameter for pathspec magic 'prefix'"
+msgstr ""
+"неприпустимий параметр для магічного значення \"prefix\" визначника шляху"
+
+#, c-format
+msgid "Invalid pathspec magic '%.*s' in '%s'"
+msgstr "Неприпустиме магічне значення \"%.*s\" для визначника шляху в \"%s\""
+
+#, c-format
+msgid "Missing ')' at the end of pathspec magic in '%s'"
+msgstr ""
+"Пропущено \")\" наприкінці магічного значення для визначника шляху в \"%s\""
+
+#, c-format
+msgid "Unimplemented pathspec magic '%c' in '%s'"
+msgstr "Нереалізоване магічне значення \"%c\" для визначника шляху в \"%s\""
+
+#, c-format
+msgid "%s: 'literal' and 'glob' are incompatible"
+msgstr "%s: \"literal\" та \"glob\" несумісні"
+
+#, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "\"%s\" знаходиться поза деревом директорій"
+
+#, c-format
+msgid "%s: '%s' is outside repository at '%s'"
+msgstr "%s: \"%s\" знаходиться за межами сховища за адресою \"%s\""
+
+#, c-format
+msgid "'%s' (mnemonic: '%c')"
+msgstr "\"%s\" (мнемонічно: \"%c\")"
+
+#, c-format
+msgid "%s: pathspec magic not supported by this command: %s"
+msgstr ""
+"%s: магічне значення для визначника шляху не підтримується цією командою: %s"
+
+#, c-format
+msgid "pathspec '%s' is beyond a symbolic link"
+msgstr "визначник шляху \"%s\" знаходиться за межами символьного посилання"
+
+#, c-format
+msgid "line is badly quoted: %s"
+msgstr "рядок взято в лапки невірно: %s"
+
+msgid "unable to write flush packet"
+msgstr "не вдалося записати flush пакет"
+
+msgid "unable to write delim packet"
+msgstr "не вдалося записати delim пакет"
+
+msgid "unable to write response end packet"
+msgstr "не вдалося записати response end пакет"
+
+msgid "flush packet write failed"
+msgstr "не вдалося записати flush пакет"
+
+msgid "protocol error: impossibly long line"
+msgstr "помилка протоколу: неймовірно довгий рядок"
+
+msgid "packet write with format failed"
+msgstr "не вдалося записати пакет з форматуванням"
+
+msgid "packet write failed - data exceeds max packet size"
+msgstr ""
+"не вдалося записати пакет - дані перевищують максимальний розмір пакета"
+
+#, c-format
+msgid "packet write failed: %s"
+msgstr "не вдалося записати пакет: %s"
+
+msgid "read error"
+msgstr "помилка зчитування"
+
+msgid "the remote end hung up unexpectedly"
+msgstr "віддалене призначення несподівано відключилося"
+
+#, c-format
+msgid "protocol error: bad line length character: %.4s"
+msgstr "помилка протоколу: невірний символ довжини рядка: %.4s"
+
+#, c-format
+msgid "protocol error: bad line length %d"
+msgstr "помилка протоколу: невірна довжина рядка %d"
+
+#, c-format
+msgid "remote error: %s"
+msgstr "помилка віддаленого призначення: %s"
+
+msgid "Refreshing index"
+msgstr "Оновлення індекса"
+
+#, c-format
+msgid "unable to create threaded lstat: %s"
+msgstr "не вдалося створити потоковий lstat: %s"
+
+msgid "unable to parse --pretty format"
+msgstr "не вдалося розібрати --pretty формат"
+
+msgid "promisor-remote: unable to fork off fetch subprocess"
+msgstr "promisor-remote: не вдалося розгалужити підпроцес отримання"
+
+msgid "promisor-remote: could not write to fetch subprocess"
+msgstr "promisor-remote: не вдалося записати до підпроцесу отримання"
+
+msgid "promisor-remote: could not close stdin to fetch subprocess"
+msgstr "promisor-remote: не вдалося закрити stdin для підпроцесу отримання"
+
+#, c-format
+msgid "promisor remote name cannot begin with '/': %s"
+msgstr "назва віддаленого promisor не може починатися з \"/\": %s"
+
+#, c-format
+msgid "could not fetch %s from promisor remote"
+msgstr "не вдалося отримати %s з віддаленого promisor"
+
+msgid "object-info: expected flush after arguments"
+msgstr "object-info: очікувався flush після аргументів"
+
+msgid "Removing duplicate objects"
+msgstr "Видалення дублікатів обʼєктів"
+
+msgid "could not start `log`"
+msgstr "не вдалося розпочати \"log\""
+
+msgid "could not read `log` output"
+msgstr "не вдалося прочитати \"log\" вивід"
+
+#, c-format
+msgid "could not parse commit '%s'"
+msgstr "не вдалося розібрати коміт \"%s\""
+
+#, c-format
+msgid ""
+"could not parse first line of `log` output: did not start with 'commit ': "
+"'%s'"
+msgstr ""
+"не вдалося розібрати перший рядок \"log\" виводу: не починався з \"commit "
+"\"': \"%s\""
+
+#, c-format
+msgid "could not parse git header '%.*s'"
+msgstr "не вдалося розібрати git заголовок \"%.*s\""
+
+msgid "failed to generate diff"
+msgstr "не вдалося згенерувати різницю"
+
+#, c-format
+msgid "could not parse log for '%s'"
+msgstr "не вдалося розібрати журнал для \"%s\""
+
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "неприпустима додаткова марна верхівка: \"%s\""
+
+msgid "unable to enumerate additional recent objects"
+msgstr "не вдалося перерахувати додаткові нещодавні обʼєкти"
+
+#, c-format
+msgid "will not add file alias '%s' ('%s' already exists in index)"
+msgstr "не додаємо псевдонім файлу \"%s\" (\"%s\" вже існує в індексі)"
+
+msgid "cannot create an empty blob in the object database"
+msgstr "неможливо створити порожній blob в базі даних обʼєктів"
+
+#, c-format
+msgid "%s: can only add regular files, symbolic links or git-directories"
+msgstr ""
+"%s: може додавати лише звичайні файли, символьні посилання або git-директорії"
+
+#, c-format
+msgid "unable to index file '%s'"
+msgstr "не вдалося проіндексувати файл \"%s\""
+
+#, c-format
+msgid "unable to add '%s' to index"
+msgstr "не вдалося додати \"%s\" до індексу"
+
+#, c-format
+msgid "'%s' appears as both a file and as a directory"
+msgstr "\"%s\" відображається як файл і як каталог"
+
+msgid "Refresh index"
+msgstr "Оновити індекс"
+
+#, c-format
+msgid ""
+"index.version set, but the value is invalid.\n"
+"Using version %i"
+msgstr ""
+"index.version встановлено, але значення неприпустиме.\n"
+"Використання версії %i"
+
+#, c-format
+msgid ""
+"GIT_INDEX_VERSION set, but the value is invalid.\n"
+"Using version %i"
+msgstr ""
+"GIT_INDEX_VERSION встановлено, але значення неприпустиме.\n"
+"Використання версії %i"
+
+#, c-format
+msgid "bad signature 0x%08x"
+msgstr "невірний підпис 0x%08x"
+
+#, c-format
+msgid "bad index version %d"
+msgstr "невірна версія індексу %d"
+
+msgid "bad index file sha1 signature"
+msgstr "невірний sha1 підпис індексного файлу"
+
+#, c-format
+msgid "index uses %.4s extension, which we do not understand"
+msgstr "індекс використовує %.4s розширення, яке ми не розуміємо"
+
+#, c-format
+msgid "ignoring %.4s extension"
+msgstr "ігнорування %.4s розширення"
+
+#, c-format
+msgid "unknown index entry format 0x%08x"
+msgstr "невідомий формат запису індексу 0x%08x"
+
+#, c-format
+msgid "malformed name field in the index, near path '%s'"
+msgstr "невірно сформоване поле назви в індексі біля шляху \"%s\""
+
+msgid "unordered stage entries in index"
+msgstr "невпорядковані записи в індексі"
+
+#, c-format
+msgid "multiple stage entries for merged file '%s'"
+msgstr "багатоступеневі записи для злитого файлу \"%s\""
+
+#, c-format
+msgid "unordered stage entries for '%s'"
+msgstr "невпорядковані записи індексу для '%s'"
+
+#, c-format
+msgid "unable to create load_cache_entries thread: %s"
+msgstr "не вдалося створити потік load_cache_entries: %s"
+
+#, c-format
+msgid "unable to join load_cache_entries thread: %s"
+msgstr "не вдалося приєднатися до потоку load_cache_entries: %s"
+
+#, c-format
+msgid "%s: index file open failed"
+msgstr "%s: не вдалося відкрити індексний файл"
+
+#, c-format
+msgid "%s: cannot stat the open index"
+msgstr "%s: не вдалося виконати stat відкритого індексу"
+
+#, c-format
+msgid "%s: index file smaller than expected"
+msgstr "%s: індексний файл менший, ніж очікувався"
+
+#, c-format
+msgid "%s: unable to map index file%s"
+msgstr "%s: не вдалося зіставити індексний файл%s"
+
+#, c-format
+msgid "unable to create load_index_extensions thread: %s"
+msgstr "не вдалося створити потік load_index_extensions: %s"
+
+#, c-format
+msgid "unable to join load_index_extensions thread: %s"
+msgstr "не вдалося приєднатися до потоку load_index_extensions: %s"
+
+#, c-format
+msgid "could not freshen shared index '%s'"
+msgstr "не вдалося оновити спільний індекс \"%s\""
+
+#, c-format
+msgid "broken index, expect %s in %s, got %s"
+msgstr "пошкоджений індекс, очікувався %s у %s, отримано %s"
+
+msgid "cannot write split index for a sparse index"
+msgstr "неможливо записати розділений індекс для розрідженого індексу"
+
+msgid "failed to convert to a sparse-index"
+msgstr "не вдалося перетворити в sparse-index"
+
+#, c-format
+msgid "unable to open git dir: %s"
+msgstr "не вдалося відкрити git-директорію: %s"
+
+#, c-format
+msgid "unable to unlink: %s"
+msgstr "не вдалося видалити: %s"
+
+#, c-format
+msgid "cannot fix permission bits on '%s'"
+msgstr "неможливо виправити біти дозволу на \"%s\""
+
+#, c-format
+msgid "%s: cannot drop to stage #0"
+msgstr "%s: неможливо скинути індекс до стану #0"
+
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "неочікуваний статус diff %c"
+
+#, c-format
+msgid "remove '%s'\n"
+msgstr "видалити \"%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 ""
+"Ви можете виправити це командою \"git rebase --edit-todo\", а потім "
+"запустити \"git rebase --continue\".\n"
+"Або ви можете перервати перебазування за допомогою \"git rebase --abort\".\n"
+
+#, c-format
+msgid ""
+"unrecognized setting %s for option rebase.missingCommitsCheck. Ignoring."
+msgstr ""
+"нерозпізнане значення %s для опції rebase.missingCommitsCheck. Ігнорується."
+
+msgid ""
+"\n"
+"Commands:\n"
+"p, pick <commit> = use commit\n"
+"r, reword <commit> = use commit, but edit the commit message\n"
+"e, edit <commit> = use commit, but stop for amending\n"
+"s, squash <commit> = use commit, but meld into previous commit\n"
+"f, fixup [-C | -c] <commit> = like \"squash\" but keep only the previous\n"
+"                   commit's log message, unless -C is used, in which case\n"
+"                   keep only this commit's message; -c is same as -C but\n"
+"                   opens the editor\n"
+"x, exec <command> = run command (the rest of the line) using shell\n"
+"b, break = stop here (continue rebase later with 'git rebase --continue')\n"
+"d, drop <commit> = remove commit\n"
+"l, label <label> = label current HEAD with a name\n"
+"t, reset <label> = reset HEAD to a label\n"
+"m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]\n"
+"        create a merge commit using the original merge commit's\n"
+"        message (or the oneline, if no original merge commit was\n"
+"        specified); use -c <commit> to reword the commit message\n"
+"u, update-ref <ref> = track a placeholder for the <ref> to be updated\n"
+"                      to this position in the new commits. The <ref> is\n"
+"                      updated at the end of the rebase\n"
+"\n"
+"These lines can be re-ordered; they are executed from top to bottom.\n"
+msgstr ""
+"\n"
+"Команди:\n"
+"p, pick <коміт> = використати коміт\n"
+"r, reword <коміт> = використати коміт, але відредагувати допис до коміту\n"
+"e, edit <коміт> = використати коміт, але зупинитись для внесення поправок\n"
+"s, squash <коміт> = використати коміт, але обʼєднати з попереднім комітом\n"
+"f, fixup [-C | -c] <коміт> = як \"squash\", але зберігає лише попередній\n"
+"                   допис до коміту, якщо тільки не вказана опція -С, у якому "
+"разі\n"
+"                   зберігається лише допис до цього коміту; -c - те саме, що "
+"й -C, але\n"
+"                   відкриває редактор\n"
+"x, exec <команда> = виконати команду (решту рядка) за допомогою shell\n"
+"b, break = зупинитись тут (продовжити перебазування пізніше за допомогою "
+"'git rebase --continue')\n"
+"d, drop <коміт> = видалити коміт\n"
+"l, label <мітка> = помітити поточний HEAD назвою\n"
+"t, reset <мітка> = скинути HEAD до мітки\n"
+"m, merge [-C <коміт> | -c <коміт>] <мітка> [# <однорядковий допис>]\n"
+"        створити коміт злиття, використовуючи допис до початкового коміту\n"
+"        злиття (або однорядковий допис, якщо не було вказано коміт\n"
+"        злиття); використовуйте -c <коміт>, щоб перефразувати допис до "
+"коміту\n"
+"u, update-ref <посилання> = відстежити заповнювач для <посилання>, яке "
+"потрібно оновити\n"
+"                      до цієї позиції у нових комітах. <посилання>\n"
+"                      оновлюється в кінці перебазування\n"
+"\n"
+"Ці рядки можна переупорядкувати; вони виконуються зверху вниз.\n"
+
+#, c-format
+msgid "Rebase %s onto %s (%d command)"
+msgid_plural "Rebase %s onto %s (%d commands)"
+msgstr[0] "Перебазування %s на %s (%d команда)"
+msgstr[1] "Перебазування %s на %s (%d команди)"
+msgstr[2] "Перебазування %s на %s (%d команд)"
+
+msgid ""
+"\n"
+"Do not remove any line. Use 'drop' explicitly to remove a commit.\n"
+msgstr ""
+"\n"
+"Не видаляйте жодного рядка. Використовуйте \"drop\", щоб явно видалити "
+"коміт.\n"
+
+msgid ""
+"\n"
+"If you remove a line here THAT COMMIT WILL BE LOST.\n"
+msgstr ""
+"\n"
+"Якщо ви видалите тут рядок, ЦЕЙ КОМІТ БУДЕ ВТРАЧЕНО.\n"
+
+msgid ""
+"\n"
+"You are editing the todo file of an ongoing interactive rebase.\n"
+"To continue rebase after editing, run:\n"
+"    git rebase --continue\n"
+"\n"
+msgstr ""
+"\n"
+"Ви редагуєте файл todo поточного інтерактивного перебазування.\n"
+"Щоб продовжити перебазування після редагування, виконайте:\n"
+"    git rebase --continue\n"
+
+msgid ""
+"\n"
+"However, if you remove everything, the rebase will be aborted.\n"
+"\n"
+msgstr ""
+"\n"
+"Однак, якщо ви видалите все, перебазування буде перервано.\n"
+"\n"
+
+#, c-format
+msgid "could not write '%s'."
+msgstr "не вдалося записати \"%s\"."
+
+#, c-format
+msgid ""
+"Warning: some commits may have been dropped accidentally.\n"
+"Dropped commits (newer to older):\n"
+msgstr ""
+"Попередження: деякі коміти могли бути видалені випадково.\n"
+"Скинуті коміти (від новіших до старіших):\n"
+
+#, c-format
+msgid ""
+"To avoid this message, use \"drop\" to explicitly remove a commit.\n"
+"\n"
+"Use 'git config rebase.missingCommitsCheck' to change the level of "
+"warnings.\n"
+"The possible behaviours are: ignore, warn, error.\n"
+"\n"
+msgstr ""
+"Щоб уникнути цього повідомлення, використовуйте \"drop\" для явного "
+"видалення коміту.\n"
+"\n"
+"Використовуйте \"git config rebase.missingCommitsCheck\" для зміни рівня "
+"попереджень.\n"
+"Можливі варіанти поведінки: ignore, warn, error.\n"
+
+#, c-format
+msgid "%s: 'preserve' superseded by 'merges'"
+msgstr "%s: \"preserve\" замінено на \"merges\""
+
+msgid "gone"
+msgstr "відсутнє"
+
+#, c-format
+msgid "ahead %d"
+msgstr "попереду %d"
+
+#, c-format
+msgid "behind %d"
+msgstr "позаду %d"
+
+#, c-format
+msgid "ahead %d, behind %d"
+msgstr "попереду %d, позаду %d"
+
+#, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) не приймає аргументів"
+
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "нерозпізнаний %%(%.*s) аргумент: %s"
+
+#, c-format
+msgid "expected format: %%(color:<color>)"
+msgstr "очікуваний формат: %%(color:<колір>)"
+
+#, c-format
+msgid "unrecognized color: %%(color:%s)"
+msgstr "нерозпізнаний колір: %%(color:%s)"
+
+#, c-format
+msgid "Integer value expected refname:lstrip=%s"
+msgstr "Очікувалось ціле число refname:lstrip=%s"
+
+#, c-format
+msgid "Integer value expected refname:rstrip=%s"
+msgstr "Очікувалось ціле число refname:rstrip=%s"
+
+#, c-format
+msgid "expected %%(trailers:key=<value>)"
+msgstr "очікувалось %%(trailers:key=<значення>)"
+
+#, c-format
+msgid "unknown %%(trailers) argument: %s"
+msgstr "невідомий %%(trailers) аргумент: %s"
+
+#, c-format
+msgid "positive value expected contents:lines=%s"
+msgstr "очікувалось додатне значення contents:lines=%s"
+
+#, c-format
+msgid "argument expected for %s"
+msgstr "очікувався аргумент для %s"
+
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "очікувалось додатне значення %s=%s"
+
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "неможливо повністю розібрати %s=%s"
+
+#, c-format
+msgid "value expected %s="
+msgstr "очікувалось значення %s="
+
+#, c-format
+msgid "positive value expected '%s' in %%(%s)"
+msgstr "очікувалось додатне значення \"%s\" в %%(%s)"
+
+#, c-format
+msgid "expected format: %%(align:<width>,<position>)"
+msgstr "очікуваний формат: %%(align:<ширина>,<позиція>)"
+
+#, c-format
+msgid "unrecognized position:%s"
+msgstr "нерозпізнана позиція:%s"
+
+#, c-format
+msgid "unrecognized width:%s"
+msgstr "нерозпізнана ширина:%s"
+
+#, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "нерозпізнаний %%(%s) аргумент: %s"
+
+#, c-format
+msgid "positive width expected with the %%(align) atom"
+msgstr "очікувалась додатна ширина з %%(align) часткою"
+
+#, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "очікуваний формат: %%(ahead-behind:<комітоподібне>)"
+
+#, c-format
+msgid "malformed field name: %.*s"
+msgstr "невірно сформована назва поля: %.*s"
+
+#, c-format
+msgid "unknown field name: %.*s"
+msgstr "невідома назва поля: %.*s"
+
+#, c-format
+msgid ""
+"not a git repository, but the field '%.*s' requires access to object data"
+msgstr "не git сховище, але поле \"%.*s\" потребує доступу до даних обʼєкта"
+
+#, c-format
+msgid "format: %%(%s) atom used without a %%(%s) atom"
+msgstr "формат: частка %%(%s) використано без частки %%(%s)"
+
+#, c-format
+msgid "format: %%(then) atom used more than once"
+msgstr "формат: %%(then) частка використана більше одного разу"
+
+#, c-format
+msgid "format: %%(then) atom used after %%(else)"
+msgstr "формат: %%(then) частка використана після %%(else)"
+
+#, c-format
+msgid "format: %%(else) atom used more than once"
+msgstr "формат: %%(else) частка використана більше одного разу"
+
+#, c-format
+msgid "format: %%(end) atom used without corresponding atom"
+msgstr "формат: %%(end) частка використана без відповідної частки"
+
+#, c-format
+msgid "malformed format string %s"
+msgstr "невірно сформований рядок форматування %s"
+
+#, c-format
+msgid "this command reject atom %%(%.*s)"
+msgstr "ця команда відхиляє частку %%(%.*s)"
+
+#, c-format
+msgid "--format=%.*s cannot be used with --python, --shell, --tcl"
+msgstr "--format=%.*s не може бути використаний з --python, --shell, --tcl"
+
+msgid "failed to run 'describe'"
+msgstr "не вдалося запустити команду \"describe\""
+
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(немає гілки, перебазування %s)"
+
+#, c-format
+msgid "(no branch, rebasing detached HEAD %s)"
+msgstr "(немає гілки, перебазування відокремленого HEAD %s)"
+
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(немає гілки, бісекцію розпочато на %s)"
+
+#, c-format
+msgid "(HEAD detached at %s)"
+msgstr "(HEAD відʼєднано на %s)"
+
+#, c-format
+msgid "(HEAD detached from %s)"
+msgstr "(HEAD відʼєднано від %s)"
+
+msgid "(no branch)"
+msgstr "(немає гілки)"
+
+#, c-format
+msgid "missing object %s for %s"
+msgstr "відсутній обʼєкт %s для %s"
+
+#, c-format
+msgid "parse_object_buffer failed on %s for %s"
+msgstr "parse_object_buffer завершився невдало на %s для %s"
+
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "невірно сформований обʼєкт за адресою \"%s\""
+
+#, c-format
+msgid "ignoring ref with broken name %s"
+msgstr "ігнорування посилання з пошкодженою назвою %s"
+
+#, c-format
+msgid "ignoring broken ref %s"
+msgstr "ігнорування пошкоджених посилань %s"
+
+#, c-format
+msgid "format: %%(end) atom missing"
+msgstr "формат: %%(end) частка відсутня"
+
+#, c-format
+msgid "malformed object name %s"
+msgstr "невірно сформована назва обʼєкта %s"
+
+#, c-format
+msgid "option `%s' must point to a commit"
+msgstr "опція \"%s\" повинна вказувати на коміт"
+
+msgid "key"
+msgstr "ключ"
+
+msgid "field name to sort on"
+msgstr "назва поля для сортування"
+
+msgid "exclude refs which match pattern"
+msgstr "виключити посилання, які збігаються з шаблоном"
+
+#, c-format
+msgid "not a reflog: %s"
+msgstr "не є журналом посилань: %s"
+
+#, c-format
+msgid "no reflog for '%s'"
+msgstr "немає журналу посилань для \"%s\""
+
+#, c-format
+msgid "%s does not point to a valid object!"
+msgstr "%s не вказує на допустимий обʼєкт!"
+
+#, 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"
+"\n"
+"\tgit config --global init.defaultBranch <name>\n"
+"\n"
+"Names commonly chosen instead of 'master' are 'main', 'trunk' and\n"
+"'development'. The just-created branch can be renamed via this command:\n"
+"\n"
+"\tgit branch -m <name>\n"
+msgstr ""
+"Використання \"%s\" як назви початкової гілки. Назву гілки за замовчуванням\n"
+"може бути змінено. Щоб налаштувати назву початкової гілки для використання у "
+"всіх\n"
+"ваших нових сховищах, що приховає це попередження, викличте\n"
+"\n"
+"\tgit config --global init.defaultBranch <назва>.\n"
+"\n"
+"Замість \"master\" зазвичай використовують такі назви, як \"main\", "
+"\"trunk\" та\n"
+"\"development\". Щойно створену гілку можна перейменувати за допомогою цієї "
+"команди:\n"
+"\n"
+"\tgit branch -m <назва>\n"
+
+#, c-format
+msgid "could not retrieve `%s`"
+msgstr "не вдалося отримати \"%s\""
+
+#, c-format
+msgid "invalid branch name: %s = %s"
+msgstr "неприпустима назва гілки: %s = %s"
+
+#, c-format
+msgid "ignoring dangling symref %s"
+msgstr "ігнорування висячих symref %s"
+
+#, c-format
+msgid "log for ref %s has gap after %s"
+msgstr "лог для посилання %s має пропуск після %s"
+
+#, c-format
+msgid "log for ref %s unexpectedly ended on %s"
+msgstr "лог для посилання %s несподівано завершився на %s"
+
+#, c-format
+msgid "log for %s is empty"
+msgstr "лог для %s порожній"
+
+#, c-format
+msgid "refusing to update ref with bad name '%s'"
+msgstr "відмовлено в оновленні посилання з невірною назвою \"%s\""
+
+#, c-format
+msgid "update_ref failed for ref '%s': %s"
+msgstr "update_ref завершився невдало для посилання \"%s\": %s"
+
+#, c-format
+msgid "multiple updates for ref '%s' not allowed"
+msgstr "багатократні оновлення для посилання \"%s\" заборонені"
+
+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\""
+
+#, c-format
+msgid "cannot process '%s' and '%s' at the same time"
+msgstr "неможливо обробити \"%s\" і \"%s\" одночасно"
+
+#, c-format
+msgid "could not delete reference %s: %s"
+msgstr "не вдалося видалити посилання %s: %s"
+
+#, c-format
+msgid "could not delete references: %s"
+msgstr "не вдалося видалити посилання: %s"
+
+#, c-format
+msgid "invalid refspec '%s'"
+msgstr "неприпустимий визначник посилання \"%s\""
+
+#, c-format
+msgid "invalid quoting in push-option value: '%s'"
+msgstr "неприпустимі лапки у значенні push-опції: \"%s\""
+
+#, c-format
+msgid "%sinfo/refs not valid: is this a git repository?"
+msgstr "%sinfo/refs не дійсні: це git сховище?"
+
+msgid "invalid server response; expected service, got flush packet"
+msgstr ""
+"неприпустима відповідь сервера; очікувалось service, отримано flush пакет"
+
+#, c-format
+msgid "invalid server response; got '%s'"
+msgstr "неприпустима відповідь сервера; отримано \"%s\""
+
+#, c-format
+msgid "repository '%s' not found"
+msgstr "сховище \"%s\" не знайдено"
+
+#, c-format
+msgid "Authentication failed for '%s'"
+msgstr "Невдала аутентифікація для \"%s\""
+
+#, c-format
+msgid "unable to access '%s' with http.pinnedPubkey configuration: %s"
+msgstr ""
+"не вдалося отримати доступ до \"%s\" з конфігурацією http.pinnedPubkey: %s"
+
+#, c-format
+msgid "unable to access '%s': %s"
+msgstr "не вдалося отримати доступ до \"%s\": %s"
+
+#, c-format
+msgid "redirecting to %s"
+msgstr "перенаправлення на %s"
+
+msgid "shouldn't have EOF when not gentle on EOF"
+msgstr "не повинен мати EOF, якщо не є обережним з EOF"
+
+msgid "remote server sent unexpected response end packet"
+msgstr "віддалений сервер надіслав неочікуваний response end пакет"
+
+msgid "unable to rewind rpc post data - try increasing http.postBuffer"
+msgstr ""
+"не вдалося перемотати  вперед rpc post дані - спробуйте збільшити http."
+"postBuffer"
+
+#, c-format
+msgid "remote-curl: bad line length character: %.4s"
+msgstr "remote-curl: невірний символ довжини рядка: %.4s"
+
+msgid "remote-curl: unexpected response end packet"
+msgstr "remote-curl: неочікуваний response end пакет"
+
+#, c-format
+msgid "RPC failed; %s"
+msgstr "RPC завершився невдало; %s"
+
+msgid "cannot handle pushes this big"
+msgstr "неможливо впоратися з такими великими надсиланнями"
+
+#, c-format
+msgid "cannot deflate request; zlib deflate error %d"
+msgstr "неможливо запакувати запит; zlib помилка пакування %d"
+
+#, c-format
+msgid "cannot deflate request; zlib end error %d"
+msgstr "неможливо запакувати запит; zlib помилка завершення %d"
+
+#, c-format
+msgid "%d bytes of length header were received"
+msgstr "%d байтів заголовку було отримано"
+
+#, c-format
+msgid "%d bytes of body are still expected"
+msgstr "%d байтів тіла все ще очікуються"
+
+msgid "dumb http transport does not support shallow capabilities"
+msgstr "засіб передачі dumb http не підтримує shallow здібності"
+
+msgid "fetch failed."
+msgstr "отримання завершилось невдало."
+
+msgid "cannot fetch by sha1 over smart http"
+msgstr "неможливо отримати дані за допомогою sha1 через smart http"
+
+#, c-format
+msgid "protocol error: expected sha/ref, got '%s'"
+msgstr "помилка протоколу: очікувалось sha/ref, отримано \"%s\""
+
+#, c-format
+msgid "http transport does not support %s"
+msgstr "http засіб передачі не підтримує %s"
+
+msgid "protocol error: expected '<url> <path>', missing space"
+msgstr "помилка протоколу: очікувалось \"<url> <шлях>\", пропущено пробіл"
+
+#, c-format
+msgid "failed to download file at URL '%s'"
+msgstr "не вдалося завантажити файл за URL-адресою \"%s\""
+
+msgid "git-http-push failed"
+msgstr "git-http-push завершився невдало"
+
+msgid "remote-curl: usage: git remote-curl <remote> [<url>]"
+msgstr ""
+"remote-curl: використання: git remote-curl <віддаленe-призначення> [<url>]"
+
+msgid "remote-curl: error reading command stream from git"
+msgstr "remote-curl: помилка зчитування потоку команд з git"
+
+msgid "remote-curl: fetch attempted without a local repo"
+msgstr "remote-curl: спроба отримання без локального сховища"
+
+#, c-format
+msgid "remote-curl: unknown command '%s' from git"
+msgstr "remote-curl: невідома команда \"%s\" з git"
+
+#, c-format
+msgid "config remote shorthand cannot begin with '/': %s"
+msgstr "скорочення віддаленої конфігураціі не може починатися з '/': %s"
+
+msgid "more than one receivepack given, using the first"
+msgstr "надано більше одного пакунка для отримання, використано перший"
+
+msgid "more than one uploadpack given, using the first"
+msgstr "надано більше одного пакунка для завантаження, використано перший"
+
+#, c-format
+msgid "unrecognized value transfer.credentialsInUrl: '%s'"
+msgstr "нерозпізнане значення transfer.credentialsInUrl: '%s'"
+
+#, c-format
+msgid "URL '%s' uses plaintext credentials"
+msgstr "URL-адреса \"%s\" використовує облікові дані у відкритому тексті"
+
+#, c-format
+msgid "Cannot fetch both %s and %s to %s"
+msgstr "Неможливо отримати як %s, так і %s до %s"
+
+#, c-format
+msgid "%s usually tracks %s, not %s"
+msgstr "%s зазвичай відстежує %s, а не %s"
+
+#, c-format
+msgid "%s tracks both %s and %s"
+msgstr "%s відстежує як %s, так і %s"
+
+#, c-format
+msgid "key '%s' of pattern had no '*'"
+msgstr "ключ '%s' шаблону не містив '*'"
+
+#, c-format
+msgid "value '%s' of pattern has no '*'"
+msgstr "значення '%s' шаблону не містить '*'"
+
+#, c-format
+msgid "src refspec %s does not match any"
+msgstr "визначник посилання джерела %s не збігається з жодним"
+
+#, c-format
+msgid "src refspec %s matches more than one"
+msgstr "визначник посилання джерела %s збігається з більш ніж одним"
+
+#. TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
+#. <remote> <src>:<dst>" push, and "being pushed ('%s')" is
+#. the <src>.
+#.
+#, c-format
+msgid ""
+"The destination you provided is not a full refname (i.e.,\n"
+"starting with \"refs/\"). We tried to guess what you meant by:\n"
+"\n"
+"- Looking for a ref that matches '%s' on the remote side.\n"
+"- Checking if the <src> being pushed ('%s')\n"
+"  is a ref in \"refs/{heads,tags}/\". If so we add a corresponding\n"
+"  refs/{heads,tags}/ prefix on the remote side.\n"
+"\n"
+"Neither worked, so we gave up. You must fully qualify the ref."
+msgstr ""
+"Призначення, що ви вказали, не є повною назвою посилання (такою,\n"
+"що починається з \"refs/\"). Ми спробували вгадати, що ви мали на увазі "
+"через:\n"
+"\n"
+"- Пошук посилання, яке відповідає \"%s\" на віддаленій стороні.\n"
+"- Перевірку, чи є <джерело> надсилання, (\"%s\")\n"
+"  посиланням у \"refs/{heads,tags}/\". Якщо так, то ми додаємо відповідний\n"
+"  refs/{heads,tags}/ префікс на віддаленій стороні.\n"
+"\n"
+"Ні те, ні інше не спрацювало, тому ми здалися. Ви повинні повністю "
+"кваліфікувати посилання."
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a commit object.\n"
+"Did you mean to create a new branch by pushing to\n"
+"'%s:refs/heads/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом коміту.\n"
+"Ви хотіли створити нову гілку, надіславши зміни до\n"
+"\"%s:refs/heads/%s\"?"
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a tag object.\n"
+"Did you mean to create a new tag by pushing to\n"
+"'%s:refs/tags/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом тегу.\n"
+"Ви хотіли створити новий тег, надіславши зміни до\n"
+"\"%s:refs/tags/%s\"?"
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a tree object.\n"
+"Did you mean to tag a new tree by pushing to\n"
+"'%s:refs/tags/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом дерева.\n"
+"Ви хотіли позначити нове дерево, надіславши зміни до\n"
+"\"%s:refs/tags/%s\"?"
+
+#, c-format
+msgid ""
+"The <src> part of the refspec is a blob object.\n"
+"Did you mean to tag a new blob by pushing to\n"
+"'%s:refs/tags/%s'?"
+msgstr ""
+"Частина <джерело> визаначника посилання є об’єктом blob.\n"
+"Ви хотіли позначити новий blob, надіславши зміни до\n"
+"\"%s:refs/tags/%s\"?"
+
+#, c-format
+msgid "%s cannot be resolved to branch"
+msgstr "%s неможливо розвʼязати в гілку"
+
+#, c-format
+msgid "unable to delete '%s': remote ref does not exist"
+msgstr "не вдалося видалити \"%s\": віддалене посилання не існує"
+
+#, c-format
+msgid "dst refspec %s matches more than one"
+msgstr "визаначник посилання призначення %s збігається з більш ніж одним"
+
+#, c-format
+msgid "dst ref %s receives from more than one src"
+msgstr "посилання призначення %s отримує з більш ніж одного src"
+
+msgid "HEAD does not point to a branch"
+msgstr "HEAD не вказує на гілку"
+
+#, c-format
+msgid "no such branch: '%s'"
+msgstr "немає такої гілки: '%s'"
+
+#, c-format
+msgid "no upstream configured for branch '%s'"
+msgstr "першоджерельне сховище не налаштовано для гілки '%s'"
+
+#, c-format
+msgid "upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "висхідна гілка \"%s\" не збережена як віддалено відстежувана гілка"
+
+#, c-format
+msgid "push destination '%s' on remote '%s' has no local tracking branch"
+msgstr ""
+"шлях призначення надсилання \"%s\" на віддаленому \"%s\" не має гілки "
+"локального відстежування"
+
+#, c-format
+msgid "branch '%s' has no remote for pushing"
+msgstr "гілка \"%s\" не має віддаленого призначення для надсилання"
+
+#, c-format
+msgid "push refspecs for '%s' do not include '%s'"
+msgstr "надіслані визначники посилань для \"%s\" не включають \"%s\""
+
+msgid "push has no destination (push.default is 'nothing')"
+msgstr "надсилання не має призначення (push.default дорівнює 'nothing')"
+
+msgid "cannot resolve 'simple' push to a single destination"
+msgstr ""
+"неможливо розвʼязати \"simple\" надсилання до єдиного пункту призначення"
+
+#, c-format
+msgid "couldn't find remote ref %s"
+msgstr "не вдалося знайти віддалене посилання %s"
+
+#, c-format
+msgid "* Ignoring funny ref '%s' locally"
+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"
+
+#, c-format
+msgid "Your branch is up to date with '%s'.\n"
+msgstr "Ваша гілка не відрізняється від \"%s\".\n"
+
+#, c-format
+msgid "Your branch and '%s' refer to different commits.\n"
+msgstr "Ваша гілка та гілка \"%s\" посилаються до різних комітів.\n"
+
+#, c-format
+msgid "  (use \"%s\" for details)\n"
+msgstr "  (використовуйте \"%s\" для отримання детальної інформації)\n"
+
+#, 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] "Ваша гілка випереджає \"%s\" на %d коміт.\n"
+msgstr[1] "Ваша гілка випереджає \"%s\" на %d коміти.\n"
+msgstr[2] "Ваша гілка випереджає \"%s\" на %d комітів.\n"
+
+msgid "  (use \"git push\" to publish your local commits)\n"
+msgstr "  (скористайтесь \"git push\", щоб надіслати локальні коміти)\n"
+
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] ""
+"Ваша гілка відстає від \"%s\" на %d коміт, і її можна перемотати вперед.\n"
+msgstr[1] ""
+"Ваша гілка відстає від \"%s\" на %d коміти, і її можна перемотати вперед.\n"
+msgstr[2] ""
+"Ваша гілка відстає від гілки '%s' на %d комітів, і її можна перемотати "
+"вперед.\n"
+
+msgid "  (use \"git pull\" to update your local branch)\n"
+msgstr "  (використовуйте \"git pull\", щоб оновити вашу локальну гілку)\n"
+
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] ""
+"Ваша гілка і гілка '%s' розійшлися,\n"
+"і мають %d і %d різний коміт відповідно.\n"
+msgstr[1] ""
+"Ваша гілка і гілка \"%s\" розійшлися,\n"
+"і мають %d і %d різних коміти відповідно.\n"
+msgstr[2] ""
+"Ваша гілка і гілка \"%s\" розійшлися,\n"
+"і мають %d і %d різних комітів відповідно.\n"
+
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr ""
+"  (скористайтесь \"git pull\", якщо ви хочете інтегрувати віддалену гілку зі "
+"своєю)\n"
+
+#, c-format
+msgid "cannot parse expected object name '%s'"
+msgstr "неможливо розібрати очікувану назву обʼєкту \"%s\""
+
+#, c-format
+msgid "cannot strip one component off url '%s'"
+msgstr "неможливо вилучити один компонент з url '%s'"
+
+#, c-format
+msgid "bad replace ref name: %s"
+msgstr "неприпустима назва заміни посилання: %s"
+
+#, c-format
+msgid "duplicate replace ref: %s"
+msgstr "дубльована заміна посилання: %s"
+
+#, c-format
+msgid "replace depth too high for object %s"
+msgstr "глибина заміни занадто висока для обʼєкта %s"
+
+msgid "corrupt MERGE_RR"
+msgstr "пошкоджений MERGE_RR"
+
+msgid "unable to write rerere record"
+msgstr "неможливо зробити rerere запис"
+
+#, c-format
+msgid "there were errors while writing '%s' (%s)"
+msgstr "виникли помилки під час запису \"%s\" (%s)"
+
+#, c-format
+msgid "could not parse conflict hunks in '%s'"
+msgstr "не вдалося розібрати конфліктні шматки в \"%s\""
+
+#, c-format
+msgid "failed utime() on '%s'"
+msgstr "невдалий utime() на \"%s\""
+
+#, c-format
+msgid "writing '%s' failed"
+msgstr "запис \"%s\" завершився невдало"
+
+#, c-format
+msgid "Staged '%s' using previous resolution."
+msgstr "Індексовано \"%s\" з використанням попереднього вирішення."
+
+#, c-format
+msgid "Recorded resolution for '%s'."
+msgstr "Записано вирішення для \"%s\"."
+
+#, c-format
+msgid "Resolved '%s' using previous resolution."
+msgstr "Вирішено \"%s\" з використанням попереднього вирішення."
+
+#, c-format
+msgid "cannot unlink stray '%s'"
+msgstr "неможливо видалити блукаючий \"%s\""
+
+#, c-format
+msgid "Recorded preimage for '%s'"
+msgstr "Записано попереднє зображення для \"%s\""
+
+#, c-format
+msgid "failed to update conflicted state in '%s'"
+msgstr "не вдалося оновити конфліктний стан у \"%s\""
+
+#, c-format
+msgid "no remembered resolution for '%s'"
+msgstr "немає записаного вирішення для \"%s\""
+
+#, c-format
+msgid "Updated preimage for '%s'"
+msgstr "Оновлено попереднє зображення для \"%s\""
+
+#, c-format
+msgid "Forgot resolution for '%s'\n"
+msgstr "Забуто вирішення для \"%s\"\n"
+
+msgid "unable to open rr-cache directory"
+msgstr "неможливо відкрити rr-cache директорію"
+
+msgid "update the index with reused conflict resolution if possible"
+msgstr ""
+"оновити індекс з повторним використанням вирішення конфліктів, якщо це "
+"можливо"
+
+msgid "could not determine HEAD revision"
+msgstr "не вдалося визначити ревізію HEAD"
+
+#, c-format
+msgid "failed to find tree of %s"
+msgstr "не вдалося знайти дерево %s"
+
+#, c-format
+msgid "unsupported section for hidden refs: %s"
+msgstr "непідтримувана секція для прихованих посилань: %s"
+
+msgid "--exclude-hidden= passed more than once"
+msgstr "--exclude-hidden= передано більше одного разу"
+
+#, c-format
+msgid "resolve-undo records `%s` which is missing"
+msgstr "resolve-undo записує \"%s\", який відсутній"
+
+#, c-format
+msgid "could not get commit for ancestry-path argument %s"
+msgstr "не вдалося отримати коміт для ancestry-path аргументу %s"
+
+msgid "--unpacked=<packfile> no longer supported"
+msgstr "--unpacked=<файл пакунка> більше не підтримується"
+
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "неприпустима опція \"%s\" у --stdin режимі"
+
+msgid "your current branch appears to be broken"
+msgstr "ваша поточна гілка виглядає пошкодженою"
+
+#, c-format
+msgid "your current branch '%s' does not have any commits yet"
+msgstr "ваша поточна гілка \"%s\" ще не має жодних комітів"
+
+msgid "object filtering requires --objects"
+msgstr "для фільтрації обʼєктів потрібен параметр --objects"
+
+msgid "-L does not yet support diff formats besides -p and -s"
+msgstr "-L поки що не підтримує diff формати, окрім -p та -s"
+
+#, c-format
+msgid "cannot create async thread: %s"
+msgstr "неможливо створити асинхронний потік: %s"
+
+#, c-format
+msgid "'%s' does not exist"
+msgstr "\"%s\" не існує"
+
+#, c-format
+msgid "could not switch to '%s'"
+msgstr "не вдалося переключитися на \"%s\""
+
+msgid "need a working directory"
+msgstr "потрібна робоча директорія"
+
+msgid "Scalar enlistments require a worktree"
+msgstr "Коренева директорія проекту потребує робоче дерево"
+
+#, c-format
+msgid "could not configure %s=%s"
+msgstr "не вдалося налаштувати %s=%s"
+
+msgid "could not configure log.excludeDecoration"
+msgstr "не вдалося налаштувати log.excludeDecoration"
+
+msgid "could not add enlistment"
+msgstr "не вдалося додати кореневу директорію проекту"
+
+msgid "could not set recommended config"
+msgstr "не вдалося встановити рекомендовану конфігурацію"
+
+msgid "could not turn on maintenance"
+msgstr "не вдалося ввімкнути технічне обслуговування"
+
+msgid "could not start the FSMonitor daemon"
+msgstr "не вдалося запустити FSMonitor демон"
+
+msgid "could not turn off maintenance"
+msgstr "не вдалося вимкнути технічне обслуговування"
+
+msgid "could not remove enlistment"
+msgstr "не вдалося видалити директорію верхнього рівня"
+
+#, c-format
+msgid "remote HEAD is not a branch: '%.*s'"
+msgstr "віддалений HEAD не є гілкою: \"%.*s\""
+
+msgid "failed to get default branch name from remote; using local default"
+msgstr ""
+"не вдалося отримати назву гілки за замовчуванням з віддаленого сховища; "
+"використано локальне значення за замовчуванням"
+
+msgid "failed to get default branch name"
+msgstr "не вдалося отримати назву гілки за замовчуванням"
+
+msgid "failed to unregister repository"
+msgstr "не вдалося скасувати реєстрацію сховища"
+
+msgid "failed to stop the FSMonitor daemon"
+msgstr "не вдалося зупинити FSMonitor демон"
+
+msgid "failed to delete enlistment directory"
+msgstr "не вдалося видалити кореневу директорію проекту"
+
+msgid "branch to checkout after clone"
+msgstr "гілка, на яку перейти після клонування"
+
+msgid "when cloning, create full working directory"
+msgstr "при клонуванні створювати повну робочу директорію"
+
+msgid "only download metadata for the branch that will be checked out"
+msgstr ""
+"завантажити метадані тільки для гілки, на яку буде здійснюватися перехід"
+
+msgid "create repository within 'src' directory"
+msgstr "створити сховище в директорії \"src\""
+
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <головна-гілка>] [--full-clone]\n"
+"\t[--[no-]src] <URL-адреса> [<коренева-директорія>]"
+
+#, c-format
+msgid "cannot deduce worktree name from '%s'"
+msgstr "неможливо вивести назву робочого дерева з \"%s\""
+
+#, c-format
+msgid "directory '%s' exists already"
+msgstr "директорія \"%s\" вже існує"
+
+#, c-format
+msgid "failed to get default branch for '%s'"
+msgstr "не вдалося отримати гілку за замовчуванням для \"%s\""
+
+#, c-format
+msgid "could not configure remote in '%s'"
+msgstr "не вдалося налаштувати віддалене сховище в \"%s\""
+
+#, c-format
+msgid "could not configure '%s'"
+msgstr "не вдалося налаштувати \"%s\""
+
+msgid "partial clone failed; attempting full clone"
+msgstr "не вдалося зробити розріджений клон; спроба зробити повний клон"
+
+msgid "could not configure for full clone"
+msgstr "не вдалося налаштувати для повного клона"
+
+msgid "scalar diagnose [<enlistment>]"
+msgstr "scalar diagnose [<коренева-директорія-проекту>]"
+
+msgid "`scalar list` does not take arguments"
+msgstr "\"scalar list\" не приймає аргументів"
+
+msgid "scalar register [<enlistment>]"
+msgstr "scalar register [<коренева-директорія-проекту>]"
+
+msgid "reconfigure all registered enlistments"
+msgstr "переналаштувати всі зареєстровані кореневі директорії проекту"
+
+msgid "scalar reconfigure [--all | <enlistment>]"
+msgstr "scalar reconfigure [--all | <коренева-директорія-проекту>]"
+
+msgid "--all or <enlistment>, but not both"
+msgstr "--all або <коренева-директорія-проекту>, але не обидва"
+
+#, c-format
+msgid "could not remove stale scalar.repo '%s'"
+msgstr "неможливо видалити застаріле scalar.repo \"%s\""
+
+#, c-format
+msgid "removed stale scalar.repo '%s'"
+msgstr "видалено застаріле scalar.repo \"%s\""
+
+#, c-format
+msgid "repository at '%s' has different owner"
+msgstr "у сховища \"%s\" інший власник"
+
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "невірній формат сховища \"%s\""
+
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "сховище \"%s\" не знайдено"
+
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"щоб скасувати реєстрацію цього репозиторію в Scalar, виконайте\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+
+msgid ""
+"scalar run <task> [<enlistment>]\n"
+"Tasks:\n"
+msgstr ""
+"scalar run <завдання> [<коренева-директорія-проекту>]\n"
+"Завдання:\n"
+
+#, c-format
+msgid "no such task: '%s'"
+msgstr "немає такого завдання: \"%s\""
+
+msgid "scalar unregister [<enlistment>]"
+msgstr "scalar unregister [<коренева-директорія-проекту>]"
+
+msgid "scalar delete <enlistment>"
+msgstr "scalar delete <коренева-директорія-проекту>"
+
+msgid "refusing to delete current working directory"
+msgstr "відмовлено у видаленні поточної робочої директорії"
+
+msgid "include Git version"
+msgstr "включити версію Git"
+
+msgid "include Git's build options"
+msgstr "включити опції збірки Git"
+
+msgid "scalar verbose [-v | --verbose] [--build-options]"
+msgstr "scalar verbose [-v | --verbose] [--build-options]"
+
+msgid "-C requires a <directory>"
+msgstr "-C потребує наявності <директорії>"
+
+#, c-format
+msgid "could not change to '%s'"
+msgstr "не вдалося змінити на \"%s\""
+
+msgid "-c requires a <key>=<value> argument"
+msgstr "-c потребує <ключ>=<значення> аргумент"
+
+msgid ""
+"scalar [-C <directory>] [-c <key>=<value>] <command> [<options>]\n"
+"\n"
+"Commands:\n"
+msgstr ""
+"scalar [-C <директорія>] [-c <ключ>=<значення>] <команда> [<опції>]\n"
+"\n"
+"Команди:\n"
+
+msgid "unexpected flush packet while reading remote unpack status"
+msgstr ""
+"несподіваний flush пакет під час зчитування статусу віддаленого розпакування"
+
+#, c-format
+msgid "unable to parse remote unpack status: %s"
+msgstr "не вдалося розібрати стан розпакування віддаленого сховища: %s"
+
+#, c-format
+msgid "remote unpack failed: %s"
+msgstr "віддалене розпакування не вдалося: %s"
+
+msgid "failed to sign the push certificate"
+msgstr "не вдалося підписати сертифікат надсилання"
+
+msgid "send-pack: unable to fork off fetch subprocess"
+msgstr "send-pack: не вдалося розгалужити підпроцес отримання"
+
+msgid "push negotiation failed; proceeding anyway with push"
+msgstr "перемовини з надсилання не вдалися; все одно продовжуємо надсилання"
+
+msgid "the receiving end does not support this repository's hash algorithm"
+msgstr "отримуюча сторона не підтримує хеш-алгоритм цього сховища"
+
+msgid "the receiving end does not support --signed push"
+msgstr "отримуюча сторона не підтримує --signed push"
+
+msgid ""
+"not sending a push certificate since the receiving end does not support --"
+"signed push"
+msgstr ""
+"сертифікат надсилання не відправлено, оскільки отримуюча сторона не "
+"підтримує --signed push"
+
+msgid "the receiving end does not support --atomic push"
+msgstr "отримуюча сторона не підтримує --atomic push"
+
+msgid "the receiving end does not support push options"
+msgstr "отримуюча сторона не підтримує опції push"
+
+#, c-format
+msgid "invalid commit message cleanup mode '%s'"
+msgstr "неприпустимий режим очищення дописів до коміту \"%s\""
+
+#, c-format
+msgid "could not delete '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+msgid "revert"
+msgstr "вивертання"
+
+msgid "cherry-pick"
+msgstr "висмикування"
+
+msgid "rebase"
+msgstr "перебазування"
+
+#, c-format
+msgid "unknown action: %d"
+msgstr "невідома дія: %d"
+
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"після вирішення конфліктів позначте виправлені шляхи\n"
+"за допомогою \"git add <шляхи>\" або \"git rm <шляхи>\""
+
+msgid ""
+"After resolving the conflicts, mark them with\n"
+"\"git add/rm <pathspec>\", then run\n"
+"\"git cherry-pick --continue\".\n"
+"You can instead skip this commit with \"git cherry-pick --skip\".\n"
+"To abort and get back to the state before \"git cherry-pick\",\n"
+"run \"git cherry-pick --abort\"."
+msgstr ""
+"Після вирішення конфліктів позначте їх за допомогою\n"
+"\"git add/rm <визначник шляху>\", а потім виконайте\n"
+"\"git cherry-pick --continue\".\n"
+"Замість цього ви можете пропустити цей коміт за допомогою \"git cherry-pick "
+"--skip\".\n"
+"Щоб перервати процес і повернутися до стану перед \"git cherry-pick\",\n"
+"виконайте \"git cherry-pick --abort\"."
+
+msgid ""
+"After resolving the conflicts, mark them with\n"
+"\"git add/rm <pathspec>\", then run\n"
+"\"git revert --continue\".\n"
+"You can instead skip this commit with \"git revert --skip\".\n"
+"To abort and get back to the state before \"git revert\",\n"
+"run \"git revert --abort\"."
+msgstr ""
+"Після вирішення конфліктів позначте їх за допомогою\n"
+"\"git add/rm <визначник шляху>\", а потім виконайте\n"
+"\"git revert --continue\".\n"
+"Замість цього ви можете пропустити цей коміт за допомогою \"git revert --"
+"skip\".\n"
+"Щоб перервати і повернутися до стану перед \"git revert\",\n"
+"виконайте \"git revert --abort\"."
+
+#, c-format
+msgid "could not lock '%s'"
+msgstr "не вдалося зафіксувати \"%s\""
+
+#, c-format
+msgid "could not write eol to '%s'"
+msgstr "не вдалося записати eol в \"%s\""
+
+#, c-format
+msgid "failed to finalize '%s'"
+msgstr "не вдалося завершити \"%s\""
+
+#, c-format
+msgid "your local changes would be overwritten by %s."
+msgstr "ваші локальні зміни будуть перезаписані %s."
+
+msgid "commit your changes or stash them to proceed."
+msgstr "зробіть коміт або додайте ваші зміни до схову, щоб продовжити."
+
+#. TRANSLATORS: %s will be "revert", "cherry-pick" or
+#. "rebase".
+#.
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Не вдалося записати новий індексний файл"
+
+msgid "unable to update cache tree"
+msgstr "не вдалося оновити дерево кешу"
+
+msgid "could not resolve HEAD commit"
+msgstr "не вдалося розпізнати HEAD коміт"
+
+#, c-format
+msgid "no key present in '%.*s'"
+msgstr "немає ключа у \"%.*s\""
+
+#, c-format
+msgid "unable to dequote value of '%s'"
+msgstr "не вдалося вилучити лапки зі значення \"%s\""
+
+msgid "'GIT_AUTHOR_NAME' already given"
+msgstr "\"GIT_AUTHOR_NAME\" вже надано"
+
+msgid "'GIT_AUTHOR_EMAIL' already given"
+msgstr "\"GIT_AUTHOR_EMAIL\" вже надано"
+
+msgid "'GIT_AUTHOR_DATE' already given"
+msgstr "\"GIT_AUTHOR_DATE\" вже надано"
+
+#, c-format
+msgid "unknown variable '%s'"
+msgstr "невідома змінна \"%s\""
+
+msgid "missing 'GIT_AUTHOR_NAME'"
+msgstr "бракує \"GIT_AUTHOR_NAME\""
+
+msgid "missing 'GIT_AUTHOR_EMAIL'"
+msgstr "бракує \"GIT_AUTHOR_EMAIL\""
+
+msgid "missing 'GIT_AUTHOR_DATE'"
+msgstr "бракує \"GIT_AUTHOR_DATE\""
+
+#, c-format
+msgid ""
+"you have staged changes in your working tree\n"
+"If these changes are meant to be squashed into the previous commit, run:\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"If they are meant to go into a new commit, run:\n"
+"\n"
+"  git commit %s\n"
+"\n"
+"In both cases, once you're done, continue with:\n"
+"\n"
+"  git rebase --continue\n"
+msgstr ""
+"ви маєте індексовані зміни у вашому робочому дереві\n"
+"Якщо ці зміни мають бути стиснуті у попередній коміт, запустіть:\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"Якщо вони мають потрапити до нового коміту, виконайте:\n"
+"\n"
+"  git commit %s\n"
+"\n"
+"В обох випадках, як тільки ви закінчите, продовжуйте за допомогою:\n"
+"\n"
+"  git rebase --continue\n"
+
+msgid "'prepare-commit-msg' hook failed"
+msgstr "\"prepare-commit-msg\" гачок завершився невдало"
+
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Ваше імʼя та адреса електронної пошти були налаштовані автоматично на "
+"основі\n"
+"вашого імені користувача та назви хосту. Будь ласка, перевірте їх "
+"правильність.\n"
+"Ви можете приховати це повідомлення, встановивши їх явно. Запустіть\n"
+"наступну команду і дотримуйтесь інструкцій у вашому редакторі, щоб "
+"відредагувати\n"
+"ваш конфігураційний файл:\n"
+"\n"
+"    git config --global --edit\n"
+"\n"
+"Після цього ви можете виправити особистість автора, використану для цього "
+"коміту, за допомогою\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Ваше імʼя та адреса електронної пошти були налаштовані автоматично на "
+"основі\n"
+"вашого імені користувача та назви хосту. Будь ласка, перевірте їх "
+"правильність.\n"
+"Ви можете приховати це повідомлення, вказавши їх явно:\n"
+"\n"
+"    git config --global user.name \"Ваше Імʼя\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"Після цього ви можете виправити особистість автора, використану для цього "
+"коміту, за допомогою\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+msgid "couldn't look up newly created commit"
+msgstr "не вдалося знайти новостворений коміт"
+
+msgid "could not parse newly created commit"
+msgstr "не вдалося розібрати новостворений коміт"
+
+msgid "unable to resolve HEAD after creating commit"
+msgstr "не вдалося розвʼязати HEAD після створення коміту"
+
+msgid "detached HEAD"
+msgstr "відʼєднаний HEAD"
+
+msgid " (root-commit)"
+msgstr " (кореневий коміт)"
+
+msgid "could not parse HEAD"
+msgstr "неможливо розібрати HEAD"
+
+#, c-format
+msgid "HEAD %s is not a commit!"
+msgstr "HEAD %s не є комітом!"
+
+msgid "unable to parse commit author"
+msgstr "не вдалося розібрати автора коміту"
+
+#, c-format
+msgid "unable to read commit message from '%s'"
+msgstr "не вдалося прочитати допис до коміту від \"%s\""
+
+#, c-format
+msgid "invalid author identity '%s'"
+msgstr "неприпустима особистість автора \"%s\""
+
+msgid "corrupt author: missing date information"
+msgstr "пошкоджені дані про автора: відсутня інформація про дату"
+
+#, c-format
+msgid "could not update %s"
+msgstr "не вдалося оновити %s"
+
+#, c-format
+msgid "could not parse commit %s"
+msgstr "не вдалося розібрати коміт %s"
+
+#, c-format
+msgid "could not parse parent commit %s"
+msgstr "не вдалося розібрати джерельний коміт %s"
+
+#, c-format
+msgid "unknown command: %d"
+msgstr "невідома команда: %d"
+
+msgid "This is the 1st commit message:"
+msgstr "Це перший допис до коміту:"
+
+#, c-format
+msgid "This is the commit message #%d:"
+msgstr "Це допис до коміту #%d:"
+
+msgid "The 1st commit message will be skipped:"
+msgstr "Перший допис до коміту буде пропущено:"
+
+#, c-format
+msgid "The commit message #%d will be skipped:"
+msgstr "Допис до коміту #%d буде пропущено:"
+
+#, c-format
+msgid "This is a combination of %d commits."
+msgstr "Це комбінація %d комітів."
+
+#, c-format
+msgid "cannot write '%s'"
+msgstr "неможливо записати \"%s\""
+
+msgid "need a HEAD to fixup"
+msgstr "потрібен HEAD, щоб виправити"
+
+msgid "could not read HEAD"
+msgstr "не вдалося прочитати HEAD"
+
+msgid "could not read HEAD's commit message"
+msgstr "не вдалося прочитати допис до коміту HEAD"
+
+#, c-format
+msgid "could not read commit message of %s"
+msgstr "не вдалося прочитати допис до коміту %s"
+
+msgid "your index file is unmerged."
+msgstr "ваш індексний файл не злитий."
+
+msgid "cannot fixup root commit"
+msgstr "не вдалося виправити кореневий коміт"
+
+#, c-format
+msgid "commit %s is a merge but no -m option was given."
+msgstr "коміт %s - це злиття, але опція -m не була вказана."
+
+#, c-format
+msgid "commit %s does not have parent %d"
+msgstr "коміт %s не має джерела %d"
+
+#, c-format
+msgid "cannot get commit message for %s"
+msgstr "неможливо отримати допис до коміту для %s"
+
+#. TRANSLATORS: The first %s will be a "todo" command like
+#. "revert" or "pick", the second %s a SHA1.
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s: не вдалося розібрати джерельний коміт %s"
+
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "не вдалося зробити вивертання %s... %s"
+
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "не вдалося застосувати %s... %s"
+
+#, c-format
+msgid "dropping %s %s -- patch contents already upstream\n"
+msgstr ""
+"скидання %s %s -- вміст латки вже знаходиться у першоджерельному сховищі\n"
+
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: не вдалося прочитати індекс"
+
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: не вдалося оновити індекс"
+
+#, c-format
+msgid "'%s' is not a valid label"
+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"
+
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "неприпустима команда \"%.*s\""
+
+#, c-format
+msgid "%s does not accept arguments: '%s'"
+msgstr "%s не приймає аргументи: \"%s\""
+
+#, c-format
+msgid "missing arguments for %s"
+msgstr "відсутні аргументи для %s"
+
+#, c-format
+msgid "could not parse '%s'"
+msgstr "не вдалося розібрати \"%s\""
+
+#, c-format
+msgid "invalid line %d: %.*s"
+msgstr "невірний рядок %d: %.*s"
+
+#, c-format
+msgid "cannot '%s' without a previous commit"
+msgstr "неможливо зробити \"%s\" без попереднього коміту"
+
+msgid "cancelling a cherry picking in progress"
+msgstr "наразі йде скасування висмикування"
+
+msgid "cancelling a revert in progress"
+msgstr "наразі йде скасування вивертання"
+
+msgid "please fix this using 'git rebase --edit-todo'."
+msgstr "будь ласка, виправте це за допомогою \"git rebase --edit-todo\"."
+
+#, c-format
+msgid "unusable instruction sheet: '%s'"
+msgstr "непридатна інструкція: \"%s\""
+
+msgid "no commits parsed."
+msgstr "не розібрано жодного коміту."
+
+msgid "cannot cherry-pick during a revert."
+msgstr "неможливо зробити висмикування під час вивертання."
+
+msgid "cannot revert during a cherry-pick."
+msgstr "неможливо зробити вивертання під час висмикування."
+
+msgid "unusable squash-onto"
+msgstr "непридатний squash-onto"
+
+#, c-format
+msgid "malformed options sheet: '%s'"
+msgstr "невірно сформований список опцій: \"%s\""
+
+msgid "empty commit set passed"
+msgstr "передано порожній набір комітів"
+
+msgid "revert is already in progress"
+msgstr "наразі вже триває вивертання"
+
+#, c-format
+msgid "try \"git revert (--continue | %s--abort | --quit)\""
+msgstr "спробуйте \"git revert (--continue | %s--abort | --quit)\""
+
+msgid "cherry-pick is already in progress"
+msgstr "наразі вже триває висмикування"
+
+#, c-format
+msgid "try \"git cherry-pick (--continue | %s--abort | --quit)\""
+msgstr "спробуйте \"git cherry-pick (--continue | %s--abort | --quit)\""
+
+#, c-format
+msgid "could not create sequencer directory '%s'"
+msgstr "не вдалося створити директорію секвенсора \"%s\""
+
+msgid "no cherry-pick or revert in progress"
+msgstr "наразі не триває ні скасування, ні висмикування"
+
+msgid "cannot resolve HEAD"
+msgstr "неможливо розпізнати HEAD"
+
+msgid "cannot abort from a branch yet to be born"
+msgstr "неможливо перервати з гілки, яка ще не народилася"
+
+#, c-format
+msgid "cannot read '%s': %s"
+msgstr "неможливо прочитати \"%s\": %s"
+
+msgid "unexpected end of file"
+msgstr "несподіваний кінець файлу"
+
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr "збережений pre-cherry-pick HEAD файл \"%s\" пошкоджено"
+
+msgid "You seem to have moved HEAD. Not rewinding, check your HEAD!"
+msgstr ""
+"Здається, ви перемістили HEAD. Перемотування назад не виконується, перевірте "
+"свій HEAD!"
+
+msgid "no revert in progress"
+msgstr "наразі не виконується вивертання"
+
+msgid "no cherry-pick in progress"
+msgstr "наразі не виконується висмикування"
+
+msgid "failed to skip the commit"
+msgstr "не вдалося пропустити коміт"
+
+msgid "there is nothing to skip"
+msgstr "немає чого пропускати"
+
+#, c-format
+msgid ""
+"have you committed already?\n"
+"try \"git %s --continue\""
+msgstr ""
+"ви вже зробили коміт?\n"
+"спробуйте \"git %s --continue\""
+
+msgid "cannot read HEAD"
+msgstr "неможливо прочитати HEAD"
+
+#, c-format
+msgid "unable to copy '%s' to '%s'"
+msgstr "не вдалося скопіювати \"%s\" в \"%s\""
+
+#, c-format
+msgid ""
+"You can amend the commit now, with\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"Once you are satisfied with your changes, run\n"
+"\n"
+"  git rebase --continue\n"
+msgstr ""
+"Ви можете внести зміни до коміту зараз за допомогою\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"Після того, як ви будете задоволені своїми змінами, виконайте\n"
+"\n"
+"  git rebase --continue\n"
+
+#, c-format
+msgid "Could not apply %s... %.*s"
+msgstr "Не вдалося застосувати %s... %.*s"
+
+#, c-format
+msgid "Could not merge %.*s"
+msgstr "Не вдалося злити %.*s"
+
+#, c-format
+msgid "Executing: %s\n"
+msgstr "Виконання: %s\n"
+
+#, c-format
+msgid ""
+"execution failed: %s\n"
+"%sYou can fix the problem, and then run\n"
+"\n"
+"  git rebase --continue\n"
+"\n"
+msgstr ""
+"не вдалося виконати : %s\n"
+"%sВи можете виправити проблему, а потім виконати\n"
+"\n"
+"  git rebase --continue\n"
+
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "та внесли зміни до індексу та/або робочого дерева\n"
+
+#, c-format
+msgid ""
+"execution succeeded: %s\n"
+"but left changes to the index and/or the working tree.\n"
+"Commit or stash your changes, and then run\n"
+"\n"
+"  git rebase --continue\n"
+"\n"
+msgstr ""
+"виконання пройшло успішно: %s\n"
+"але залишило зміни в індексі та/або робочому дереві\n"
+"Зробіть коміт або додайте зміни до схову, а потім запустіть\n"
+"\n"
+"  git rebase --continue\n"
+
+#, c-format
+msgid "illegal label name: '%.*s'"
+msgstr "неприпустима назва мітки: \"%.*s\""
+
+#, c-format
+msgid "could not resolve '%s'"
+msgstr "не вдалося розпізнати \"%s\""
+
+msgid "writing fake root commit"
+msgstr "написання підробленого кореневого коміту"
+
+msgid "writing squash-onto"
+msgstr "запис squash-onto"
+
+msgid "cannot merge without a current revision"
+msgstr "неможливо злити без поточної ревізії"
+
+#, c-format
+msgid "unable to parse '%.*s'"
+msgstr "не вдалося розібрати \"%.*s\""
+
+#, c-format
+msgid "nothing to merge: '%.*s'"
+msgstr "нічого зливати: \"%.*s\""
+
+msgid "octopus merge cannot be executed on top of a [new root]"
+msgstr "octopus злиття неможливо виконати поверх [new root]"
+
+#, c-format
+msgid "could not get commit message of '%s'"
+msgstr "не вдалося отримати допис до коміту \"%s\""
+
+#, c-format
+msgid "could not even attempt to merge '%.*s'"
+msgstr "не вдалося навіть спробувати злити \"%.*s\""
+
+msgid "merge: Unable to write new index file"
+msgstr "злиття: Не вдалося записати новий файл індексу"
+
+#, c-format
+msgid ""
+"another 'rebase' process appears to be running; '%s.lock' already exists"
+msgstr "здається, запущено інший процес \"rebase\"; \"%s.lock\" вже існує"
+
+#, c-format
+msgid ""
+"Updated the following refs with %s:\n"
+"%s"
+msgstr ""
+"Оновлені наступні посилання з %s:\n"
+"%s"
+
+#, c-format
+msgid ""
+"Failed to update the following refs with %s:\n"
+"%s"
+msgstr ""
+"Не вдалося оновити наступні посилання з %s:\n"
+"%s"
+
+msgid "Cannot autostash"
+msgstr "Неможливо зробити автосхов"
+
+#, c-format
+msgid "Unexpected stash response: '%s'"
+msgstr "Неочікувана відповідь схову: \"%s\""
+
+#, c-format
+msgid "Could not create directory for '%s'"
+msgstr "Не вдалося створити директорію для \"%s\""
+
+#, c-format
+msgid "Created autostash: %s\n"
+msgstr "Створено автосхов: %s\n"
+
+msgid "could not reset --hard"
+msgstr "не вдалося виконати скидання з --hard"
+
+#, c-format
+msgid "Applied autostash.\n"
+msgstr "Застосовано автосхов.\n"
+
+#, c-format
+msgid "cannot store %s"
+msgstr "неможливо зберегти %s"
+
+#, c-format
+msgid ""
+"%s\n"
+"Your changes are safe in the stash.\n"
+"You can run \"git stash pop\" or \"git stash drop\" at any time.\n"
+msgstr ""
+"%s\n"
+"Ваші зміни в безпеці у схову.\n"
+"Ви можете запустити \"git stash pop\" або \"git stash drop\" у будь-який "
+"час.\n"
+
+msgid "Applying autostash resulted in conflicts."
+msgstr "Застосування автосхову призвело до конфліктів."
+
+msgid "Autostash exists; creating a new stash entry."
+msgstr "Автосхов існує; створення нового запису схову."
+
+msgid "autostash reference is a symref"
+msgstr "посилання автосхову є символьним посиланням"
+
+msgid "could not detach HEAD"
+msgstr "не вдалося відʼєднати HEAD"
+
+#, c-format
+msgid "Stopped at HEAD\n"
+msgstr "Зупинено на HEAD\n"
+
+#, c-format
+msgid "Stopped at %s\n"
+msgstr "Зупинено на %s\n"
+
+#, c-format
+msgid ""
+"Could not execute the todo command\n"
+"\n"
+"    %.*s\n"
+"It has been rescheduled; To edit the command before continuing, please\n"
+"edit the todo list first:\n"
+"\n"
+"    git rebase --edit-todo\n"
+"    git rebase --continue\n"
+msgstr ""
+"Не вдалося виконати команду todo\n"
+"\n"
+"    %.*s\n"
+"Її було перенесено; Щоб відредагувати команду перед продовженням, будь "
+"ласка\n"
+"спочатку відредагуйте список справ:\n"
+"\n"
+"    git rebase --edit-todo\n"
+"    git rebase --continue\n"
+
+#, c-format
+msgid "Stopped at %s...  %.*s\n"
+msgstr "Зупинено на %s... %.*s\n"
+
+#, c-format
+msgid "Rebasing (%d/%d)%s"
+msgstr "Перебазування (%d/%d)%s"
+
+#, c-format
+msgid "unknown command %d"
+msgstr "невідома команда %d"
+
+msgid "could not read orig-head"
+msgstr "не вдалося прочитати orig-head"
+
+msgid "could not read 'onto'"
+msgstr "не вдалося прочитати  \"onto\""
+
+#, c-format
+msgid "could not update HEAD to %s"
+msgstr "не вдалося оновити HEAD до %s"
+
+#, c-format
+msgid "Successfully rebased and updated %s.\n"
+msgstr "Успішно перебазовано та оновлено %s.\n"
+
+msgid "cannot rebase: You have unstaged changes."
+msgstr "неможливо перебазувати: у вас є неіндексовані зміни."
+
+msgid "cannot amend non-existing commit"
+msgstr "неможливо виправити неіснуючий коміт"
+
+#, c-format
+msgid "invalid file: '%s'"
+msgstr "неприпустимий файл: \"%s\""
+
+#, c-format
+msgid "invalid contents: '%s'"
+msgstr "неприпустимий вміст: \"%s\""
+
+msgid ""
+"\n"
+"You have uncommitted changes in your working tree. Please, commit them\n"
+"first and then run 'git rebase --continue' again."
+msgstr ""
+"\n"
+"У вас є незакомічені зміни у вашому робочому дереві. Будь ласка, спочатку "
+"зробіть коміт,\n"
+"а потім знову виконайте \"git rebase --continue\"."
+
+#, c-format
+msgid "could not write file: '%s'"
+msgstr "не вдалося записати файл: \"%s\""
+
+msgid "could not remove CHERRY_PICK_HEAD"
+msgstr "не вдалося видалити CHERRY_PICK_HEAD"
+
+msgid "could not commit staged changes."
+msgstr "не вдалося записати індексовані зміни."
+
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s: неможливо зробити висмикування %s"
+
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s: невірна ревізія"
+
+msgid "can't revert as initial commit"
+msgstr "неможливо зробити вивертання як початковий коміт"
+
+#, c-format
+msgid "skipped previously applied commit %s"
+msgstr "пропущено попередньо застосований коміт %s"
+
+msgid "use --reapply-cherry-picks to include skipped commits"
+msgstr "використовуйте --reapply-cherry-picks, щоб включити пропущені коміти"
+
+msgid "make_script: unhandled options"
+msgstr "make_script: необроблені опції"
+
+msgid "make_script: error preparing revisions"
+msgstr "make_script: помилка при підготовці ревізій"
+
+msgid "nothing to do"
+msgstr "нічого робити"
+
+msgid "could not skip unnecessary pick commands"
+msgstr "не вдалося пропустити непотрібні команди вибору"
+
+msgid "the script was already rearranged."
+msgstr "скрипт вже був перероблений."
+
+#, c-format
+msgid "update-refs file at '%s' is invalid"
+msgstr "файл update-refs за адресою \"%s\" є неприпустимим"
+
+#, c-format
+msgid "'%s' is outside repository at '%s'"
+msgstr "\"%s\" зовнішнє сховище в \"%s\""
+
+#, c-format
+msgid ""
+"%s: no such path in the working tree.\n"
+"Use 'git <command> -- <path>...' to specify paths that do not exist locally."
+msgstr ""
+"%s: немає такого шляху у робочому дереві.\n"
+"Використовуйте \"git <команда> -- <шлях>...\", щоб вказати шляхи, які не "
+"існують локально."
+
+#, c-format
+msgid ""
+"ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+"Use '--' to separate paths from revisions, like this:\n"
+"'git <command> [<revision>...] -- [<file>...]'"
+msgstr ""
+"неоднозначний аргумент \"%s\": невідома ревізія або шлях не у робочому "
+"дереві.\n"
+"Використовуйте \"--\", щоб відокремити шлях від ревізій, наприклад, так:\n"
+"\"git <команда> [<ревізія>...] -- [<файл>...]\""
+
+#, c-format
+msgid "option '%s' must come before non-option arguments"
+msgstr "опція \"%s\" повинна стояти перед аргументами без опцій"
+
+#, c-format
+msgid ""
+"ambiguous argument '%s': both revision and filename\n"
+"Use '--' to separate paths from revisions, like this:\n"
+"'git <command> [<revision>...] -- [<file>...]'"
+msgstr ""
+"неоднозначний аргумент \"%s\": і ревізія, і назва файлу\n"
+"Використовуйте \"--\", щоб відокремити шляхи від ревізій, наприклад, так:\n"
+"\"git <команда> [<ревізія>...] -- [<файл>...]\""
+
+msgid "unable to set up work tree using invalid config"
+msgstr ""
+"не вдалося налаштувати робоче дерево, використовуючи неприпустиму "
+"конфігурацію"
+
+#, c-format
+msgid "Expected git repo version <= %d, found %d"
+msgstr "Очікувалось git сховищe версії <= %d, знайдено %d"
+
+msgid "unknown repository extension found:"
+msgid_plural "unknown repository extensions found:"
+msgstr[0] "знайдено невідомих розширень сховища:"
+msgstr[1] "знайдено невідомих розширень сховища:"
+msgstr[2] "знайдено невідомих розширень сховища:"
+
+msgid "repo version is 0, but v1-only extension found:"
+msgid_plural "repo version is 0, but v1-only extensions found:"
+msgstr[0] "версія сховища дорівнює 0, але знайдено v1-only розширень:"
+msgstr[1] "версія сховища дорівнює 0, але знайдено v1-only розширень:"
+msgstr[2] "версія сховища дорівнює 0, але знайдено v1-only розширень:"
+
+#, c-format
+msgid "error opening '%s'"
+msgstr "помилка при відкритті \"%s\""
+
+#, c-format
+msgid "too large to be a .git file: '%s'"
+msgstr "занадто великий для .git-файлу: \"%s\""
+
+#, c-format
+msgid "error reading %s"
+msgstr "помилка зчитування %s"
+
+#, c-format
+msgid "invalid gitfile format: %s"
+msgstr "неприпустимий формат git файлу: %s"
+
+#, c-format
+msgid "no path in gitfile: %s"
+msgstr "немає шляху в gitfile: %s"
+
+#, c-format
+msgid "not a git repository: %s"
+msgstr "не є git сховищем: %s"
+
+#, c-format
+msgid "'$%s' too big"
+msgstr "\"$%s\" занадто великий"
+
+#, c-format
+msgid "not a git repository: '%s'"
+msgstr "не є git сховищем: \"%s\""
+
+#, c-format
+msgid "cannot chdir to '%s'"
+msgstr "неможливо змінити директорію на \"%s\""
+
+msgid "cannot come back to cwd"
+msgstr "неможливо повернутися до поточної робочої директорії"
+
+#, c-format
+msgid "failed to stat '%*s%s%s'"
+msgstr "не вдалося записати \"%*s%s%s\""
+
+msgid "Unable to read current working directory"
+msgstr "Не вдалося прочитати поточну робочу директорію"
+
+#, c-format
+msgid "cannot change to '%s'"
+msgstr "неможливо змінити на \"%s\""
+
+#, c-format
+msgid "not a git repository (or any of the parent directories): %s"
+msgstr "не є git сховищем (як і жодна з батьківських директорій): %s"
+
+#, c-format
+msgid ""
+"not a git repository (or any parent up to mount point %s)\n"
+"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
+msgstr ""
+"не є git сховищем (як і жодна з батьківських директорій до місця монтування "
+"%s)\n"
+"Зупинка на межі файлової системи (GIT_DISCOVERY_ACROSS_FILESYSTEM не "
+"встановлено)."
+
+#, c-format
+msgid ""
+"detected dubious ownership in repository at '%s'\n"
+"%sTo add an exception for this directory, call:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+msgstr ""
+"виявлено сумнівне право власності у сховищі за адресою \"%s\"\n"
+"%sЩоб додати виняток для цієї директорії, виконайте:\n"
+"\n"
+"\tgit config --global --add safe.directory %s"
+
+#, c-format
+msgid "cannot use bare repository '%s' (safe.bareRepository is '%s')"
+msgstr ""
+"неможливо використати порожнє сховище \"%s\" (safe.bareRepository "
+"встановлено в \"%s\")"
+
+#, c-format
+msgid ""
+"problem with core.sharedRepository filemode value (0%.3o).\n"
+"The owner of files must always have read and write permissions."
+msgstr ""
+"проблема зі значенням файлового режиму core.sharedRepository (0%.3o).\n"
+"Власник файлів завжди повинен мати дозвіл на читання та запис."
+
+msgid "fork failed"
+msgstr "fork завершився невдало"
+
+msgid "setsid failed"
+msgstr "setsid завершився невдало"
+
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "неможливо виконати stat шаблона \"%s\""
+
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "неможливо виконати opendir \"%s\""
+
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "неможливо виконати readlink \"%s\""
+
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "неможливо виконати symlink \"%s\" \"%s\""
+
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "неможливо скопіювати \"%s\" до \"%s\""
+
+#, c-format
+msgid "ignoring template %s"
+msgstr "ігнорування шаблону %s"
+
+#, c-format
+msgid "templates not found in %s"
+msgstr "шаблонів не знайдено в %s"
+
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "не копіюються шаблони з \"%s\": %s"
+
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "неприпустиме початкове ім’я гілки: \"%s\""
+
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: ігноровано --initial-branch=%s"
+
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "не вдалося обробити тип файлу %d"
+
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "не вдалося перемістити %s на %s"
+
+msgid "attempt to reinitialize repository with different hash"
+msgstr "спроба переініціалізувати репозиторій з іншим хеш-алгоритмом"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "спроба переініціалізувати сховище з іншим форматом зберігання"
+
+#, c-format
+msgid "%s already exists"
+msgstr "%s вже існує"
+
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "Переініціалізовано існуюче спільне Git сховище в %s%s\n"
+
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "Переініціалізовано існуюче Git сховище в %s%s\n"
+
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "Ініціалізовано порожнє спільне Git сховище в %s%s\n"
+
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "Ініціалізовано порожнє Git сховище в %s%s\n"
+
+#, c-format
+msgid "index entry is a directory, but not sparse (%08x)"
+msgstr "запис індексу є директорією, але не розрідженою (%08x)"
+
+msgid "cannot use split index with a sparse index"
+msgstr "неможливо використовувати розділений індекс з розрідженим індексом"
+
+#. TRANSLATORS: IEC 80000-13:2008 gibibyte
+#, c-format
+msgid "%u.%2.2u GiB"
+msgstr "%u.%2.2u ГіБ"
+
+#. TRANSLATORS: IEC 80000-13:2008 gibibyte/second
+#, c-format
+msgid "%u.%2.2u GiB/s"
+msgstr "%u.%2.2u ГіБ/с"
+
+#. TRANSLATORS: IEC 80000-13:2008 mebibyte
+#, c-format
+msgid "%u.%2.2u MiB"
+msgstr "%u.%2.2u МіБ"
+
+#. TRANSLATORS: IEC 80000-13:2008 mebibyte/second
+#, c-format
+msgid "%u.%2.2u MiB/s"
+msgstr "%u.%2.2u МіБ/с"
+
+#. TRANSLATORS: IEC 80000-13:2008 kibibyte
+#, c-format
+msgid "%u.%2.2u KiB"
+msgstr "%u.%2.2u КіБ"
+
+#. TRANSLATORS: IEC 80000-13:2008 kibibyte/second
+#, c-format
+msgid "%u.%2.2u KiB/s"
+msgstr "%u.%2.2u КіБ/с"
+
+#. TRANSLATORS: IEC 80000-13:2008 byte
+#, c-format
+msgid "%u byte"
+msgid_plural "%u bytes"
+msgstr[0] "%u байт"
+msgstr[1] "%u байти"
+msgstr[2] "%u байтів"
+
+#. TRANSLATORS: IEC 80000-13:2008 byte/second
+#, c-format
+msgid "%u byte/s"
+msgid_plural "%u bytes/s"
+msgstr[0] "%u байт/s"
+msgstr[1] "%u байти/s"
+msgstr[2] "%u байтів/s"
+
+#, c-format
+msgid "ignoring suspicious submodule name: %s"
+msgstr "ігнорування підозрілої назви підмодуля: %s"
+
+msgid "negative values not allowed for submodule.fetchJobs"
+msgstr "негативні значення не дозволені для submodule.fetchJobs"
+
+#, c-format
+msgid "ignoring '%s' which may be interpreted as a command-line option: %s"
+msgstr ""
+"ігнорування \"%s\", який може бути інтерпретований як параметр командного "
+"рядка: %s"
+
+#, c-format
+msgid "Could not update .gitmodules entry %s"
+msgstr "Не вдалося оновити запис %s у .gitmodules"
+
+msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first"
+msgstr ""
+"Неможливо змінити не злиті .gitmodules, спочатку розвʼяжіть конфлікти злиття"
+
+#, c-format
+msgid "Could not find section in .gitmodules where path=%s"
+msgstr "Не вдалося знайти розділ у .gitmodules де path=%s"
+
+#, c-format
+msgid "Could not remove .gitmodules entry for %s"
+msgstr "Не вдалося видалити запис .gitmodules для %s"
+
+msgid "staging updated .gitmodules failed"
+msgstr "індексація оновленого .gitmodules завершилася невдало"
+
+#, c-format
+msgid "in unpopulated submodule '%s'"
+msgstr "у незаповненому підмодулі \"%s\""
+
+#, c-format
+msgid "Pathspec '%s' is in submodule '%.*s'"
+msgstr "Визначник шляху \"%s\" знаходиться у підмодулі \"%.*s\""
+
+#, c-format
+msgid "bad --ignore-submodules argument: %s"
+msgstr "невірний --ignore-submodules аргумент: %s"
+
+#, c-format
+msgid ""
+"Submodule in commit %s at path: '%s' collides with a submodule named the "
+"same. Skipping it."
+msgstr ""
+"Підмодуль у коміті %s за шляхом \"%s\" зіткнувся з підмодулем з такою самою "
+"назвою. Пропускаємо його."
+
+#, c-format
+msgid "submodule entry '%s' (%s) is a %s, not a commit"
+msgstr "запис підмодуля \"%s\" (%s) є %s, а не комітом"
+
+#, c-format
+msgid ""
+"Could not run 'git rev-list <commits> --not --remotes -n 1' command in "
+"submodule %s"
+msgstr ""
+"Не вдалося виконати команду \"git rev-list <коміти> --not --remotes -n 1\" у "
+"підмодулі %s"
+
+#, c-format
+msgid "process for submodule '%s' failed"
+msgstr "процес для підмодуля \"%s\" завершився невдало"
+
+#, c-format
+msgid "Pushing submodule '%s'\n"
+msgstr "Надсилання підмодуля \"%s\"\n"
+
+#, c-format
+msgid "Unable to push submodule '%s'\n"
+msgstr "Не вдалося надіслати підмодуль \"%s\"\n"
+
+#, c-format
+msgid "Fetching submodule %s%s\n"
+msgstr "Отримання підмодуля %s%s\n"
+
+#, c-format
+msgid "Could not access submodule '%s'\n"
+msgstr "Не вдалося отримати доступ до підмодуля \"%s\"\n"
+
+#, c-format
+msgid "Could not access submodule '%s' at commit %s\n"
+msgstr "Не вдалося отримати доступ до підмодуля \"%s\" для коміту %s\n"
+
+#, c-format
+msgid "Fetching submodule %s%s at commit %s\n"
+msgstr "Отримання підмодуля %s%s для коміту %s\n"
+
+#, c-format
+msgid ""
+"Errors during submodule fetch:\n"
+"%s"
+msgstr ""
+"Помилки під час отримання підмодуля:\n"
+"%s"
+
+#, c-format
+msgid "'%s' not recognized as a git repository"
+msgstr "\"%s\" не розпізнано як git сховище"
+
+#, c-format
+msgid "Could not run 'git status --porcelain=2' in submodule %s"
+msgstr ""
+"Не вдалося виконати команду \"git status --porcelain=2\" у підмодулі %s"
+
+#, c-format
+msgid "'git status --porcelain=2' failed in submodule %s"
+msgstr "\"git status --porcelain=2\" завершився невдало у підмодулі %s"
+
+#, c-format
+msgid "could not start 'git status' in submodule '%s'"
+msgstr "не вдалося запустити \"git status\" у підмодулі \"%s\""
+
+#, c-format
+msgid "could not run 'git status' in submodule '%s'"
+msgstr "не вдалося запустити \"git status\" у підмодулі \"%s\""
+
+#, c-format
+msgid "Could not unset core.worktree setting in submodule '%s'"
+msgstr "Не вдалося скинути налаштування core.worktree у підмодулі \"%s\""
+
+#, c-format
+msgid "could not recurse into submodule '%s'"
+msgstr "не вдалося рекурсивно перейти в підмодуль \"%s\""
+
+msgid "could not reset submodule index"
+msgstr "не вдалося скинути індекс підмодуля"
+
+#, c-format
+msgid "submodule '%s' has dirty index"
+msgstr "підмодуль \"%s\" має брудний індекс"
+
+#, c-format
+msgid "Submodule '%s' could not be updated."
+msgstr "Не вдалося оновити підмодуль \"%s\"."
+
+#, c-format
+msgid "submodule git dir '%s' is inside git dir '%.*s'"
+msgstr "підмодуль git dir \"%s\" знаходиться всередині git директорії \"%.*s\""
+
+#, c-format
+msgid ""
+"relocate_gitdir for submodule '%s' with more than one worktree not supported"
+msgstr ""
+"relocate_gitdir для підмодуля \"%s\" з більш ніж одним робочим деревом не "
+"підтримується"
+
+#, c-format
+msgid "could not lookup name for submodule '%s'"
+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"
+"'%s'\n"
+msgstr ""
+"Міграція git-директорії \"%s%s\" з\n"
+"\"%s\" до\n"
+"\"%s\"\n"
+
+msgid "could not start ls-files in .."
+msgstr "не вдалося запустити ls-файли в .."
+
+#, c-format
+msgid "ls-tree returned unexpected return code %d"
+msgstr "ls-tree повернув неочікуваний код повернення %d"
+
+#, c-format
+msgid "failed to lstat '%s'"
+msgstr "не вдалося виконати lstat для \"%s\""
+
+msgid "no remote configured to get bundle URIs from"
+msgstr ""
+"немає налаштованого віддаленого призначення для отримання URI пакунків з "
+"нього"
+
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "віддалений \"%s\" не має налаштованої URL-адреси"
+
+msgid "could not get the bundle-uri list"
+msgstr "не вдалося отримати список bundle-uri"
+
+msgid "test-tool cache-tree <options> (control|prime|update)"
+msgstr "test-tool cache-tree <опції> (control|prime|update)"
+
+msgid "clear the cache tree before each iteration"
+msgstr "очищати дерево кешу перед кожною ітерацією"
+
+msgid "number of entries in the cache tree to invalidate (default 0)"
+msgstr ""
+"кількість записів у дереві кешу, які потрібно анулювати (за замовчуванням 0)"
+
+#, c-format
+msgid "commit %s is not marked reachable"
+msgstr "коміт %s не позначений як досяжний"
+
+msgid "too many commits marked reachable"
+msgstr "забагато комітів позначено як досяжні"
+
+msgid "test-tool serve-v2 [<options>]"
+msgstr "test-tool serve-v2 [<опції>]"
+
+msgid "exit immediately after advertising capabilities"
+msgstr "вихід відразу після показу здібностей"
+
+msgid "test-helper simple-ipc is-active    [<name>] [<options>]"
+msgstr "test-helper simple-ipc is-active    [<назва>] [<опції>]"
+
+msgid "test-helper simple-ipc run-daemon   [<name>] [<threads>]"
+msgstr "test-helper simple-ipc run-daemon   [<назва>] [<потоки>]"
+
+msgid "test-helper simple-ipc start-daemon [<name>] [<threads>] [<max-wait>]"
+msgstr ""
+"test-helper simple-ipc start-daemon [<назва>] [<потоки>] [<максимальний час "
+"очікування>]"
+
+msgid "test-helper simple-ipc stop-daemon  [<name>] [<max-wait>]"
+msgstr ""
+"test-helper simple-ipc stop-daemon  [<назва>] [<максимальний час очікування>]"
+
+msgid "test-helper simple-ipc send         [<name>] [<token>]"
+msgstr "test-helper simple-ipc send         [<назва>] [<токен>]"
+
+msgid "test-helper simple-ipc sendbytes    [<name>] [<bytecount>] [<byte>]"
+msgstr ""
+"test-helper simple-ipc sendbytes    [<назва>] [<кількість байт>] [<байт>]"
+
+msgid ""
+"test-helper simple-ipc multiple     [<name>] [<threads>] [<bytecount>] "
+"[<batchsize>]"
+msgstr ""
+"test-helper simple-ipc multiple     [<назва>] [<потоки>] [<кількість байт>] "
+"[<розмір пакетів>]"
+
+msgid "name or pathname of unix domain socket"
+msgstr "назва або назва шляху сокета домену unix"
+
+msgid "named-pipe name"
+msgstr "назва іменованого канала"
+
+msgid "number of threads in server thread pool"
+msgstr "кількість потоків у пулі потоків сервера"
+
+msgid "seconds to wait for daemon to start or stop"
+msgstr "секунд чекати на запуск або зупинку демона"
+
+msgid "number of bytes"
+msgstr "кількість байтів"
+
+msgid "number of requests per thread"
+msgstr "кількість запитів на потік"
+
+msgid "byte"
+msgstr "байт"
+
+msgid "ballast character"
+msgstr "баластний символ"
+
+msgid "token"
+msgstr "токен"
+
+msgid "command token to send to the server"
+msgstr "токен команди для відправки на сервер"
+
+#, c-format
+msgid "running trailer command '%s' failed"
+msgstr "не вдалося виконати команду трейлера \"%s\""
+
+#, c-format
+msgid "unknown value '%s' for key '%s'"
+msgstr "невідоме значення \"%s\" для ключа \"%s\""
+
+#, c-format
+msgid "empty trailer token in trailer '%.*s'"
+msgstr "порожній токен трейлера в трейлері \"%.*s\""
+
+#, c-format
+msgid "could not read input file '%s'"
+msgstr "не вдалося прочитати вхідний файл \"%s\""
+
+#, c-format
+msgid "could not stat %s"
+msgstr "не вдалося прочитати %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 rename temporary file to %s"
+msgstr "не вдалося перейменувати тимчасовий файл на %s"
+
+msgid "full write to remote helper failed"
+msgstr "не вдалося виконати повний запис до віддаленого помічника"
+
+#, c-format
+msgid "unable to find remote helper for '%s'"
+msgstr "не вдалося знайти віддаленого помічника для \"%s\""
+
+msgid "can't dup helper output fd"
+msgstr "неможливо зробити копію дескриптора файла виводу помічника"
+
+#, c-format
+msgid ""
+"unknown mandatory capability %s; this remote helper probably needs newer "
+"version of Git"
+msgstr ""
+"невідома обовʼязкова здібність %s; цей віддалений помічник напевно потребує "
+"новішої версії Git"
+
+msgid "this remote helper should implement refspec capability"
+msgstr "цей віддалений помічник має реалізовувати здібність refspec"
+
+#, c-format
+msgid "%s unexpectedly said: '%s'"
+msgstr "%s несподівано сказав: \"%s\""
+
+#, c-format
+msgid "%s also locked %s"
+msgstr "%s також заблокувало %s"
+
+msgid "couldn't run fast-import"
+msgstr "не вдалося запустити fast-import"
+
+msgid "error while running fast-import"
+msgstr "помилка під час виконання швидкого імпорту"
+
+#, c-format
+msgid "could not read ref %s"
+msgstr "не вдалося прочитати посилання %s"
+
+#, c-format
+msgid "unknown response to connect: %s"
+msgstr "невідома відповідь на зʼєднання: %s"
+
+msgid "setting remote service path not supported by protocol"
+msgstr "встановлення шляху до віддаленого сервісу не підтримується протоколом"
+
+msgid "invalid remote service path"
+msgstr "неприпустимий шлях до віддаленої служби"
+
+#, c-format
+msgid "can't connect to subservice %s"
+msgstr "неможливо підключитися до підсервісу %s"
+
+msgid "--negotiate-only requires protocol v2"
+msgstr "--negotiate-only потребує протоколу v2"
+
+msgid "'option' without a matching 'ok/error' directive"
+msgstr "\"option\" без відповідної директиви \"ok/error\""
+
+#, c-format
+msgid "expected ok/error, helper said '%s'"
+msgstr "очікувалось ok/error, помічник сказав \"%s\""
+
+#, c-format
+msgid "helper reported unexpected status of %s"
+msgstr "helper повідомив про неочікуваний статус %s"
+
+#, c-format
+msgid "helper %s does not support dry-run"
+msgstr "помічник %s не підтримує dry-run"
+
+#, c-format
+msgid "helper %s does not support --signed"
+msgstr "помічник %s не підтримує --signed"
+
+#, c-format
+msgid "helper %s does not support --signed=if-asked"
+msgstr "помічник %s не підтримує --signed=if-asked"
+
+#, c-format
+msgid "helper %s does not support --atomic"
+msgstr "помічник %s не підтримує --atomic"
+
+#, c-format
+msgid "helper %s does not support --%s"
+msgstr "помічник %s не підтримує --%s"
+
+#, c-format
+msgid "helper %s does not support 'push-option'"
+msgstr "помічник %s не підтримує \"push-option\""
+
+msgid "remote-helper doesn't support push; refspec needed"
+msgstr "remote-helper не підтримує push; потрібен refspec"
+
+#, c-format
+msgid "helper %s does not support 'force'"
+msgstr "помічник %s не підтримує \"force\""
+
+msgid "couldn't run fast-export"
+msgstr "не вдалося запустити fast-export"
+
+msgid "error while running fast-export"
+msgstr "помилка під час виконання швидкого експорту"
+
+#, c-format
+msgid ""
+"No refs in common and none specified; doing nothing.\n"
+"Perhaps you should specify a branch.\n"
+msgstr ""
+"Немає спільних посилань і жодного не вказано; нічого не робиться.\n"
+"Можливо, вам слід вказати гілку.\n"
+
+#, c-format
+msgid "unsupported object format '%s'"
+msgstr "непідтримуваний обʼєкт формата \"%s\""
+
+#, c-format
+msgid "malformed response in ref list: %s"
+msgstr "невірно сформована відповідь у списку посилань: %s"
+
+#, c-format
+msgid "read(%s) failed"
+msgstr "read(%s) завершився невдало"
+
+#, c-format
+msgid "write(%s) failed"
+msgstr "write(%s) завершився невдало"
+
+#, c-format
+msgid "%s thread failed"
+msgstr "%s потік завершився невдало"
+
+#, c-format
+msgid "%s thread failed to join: %s"
+msgstr "%s потік не зміг приєднатись: %s"
+
+#, c-format
+msgid "can't start thread for copying data: %s"
+msgstr "неможливо запустити потік для копіювання даних: %s"
+
+#, c-format
+msgid "%s process failed to wait"
+msgstr "%s процес не зміг дочекатися"
+
+#, c-format
+msgid "%s process failed"
+msgstr "%s процес завершився невдало"
+
+msgid "can't start thread for copying data"
+msgstr "неможливо запустити потік для копіювання даних"
+
+#, c-format
+msgid "Would set upstream of '%s' to '%s' of '%s'\n"
+msgstr "Встановить першоджерельне сховище для \"%s\" на \"%s\" в \"%s\"\n"
+
+#, c-format
+msgid "could not read bundle '%s'"
+msgstr "не вдалося прочитати пакунок \"%s\""
+
+#, c-format
+msgid "transport: invalid depth option '%s'"
+msgstr "transport: неприпустимий параметр глибини \"%s\""
+
+msgid "see protocol.version in 'git help config' for more details"
+msgstr ""
+"дивіться protocol.version у \"git help config\" для більш детальної "
+"інформації"
+
+msgid "server options require protocol version 2 or later"
+msgstr "опції сервера вимагають протокол версії 2 або пізнішої"
+
+msgid "server does not support wait-for-done"
+msgstr "сервер не підтримує wait-for-done"
+
+msgid "could not parse transport.color.* config"
+msgstr "не вдалося розібрати конфігурацію transport.color.*"
+
+msgid "support for protocol v2 not implemented yet"
+msgstr "підтримка протоколу v2 ще не запроваджена"
+
+#, c-format
+msgid "transport '%s' not allowed"
+msgstr "засіб передачі \"%s\" не дозволений"
+
+msgid "git-over-rsync is no longer supported"
+msgstr "git-over-rsync більше не підтримується"
+
+#, c-format
+msgid ""
+"The following submodule paths contain changes that can\n"
+"not be found on any remote:\n"
+msgstr ""
+"Наступні шляхи до підмодулів містять зміни, які неможливо\n"
+"знайти в жодному віддаленому призначенні:\n"
+
+#, c-format
+msgid ""
+"\n"
+"Please try\n"
+"\n"
+"\tgit push --recurse-submodules=on-demand\n"
+"\n"
+"or cd to the path and use\n"
+"\n"
+"\tgit push\n"
+"\n"
+"to push them to a remote.\n"
+"\n"
+msgstr ""
+"\n"
+"Будь ласка, спробуйте\n"
+"\n"
+"\tgit push --recurse-submodules=on-demand\n"
+"\n"
+"або перейдіть до директорії та використайте\n"
+"\n"
+"\tgit push\n"
+"\n"
+"щоб надіслати їх до віддаленого призначення.\n"
+
+msgid "Aborting."
+msgstr "Відміна."
+
+msgid "failed to push all needed submodules"
+msgstr "не вдалося надіслати всі необхідні підмодулі"
+
+msgid "bundle-uri operation not supported by protocol"
+msgstr "bundle-uri операція не підтримується протоколом"
+
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "не вдалося отримати список адрес пакетів, оголошений сервером"
+
+msgid "operation not supported by protocol"
+msgstr "операція не підтримується протоколом"
+
+msgid "too-short tree object"
+msgstr "занадто короткий обʼєкт дерева"
+
+msgid "malformed mode in tree entry"
+msgstr "невірно визначений режим у записі дерева"
+
+msgid "empty filename in tree entry"
+msgstr "порожня назва файлу у записі дерева"
+
+msgid "too-short tree file"
+msgstr "занадто короткий файл дерева"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by checkout:\n"
+"%%sPlease commit your changes or stash them before you switch branches."
+msgstr ""
+"Ваші локальні зміни в наступних файлах будуть перезаписані при переключенні "
+"стану:\n"
+"%%sБудь ласка, зробіть коміт або додайте їх до схову перед переходом до "
+"іншої гілки."
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by checkout:\n"
+"%%s"
+msgstr ""
+"Ваші локальні зміни в наступних файлах будуть перезаписані при переключенні "
+"стану:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"%%sPlease commit your changes or stash them before you merge."
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані під час злиття:\n"
+"%%sБудь ласка, зробіть коміт або додайте їх до схову перед злиттям."
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by merge:\n"
+"%%s"
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані під час злиття:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by %s:\n"
+"%%sPlease commit your changes or stash them before you %s."
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані %s:\n"
+"%%sБудь ласка, зробіть коміт або додайте їх до схову перед %s."
+
+#, c-format
+msgid ""
+"Your local changes to the following files would be overwritten by %s:\n"
+"%%s"
+msgstr ""
+"Ваші локальні зміни у наступних файлах будуть перезаписані %s:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"Updating the following directories would lose untracked files in them:\n"
+"%s"
+msgstr ""
+"Оновлення наступних директорій призведе до втрати невідстежуваних файлів у "
+"цих директоріях:\n"
+"%s"
+
+#, c-format
+msgid ""
+"Refusing to remove the current working directory:\n"
+"%s"
+msgstr ""
+"Відмовлено у видаленні поточної робочої директорії:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by checkout:\n"
+"%%sPlease move or remove them before you switch branches."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені під час "
+"переключення:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед переходом до іншої гілки."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by checkout:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені під час "
+"переключення:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by merge:\n"
+"%%sPlease move or remove them before you merge."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"злиття:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед злиттям."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by merge:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"злиття:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by %s:\n"
+"%%sPlease move or remove them before you %s."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"%s:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед %s."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be removed by %s:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть видалені при виконанні "
+"%s:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by "
+"checkout:\n"
+"%%sPlease move or remove them before you switch branches."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"переключенні стану\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед переходом до іншої гілки."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by "
+"checkout:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"переключенні стану:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by merge:\n"
+"%%sPlease move or remove them before you merge."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"злитті:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед злиттям."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by merge:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані при "
+"виконанні злиття:\n"
+"%%s"
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by %s:\n"
+"%%sPlease move or remove them before you %s."
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані %s:\n"
+"%%sБудь ласка, перемістіть або видаліть їх перед %s."
+
+#, c-format
+msgid ""
+"The following untracked working tree files would be overwritten by %s:\n"
+"%%s"
+msgstr ""
+"Наступні невідстежувані файли робочого дерева будуть перезаписані %s:\n"
+"%%s"
+
+#, c-format
+msgid "Entry '%s' overlaps with '%s'.  Cannot bind."
+msgstr "Запис \"%s\" перетинається з \"%s\".  Неможливо звʼязати."
+
+#, c-format
+msgid ""
+"Cannot update submodule:\n"
+"%s"
+msgstr ""
+"Не вдалося оновити підмодуль:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following paths are not up to date and were left despite sparse "
+"patterns:\n"
+"%s"
+msgstr ""
+"Наступні шляхи не є актуальними і були залишені, незважаючи на розріджені "
+"шаблони:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following paths are unmerged and were left despite sparse patterns:\n"
+"%s"
+msgstr ""
+"Наступні шляхи не злиті і були залишені, незважаючи на розріджені шаблони:\n"
+"%s"
+
+#, c-format
+msgid ""
+"The following paths were already present and thus not updated despite sparse "
+"patterns:\n"
+"%s"
+msgstr ""
+"Наступні шляхи вже існували і тому не були оновлені, незважаючи на "
+"розріджені шаблони:\n"
+"%s"
+
+#, c-format
+msgid "Aborting\n"
+msgstr "Переривання\n"
+
+#, c-format
+msgid ""
+"After fixing the above paths, you may want to run `git sparse-checkout "
+"reapply`.\n"
+msgstr ""
+"Після виправлення наведених вище шляхів можливо ви забажаєте виконати \"git "
+"sparse-checkout reapply\".\n"
+
+msgid "Updating files"
+msgstr "Оновлення файлів"
+
+msgid ""
+"the following paths have collided (e.g. case-sensitive paths\n"
+"on a case-insensitive filesystem) and only one from the same\n"
+"colliding group is in the working tree:\n"
+msgstr ""
+"зіткнулися наступні шляхи (наприклад, шляхи, чутливі до регістру\n"
+"у файловій системі, не чутливій до регістру) і лише один шлях з тієї \n"
+"групи, що зіткнулися, знаходиться у робочому дереві:\n"
+
+msgid "Updating index flags"
+msgstr "Оновлення прапорців індексу"
+
+#, c-format
+msgid "worktree and untracked commit have duplicate entries: %s"
+msgstr "робоче дерево та невідстежуваний коміт мають дубльовані записи: %s"
+
+msgid "expected flush after fetch arguments"
+msgstr "очікувалось flush після аргументів отримання"
+
+msgid "invalid URL scheme name or missing '://' suffix"
+msgstr "неприпустима назва схеми URL-адреси або відсутній \"://\" суфікс"
+
+#, c-format
+msgid "invalid %XX escape sequence"
+msgstr "неприпустима %XX екрануюча послідовність"
+
+msgid "missing host and scheme is not 'file:'"
+msgstr "відсутній хост і схема не \"file:\""
+
+msgid "a 'file:' URL may not have a port number"
+msgstr "\"file:\" URL-адреса не може мати номера порту"
+
+msgid "invalid characters in host name"
+msgstr "неприпустимі символи в назві хоста"
+
+msgid "invalid port number"
+msgstr "неприпустимий номер порту"
+
+msgid "invalid '..' path segment"
+msgstr "неприпустимий \"..\" сегмент шляху"
+
+msgid "usage: "
+msgstr "використання: "
+
+msgid "fatal: "
+msgstr "збій: "
+
+msgid "error: "
+msgstr "помилка: "
+
+msgid "warning: "
+msgstr "попередження: "
+
+msgid "Fetching objects"
+msgstr "Отримання обʼєктів"
+
+#, c-format
+msgid "'%s' at main working tree is not the repository directory"
+msgstr "\"%s\" у головному робочому дереві не є директорією сховища"
+
+#, c-format
+msgid "'%s' file does not contain absolute path to the working tree location"
+msgstr ""
+"\"%s\" файл не містить абсолютного шляху до розташування робочого дерева"
+
+#, c-format
+msgid "'%s' is not a .git file, error code %d"
+msgstr "\"%s\" не є .git файлом, код помилки %d"
+
+#, c-format
+msgid "'%s' does not point back to '%s'"
+msgstr "\"%s\" не вказує назад на \"%s\""
+
+msgid "not a directory"
+msgstr "не є директорією"
+
+msgid ".git is not a file"
+msgstr ".git не є файлом"
+
+msgid ".git file broken"
+msgstr ".git файл пошкоджено"
+
+msgid ".git file incorrect"
+msgstr ".git файл не є коректним"
+
+msgid "not a valid path"
+msgstr "неприпустимий шлях"
+
+msgid "unable to locate repository; .git is not a file"
+msgstr "не вдалося знайти сховище; .git не є файлом"
+
+msgid "unable to locate repository; .git file does not reference a repository"
+msgstr "не вдалося знайти сховище; .git-файл не посилається на сховище"
+
+msgid "unable to locate repository; .git file broken"
+msgstr "не вдалося знайти сховище; файл .git пошкоджено"
+
+msgid "gitdir unreadable"
+msgstr "нечитабельна git директорія"
+
+msgid "gitdir incorrect"
+msgstr "невірна git директорія"
+
+msgid "not a valid directory"
+msgstr "неприпустима директорія"
+
+msgid "gitdir file does not exist"
+msgstr "файл git директорії не існує"
+
+#, c-format
+msgid "unable to read gitdir file (%s)"
+msgstr "не вдалося прочитати файл git директорії (%s)"
+
+#, c-format
+msgid "short read (expected %<PRIuMAX> bytes, read %<PRIuMAX>)"
+msgstr ""
+"помилка при зчитуванні (очікувалось %<PRIuMAX> байтів, прочитано %<PRIuMAX>)"
+
+msgid "invalid gitdir file"
+msgstr "неприпустимий файл git директорії"
+
+msgid "gitdir file points to non-existent location"
+msgstr "файл git директорії вказує на неіснуюче розташування"
+
+#, c-format
+msgid "unable to set %s in '%s'"
+msgstr "не вдалося встановити %s в \"%s\""
+
+#, c-format
+msgid "unable to unset %s in '%s'"
+msgstr "не вдалося скинути %s в \"%s\""
+
+msgid "failed to set extensions.worktreeConfig setting"
+msgstr "не вдалося встановити extensions.worktreeConfig параметр"
+
+#, c-format
+msgid "could not setenv '%s'"
+msgstr "не вдалося встановити змінну оточення \"%s\""
+
+#, c-format
+msgid "unable to create '%s'"
+msgstr "не вдалося створити \"%s\""
+
+#, c-format
+msgid "could not open '%s' for reading and writing"
+msgstr "не вдалося відкрити \"%s\" для читання та запису"
+
+#, c-format
+msgid "unable to access '%s'"
+msgstr "не вдалося отримати доступ до \"%s\""
+
+msgid "unable to get current working directory"
+msgstr "не вдалося завантажити поточну робочу директорію"
+
+msgid "unable to get random bytes"
+msgstr "не вдалося отримати випадкові байти"
+
+msgid "Unmerged paths:"
+msgstr "Не злиті шляхи:"
+
+msgid "  (use \"git restore --staged <file>...\" to unstage)"
+msgstr ""
+"  (використовуйте \"git restore --staged <файл>...\", щоб видалити з індексу)"
+
+#, c-format
+msgid "  (use \"git restore --source=%s --staged <file>...\" to unstage)"
+msgstr ""
+"  (використовуйте \"git restore --source=%s --staged <file>...\", щоб "
+"прибрати з індексу)"
+
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+"  (використовуйте \"git rm --cached <file>...\", щоб видалити з індексу)"
+
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (використовуйте \"git add <файл>...\", щоб позначити як розвʼязане)"
+
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+"  (використовуйте \"git add/rm <файл>...\" за потребою, щоб позначити "
+"вирішення)"
+
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (використовуйте \"git rm <файл>...\", щоб позначити вирішення)"
+
+msgid "Changes to be committed:"
+msgstr "Зміни, додані до майбутнього коміту:"
+
+msgid "Changes not staged for commit:"
+msgstr "Зміни, не додані до майбутнього коміту:"
+
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (скористайтесь \"git add <файл>...\", щоб оновити майбутній коміт)"
+
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+"  (скористайтесь \"git add/rm <файл>...\", щоб оновити майбутній коміт)"
+
+msgid ""
+"  (use \"git restore <file>...\" to discard changes in working directory)"
+msgstr ""
+"  (скористайтесь \"git restore <файл>...\", щоб скасувати зміни в робочій "
+"директорії)"
+
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+"  (зробіть коміт або відкиньте невідстежуваний або змінений вміст у "
+"підмодулях)"
+
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+"  (скористайтесь \"git %s <файл>...\", щоб додати до майбутнього коміту)"
+
+msgid "both deleted:"
+msgstr "видалено обома:"
+
+msgid "added by us:"
+msgstr "додано нами:"
+
+msgid "deleted by them:"
+msgstr "видалено ними:"
+
+msgid "added by them:"
+msgstr "додано ними:"
+
+msgid "deleted by us:"
+msgstr "видалено нами:"
+
+msgid "both added:"
+msgstr "додано обома:"
+
+msgid "both modified:"
+msgstr "змінено обома:"
+
+msgid "new file:"
+msgstr "новий файл:"
+
+msgid "copied:"
+msgstr "скопійовано:"
+
+msgid "deleted:"
+msgstr "видалено:"
+
+msgid "modified:"
+msgstr "змінено:"
+
+msgid "renamed:"
+msgstr "перейменовано:"
+
+msgid "typechange:"
+msgstr "змінено тип:"
+
+msgid "unknown:"
+msgstr "невідомо:"
+
+msgid "unmerged:"
+msgstr "не злито:"
+
+msgid "new commits, "
+msgstr "нові коміти, "
+
+msgid "modified content, "
+msgstr "змінений контент, "
+
+msgid "untracked content, "
+msgstr "невідстежуваний контент, "
+
+#, c-format
+msgid "Your stash currently has %d entry"
+msgid_plural "Your stash currently has %d entries"
+msgstr[0] "У вашій схованці наразі є %d запис"
+msgstr[1] "У вашій схованці наразі є %d записи"
+msgstr[2] "У вашій схованці наразі є %d записів"
+
+msgid "Submodules changed but not updated:"
+msgstr "Підмодулі змінено, але не оновлено:"
+
+msgid "Submodule changes to be committed:"
+msgstr "Зміни в підмодулі, які будуть закомічені:"
+
+msgid ""
+"Do not modify or remove the line above.\n"
+"Everything below it will be ignored."
+msgstr ""
+"Не змінюйте та не видаляйте рядок вище.\n"
+"Все, що нижче, буде проігноровано."
+
+#, c-format
+msgid ""
+"\n"
+"It took %.2f seconds to compute the branch ahead/behind values.\n"
+"You can use '--no-ahead-behind' to avoid this.\n"
+msgstr ""
+"\n"
+"Обчислення значень попереду/позаду для гілки зайняло %.2f секунд.\n"
+"Ви можете використати параметр '--no-ahead-behind', щоб уникнути цього.\n"
+
+msgid "You have unmerged paths."
+msgstr "У вас є незлиті шляхи."
+
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr "  (виправте конфлікти та виконайте \"git commit\")"
+
+msgid "  (use \"git merge --abort\" to abort the merge)"
+msgstr ""
+"  (скористайтесь командою \"git merge --abort\", щоб перервати злиття)."
+
+msgid "All conflicts fixed but you are still merging."
+msgstr "Усі конфлікти виправлено, але ви все ще продовжуєте злиття."
+
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (скористайтесь \"git commit\", щоб завершити злиття)"
+
+msgid "You are in the middle of an am session."
+msgstr "Ви всередині am сеансу."
+
+msgid "The current patch is empty."
+msgstr "Поточна латка порожня."
+
+msgid "  (fix conflicts and then run \"git am --continue\")"
+msgstr "  (виправте конфлікти, а потім виконайте \"git am --continue\")"
+
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr "  (скористайтесь командою \"git am --skip\", щоб пропустити цю латку)"
+
+msgid ""
+"  (use \"git am --allow-empty\" to record this patch as an empty commit)"
+msgstr ""
+"  (скористайтесь \"git am --allow-empty\", щоб записати цю латку як порожній "
+"коміт)"
+
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (скористайтесь \"git am --abort\", щоб відновити початкову гілку)"
+
+msgid "git-rebase-todo is missing."
+msgstr "git-rebase-todo відсутній."
+
+msgid "No commands done."
+msgstr "Не виконано жодної команди."
+
+#, c-format
+msgid "Last command done (%<PRIuMAX> command done):"
+msgid_plural "Last commands done (%<PRIuMAX> commands done):"
+msgstr[0] "Останню команду виконано (%<PRIuMAX> команду виконано):"
+msgstr[1] "Останні команди виконано (%<PRIuMAX> команди виконано):"
+msgstr[2] "Останніх команд виконано (%<PRIuMAX> команд виконано):"
+
+#, c-format
+msgid "  (see more in file %s)"
+msgstr "  (дивіться більше у файлі %s)"
+
+msgid "No commands remaining."
+msgstr "Не залишилось команд."
+
+#, c-format
+msgid "Next command to do (%<PRIuMAX> remaining command):"
+msgid_plural "Next commands to do (%<PRIuMAX> remaining commands):"
+msgstr[0] "Наступна команда для виконання (залишилась %<PRIuMAX> команда):"
+msgstr[1] "Наступні команди для виконання (залишилось %<PRIuMAX> команди):"
+msgstr[2] "Наступні команди для виконання (залишилось %<PRIuMAX> команд):"
+
+msgid "  (use \"git rebase --edit-todo\" to view and edit)"
+msgstr ""
+"  (скористайтесь \"git rebase --edit-todo\", щоб переглянути та "
+"відредагувати)"
+
+#, c-format
+msgid "You are currently rebasing branch '%s' on '%s'."
+msgstr "Наразі ви перебазовуєте гілку \"%s\" на \"%s\"."
+
+msgid "You are currently rebasing."
+msgstr "Наразі йде перебазування."
+
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (виправте конфлікти, а потім виконайте \"git rebase --continue\")"
+
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (скористайтесь \"git rebase --skip\", щоб пропустити цю латку)"
+
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr ""
+"  (скористайтесь \"git rebase --abort\", щоб перейти до початкової гілки)"
+
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (усі конфлікти виправлено: виконайте \"git rebase --continue\")"
+
+#, c-format
+msgid ""
+"You are currently splitting a commit while rebasing branch '%s' on '%s'."
+msgstr ""
+"Наразі виконується розщеплення коміту під час перебазування гілки \"%s\" на "
+"\"%s\"."
+
+msgid "You are currently splitting a commit during a rebase."
+msgstr "Ви розщеплюєте коміт під час перебазування."
+
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr ""
+"  (Після очищення робочої директорії виконайте \"git rebase --continue\")"
+
+#, c-format
+msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
+msgstr "Наразі ви редагуєте коміт при перебазуванні гілки \"%s\" на \"%s\"."
+
+msgid "You are currently editing a commit during a rebase."
+msgstr "Наразі ви редагуєте коміт при перебазуванні."
+
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr ""
+"  (скористайтесь \"git commit --amend\", щоб внести зміни до поточного "
+"коміту)"
+
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr ""
+"  (скористайтесь командою \"git rebase --continue\", коли ви будете "
+"задоволені своїми змінами)"
+
+msgid "Cherry-pick currently in progress."
+msgstr "Наразі триває висмикування."
+
+#, c-format
+msgid "You are currently cherry-picking commit %s."
+msgstr "Наразі ви висмикуєте коміт %s."
+
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
+msgstr "  (виправте конфлікти і виконайте \"git cherry-pick --continue\")"
+
+msgid "  (run \"git cherry-pick --continue\" to continue)"
+msgstr "  (виконайте \"git cherry-pick --continue\", щоб продовжити)"
+
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr "  (усі конфлікти виправлено: виконайте \"git cherry-pick --continue\")"
+
+msgid "  (use \"git cherry-pick --skip\" to skip this patch)"
+msgstr ""
+"  (скористайтесь командою \"git cherry-pick --skip\", щоб пропустити цю "
+"латку)"
+
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr ""
+" (скористайтесь командою \"git cherry-pick --abort\", щоб скасувати операцію "
+"висмикування)"
+
+msgid "Revert currently in progress."
+msgstr "Наразі триває вивертання."
+
+#, c-format
+msgid "You are currently reverting commit %s."
+msgstr "Наразі ви вивертаєте коміт %s."
+
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr "  (виправте конфлікти і виконайте \"git revert --continue\")"
+
+msgid "  (run \"git revert --continue\" to continue)"
+msgstr "  (виконайте \"git revert --continue\", щоб продовжити)"
+
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr "  (усі конфлікти виправлено: виконайте \"git revert --continue\")"
+
+msgid "  (use \"git revert --skip\" to skip this patch)"
+msgstr "  (скористайтесь \"git revert --skip\", щоб пропустити цю латку)"
+
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr ""
+"  (скористайтесь \"git revert --abort\", щоб скасувати операцію повернення)"
+
+#, c-format
+msgid "You are currently bisecting, started from branch '%s'."
+msgstr "Наразі ви робите бісекцію, починаючи з гілки '%s'."
+
+msgid "You are currently bisecting."
+msgstr "Наразі ви робите бісекцію."
+
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr ""
+"  (скористайтесь \"git bisect reset\", щоб повернутись на початкову гілку)"
+
+msgid "You are in a sparse checkout."
+msgstr "Ви перебуваєте в розрідженому переході на гілку."
+
+#, c-format
+msgid "You are in a sparse checkout with %d%% of tracked files present."
+msgstr ""
+"Ви перебуваєте в розрідженому переході на гілку з %d%% наявних відстежуваних "
+"файлів."
+
+msgid "On branch "
+msgstr "На гілці "
+
+msgid "interactive rebase in progress; onto "
+msgstr "триває інтерактивне перебазування на "
+
+msgid "rebase in progress; onto "
+msgstr "триває перебазування на "
+
+msgid "HEAD detached at "
+msgstr "HEAD відʼєднано на "
+
+msgid "HEAD detached from "
+msgstr "HEAD відʼєднано від "
+
+msgid "Not currently on any branch."
+msgstr "Не на гілці."
+
+msgid "Initial commit"
+msgstr "Початковий коміт"
+
+msgid "No commits yet"
+msgstr "Поки що немає комітів"
+
+msgid "Untracked files"
+msgstr "Невідстежувані файли"
+
+msgid "Ignored files"
+msgstr "Ігноровані файли"
+
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
+msgstr ""
+"На перерахування невідстежуваних файлів пішло %.2f секунд,\n"
+"але результат було додано у кеш, тому наступні запуски можуть бути швидшими."
+
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "На перерахування невідстежуваних файлів пішло %.2f секунд."
+
+msgid "See 'git help status' for information on how to improve this."
+msgstr "Дивіться \"git help status\" для інформації про те, як це покращити."
+
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Невідстежувані файли не показані%s"
+
+msgid " (use -u option to show untracked files)"
+msgstr " (використовуйте опцію -u, щоб показати невідстежувані файли)"
+
+msgid "No changes"
+msgstr "Немає змін"
+
+#, c-format
+msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
+msgstr ""
+"не додано жодних змін до коміту (скористайтесь \"git add\" та/або \"git "
+"commit -a\")\n"
+
+#, c-format
+msgid "no changes added to commit\n"
+msgstr "нічого не додано до коміту\n"
+
+#, c-format
+msgid ""
+"nothing added to commit but untracked files present (use \"git add\" to "
+"track)\n"
+msgstr ""
+"нічого не додано до коміту, але є невідстежувані файли (скористайтесь \"git "
+"add\" для відстежування)\n"
+
+#, c-format
+msgid "nothing added to commit but untracked files present\n"
+msgstr "нічого не додано до коміту, але присутні невідстежувані файли\n"
+
+#, c-format
+msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
+msgstr ""
+"нічого комітити (створіть/скопіюйте файли та скористайтесь \"git add\" для "
+"відстежування)\n"
+
+#, c-format
+msgid "nothing to commit\n"
+msgstr "нічого комітити\n"
+
+#, c-format
+msgid "nothing to commit (use -u to show untracked files)\n"
+msgstr "нічого комітити (скористайтесь -u щоб показати невідстежувані файли)\n"
+
+#, c-format
+msgid "nothing to commit, working tree clean\n"
+msgstr "нічого комітити, робоче дерево чисте\n"
+
+msgid "No commits yet on "
+msgstr "Поки що немає комітів у "
+
+msgid "HEAD (no branch)"
+msgstr "HEAD (немає гілки)"
+
+msgid "different"
+msgstr "відрізняється"
+
+msgid "behind "
+msgstr "позаду "
+
+msgid "ahead "
+msgstr "попереду "
+
+#. TRANSLATORS: the action is e.g. "pull with rebase"
+#, c-format
+msgid "cannot %s: You have unstaged changes."
+msgstr "неможливо %s: У вас є неіндексовані зміни."
+
+msgid "additionally, your index contains uncommitted changes."
+msgstr "крім того, ваш індекс містить незакомічені зміни."
+
+#, c-format
+msgid "cannot %s: Your index contains uncommitted changes."
+msgstr "неможливо виконати %s: Ваш індекс містить незакомічені зміни."
+
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "невідомий стиль \"%s\" наданий для \"%s\""
+
+msgid ""
+"Error: Your local changes to the following files would be overwritten by "
+"merge"
+msgstr ""
+"Помилка: Ваші локальні зміни в наступних файлах були б перезаписані під час "
+"злиття"
+
+msgid "Automated merge did not work."
+msgstr "Автоматичне злиття не спрацювало."
+
+msgid "Should not be doing an octopus."
+msgstr "Не слід робити octopus злиття."
+
+#, sh-format
+msgid "Unable to find common commit with $pretty_name"
+msgstr "Не вдалося знайти спільний коміт з $pretty_name"
+
+#, sh-format
+msgid "Already up to date with $pretty_name"
+msgstr "Вже в актуальному стані з $pretty_name"
+
+#, sh-format
+msgid "Fast-forwarding to: $pretty_name"
+msgstr "Перемотування вперед до: $pretty_name"
+
+#, sh-format
+msgid "Trying simple merge with $pretty_name"
+msgstr "Спроба простого злиття з $pretty_name"
+
+msgid "Simple merge did not work, trying automatic merge."
+msgstr "Просте злиття не спрацювало, спроба автоматичного злиття."
+
+#, sh-format
+msgid "usage: $dashless $USAGE"
+msgstr "використання: $dashless $USAGE"
+
+#, sh-format
+msgid "Cannot chdir to $cdup, the toplevel of the working tree"
+msgstr "Неможливо виконати chdir до $cdup, верхнього рівня робочого дерева"
+
+#, sh-format
+msgid "fatal: $program_name cannot be used without a working tree."
+msgstr "збій: $program_name неможливо використовувати без робочого дерева."
+
+msgid "Cannot rewrite branches: You have unstaged changes."
+msgstr "Неможливо переписати гілку: у вас є неіндексовані зміни."
+
+#, sh-format
+msgid "Cannot $action: You have unstaged changes."
+msgstr "Неможливо $action: у вас є неіндексовані зміни."
+
+#, sh-format
+msgid "Cannot $action: Your index contains uncommitted changes."
+msgstr "Неможливо $action: ваш індекс містить незакомічені зміни."
+
+msgid "Additionally, your index contains uncommitted changes."
+msgstr "Крім того, ваш індекс містить незакомічені зміни."
+
+msgid "You need to run this command from the toplevel of the working tree."
+msgstr "Цю команду потрібно запускати з верхнього рівня робочого дерева."
+
+msgid "Unable to determine absolute path of git directory"
+msgstr "Не вдалося визначити абсолютний шлях git директорії"
+
+msgid "local zone differs from GMT by a non-minute interval\n"
+msgstr "місцева зона відрізняється від GMT на нехвилинний інтервал\n"
+
+msgid "local time offset greater than or equal to 24 hours\n"
+msgstr "зміщення місцевого часу більше або дорівнює 24 годинам\n"
+
+#, perl-format
+msgid "fatal: command '%s' died with exit code %d"
+msgstr "збій: команда \"%s\" завершилася невдало з кодом виходу %d"
+
+msgid "the editor exited uncleanly, aborting everything"
+msgstr "редактор вийшов неналежним чином, перервано всі процеси"
+
+#, perl-format
+msgid ""
+"'%s' contains an intermediate version of the email you were composing.\n"
+msgstr "\"%s\" містить проміжну версію листа, який ви створювали.\n"
+
+#, perl-format
+msgid "'%s.final' contains the composed email.\n"
+msgstr "\"%s.final\" містить створений лист.\n"
+
+msgid "--dump-aliases incompatible with other options\n"
+msgstr "--dump-aliases несумісна з іншими опціями\n"
+
+msgid ""
+"fatal: found configuration options for 'sendmail'\n"
+"git-send-email is configured with the sendemail.* options - note the 'e'.\n"
+"Set sendemail.forbidSendmailVariables to false to disable this check.\n"
+msgstr ""
+"збій: знайдено опції конфігурації для \"sendmail\"\n"
+"git-send-email налаштовано з параметрами sendemail.* options - зверніть "
+"увагу на \"e\".\n"
+"Встановіть sendemail.forbidSendmailVariables у false, щоб вимкнути цю "
+"перевірку.\n"
+
+msgid "Cannot run git format-patch from outside a repository\n"
+msgstr "Неможливо запустити git format-patch поза сховищем\n"
+
+msgid ""
+"`batch-size` and `relogin` must be specified together (via command-line or "
+"configuration option)\n"
+msgstr ""
+"\"batch-size\" і \"relogin\" повинні бути вказані разом (через командний "
+"рядок або опції конфігурації)\n"
+
+#, perl-format
+msgid "Unknown --suppress-cc field: '%s'\n"
+msgstr "Невідоме --suppress-cc поле: \"%s\"\n"
+
+#, perl-format
+msgid "Unknown --confirm setting: '%s'\n"
+msgstr "Невідомий --confirm параметр: \"%s\"\n"
+
+#, perl-format
+msgid "warning: sendmail alias with quotes is not supported: %s\n"
+msgstr "попередження: sendmail псевдонім у лапках не підтримується: %s\n"
+
+#, perl-format
+msgid "warning: `:include:` not supported: %s\n"
+msgstr "попередження: \":include:\" не підтримується: %s\n"
+
+#, perl-format
+msgid "warning: `/file` or `|pipe` redirection not supported: %s\n"
+msgstr ""
+"попередження: \"/file\" або \"|pipe\" перенаправлення не підтримуються: %s\n"
+
+#, perl-format
+msgid "warning: sendmail line is not recognized: %s\n"
+msgstr "попередження: рядок sendmail не розпізнано: %s\n"
+
+#, perl-format
+msgid ""
+"File '%s' exists but it could also be the range of commits\n"
+"to produce patches for.  Please disambiguate by...\n"
+"\n"
+"    * Saying \"./%s\" if you mean a file; or\n"
+"    * Giving --format-patch option if you mean a range.\n"
+msgstr ""
+"Файл \"%s\" існує, але це також може бути діапазон комітів\n"
+"для яких створюються латки.  Будь ласка, розтлумачте...\n"
+"\n"
+"    * Вказавши \"./%s\", якщо на увазі мається файл\n"
+"    * Додавши --format-patch, якщо на увазі мається діапазон.\n"
+
+#, perl-format
+msgid "Failed to opendir %s: %s"
+msgstr "Не вдалося виконати opendir %s: %s"
+
+msgid ""
+"\n"
+"No patch files specified!\n"
+"\n"
+msgstr ""
+"\n"
+"Файли латок не вказано!\n"
+"\n"
+
+#, perl-format
+msgid "No subject line in %s?"
+msgstr "Немає рядка теми в %s?"
+
+#, perl-format
+msgid "Failed to open for writing %s: %s"
+msgstr "Не вдалося відкрити для запису %s: %s"
+
+msgid ""
+"Lines beginning in \"GIT:\" will be removed.\n"
+"Consider including an overall diffstat or table of contents\n"
+"for the patch you are writing.\n"
+"\n"
+"Clear the body content if you don't wish to send a summary.\n"
+msgstr ""
+"Рядки, що починаються з \"GIT:\", будуть вилучені.\n"
+"Розгляньте можливість включення загального diffstat або змісту\n"
+"для латки, яку ви пишете.\n"
+"\n"
+"Очистіть вміст тіла, якщо ви не бажаєте надсилати підсумок.\n"
+
+#, perl-format
+msgid "Failed to open %s.final: %s"
+msgstr "Не вдалося відкрити %s.final: %s"
+
+#, perl-format
+msgid "Failed to open %s: %s"
+msgstr "Не вдалося відкрити %s: %s"
+
+msgid "Summary email is empty, skipping it\n"
+msgstr "Підсумковий лист порожній, пропущено\n"
+
+#. TRANSLATORS: please keep [y/N] as is.
+#, perl-format
+msgid "Are you sure you want to use <%s> [y/N]? "
+msgstr "Ви впевнені, що хочете використати <%s> [y/N]? "
+
+msgid ""
+"The following files are 8bit, but do not declare a Content-Transfer-"
+"Encoding.\n"
+msgstr ""
+"Наступні файли є 8-бітними, але не містять Content-Transfer-Encoding.\n"
+
+msgid "Which 8bit encoding should I declare [UTF-8]? "
+msgstr "Яке 8-бітне кодування слід оголосити [UTF-8]? "
+
+#, perl-format
+msgid ""
+"Refusing to send because the patch\n"
+"\t%s\n"
+"has the template subject '*** SUBJECT HERE ***'. Pass --force if you really "
+"want to send.\n"
+msgstr ""
+"Відмовлено в надсиланні, тому що латка\n"
+"\t%s\n"
+"має шаблонну тему \"*** SUBJECT HERE ***\". Додайте --force, якщо ви дійсно "
+"хочете відправити.\n"
+
+msgid "To whom should the emails be sent (if anyone)?"
+msgstr "Кому слід надсилати електронні листи (якщо комусь)?"
+
+#, perl-format
+msgid "fatal: alias '%s' expands to itself\n"
+msgstr "збій: псевдонім \"%s\" розгортається на самого себе\n"
+
+msgid "Message-ID to be used as In-Reply-To for the first email (if any)? "
+msgstr ""
+"Message-ID, який буде використовуватися як In-Reply-To для першого листа "
+"(якщо такий є)? "
+
+#, perl-format
+msgid "error: unable to extract a valid address from: %s\n"
+msgstr "помилка: не вдалося витягти дійсну адресу з: %s\n"
+
+#. TRANSLATORS: Make sure to include [q] [d] [e] in your
+#. translation. The program will only accept English input
+#. at this point.
+msgid "What to do with this address? ([q]uit|[d]rop|[e]dit): "
+msgstr "Що робити з цією адресою? ([q]uit|[d]rop|[e]dit): "
+
+#, perl-format
+msgid "CA path \"%s\" does not exist"
+msgstr "Шлях до ЦВС \"%s\" не існує"
+
+msgid ""
+"    The Cc list above has been expanded by additional\n"
+"    addresses found in the patch commit message. By default\n"
+"    send-email prompts before sending whenever this occurs.\n"
+"    This behavior is controlled by the sendemail.confirm\n"
+"    configuration setting.\n"
+"\n"
+"    For additional information, run 'git send-email --help'.\n"
+"    To retain the current behavior, but squelch this message,\n"
+"    run 'git config --global sendemail.confirm auto'.\n"
+"\n"
+msgstr ""
+"    Наведений вище список копій було розширено додатковими\n"
+"    адресами, знайденими у дописі до коміту латки. Зазвичай\n"
+"    send-email запитує перед надсиланням, коли це трапляється.\n"
+"    Цю поведінку можна контролювати за допомогою параметра sendemail."
+"confirm\n"
+"    налаштування конфігурації.\n"
+"\n"
+"    Для отримання додаткової інформації виконайте команду \"git send-email --"
+"help\".\n"
+"Для утримання поточної поведінки, але без цього повідомлення,\n"
+"    виконайте \"git config --global sendemail.confirm auto\".\n"
+
+#. TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your
+#. translation. The program will only accept English input
+#. at this point.
+msgid "Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "
+msgstr "Надіслати цей лист? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "
+
+msgid "Send this email reply required"
+msgstr "Відповідь на запитання \"Надіслати цей лист?\" є обовʼязковою"
+
+msgid "The required SMTP server is not properly defined."
+msgstr "Потрібний SMTP-сервер не визначено належним чином."
+
+#, perl-format
+msgid "Server does not support STARTTLS! %s"
+msgstr "Сервер не підтримує STARTTLS! %s"
+
+#, perl-format
+msgid "STARTTLS failed! %s"
+msgstr "STARTTLS завершився невдало! %s"
+
+msgid "Unable to initialize SMTP properly. Check config and use --smtp-debug."
+msgstr ""
+"Не вдалося правильно ініціалізувати SMTP. Перевірте конфігурацію і "
+"скористайтесь --smtp-debug."
+
+#, perl-format
+msgid "Failed to send %s\n"
+msgstr "Не вдалося надіслати %s\n"
+
+#, perl-format
+msgid "Dry-Sent %s\n"
+msgstr "Пробно відправлено %s\n"
+
+#, perl-format
+msgid "Sent %s\n"
+msgstr "Відправлено %s\n"
+
+msgid "Dry-OK. Log says:\n"
+msgstr "Пробно OK. Журнал каже:\n"
+
+msgid "OK. Log says:\n"
+msgstr "ОК. Журнал каже:\n"
+
+msgid "Result: "
+msgstr "Результат: "
+
+msgid "Result: OK\n"
+msgstr "Результат: OK\n"
+
+#, perl-format
+msgid "can't open file %s"
+msgstr "неможливо відкрити файл %s"
+
+#, perl-format
+msgid "(mbox) Adding cc: %s from line '%s'\n"
+msgstr "(mbox) Додавання cc: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(mbox) Adding to: %s from line '%s'\n"
+msgstr "(mbox) Додавання до: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(non-mbox) Adding cc: %s from line '%s'\n"
+msgstr "(non-mbox) Додавання cc: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(body) Adding cc: %s from line '%s'\n"
+msgstr "(тіло) Додавання cc: %s з рядка \"%s\"\n"
+
+#, perl-format
+msgid "(%s) Could not execute '%s'"
+msgstr "(%s) Не вдалося виконати \"%s\""
+
+#, perl-format
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) Невірно сформований вивід з \"%s\""
+
+#, perl-format
+msgid "(%s) failed to close pipe to '%s'"
+msgstr "(%s) не вдалося закрити канал до \"%s\""
+
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) Додавання %s: %s з: \"%s\"\n"
+
+msgid "cannot send message as 7bit"
+msgstr "неможливо надіслати повідомлення як 7bit"
+
+msgid "invalid transfer encoding"
+msgstr "неприпустиме кодування передачі"
+
+#, perl-format
+msgid ""
+"fatal: %s: rejected by %s hook\n"
+"%s\n"
+"warning: no patches were sent\n"
+msgstr ""
+"збій: %s: відхилено %s хуком\n"
+"%s\n"
+"попередження: не було надіслано жодних латок\n"
+
+#, perl-format
+msgid "unable to open %s: %s\n"
+msgstr "не вдалося відкрити %s: %s\n"
+
+#, perl-format
+msgid ""
+"fatal: %s:%d is longer than 998 characters\n"
+"warning: no patches were sent\n"
+msgstr ""
+"збій: %s:%d довше ніж 998 символів\n"
+"попередження: не було надіслано жодних латок\n"
+
+#, perl-format
+msgid "Skipping %s with backup suffix '%s'.\n"
+msgstr "Пропуск %s з суфіксом резервної копії \"%s\".\n"
+
+#. TRANSLATORS: please keep "[y|N]" as is.
+#, perl-format
+msgid "Do you really want to send %s? [y|N]: "
+msgstr "Ви дійсно хочете відправити %s? [y|N]: "
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 2b88f9b..39efaf1 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -14,6 +14,7 @@
 #   - 依云 <lilydjwg AT gmail.com>
 #   - Yichao Yu <yyc1992 AT gmail.com>
 #   - Zhuang Ya <zhuangya AT me.com>
+#   - Teng Long <dyroneteng AT gmail.com>
 #
 #  Git glossary for Chinese translators
 #
@@ -22,11 +23,13 @@
 #   3-way merge                      |  三方合并
 #   abbreviate                       |  简写(的 SHA-1 值)
 #   alternate object database        |  备用对象库
+#   attribute source                 |  属性来源
 #   amend                            |  修补
 #   ancestor                         |  祖先,祖先提交
 #   annotated tag                    |  附注标签
 #   bare repository                  |  纯仓库
 #   bisect                           |  二分查找
+#   bitmap                           |  位图
 #   blob object                      |  数据对象
 #   bloom filter                     |  布隆过滤器
 #   branch                           |  分支
@@ -43,6 +46,7 @@
 #   commit                           |  提交
 #   commit message                   |  提交说明
 #   commit object                    |  提交对象
+#   commit-graph                     |  提交图
 #   commit-ish (also committish)     |  提交号
 #   cone                             |  锥形(稀疏检出模型);锥(稀疏检出)
 #   conflict                         |  冲突
@@ -74,6 +78,7 @@
 #   index entry                      |  索引条目
 #   loose object                     |  松散对象
 #   loose refs                       |  松散引用
+#   magic                            |  神奇前缀(路径规格支持的一种前缀表达式)
 #   master                           |  master(默认分支名)
 #   merge                            |  合并
 #   object                           |  对象
@@ -83,6 +88,7 @@
 #   object type                      |  对象类型
 #   octopus                          |  章鱼式合并(两分支以上的合并)
 #   origin                           |  origin(默认的远程名称)
+#   orphan                           |  孤立(一般指孤立分支,即没有任何提交的分支)
 #   pack                             |  包
 #   pack index                       |  包索引
 #   packfile                         |  包文件
@@ -94,6 +100,7 @@
 #   plumbing                         |  管件(Git 底层核心命令的别称)
 #   porcelain                        |  瓷件(Git 上层封装命令的别称)
 #   precious-objects repo            |  珍品仓库
+#   preferred pack                   |  首选包(多包索引中引入的首选包概念)
 #   promisor                         |  承诺者
 #   prune                            |  清除
 #   pull                             |  拉,拉取
@@ -102,6 +109,7 @@
 #   rebase                           |  变基
 #   ref                              |  引用
 #   reflog                           |  引用日志
+#   refmap                           |  引用映射
 #   refspec                          |  引用规格
 #   remote                           |  远程,远程仓库
 #   remote-tracking branch           |  远程跟踪分支
@@ -145,10 +153,10 @@
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-03-07 23:37+0000\n"
-"PO-Revision-Date: 2023-03-07 23:40+0000\n"
-"Last-Translator: Fangyi Zhou <me@fangyi.io>\n"
-"Language-Team: GitHub <https://github.com/fangyi-zhou/git-po/>\n"
+"POT-Creation-Date: 2024-02-16 14:27+0800\n"
+"PO-Revision-Date: 2024-02-18 11:47+0800\n"
+"Last-Translator: Teng Long <dyroneteng@gmail.com>\n"
+"Language-Team: GitHub <https://github.com/dyrone/git/>\n"
 "Language: zh_CN\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -888,9 +896,8 @@
 msgstr "无法回退,因为您有未合并的文件。"
 
 #: advice.c
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "无法 %s,因为您有未合并的文件。"
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "无法变基,因为您有未合并的文件。"
 
 #: advice.c
 msgid ""
@@ -917,6 +924,24 @@
 msgstr "因为存在未完成的合并而退出。"
 
 #: advice.c
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"无法在偏离的分支上进行快进操作,您需要:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"或者:\n"
+"\n"
+"\tgit rebase\n"
+
+#: advice.c
 msgid "Not possible to fast-forward, aborting."
 msgstr "无法快进,终止。"
 
@@ -1008,7 +1033,7 @@
 msgstr "未关闭的引号"
 
 #: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
+#: builtin/receive-pack.c builtin/tag.c t/helper/test-pkt-line.c
 msgid "too many arguments"
 msgstr "太多参数"
 
@@ -1022,14 +1047,16 @@
 msgid "unrecognized whitespace ignore option '%s'"
 msgstr "未能识别的空白字符忽略选项 '%s'"
 
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout.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.c builtin/pack-objects.c builtin/push.c
-#: builtin/rebase.c builtin/repack.c builtin/reset.c builtin/rev-list.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/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/pack-objects.c builtin/rebase.c
+#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.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 "选项 '%s' 和 '%s' 不能同时使用"
@@ -1040,6 +1067,14 @@
 msgstr "'%s' 在仓库之外"
 
 #: apply.c
+msgid "failed to read patch"
+msgstr "无法读取补丁"
+
+#: apply.c
+msgid "patch too large"
+msgstr "补丁过大"
+
+#: apply.c
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "无法准备时间戳正则表达式 %s"
@@ -1444,6 +1479,11 @@
 msgid "cannot open %s"
 msgstr "不能打开 %s"
 
+#: apply.c rerere.c
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "不能删除 '%s'"
+
 #: apply.c
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1493,7 +1533,7 @@
 msgstr[0] "修复空白错误后,应用了 %d 行。"
 msgstr[1] "修复空白错误后,应用了 %d 行。"
 
-#: apply.c builtin/add.c builtin/mv.c builtin/rm.c
+#: apply.c builtin/mv.c builtin/rm.c
 msgid "Unable to write new index file"
 msgstr "无法写入新索引文件"
 
@@ -1681,6 +1721,11 @@
 msgid "cannot read '%s'"
 msgstr "不能读取 '%s'"
 
+#: archive.c
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "路径规格 '%s' 匹配了当前目录外的文件'"
+
 #: archive.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1702,10 +1747,6 @@
 msgstr "不是一个树对象:%s"
 
 #: archive.c
-msgid "current working directory is untracked"
-msgstr "当前工作目录未被跟踪"
-
-#: archive.c
 #, c-format
 msgid "File not found: %s"
 msgstr "文件未找到:%s"
@@ -1823,6 +1864,11 @@
 
 #: archive.c
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "额外的命令行参数:'%s'"
+
+#: archive.c
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "未知归档格式 '%s'"
 
@@ -1873,6 +1919,21 @@
 msgid "ignoring overly large gitattributes blob '%s'"
 msgstr "忽略过大的 gitattributes 数据对象 '%s'"
 
+#: attr.c
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "错误的 --attr-source 或 GIT_ATTR_SOURCE"
+
+#: attr.c read-cache.c
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "无法对 %s 执行 stat"
+
+#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
+#: builtin/pack-objects.c combine-diff.c rerere.c
+#, c-format
+msgid "unable to read %s"
+msgstr "不能读 %s"
+
 #: bisect.c
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
@@ -1997,16 +2058,12 @@
 msgstr "--contents 和 --reverse 不能混用。"
 
 #: blame.c
-msgid "cannot use --contents with final commit object name"
-msgstr "不能将 --contents 和最终的提交对象名共用"
-
-#: blame.c
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr "--reverse 和 --first-parent 共用,需要指定最新的提交"
 
 #: blame.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c ref-filter.c
-#: remote.c sequencer.c submodule.c
+#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c remote.c
+#: sequencer.c submodule.c
 msgid "revision walk setup failed"
 msgstr "版本遍历初始化失败"
 
@@ -2130,8 +2187,8 @@
 
 #: branch.c
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "无法强制更新检出于 '%2$s' 的分支 '%1$s'"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr "无法强制更新被工作区 '%2$s' 所使用的分支 '%1$s'"
 
 #: branch.c
 #, c-format
@@ -2197,13 +2254,8 @@
 
 #: branch.c
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "'%s' 已经检出到 '%s'"
-
-#: branch.c
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "工作区 %s 的 HEAD 指向没有被更新"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "'%s' 已经被工作区 '%s' 使用"
 
 #: builtin/add.c
 msgid "git add [<options>] [--] <pathspec>..."
@@ -2215,20 +2267,6 @@
 msgstr "不能 chmod %cx '%s'"
 
 #: builtin/add.c
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "意外的差异状态 %c"
-
-#: builtin/add.c builtin/commit.c
-msgid "updating files failed"
-msgstr "更新文件失败"
-
-#: builtin/add.c
-#, c-format
-msgid "remove '%s'\n"
-msgstr "删除 '%s'\n"
-
-#: builtin/add.c
 msgid "Unstaged changes after refreshing the index:"
 msgstr "刷新索引之后尚未被暂存的变更:"
 
@@ -2240,30 +2278,26 @@
 "设置 add.interactive.useBuiltin 已经被移除!\n"
 "查看 'git help config' 中的相关条目以获取更多信息。"
 
-#: builtin/add.c builtin/rev-parse.c
-msgid "Could not read the index"
-msgstr "不能读取索引"
-
 #: builtin/add.c
-msgid "Could not write patch"
-msgstr "不能生成补丁"
+msgid "could not read the index"
+msgstr "不能读取索引"
 
 #: builtin/add.c
 msgid "editing patch failed"
 msgstr "编辑补丁失败"
 
-#: builtin/add.c
+#: builtin/add.c read-cache.c
 #, c-format
-msgid "Could not stat '%s'"
+msgid "could not stat '%s'"
 msgstr "不能对 '%s' 调用 stat"
 
 #: builtin/add.c
-msgid "Empty patch. Aborted."
-msgstr "空补丁。异常终止。"
+msgid "empty patch. aborted"
+msgstr "空补丁。异常终止"
 
 #: builtin/add.c
 #, c-format
-msgid "Could not apply '%s'"
+msgid "could not apply '%s'"
 msgstr "不能应用 '%s'"
 
 #: builtin/add.c
@@ -2422,14 +2456,19 @@
 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
+msgid "unable to write new index file"
+msgstr "无法写新的索引文件"
+
 #: builtin/am.c builtin/mailinfo.c mailinfo.c
 #, c-format
 msgid "bad action '%s' for '%s'"
 msgstr "'%2$s' 的错误动作 '%1$s'"
 
 #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
-#: sequencer.c setup.c
+#: builtin/pull.c config.c diff-merges.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c
 #, c-format
 msgid "invalid value for '%s': '%s'"
 msgstr "'%s' 的值无效:'%s'"
@@ -2472,7 +2511,7 @@
 msgid "could not open '%s' for reading"
 msgstr "无法打开 '%s' 进行读取"
 
-#: builtin/am.c builtin/rebase.c sequencer.c strbuf.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
 msgstr "无法打开 '%s' 进行写入"
@@ -2582,8 +2621,7 @@
 msgid "applying to an empty history"
 msgstr "正应用到一个空历史上"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
-#: t/helper/test-fast-rebase.c
+#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
 msgid "failed to write commit object"
 msgstr "无法写提交对象"
 
@@ -2671,11 +2709,6 @@
 "您应该对已经冲突解决的每一个文件执行 'git add' 来标记已经完成。 \n"
 "您可以对 \"由他们删除\" 的文件执行 `git rm` 命令。"
 
-#: builtin/am.c builtin/checkout.c builtin/clone.c builtin/stash.c merge.c
-#: rerere.c
-msgid "unable to write new index file"
-msgstr "无法写新的索引文件"
-
 #: builtin/am.c builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
@@ -2697,11 +2730,6 @@
 msgstr "无法读取 '%s'"
 
 #: builtin/am.c
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "选项 '%s=%s' 和 '%s=%s' 不能同时使用"
-
-#: builtin/am.c
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<选项>] [(<mbox> | <Maildir>)...]"
 
@@ -2755,10 +2783,6 @@
 msgstr "针对 mbox 格式,向 git-mailsplit 传递 --keep-cr 参数"
 
 #: builtin/am.c
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "不向 git-mailsplit 传递 --keep-cr 参数,覆盖 am.keepcr 的设置"
-
-#: builtin/am.c
 msgid "strip everything before a scissors line"
 msgstr "丢弃裁切线前的所有内容"
 
@@ -2777,8 +2801,9 @@
 msgstr "n"
 
 #: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
-#: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.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/replace.c builtin/tag.c
+#: builtin/verify-tag.c
 msgid "format"
 msgstr "格式"
 
@@ -2908,10 +2933,10 @@
 
 #: builtin/bisect.c
 msgid ""
-"git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]    [--no-"
+"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-{old,good}=<术语>]    [--no-"
+"git bisect start [--term-{new|bad}=<术语> --term-{old|good}=<术语>]    [--no-"
 "checkout] [--first-parent] [<坏> [<好>...]] [--]    [<路径规格>...]"
 
 #: builtin/bisect.c
@@ -2931,8 +2956,8 @@
 msgstr "git bisect replay <日志文件>"
 
 #: builtin/bisect.c
-msgid "git bisect run <cmd>..."
-msgstr "git bisect run <命令>..."
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <命令> [<参数>...]"
 
 #: builtin/bisect.c
 #, c-format
@@ -3447,37 +3472,38 @@
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
 "将要删除的分支 '%s' 已经被合并到\n"
-"         '%s',但未合并到 HEAD。"
+"         '%s',但未合并到 HEAD"
 
 #  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
 #: builtin/branch.c
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"并未删除分支 '%s', 虽然它已经合并到 HEAD,\n"
-"         然而却尚未被合并到分支 '%s' 。"
+"并未删除分支 '%s',虽然它已经合并到 HEAD,\n"
+"         然而却尚未被合并到分支 '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
+msgid "couldn't look up commit object for '%s'"
 msgstr "无法查询 '%s' 指向的提交对象"
 
 #: builtin/branch.c
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"分支 '%s' 没有完全合并。\n"
-"如果您确认要删除它,执行 'git branch -D %s'。"
+msgid "the branch '%s' is not fully merged"
+msgstr "分支 '%s' 没有完全合并"
 
 #: builtin/branch.c
-msgid "Update of config-file failed"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "如果您确认要删除它,执行 'git branch -D %s'"
+
+#: builtin/branch.c
+msgid "update of config-file failed"
 msgstr "更新配置文件失败"
 
 #: builtin/branch.c
@@ -3486,18 +3512,27 @@
 
 #: builtin/branch.c
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "无法删除检出于 '%2$s' 的分支 '%1$s'。"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr "无法强制更新被工作区 '%2$s' 所使用的分支 '%1$s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "未能找到远程跟踪分支 '%s'。"
+msgid "remote-tracking branch '%s' not found"
+msgstr "未能找到远程跟踪分支 '%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "branch '%s' not found."
-msgstr "分支 '%s' 未发现。"
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"分支 '%s' 未找到。\n"
+"是否忘记指定 --remote 选项?"
+
+#: builtin/branch.c
+#, c-format
+msgid "branch '%s' not found"
+msgstr "分支 '%s' 未发现"
 
 #: builtin/branch.c
 #, c-format
@@ -3524,58 +3559,63 @@
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being rebased at %s"
+msgid "branch %s is being rebased at %s"
 msgstr "分支 %s 正被变基到 %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being bisected at %s"
+msgid "branch %s is being bisected at %s"
 msgstr "分支 %s 正被二分查找于 %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Invalid branch name: '%s'"
+msgid "HEAD of working tree %s is not updated"
+msgstr "工作区 %s 的 HEAD 指向没有被更新"
+
+#: builtin/branch.c
+#, c-format
+msgid "invalid branch name: '%s'"
 msgstr "无效的分支名:'%s'"
 
 #: builtin/branch.c
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "分支 '%s' 尚无提交。"
+msgid "no commit on branch '%s' yet"
+msgstr "分支 '%s' 尚无提交"
 
 #: builtin/branch.c
 #, c-format
-msgid "No branch named '%s'."
-msgstr "没有分支 '%s'。"
+msgid "no branch named '%s'"
+msgstr "没有分支 '%s'"
 
 #: builtin/branch.c
-msgid "Branch rename failed"
+msgid "branch rename failed"
 msgstr "分支重命名失败"
 
 #: builtin/branch.c
-msgid "Branch copy failed"
+msgid "branch copy failed"
 msgstr "分支拷贝失败"
 
 #: builtin/branch.c
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
+msgid "created a copy of a misnamed branch '%s'"
 msgstr "已为错误命名的分支 '%s' 创建了一个副本"
 
 #: builtin/branch.c
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
+msgid "renamed a misnamed branch '%s' away"
 msgstr "已将错误命名的分支 '%s' 重命名"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "分支重命名为 %s,但 HEAD 没有更新!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "分支重命名为 %s,但 HEAD 没有更新"
 
 #: builtin/branch.c
-msgid "Branch is renamed, but update of config-file failed"
+msgid "branch is renamed, but update of config-file failed"
 msgstr "分支被重命名,但更新配置文件失败"
 
 #: builtin/branch.c
-msgid "Branch is copied, but update of config-file failed"
+msgid "branch is copied, but update of config-file failed"
 msgstr "分支已拷贝,但更新配置文件失败"
 
 #: builtin/branch.c
@@ -3661,6 +3701,10 @@
 msgid "move/rename a branch, even if target exists"
 msgstr "移动/重命名一个分支,即使目标已存在"
 
+#: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
+msgid "do not output a newline after empty formatted refs"
+msgstr "在格式化引用结果为空之后,不输出换行符"
+
 #: builtin/branch.c
 msgid "copy a branch and its reflog"
 msgstr "拷贝一个分支和它的引用日志"
@@ -3722,9 +3766,9 @@
 msgid "format to use for the output"
 msgstr "输出格式"
 
-#: builtin/branch.c builtin/submodule--helper.c submodule.c
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "无法将 HEAD 解析为有效引用。"
+#: builtin/branch.c
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "无法将 HEAD 解析为有效引用"
 
 #: builtin/branch.c builtin/clone.c
 msgid "HEAD not found below refs/heads!"
@@ -3746,7 +3790,7 @@
 msgstr "必须提供分支名"
 
 #: builtin/branch.c
-msgid "Cannot give description to detached HEAD"
+msgid "cannot give description to detached HEAD"
 msgstr "不能向分离头指针提供描述"
 
 #: builtin/branch.c
@@ -3754,12 +3798,12 @@
 msgstr "不能为一个以上的分支编辑描述"
 
 #: builtin/branch.c
-msgid "cannot copy the current branch while not on any."
-msgstr "不处于任何分支上,无法拷贝当前分支。"
+msgid "cannot copy the current branch while not on any"
+msgstr "不处于任何分支上,无法拷贝当前分支"
 
 #: builtin/branch.c
-msgid "cannot rename the current branch while not on any."
-msgstr "不处于任何分支上,无法重命名当前分支。"
+msgid "cannot rename the current branch while not on any"
+msgstr "不处于任何分支上,无法重命名当前分支"
 
 #: builtin/branch.c
 msgid "too many branches for a copy operation"
@@ -3776,8 +3820,8 @@
 #: builtin/branch.c
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
-msgstr "无法设置 HEAD 的上游为 %s,因为 HEAD 没有指向任何分支。"
+"could not set upstream of HEAD to %s when it does not point to any branch"
+msgstr "无法设置 HEAD 的上游为 %s,因为 HEAD 没有指向任何分支"
 
 #: builtin/branch.c
 #, c-format
@@ -3794,17 +3838,17 @@
 msgstr "为取消上游设置操作提供了太多的参数"
 
 #: builtin/branch.c
-msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgid "could not unset upstream of HEAD when it does not point to any branch"
 msgstr "无法取消 HEAD 的上游设置因为它没有指向一个分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch '%s' has no upstream information"
+msgid "branch '%s' has no upstream information"
 msgstr "分支 '%s' 没有上游信息"
 
 #: builtin/branch.c
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
 "'git branch' 的 -a 和 -r 选项不带一个分支名。\n"
@@ -3813,9 +3857,8 @@
 #: builtin/branch.c
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
-msgstr ""
-"不再支持选项 '--set-upstream'。请使用 '--track' 或 '--set-upstream-to'。"
+"'--set-upstream-to' instead"
+msgstr "不再支持选项 '--set-upstream'。请使用 '--track' 或 '--set-upstream-to'"
 
 #: builtin/bugreport.c
 msgid "git version:\n"
@@ -3898,6 +3941,11 @@
 msgid "specify a strftime format suffix for the filename(s)"
 msgstr "指定文件的 strftime 格式后缀"
 
+#: builtin/bugreport.c
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "未知参数 `%s'"
+
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
 msgid "could not create leading directories for '%s'"
@@ -3928,12 +3976,10 @@
 
 #: builtin/bundle.c
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<版本>] <文件> <git-rev-list-参数>"
 
 #: builtin/bundle.c
@@ -3960,13 +4006,13 @@
 msgid "show progress meter"
 msgstr "显示进度表"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "show progress meter during object writing phase"
-msgstr "在对象写入阶段显示进度表"
+#: builtin/bundle.c
+msgid "historical; same as --progress"
+msgstr "老的参数;等同于 --progress"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "当进度表显示时类似于 --all-progress"
+#: builtin/bundle.c
+msgid "historical; does nothing"
+msgstr "老的参数;无作用"
 
 #: builtin/bundle.c
 msgid "specify bundle format version"
@@ -4039,18 +4085,6 @@
 
 #: builtin/cat-file.c
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
-
-#: builtin/cat-file.c
-msgid ""
 "git cat-file (--textconv | --filters)\n"
 "             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
@@ -4058,6 +4092,18 @@
 "             [<版本>:<路径|树对象> | --path=<路径|树对象> <版本>]"
 
 #: builtin/cat-file.c
+msgid ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+msgstr ""
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
+
+#: builtin/cat-file.c
 msgid "Check object existence or emit object contents"
 msgstr "检查对象存在或输出对象内容"
 
@@ -4106,6 +4152,10 @@
 msgstr "标准输入以 NUL 字符分隔"
 
 #: builtin/cat-file.c
+msgid "stdin and stdout is NUL-terminated"
+msgstr "标准输入和标准输出以 NUL 字符分隔"
+
+#: builtin/cat-file.c
 msgid "read commands from stdin"
 msgstr "从标准输入读取命令"
 
@@ -4430,6 +4480,11 @@
 
 #: builtin/checkout.c
 #, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "'%s'、'%s' 或 '%s' 不能在检出一个树时使用"
+
+#: builtin/checkout.c
+#, c-format
 msgid "path '%s' is unmerged"
 msgstr "路径 '%s' 未合并"
 
@@ -4455,7 +4510,7 @@
 msgid "HEAD is now at"
 msgstr "HEAD 目前位于"
 
-#: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
+#: builtin/checkout.c builtin/clone.c
 msgid "unable to update HEAD"
 msgstr "不能更新 HEAD"
 
@@ -4724,8 +4779,8 @@
 msgstr "新分支"
 
 #: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "新的没有父提交的分支"
+msgid "new unborn branch"
+msgstr "新的未诞生的分支"
 
 #: builtin/checkout.c builtin/merge.c
 msgid "update ignored files (default)"
@@ -4791,7 +4846,7 @@
 msgid "you must specify path(s) to restore"
 msgstr "您必须指定要恢复的路径"
 
-#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
 #: builtin/submodule--helper.c builtin/worktree.c
 msgid "branch"
 msgstr "分支"
@@ -5001,7 +5056,8 @@
 msgstr "删除整个目录"
 
 #: builtin/clean.c builtin/describe.c builtin/grep.c builtin/log.c
-#: builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c builtin/show-ref.c
+#: ref-filter.h
 msgid "pattern"
 msgstr "模式"
 
@@ -5031,10 +5087,6 @@
 msgstr ""
 "clean.requireForce 默认为 true 且未提供 -i、-n 或 -f 选项,拒绝执行清理动作"
 
-#: builtin/clean.c
-msgid "-x and -X cannot be used together"
-msgstr "-x 和 -X 不能同时使用"
-
 #: builtin/clone.c
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<选项>] [--] <仓库> [<路径>]"
@@ -5113,7 +5165,7 @@
 msgid "path to git-upload-pack on the remote"
 msgstr "远程 git-upload-pack 路径"
 
-#: builtin/clone.c builtin/fetch.c builtin/grep.c builtin/pull.c
+#: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "depth"
 msgstr "深度"
 
@@ -5126,6 +5178,7 @@
 msgstr "从一个特定时间创建一个浅克隆"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
 msgid "revision"
 msgstr "版本"
 
@@ -5153,6 +5206,10 @@
 msgid "separate git dir from working tree"
 msgstr "git目录和工作区分离"
 
+#: builtin/clone.c builtin/init-db.c
+msgid "specify the reference format to use"
+msgstr "指定要使用的引用格式"
+
 #: builtin/clone.c
 msgid "key=value"
 msgstr "key=value"
@@ -5171,14 +5228,6 @@
 msgid "option to transmit"
 msgstr "传输选项"
 
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv4 addresses only"
-msgstr "只使用 IPv4 地址"
-
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv6 addresses only"
-msgstr "只使用 IPv6 地址"
-
 #: builtin/clone.c
 msgid "apply partial clone filters to submodules"
 msgstr "对子模组使用部分克隆过滤器"
@@ -5216,6 +5265,11 @@
 
 #: builtin/clone.c
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' 为符号链接,拒绝用 --local 克隆"
+
+#: builtin/clone.c
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "无法在 '%s' 上启动迭代器"
 
@@ -5305,11 +5359,10 @@
 msgid "You must specify a repository to clone."
 msgstr "您必须指定一个仓库来克隆。"
 
-#: builtin/clone.c
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr "--bundle-uri 与 --depth、--shallow-since 和 --shallow-exclude 不兼容"
+#: builtin/clone.c builtin/init-db.c setup.c
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "未知的引用存储格式 '%s'"
 
 #: builtin/clone.c
 #, c-format
@@ -5470,7 +5523,7 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <目录>] [--append]\n"
 "                       [--split[=<策略>]] [--reachable | --stdin-packs | --"
@@ -5489,12 +5542,17 @@
 
 #: builtin/commit-graph.c
 msgid "if the commit-graph is split, only verify the tip file"
-msgstr "如果提交图形被拆分,只验证头一个文件"
+msgstr "如果提交图被拆分,只验证头一个文件"
 
 #: builtin/commit-graph.c
 #, c-format
 msgid "Could not open commit-graph '%s'"
-msgstr "无法打开提交图形 '%s'"
+msgstr "无法打开提交图 '%s'"
+
+#: builtin/commit-graph.c
+#, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "无法打开提交图链 '%s'"
 
 #: builtin/commit-graph.c
 #, c-format
@@ -5538,15 +5596,15 @@
 
 #: builtin/commit-graph.c
 msgid "allow writing an incremental commit-graph file"
-msgstr "允许写一个增量提交图形文件"
+msgstr "允许写一个增量提交图文件"
 
 #: builtin/commit-graph.c
 msgid "maximum number of commits in a non-base split commit-graph"
-msgstr "在非基本拆分提交图形中的最大提交数"
+msgstr "在非基本拆分提交图中的最大提交数"
 
 #: builtin/commit-graph.c
 msgid "maximum ratio between two levels of a split commit-graph"
-msgstr "一个拆分提交图形的两个级别之间的最大比率"
+msgstr "一个拆分提交图的两个级别之间的最大比率"
 
 #: builtin/commit-graph.c
 msgid "only expire files older than a given date-time"
@@ -5708,6 +5766,10 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+#: builtin/commit.c read-cache.c
+msgid "updating files failed"
+msgstr "更新文件失败"
+
 #: builtin/commit.c
 msgid "failed to unpack HEAD tree object"
 msgstr "无法解包 HEAD 树对象"
@@ -5733,10 +5795,6 @@
 msgstr "不能更新树的主缓存"
 
 #: builtin/commit.c
-msgid "unable to write new_index file"
-msgstr "无法写 new_index 文件"
-
-#: builtin/commit.c
 msgid "cannot do a partial commit during a merge."
 msgstr "在合并过程中不能做部分提交。"
 
@@ -5783,8 +5841,8 @@
 
 #: builtin/commit.c builtin/merge-tree.c
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "不能查询提交 %s"
+msgid "could not lookup commit '%s'"
+msgstr "不能查询提交 '%s'"
 
 #: builtin/commit.c builtin/shortlog.c
 #, c-format
@@ -6001,7 +6059,7 @@
 msgid "version"
 msgstr "版本"
 
-#: builtin/commit.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "机器可读的输出"
 
@@ -6224,10 +6282,10 @@
 #: builtin/commit.c
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"仓库已更新,但无法写 new_index 文件。检查是否磁盘已满或\n"
+"仓库已更新,但无法写入索引文件。检查是否磁盘已满或\n"
 "磁盘配额已耗尽,然后执行 \"git restore --staged :/\" 恢复。"
 
 #: builtin/config.c
@@ -7076,147 +7134,17 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel 不能为负数"
 
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch from all remotes"
-msgstr "从所有的远程抓取"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "set upstream for git pull/fetch"
-msgstr "为 git pull/fetch 设置上游"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "append to .git/FETCH_HEAD instead of overwriting"
-msgstr "追加到 .git/FETCH_HEAD 而不是覆盖它"
-
-#: builtin/fetch.c
-msgid "use atomic transaction to update references"
-msgstr "使用原子事务更新引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "path to upload pack on remote end"
-msgstr "上传包到远程的路径"
-
-#: builtin/fetch.c
-msgid "force overwrite of local reference"
-msgstr "强制覆盖本地引用"
-
-#: builtin/fetch.c
-msgid "fetch from multiple remotes"
-msgstr "从多个远程抓取"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "fetch all tags and associated objects"
-msgstr "抓取所有的标签和关联对象"
-
-#: builtin/fetch.c
-msgid "do not fetch all tags (--no-tags)"
-msgstr "不抓取任何标签(--no-tags)"
-
-#: builtin/fetch.c
-msgid "number of submodules fetched in parallel"
-msgstr "子模组获取的并发数"
-
-#: builtin/fetch.c
-msgid "modify the refspec to place all refs within refs/prefetch/"
-msgstr "修改引用规格以将所有引用放入 refs/prefetch/"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "prune remote-tracking branches no longer on remote"
-msgstr "清除远程已经不存在的分支的跟踪分支"
-
-#: builtin/fetch.c
-msgid "prune local tags no longer on remote and clobber changed tags"
-msgstr "清除远程不存在的本地标签,并且替换变更标签"
-
-#  译者:可选值,不能翻译
-#: builtin/fetch.c builtin/pull.c
-msgid "on-demand"
-msgstr "on-demand"
-
-#: builtin/fetch.c
-msgid "control recursive fetching of submodules"
-msgstr "控制子模组的递归抓取"
-
-#: builtin/fetch.c
-msgid "write fetched references to the FETCH_HEAD file"
-msgstr "将获取到的引用写入 FETCH_HEAD 文件"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "keep downloaded pack"
-msgstr "保持下载包"
-
-#: builtin/fetch.c
-msgid "allow updating of HEAD ref"
-msgstr "允许更新 HEAD 引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow clone"
-msgstr "深化浅克隆的历史"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "deepen history of shallow repository based on time"
-msgstr "基于时间来深化浅克隆的历史"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "convert to a complete repository"
-msgstr "转换为一个完整的仓库"
-
-#: builtin/fetch.c
-msgid "re-fetch without negotiating common commits"
-msgstr "不协商共有提交重新获取"
-
-#: builtin/fetch.c
-msgid "prepend this to submodule path output"
-msgstr "在子模组路径输出的前面加上此目录"
-
-#: builtin/fetch.c
-msgid ""
-"default for recursive fetching of submodules (lower priority than config "
-"files)"
-msgstr "递归获取子模组的缺省值(比配置文件优先级低)"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "accept refs that update .git/shallow"
-msgstr "接受更新 .git/shallow 的引用"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "refmap"
-msgstr "引用映射"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "specify fetch refmap"
-msgstr "指定获取操作的引用映射"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "report that we have only objects reachable from this object"
-msgstr "报告我们只拥有从该对象开始可达的对象"
-
-#: builtin/fetch.c
-msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
-msgstr "不获取包文件;而是打印协商的祖先提交"
-
-#: builtin/fetch.c
-msgid "run 'maintenance --auto' after fetching"
-msgstr "获取后执行 'maintenance --auto'"
-
-#: builtin/fetch.c builtin/pull.c
-msgid "check for forced-updates on all updated branches"
-msgstr "在所有更新分支上检查强制更新"
-
-#: builtin/fetch.c
-msgid "write the commit-graph after fetching"
-msgstr "抓取后写提交图"
-
-#: builtin/fetch.c
-msgid "accept refspecs from stdin"
-msgstr "从标准输入获取引用规格"
-
 #: builtin/fetch.c
 msgid "couldn't find remote ref HEAD"
 msgstr "无法发现远程 HEAD 引用"
 
 #: builtin/fetch.c
 #, c-format
+msgid "From %.*s\n"
+msgstr "来自 %.*s\n"
+
+#: builtin/fetch.c
+#, c-format
 msgid "object %s not found"
 msgstr "对象 %s 未发现"
 
@@ -7302,11 +7230,6 @@
 
 #: builtin/fetch.c
 #, c-format
-msgid "From %.*s\n"
-msgstr "来自 %.*s\n"
-
-#: builtin/fetch.c
-#, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
 " 'git remote prune %s' to remove any old, conflicting branches"
@@ -7415,6 +7338,141 @@
 msgid "you need to specify a tag name"
 msgstr "您需要指定一个标签名称"
 
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch from all remotes"
+msgstr "从所有远程抓取"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "set upstream for git pull/fetch"
+msgstr "为 git pull/fetch 设置上游"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr "追加到 .git/FETCH_HEAD 而不是覆盖它"
+
+#: builtin/fetch.c
+msgid "use atomic transaction to update references"
+msgstr "使用原子事务更新引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "path to upload pack on remote end"
+msgstr "上传包到远程的路径"
+
+#: builtin/fetch.c
+msgid "force overwrite of local reference"
+msgstr "强制覆盖本地引用"
+
+#: builtin/fetch.c
+msgid "fetch from multiple remotes"
+msgstr "从多个远程抓取"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "fetch all tags and associated objects"
+msgstr "抓取所有的标签和关联对象"
+
+#: builtin/fetch.c
+msgid "do not fetch all tags (--no-tags)"
+msgstr "不抓取任何标签(--no-tags)"
+
+#: builtin/fetch.c
+msgid "number of submodules fetched in parallel"
+msgstr "获取子模组的并发数"
+
+#: builtin/fetch.c
+msgid "modify the refspec to place all refs within refs/prefetch/"
+msgstr "修改引用规格以将所有引用放入 refs/prefetch/"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "prune remote-tracking branches no longer on remote"
+msgstr "清除远程已经不存在的分支的跟踪分支"
+
+#: builtin/fetch.c
+msgid "prune local tags no longer on remote and clobber changed tags"
+msgstr "清除远程不存在的本地标签,并且替换变更标签"
+
+#  译者:可选值,不能翻译
+#: builtin/fetch.c builtin/pull.c
+msgid "on-demand"
+msgstr "on-demand"
+
+#: builtin/fetch.c
+msgid "control recursive fetching of submodules"
+msgstr "控制子模组的递归抓取"
+
+#: builtin/fetch.c
+msgid "write fetched references to the FETCH_HEAD file"
+msgstr "将获取到的引用写入 FETCH_HEAD 文件"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "keep downloaded pack"
+msgstr "保持已下载的包"
+
+#: builtin/fetch.c
+msgid "allow updating of HEAD ref"
+msgstr "允许更新 HEAD 引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow clone"
+msgstr "深化浅克隆的历史"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "deepen history of shallow repository based on time"
+msgstr "基于时间来深化浅克隆的历史"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "convert to a complete repository"
+msgstr "转换为一个完整的仓库"
+
+#: builtin/fetch.c
+msgid "re-fetch without negotiating common commits"
+msgstr "重新获取而不协商共同提交"
+
+#: builtin/fetch.c
+msgid "prepend this to submodule path output"
+msgstr "在子模组路径输出的前面加上此目录"
+
+#: builtin/fetch.c
+msgid ""
+"default for recursive fetching of submodules (lower priority than config "
+"files)"
+msgstr "递归获取子模组的缺省值(比配置文件优先级低)"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "accept refs that update .git/shallow"
+msgstr "接受更新 .git/shallow 的引用"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "refmap"
+msgstr "引用映射"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "specify fetch refmap"
+msgstr "指定获取操作的引用映射"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "report that we have only objects reachable from this object"
+msgstr "报告我们只拥有从该对象开始可达的对象"
+
+#: builtin/fetch.c
+msgid "do not fetch a packfile; instead, print ancestors of negotiation tips"
+msgstr "不获取包文件;而是打印协商的祖先提交"
+
+#: builtin/fetch.c
+msgid "run 'maintenance --auto' after fetching"
+msgstr "获取后执行 'maintenance --auto'"
+
+#: builtin/fetch.c builtin/pull.c
+msgid "check for forced-updates on all updated branches"
+msgstr "在所有更新分支上检查强制更新"
+
+#: builtin/fetch.c
+msgid "write the commit-graph after fetching"
+msgstr "抓取后写提交图"
+
+#: builtin/fetch.c
+msgid "accept refspecs from stdin"
+msgstr "从标准输入获取引用规格"
+
 #: builtin/fetch.c
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only 需要一个或多个 --negotiation-tip=*"
@@ -7560,6 +7618,14 @@
 msgid "print only refs which don't contain the commit"
 msgstr "只打印不包含该提交的引用"
 
+#: builtin/for-each-ref.c
+msgid "read reference patterns from stdin"
+msgstr "从标准输入读取引用的模式"
+
+#: builtin/for-each-ref.c
+msgid "unknown arguments supplied with --stdin"
+msgstr "为 --stdin 提供了未知的命令参数"
+
 #: builtin/for-each-repo.c
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<配置> [--] <命令参数>"
@@ -7576,6 +7642,11 @@
 msgid "missing --config=<config>"
 msgstr "缺少 --config=<配置>"
 
+#: builtin/for-each-repo.c
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "发现错误的配置行 --config=%s"
+
 #: builtin/fsck.c
 msgid "unknown"
 msgstr "未知"
@@ -7761,13 +7832,14 @@
 msgstr "注意:%s 指向一个尚未诞生的分支(%s)"
 
 #: builtin/fsck.c
-msgid "Checking cache tree"
-msgstr "正在检查缓存树"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "正在检查缓存树 %s"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s:cache-tree 中无效的 sha1 指针"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s:cache-tree %s 中存在无效的 sha1 指针"
 
 #: builtin/fsck.c
 msgid "non-tree in cache-tree"
@@ -7775,8 +7847,18 @@
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s:resolve-undo 中无效的 sha1 指针"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s:resolve-undo %s 中存在无效的 sha1 指针"
+
+#: builtin/fsck.c
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "无法为包文件 %s 加载反向索引"
+
+#: builtin/fsck.c
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "包文件 '%s' 的反向索引文件无效"
 
 #: builtin/fsck.c
 msgid ""
@@ -7983,7 +8065,7 @@
 msgid "failed to parse '%s' value '%s'"
 msgstr "无法解析 '%s' 值 '%s'"
 
-#: builtin/gc.c builtin/init-db.c
+#: builtin/gc.c setup.c
 #, c-format
 msgid "cannot stat '%s'"
 msgstr "不能对 '%s' 调用 stat"
@@ -8010,6 +8092,10 @@
 msgid "pack unreferenced objects separately"
 msgstr "分开打包未引用的对象"
 
+#: builtin/gc.c builtin/repack.c
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "使用 --cruft,限制新 cruft 包的总大小"
+
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
 msgstr "更彻底(增加运行时间)"
@@ -8225,14 +8311,6 @@
 msgid "'crontab' died"
 msgstr "'crontab' 终止"
 
-#: builtin/gc.c
-msgid "failed to start systemctl"
-msgstr "无法启动 systemctl"
-
-#: builtin/gc.c
-msgid "failed to run systemctl"
-msgstr "无法运行 systemctl"
-
 #: builtin/gc.c builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
@@ -8244,6 +8322,14 @@
 msgstr "无法刷新 '%s'"
 
 #: builtin/gc.c
+msgid "failed to start systemctl"
+msgstr "无法启动 systemctl"
+
+#: builtin/gc.c
+msgid "failed to run systemctl"
+msgstr "无法运行 systemctl"
+
+#: builtin/gc.c
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "无法识别的 --scheduler 参数 '%s'"
@@ -8274,6 +8360,10 @@
 msgstr "触发 git maintenance 执行的调度器"
 
 #: builtin/gc.c
+msgid "failed to set up maintenance schedule"
+msgstr "无法设置维护计划"
+
+#: builtin/gc.c
 msgid "failed to add repo to global config"
 msgstr "无法将仓库添加到全局配置"
 
@@ -8312,6 +8402,11 @@
 
 #: builtin/grep.c
 #, c-format
+msgid "unable to read tree %s"
+msgstr "无法读取树 %s"
+
+#: builtin/grep.c
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "无法抓取来自于 %s 类型的对象"
 
@@ -8369,8 +8464,8 @@
 msgstr "在子目录中寻找(默认)"
 
 #: builtin/grep.c
-msgid "descend at most <depth> levels"
-msgstr "最多以指定的深度向下寻找"
+msgid "descend at most <n> levels"
+msgstr "最多向下寻找 <n> 层"
 
 #: builtin/grep.c
 msgid "use extended POSIX regular expressions"
@@ -8833,11 +8928,6 @@
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "发现 %s 出现 SHA1 冲突!"
 
-#: builtin/index-pack.c builtin/pack-objects.c
-#, c-format
-msgid "unable to read %s"
-msgstr "不能读 %s"
-
 #: builtin/index-pack.c
 #, c-format
 msgid "cannot read existing object info %s"
@@ -8994,7 +9084,7 @@
 msgid "bad %s"
 msgstr "错误选项 %s"
 
-#: builtin/index-pack.c builtin/init-db.c
+#: builtin/index-pack.c builtin/init-db.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "未知的哈希算法 '%s'"
@@ -9012,103 +9102,16 @@
 msgstr "在打包对象中 fsck 检查出错"
 
 #: builtin/init-db.c
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "不能对模版 '%s' 调用 stat"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "不能打开目录 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "不能读取链接 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "不能自 '%s' 到 '%s' 创建符号链接"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "不能拷贝 '%s' 至 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "ignoring template %s"
-msgstr "忽略模版 %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "templates not found in %s"
-msgstr "没有在 %s 中找到模版"
-
-#: builtin/init-db.c
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "没有从 '%s' 复制模版:%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "无效的初始分支名:'%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "不能处理 %d 类型的文件"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "不能移动 %s 至 %s"
-
-#: builtin/init-db.c
-msgid "attempt to reinitialize repository with different hash"
-msgstr "尝试用不同的哈希算法重新初始化仓库"
-
-#: builtin/init-db.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已经存在"
-
-#: builtin/init-db.c
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init:已忽略 --initial-branch=%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "重新初始化已存在的共享 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "已重新初始化已存在的 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "已初始化空的共享 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "已初始化空的 Git 仓库于 %s%s\n"
-
-#: builtin/init-db.c
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<模板目录>]\n"
 "         [--separate-git-dir <git 目录>] [--object-format=<格式>]\n"
+"         [--ref-format=<格式>]\n"
 "         [-b <分支名> | --initial-branch=<分支名>]\n"
 "         [--shared[=<权限>]] [<目录>]"
 
@@ -9161,11 +9164,11 @@
 #: builtin/interpret-trailers.c
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <键>[(=|:)<值>])...]\n"
+"                       [(--trailer (<键|键别名>)[(=|:)<值>])...]\n"
 "                       [--parse] [<文件>...]"
 
 #: builtin/interpret-trailers.c
@@ -9177,6 +9180,10 @@
 msgstr "删除空的尾注"
 
 #: builtin/interpret-trailers.c
+msgid "placement"
+msgstr "安置"
+
+#: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
 msgstr "在哪里放置新的尾注"
 
@@ -9193,20 +9200,20 @@
 msgstr "只输出尾注"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply config rules"
-msgstr "不要应用配置规则"
+msgid "do not apply trailer.* configuration variables"
+msgstr "不应用 trailer.* 配置变量"
 
 #: builtin/interpret-trailers.c
-msgid "join whitespace-continued values"
-msgstr "连接空白折行的值"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "将多行尾注值重新格式化为单行值"
 
 #: builtin/interpret-trailers.c
-msgid "set parsing options"
-msgstr "设置解析选项"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "--only-trailers --only-input --unfold 的别名"
 
 #: builtin/interpret-trailers.c
-msgid "do not treat --- specially"
-msgstr "不要对 --- 特殊处理"
+msgid "do not treat \"---\" as the end of input"
+msgstr "不要将 \"---\" 视为输入的结束"
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
@@ -9263,7 +9270,7 @@
 "<file>"
 msgstr "跟踪 <文件> 中 <开始>,<结束> 范围内的行或函数 :<函数名> 的演变"
 
-#: builtin/log.c builtin/shortlog.c bundle.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "未能识别的参数:%s"
@@ -9319,6 +9326,11 @@
 msgstr "不是一个范围"
 
 #: builtin/log.c
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "无法读取分支描述文件 '%s'"
+
+#: builtin/log.c
 msgid "cover letter needs email format"
 msgstr "附函需要邮件地址格式"
 
@@ -9445,6 +9457,10 @@
 msgstr "基于一个分支描述生成部分附函"
 
 #: builtin/log.c
+msgid "use branch description from file"
+msgstr "使用来自文件的分支描述"
+
+#: builtin/log.c
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "使用 [<前缀>] 代替 [PATCH]"
 
@@ -9647,6 +9663,11 @@
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "不能找到跟踪的远程分支,请手工指定 <上游>。\n"
 
+#: builtin/ls-files.c builtin/ls-tree.c
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "无法获得关于 '%s' 的对象信息"
+
 #: builtin/ls-files.c
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
@@ -9834,11 +9855,6 @@
 
 #: builtin/ls-tree.c
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "无法获得关于 '%s' 的对象信息"
-
-#: builtin/ls-tree.c
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "坏的 ls-tree 格式:元素 '%s' 没有以 '(' 开头"
 
@@ -9998,11 +10014,21 @@
 "git merge-file [<选项>] [-L <名字1> [-L <初始名字> [-L <名字2>]]] <文件1> <初"
 "始文件> <文件2>"
 
+#: builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr "选项 diff-algorithm 接受参数 \"myers\"、\"minimal\"、\"patience\" 和 \"histogram\""
+
 #: builtin/merge-file.c
 msgid "send results to standard output"
 msgstr "将结果发送到标准输出"
 
 #: builtin/merge-file.c
+msgid "use object IDs instead of filenames"
+msgstr "使用对象 ID 替换文件名"
+
+#: builtin/merge-file.c
 msgid "use a diff3 based merge"
 msgstr "使用基于 diff3 的合并"
 
@@ -10022,6 +10048,14 @@
 msgid "for conflicts, use a union version"
 msgstr "如果冲突,使用联合版本"
 
+#: builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<算法>"
+
+#: builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "选择一个差异算法"
+
 #: builtin/merge-file.c
 msgid "for conflicts, use this marker size"
 msgstr "如果冲突,使用指定长度的标记"
@@ -10034,6 +10068,15 @@
 msgid "set labels for file1/orig-file/file2"
 msgstr "为 文件1/初始文件/文件2 设置标签"
 
+#: builtin/merge-file.c
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "对象 '%s' 不存在"
+
+#: builtin/merge-file.c
+msgid "Could not write object file"
+msgstr "不能写入对象文件"
+
 #: builtin/merge-recursive.c
 #, c-format
 msgid "unknown option %s"
@@ -10113,13 +10156,22 @@
 msgid "specify a merge-base for the merge"
 msgstr "指定用于合并的合并基线"
 
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option=value"
+msgstr "option=value"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option for selected merge strategy"
+msgstr "所选的合并策略的选项"
+
 #: builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge 与其他所有选项不兼容"
 
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base 与 --stdin 不兼容"
+#: builtin/merge-tree.c builtin/merge.c
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "未知的策略选项:-X%s"
 
 #: builtin/merge-tree.c builtin/notes.c
 #, c-format
@@ -10208,14 +10260,6 @@
 msgid "merge strategy to use"
 msgstr "要使用的合并策略"
 
-#: builtin/merge.c builtin/pull.c
-msgid "option=value"
-msgstr "option=value"
-
-#: builtin/merge.c builtin/pull.c
-msgid "option for selected merge strategy"
-msgstr "所选的合并策略的选项"
-
 #: builtin/merge.c
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "合并的提交说明(针对非快进式合并)"
@@ -10286,7 +10330,7 @@
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "坏的 branch.%s.mergeoptions 字符串:%s"
 
-#: builtin/merge.c builtin/stash.c merge-recursive.c
+#: builtin/merge.c merge-recursive.c
 msgid "Unable to write index."
 msgstr "不能写入索引。"
 
@@ -10296,11 +10340,6 @@
 
 #: builtin/merge.c
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "未知的策略选项:-X%s"
-
-#: builtin/merge.c t/helper/test-fast-rebase.c
-#, c-format
 msgid "unable to write %s"
 msgstr "不能写 %s"
 
@@ -10371,7 +10410,7 @@
 msgid "Bad value '%s' in environment '%s'"
 msgstr "环境 '%2$s' 中存在坏的取值 '%1$s'"
 
-#: builtin/merge.c read-cache.c strbuf.c wrapper.c
+#: builtin/merge.c editor.c read-cache.c wrapper.c
 #, c-format
 msgid "could not close '%s'"
 msgstr "不能关闭 '%s'"
@@ -10657,8 +10696,8 @@
 msgstr "不能将目录移动到自身"
 
 #: builtin/mv.c
-msgid "cannot move directory over file"
-msgstr "不能将目录移动到文件"
+msgid "destination already exists"
+msgstr "目标已存在"
 
 #: builtin/mv.c
 msgid "source directory is empty"
@@ -10762,11 +10801,13 @@
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <注解引用>] add [-f] [--allow-empty] [-m <说明> | -F <文件> "
-"| (-c | -C) <对象>] [<对象>]"
+"git notes [--ref <注解引用>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<分段符>] [--[no-]stripspace] [-m <说明> | -F <文件> | (-c | -C) <"
+"对象>] [<对象>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -10774,11 +10815,13 @@
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <注解引用>] append [--allow-empty] [-m <说明> | -F <文件> | "
-"(-c | -C) <对象>] [<对象>]"
+"git notes [--ref <注解引用>] append [--allow-empty] [--[no-]separator|--"
+"separator=<分段符>] [--[no-]stripspace] [-m <说明> | -F <文件> | (-c | -C) <"
+"对象>] [<对象>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -10947,6 +10990,18 @@
 msgstr "替换已存在的注解"
 
 #: builtin/notes.c
+msgid "<paragraph-break>"
+msgstr "<分段符>"
+
+#: builtin/notes.c
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "在段落之间插入<分段符>"
+
+#: builtin/notes.c
+msgid "remove unnecessary whitespace"
+msgstr "删除不必要的空白字符"
+
+#: builtin/notes.c
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -11204,7 +11259,7 @@
 msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
 msgstr "写入 %<PRIu32> 个对象而预期 %<PRIu32> 个"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repack.c
 msgid "disabling bitmap writing, as some objects are not being packed"
 msgstr "禁用 bitmap 写入,因为一些对象将不会被打包"
 
@@ -11271,6 +11326,11 @@
 
 #: builtin/pack-objects.c
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "无效的 pack.allowPackReuse 值:'%s'"
+
+#: builtin/pack-objects.c
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -11371,6 +11431,14 @@
 msgstr "坏的索引版本 '%s'"
 
 #: builtin/pack-objects.c
+msgid "show progress meter during object writing phase"
+msgstr "在对象写入阶段显示进度表"
+
+#: builtin/pack-objects.c
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "当进度表显示时类似于 --all-progress"
+
+#: builtin/pack-objects.c
 msgid "<version>[,<offset>]"
 msgstr "<版本>[,<偏移>]"
 
@@ -11562,10 +11630,6 @@
 msgstr "--thin 不能用于创建一个可索引包"
 
 #: builtin/pack-objects.c
-msgid "cannot use --filter without --stdout"
-msgstr "不能在没有 --stdout 的情况下使用 --filter"
-
-#: builtin/pack-objects.c
 msgid "cannot use --filter with --stdin-packs"
 msgstr "不能同时使用 --filter 和 --stdin-packs"
 
@@ -11582,10 +11646,6 @@
 msgstr "不能将 --stdin-packs 和 --cruft 同时使用"
 
 #: builtin/pack-objects.c
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "不能将 --max-pack-size 和 --cruft 同时使用"
-
-#: builtin/pack-objects.c
 msgid "Enumerating objects"
 msgstr "枚举对象中"
 
@@ -11593,10 +11653,10 @@
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "总共 %<PRIu32>(差异 %<PRIu32>),复用 %<PRIu32>(差异 %<PRIu32>),包复用 "
-"%<PRIu32>"
+"%<PRIu32>(来自  %<PRIuMAX> 个包)"
 
 #: builtin/pack-redundant.c
 msgid ""
@@ -11611,9 +11671,16 @@
 "并通过发送邮件到 <git@vger.kernel.org> 让我们知道您仍旧\n"
 "使用它。 谢谢。\n"
 
+#: builtin/pack-redundant.c
+msgid "refusing to run without --i-still-use-this"
+msgstr "拒绝在未指定 --i-still-use-this 选项时运行"
+
 #: builtin/pack-refs.c
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <模式>] [--exclude <模式>]"
 
 #: builtin/pack-refs.c
 msgid "pack everything"
@@ -11623,6 +11690,14 @@
 msgid "prune loose refs (default)"
 msgstr "清除松散的引用(默认)"
 
+#: builtin/pack-refs.c
+msgid "references to include"
+msgstr "需包含的引用"
+
+#: builtin/pack-refs.c
+msgid "references to exclude"
+msgstr "需排除的引用"
+
 #: builtin/patch-id.c
 msgid "git patch-id [--stable | --unstable | --verbatim]"
 msgstr "git patch-id [--stable | --unstable | --verbatim]"
@@ -11699,6 +11774,14 @@
 msgid "number of submodules pulled in parallel"
 msgstr "并发拉取的子模组的数量"
 
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "只使用 IPv4 地址"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "只使用 IPv6 地址"
+
 #: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -11812,9 +11895,9 @@
 msgid "pull with rebase"
 msgstr "变基式拉取"
 
-#: builtin/pull.c
-msgid "please commit or stash them."
-msgstr "请提交或贮藏它们。"
+#: builtin/pull.c builtin/rebase.c
+msgid "Please commit or stash them."
+msgstr "请提交或贮藏修改。"
 
 #: builtin/pull.c
 #, c-format
@@ -11980,36 +12063,36 @@
 #: builtin/push.c
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。\n"
-"再次推送前,先与远程变更合并(如 'git pull ...')。详见\n"
-"'git push --help' 中的 'Note about fast-forwards' 小节。"
+"如果您希望先与远程变更合并,请在推送前执行 'git pull'。\n"
+"详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
-"检出该分支并整合远程变更(如 'git pull ...'),然后再推送。详见\n"
-"'git push --help' 中的 'Note about fast-forwards' 小节。"
+"如果您希望先与远程变更合并,请在推送前执行 'git pull'。\n"
+"详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外\n"
-"一个仓库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更\n"
-"(如 'git pull ...')。\n"
+"一个仓库已向该引用进行了推送。如果您希望先与远程变更合并,请在推送\n"
+"前执行 'git pull'。\n"
 "详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
@@ -12027,13 +12110,14 @@
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为远程跟踪分支的最新提交自从上次检出之后已被更新。\n"
-"在强制更新前,您可能想将这些变更整合到本地(如 'git pull ...')。\n"
+"如果您希望先与远程变更合并,请在推送前执行 'git pull'。\n"
+"详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
 #: builtin/push.c
 #, c-format
@@ -12061,9 +12145,9 @@
 msgid "repository"
 msgstr "仓库"
 
-#: builtin/push.c builtin/send-pack.c
-msgid "push all refs"
-msgstr "推送所有引用"
+#: builtin/push.c
+msgid "push all branches"
+msgstr "推送所有分支"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "mirror all refs"
@@ -12074,8 +12158,8 @@
 msgstr "删除引用"
 
 #: builtin/push.c
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "推送标签(不能使用 --all or --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "推送标签(不能使用 --all or --branches or --mirror)"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "force updates"
@@ -12399,6 +12483,11 @@
 
 #: builtin/rebase.c
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "未知的变基合并模式:%s"
+
+#: builtin/rebase.c
+#, c-format
 msgid "could not switch to %s"
 msgstr "无法切换到 %s"
 
@@ -12414,6 +12503,16 @@
 msgstr "无法识别的空类型 '%s';有效值有 \"drop\"、\"keep\" 和 \"ask\"。"
 
 #: builtin/rebase.c
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"为 --rebase-merges 指定空值参数的方式已经被废弃并且在 Git 未来\n"
+"的版本中将不再支持。 支持使用无参数 --rebase-merges 的方式进行替\n"
+"代,它们具有相同的作用。"
+
+#: builtin/rebase.c
 #, c-format
 msgid ""
 "%s\n"
@@ -12631,7 +12730,7 @@
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr "动作 --edit-todo 只能用在交互式变基过程中。"
 
-#: builtin/rebase.c t/helper/test-fast-rebase.c
+#: builtin/rebase.c
 msgid "Cannot read HEAD"
 msgstr "不能读取 HEAD"
 
@@ -12676,19 +12775,10 @@
 msgstr "开关 `C' 期望一个数字值"
 
 #: builtin/rebase.c
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "未知模式:%s"
-
-#: builtin/rebase.c
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy 需要 --merge 或 --interactive"
-
-#: builtin/rebase.c
 msgid ""
-"apply options are incompatible with rebase.autosquash.  Consider adding --no-"
-"autosquash"
-msgstr "应用的选项与 rebase.autosquash 不兼容。考虑加上 --no-autosquash"
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr "应用的选项与 rebase.rebaseMerges 不兼容。考虑加上 --no-rebase-merges"
 
 #: builtin/rebase.c
 msgid ""
@@ -12744,10 +12834,6 @@
 msgstr "没有指向一个有效的提交 '%s'"
 
 #: builtin/rebase.c
-msgid "Please commit or stash them."
-msgstr "请提交或贮藏修改。"
-
-#: builtin/rebase.c
 msgid "HEAD is up to date."
 msgstr "HEAD 是最新的。"
 
@@ -13050,12 +13136,10 @@
 msgstr "抓取远程的分支"
 
 #: builtin/remote.c
-msgid "import all tags and associated objects when fetching"
-msgstr "抓取时导入所有的标签和关联对象"
-
-#: builtin/remote.c
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "或不抓取任何标签(--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr "抓取时导入所有的标签和关联对象,或不抓取任何标签(--no-tags)"
 
 #: builtin/remote.c
 msgid "branch(es) to track"
@@ -13173,8 +13257,8 @@
 msgid_plural ""
 "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
 "to delete them, use:"
-msgstr[0] "注意:ref/remotes 层级之外的一个分支未被移除。要删除它,使用:"
-msgstr[1] "注意:ref/remotes 层级之外的一些分支未被移除。要删除它们,使用:"
+msgstr[0] "注意:refs/remotes/ 层级之外的一个分支未被移除。要删除它,使用:"
+msgstr[1] "注意:refs/remotes/ 层级之外的一些分支未被移除。要删除它们,使用:"
 
 #: builtin/remote.c
 #, c-format
@@ -13533,6 +13617,11 @@
 msgstr "无法删除过期的位图: %s"
 
 #: builtin/repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "包前缀 %s 没有以对象目录 %s 开始"
+
+#: builtin/repack.c
 msgid "pack everything in a single pack"
 msgstr "所有内容打包到一个包文件中"
 
@@ -13549,8 +13638,8 @@
 msgstr "近似日期"
 
 #: builtin/repack.c
-msgid "with -C, expire objects older than this"
-msgstr "使用 -C,使早于给定时间的对象过期"
+msgid "with --cruft, expire objects older than this"
+msgstr "使用 --cruft,使早于给定时间的对象过期"
 
 #: builtin/repack.c
 msgid "remove redundant packs, and run git-prune-packed"
@@ -13633,17 +13722,21 @@
 msgstr "储存被清除的对象的包的前缀"
 
 #: builtin/repack.c
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "储存被过滤的对象的包的前缀"
+
+#: builtin/repack.c
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "不能删除珍品仓库中的打包文件"
 
 #: builtin/repack.c
-msgid "Nothing new to pack."
-msgstr "没有新的要打包。"
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "选项 '%s' 只能和 '%s' 搭配使用"
 
 #: builtin/repack.c
-#, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "包前缀 %s 没有以对象目录 .%s 开始"
+msgid "Nothing new to pack."
+msgstr "没有新的要打包。"
 
 #: builtin/repack.c
 #, c-format
@@ -13896,6 +13989,84 @@
 msgid "only one pattern can be given with -l"
 msgstr "只能为 -l 提供一个模式"
 
+#: builtin/replay.c
+msgid "need some commits to replay"
+msgstr "需要一些提交来重放"
+
+#: builtin/replay.c
+msgid "--onto and --advance are incompatible"
+msgstr "--onto 和 --advance 不兼容"
+
+#: 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
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr "不能隐式地确定这是 --advance 还是 --onto 的操作"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr "不能使用多个源分支推进目标,因为无法明确如何排序"
+
+#: builtin/replay.c
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "不能隐式地确定 --onto 正确的基线"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr "(试验中!)git replay ([--contained] --onto <新基线> | --advance <分支>) <版本范围>..."
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "重放时演进给定的分支"
+
+#: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "重放到给定提交"
+
+#: builtin/replay.c
+msgid "advance all branches contained in revision-range"
+msgstr "演进版本范围中包含的所有分支"
+
+#: builtin/replay.c
+msgid "option --onto or --advance is mandatory"
+msgstr "选项 --onto 或 --advance 必须指定其一"
+
+#: builtin/replay.c
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr "一些版本遍历选项将被覆盖,如 'struct rev_info' 中的 '%s' 位将被强制设定"
+
+#: builtin/replay.c
+msgid "error preparing revisions"
+msgstr "准备版本时错误"
+
+#: builtin/replay.c
+msgid "replaying down to root commit is not supported yet!"
+msgstr "目前还不支持重放到根提交!"
+
+#: builtin/replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "目前还不支持重放到合并提交!"
+
 #: builtin/rerere.c
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
@@ -14155,23 +14326,15 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "未知的 --abbrev-ref 模式:%s"
 
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden 不能与 --branches 一起使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden 不能与 --tags 一起使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden 不能与 --remotes 一起使用"
-
 #: builtin/rev-parse.c setup.c
 msgid "this operation must be run in a work tree"
 msgstr "该操作必须在一个工作区中运行"
 
 #: builtin/rev-parse.c
+msgid "Could not read the index"
+msgstr "不能读取索引"
+
+#: builtin/rev-parse.c
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "未知的 --show-object-format 模式:%s"
@@ -14382,6 +14545,10 @@
 msgstr "远程名称"
 
 #: builtin/send-pack.c
+msgid "push all refs"
+msgstr "推送所有引用"
+
+#: builtin/send-pack.c
 msgid "use stateless RPC protocol"
 msgstr "使用无状态的 RPC 协议"
 
@@ -14592,19 +14759,41 @@
 
 #: builtin/show-ref.c
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<模式>...]"
 
 #: builtin/show-ref.c
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<引用>...]"
+
+#: builtin/show-ref.c
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<模式>]"
 
 #: builtin/show-ref.c
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <引用>"
+
+#: builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "引用不存在"
+
+#: builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "无法找到引用"
+
+#: builtin/show-ref.c
 msgid "only show tags (can be combined with heads)"
 msgstr "只显示标签(可以和头共用)"
 
@@ -14613,6 +14802,10 @@
 msgstr "只显示头(可以和标签共用)"
 
 #: builtin/show-ref.c
+msgid "check for reference existence without resolving"
+msgstr "检查引用是否存在但不解析"
+
+#: builtin/show-ref.c
 msgid "stricter reference checking, requires exact ref path"
 msgstr "更严格的引用检测,需要精确的引用路径"
 
@@ -14638,9 +14831,11 @@
 
 #: builtin/sparse-checkout.c
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<选项>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check\n"
+"-rules) [<选项>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -14780,6 +14975,26 @@
 msgid "error while refreshing working directory"
 msgstr "刷新工作目录时出错"
 
+#: builtin/sparse-checkout.c
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <文件>]"
+
+#: builtin/sparse-checkout.c
+msgid "terminate input and output files by a NUL character"
+msgstr "输入和输出的文件使用 NUL 字符终结"
+
+#: builtin/sparse-checkout.c
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr "通过 --rules-file 选项传递的模型将被作为锥形(稀疏检出模型)进行解析"
+
+#: builtin/sparse-checkout.c
+msgid "use patterns in <file> instead of the current ones."
+msgstr "从 <文件> 参数中读取模式,而不是从标准输入"
+
 #: builtin/stash.c
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<日志选项>]"
@@ -15415,6 +15630,11 @@
 
 #: builtin/submodule--helper.c
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "无法在不提供 URL 时克隆子模组 '%s'"
+
+#: builtin/submodule--helper.c
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "克隆 '%s' 失败。按计划重试"
 
@@ -15571,6 +15791,10 @@
 "shallow] [--reference <仓库>] [--recursive] [--[no-]single-branch] [--] [<路"
 "径>...]"
 
+#: builtin/submodule--helper.c submodule.c
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "无法将 HEAD 解析为有效引用。"
+
 #: builtin/submodule--helper.c
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<选项>] [<路径>...]"
@@ -16147,6 +16371,10 @@
 msgstr "以这种格式写入索引区"
 
 #: builtin/update-index.c
+msgid "report on-disk index format version"
+msgstr "报告磁盘索引格式的版本"
+
+#: builtin/update-index.c
 msgid "enable or disable split index"
 msgstr "启用或禁用索引拆分"
 
@@ -16179,6 +16407,16 @@
 msgstr "清除 fsmonitor 有效位"
 
 #: builtin/update-index.c
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#: builtin/update-index.c
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "索引版本:从 %d 设置为 %d"
+
+#: builtin/update-index.c
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -16325,10 +16563,10 @@
 #: builtin/worktree.c
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <字符串>]]\n"
-"                 [-b <新分支>] <路径> [<提交号>]"
+"                 [--orphan] [(-b | -B) <新分支>] <路径> [<提交号>]"
 
 #: builtin/worktree.c
 msgid "git worktree list [-v | --porcelain [-z]]"
@@ -16359,6 +16597,40 @@
 msgstr "git worktree unlock <工作区>"
 
 #: builtin/worktree.c
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "没有可用的源分支,将基于 '--orphan' 选项进行推断"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"如果你打算为此仓库创建一个包含新的未诞生的\n"
+"分支(没有提交的分支)的工作区,你可以使用\n"
+"选项 --orphan 来执行此操作:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"如果你打算为此仓库创建一个包含新的未诞生的\n"
+"分支(没有提交的分支)的工作区,你可以使用\n"
+"选项 --orphan 来执行此操作:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
+#: builtin/worktree.c
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "删除 %s/%s: %s"
@@ -16425,6 +16697,11 @@
 
 #: builtin/worktree.c
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "无法找到已创建的工作树 '%s'"
+
+#: builtin/worktree.c
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "准备工作区(新分支 '%s')"
 
@@ -16440,10 +16717,34 @@
 
 #: builtin/worktree.c
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "不可达:无效引用:%s"
+
+#: builtin/worktree.c
+#, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 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'"
+
+#: builtin/worktree.c
+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"
+"请先使用 'add -f' 来覆盖或拉取一个远程仓库"
+
+#: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "检出 <分支>,即使已经被检出到其它工作区"
 
@@ -16456,6 +16757,10 @@
 msgstr "创建或重置一个分支"
 
 #: builtin/worktree.c
+msgid "create unborn branch"
+msgstr "创建一个尚未诞生的分支"
+
+#: builtin/worktree.c
 msgid "populate the new working tree"
 msgstr "生成新的工作区"
 
@@ -16481,6 +16786,11 @@
 msgstr "选项 '%s'、'%s' 和 '%s' 不能同时使用"
 
 #: builtin/worktree.c
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "选项 '%s' 和提交号不能同时使用"
+
+#: builtin/worktree.c
 msgid "added with --lock"
 msgstr "由 --lock 添加"
 
@@ -16761,6 +17071,16 @@
 msgstr[1] "这个归档包需要 %<PRIuMAX> 个引用:"
 
 #: bundle.c
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "该归档包使用的哈希算法:%s"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "归档包使用了过滤器:%s"
+
+#: bundle.c
 msgid "unable to dup bundle descriptor"
 msgstr "无法复制归档包描述符"
 
@@ -16806,6 +17126,11 @@
 
 #: chunk-format.c
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "块 id %<PRIx32> 未 %d 字节对齐"
+
+#: chunk-format.c
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "不正确的块偏移 %<PRIx64> 和 %<PRIx64>"
 
@@ -16873,8 +17198,8 @@
 msgstr "通过归档移动对象和引用"
 
 #: command-list.h
-msgid "Provide content or type and size information for repository objects"
-msgstr "提供仓库对象的内容、类型或大小"
+msgid "Provide contents or details of repository objects"
+msgstr "提供仓库对象的内容或详情"
 
 #: command-list.h
 msgid "Display gitattributes information"
@@ -17057,8 +17382,8 @@
 msgstr "一个便携的 Git 图形客户端"
 
 #: command-list.h
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "从一个文件计算对象 ID,并可以创建 blob 数据对象"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "从一个文件计算对象 ID,并支持可选地创建一个对象"
 
 #: command-list.h
 msgid "Display help information about Git"
@@ -17254,6 +17579,10 @@
 msgstr "创建、列出、删除对象替换引用"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr "试验中:基于一个新基线重放提交,同样适用于纯仓库"
+
+#: command-list.h
 msgid "Generates a summary of pending changes"
 msgstr "生成待定更改的摘要"
 
@@ -17415,7 +17744,7 @@
 msgstr "显示关于 Git 的版本信息"
 
 #: command-list.h
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "显示每一个提交引入的差异日志"
 
 #: command-list.h
@@ -17568,48 +17897,104 @@
 
 #: commit-graph.c
 msgid "commit-graph file is too small"
-msgstr "提交图形文件太小"
+msgstr "提交图文件太小"
+
+#: commit-graph.c
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "提交图中对象 ID 的扇出块大小错误"
+
+#: commit-graph.c
+msgid "commit-graph fanout values out of order"
+msgstr "提交图的扇出值失序"
+
+#: commit-graph.c
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "提交图的对象 ID 查询块大小错误"
+
+#: commit-graph.c
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "提交图的提交数据块大小错误"
+
+#: commit-graph.c
+msgid "commit-graph generations chunk is wrong size"
+msgstr "提交图的世代块大小错误"
+
+#: commit-graph.c
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "提交图的变更路径的索引块太小"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr "忽略提交图文件中过小的更改路径块(%<PRIuMAX> < %<PRIuMAX>)"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
-msgstr "提交图形签名 %X 和签名 %X 不匹配"
+msgstr "提交图签名 %X 和签名 %X 不匹配"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph version %X does not match version %X"
-msgstr "提交图形版本 %X 和版本 %X 不匹配"
+msgstr "提交图版本 %X 和版本 %X 不匹配"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph hash version %X does not match version %X"
-msgstr "提交图形哈希版本 %X 和版本 %X 不匹配"
+msgstr "提交图哈希版本 %X 和版本 %X 不匹配"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph file is too small to hold %u chunks"
-msgstr "提交图形文件太小,容不下 %u 个块"
+msgstr "提交图文件太小,容不下 %u 个块"
+
+#: commit-graph.c
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "提交图所需的对象 ID 扇出块缺失或损坏"
+
+#: commit-graph.c
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "提交图所需的对象 ID 查询块缺失或损坏"
+
+#: commit-graph.c
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "提交图所需的提交数据块缺失或损坏"
 
 #: commit-graph.c
 msgid "commit-graph has no base graphs chunk"
-msgstr "提交图形没有基础图形块"
+msgstr "提交图没有基础图形块"
+
+#: commit-graph.c
+msgid "commit-graph base graphs chunk is too small"
+msgstr "提交图的基础图形块过小"
 
 #: commit-graph.c
 msgid "commit-graph chain does not match"
-msgstr "提交图形链不匹配"
+msgstr "提交图链不匹配"
+
+#: commit-graph.c
+#, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "基础图形中的提交数量过高:%<PRIuMAX>"
+
+#: commit-graph.c
+msgid "commit-graph chain file too small"
+msgstr "提交图链文件太小"
 
 #: commit-graph.c
 #, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
-msgstr "无效的提交图形链:行 '%s' 不是一个哈希值"
+msgstr "无效的提交图链:行 '%s' 不是一个哈希值"
 
 #: commit-graph.c
 msgid "unable to find all commit-graph files"
-msgstr "无法找到所有提交图形文件"
+msgstr "无法找到所有提交图文件"
 
 #: commit-graph.c
 msgid "invalid commit position. commit-graph is likely corrupt"
-msgstr "无效的提交位置。提交图形可能已损坏"
+msgstr "无效的提交位置。提交图可能已损坏"
 
 #: commit-graph.c
 #, c-format
@@ -17621,6 +18006,14 @@
 msgstr "提交图需要溢出世代数据,但是没有"
 
 #: commit-graph.c
+msgid "commit-graph overflow generation data is too small"
+msgstr "提交图溢出世代数据过小"
+
+#: commit-graph.c
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "提交图额外边的指针越界"
+
+#: commit-graph.c
 msgid "Loading known commits in commit graph"
 msgstr "正在加载提交图中的已知提交"
 
@@ -17695,15 +18088,25 @@
 
 #: commit-graph.c
 msgid "unable to open commit-graph chain file"
-msgstr "无法打开提交图形链文件"
+msgstr "无法打开提交图链文件"
 
 #: commit-graph.c
 msgid "failed to rename base commit-graph file"
-msgstr "无法重命名基础提交图形文件"
+msgstr "无法重命名基础提交图文件"
 
 #: commit-graph.c
 msgid "failed to rename temporary commit-graph file"
-msgstr "无法重命名临时提交图形文件"
+msgstr "无法重命名临时提交图文件"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "无法合并提交图,总共已累加提交数:%<PRIuMAX>,当前待累加提交数:%<PRIuMAX>"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "无法合并提交图 %s, 提交过多:%<PRIuMAX>"
 
 #: commit-graph.c
 msgid "Scanning merged commits"
@@ -17711,7 +18114,7 @@
 
 #: commit-graph.c
 msgid "Merging commit-graph"
-msgstr "正在合并提交图形"
+msgstr "正在合并提交图"
 
 #: commit-graph.c
 msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
@@ -17728,68 +18131,63 @@
 #: commit-graph.c
 #, c-format
 msgid "commit-graph has incorrect OID order: %s then %s"
-msgstr "提交图形的对象 ID 顺序不正确:%s 然后 %s"
+msgstr "提交图的对象 ID 顺序不正确:%s 然后 %s"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
-msgstr "提交图形有不正确的扇出值:fanout[%d] = %u != %u"
+msgstr "提交图有不正确的扇出值:fanout[%d] = %u != %u"
 
 #: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from commit-graph"
-msgstr "无法从提交图形中解析提交 %s"
-
-#: commit-graph.c
-msgid "Verifying commits in commit graph"
-msgstr "正在校验提交图中的提交"
+msgstr "无法从提交图中解析提交 %s"
 
 #: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
-msgstr "无法从提交图形的对象库中解析提交 %s"
+msgstr "无法从提交图的对象库中解析提交 %s"
 
 #: commit-graph.c
 #, c-format
 msgid "root tree OID for commit %s in commit-graph is %s != %s"
-msgstr "提交图形中的提交 %s 的根树对象 ID 是 %s != %s"
+msgstr "提交图中的提交 %s 的根树对象 ID 是 %s != %s"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph parent list for commit %s is too long"
-msgstr "提交 %s 的提交图形父提交列表太长了"
+msgstr "提交 %s 的提交图父提交列表太长了"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph parent for %s is %s != %s"
-msgstr "%s 的提交图形父提交是 %s != %s"
+msgstr "%s 的提交图父提交是 %s != %s"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph parent list for commit %s terminates early"
-msgstr "提交 %s 的提交图形父提交列表过早终止"
-
-#: commit-graph.c
-#, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr "提交图形中提交 %s 的世代号是零,但其它地方非零"
-
-#: commit-graph.c
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr "提交图形中提交 %s 的世代号非零,但其它地方是零"
+msgstr "提交 %s 的提交图父提交列表过早终止"
 
 #: commit-graph.c
 #, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
-msgstr "提交图形中的提交 %s 的世代号是 %<PRIuMAX> < %<PRIuMAX>"
+msgstr "提交图中的提交 %s 的世代号是 %<PRIuMAX> < %<PRIuMAX>"
 
 #: commit-graph.c
 #, c-format
 msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
-msgstr "提交图形中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
+msgstr "提交图中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr "提交图具有零和非零的世代(例如:提交 '%s' 和 '%s')"
+
+#: commit-graph.c
+msgid "Verifying commits in commit graph"
+msgstr "正在校验提交图中的提交"
 
 #: commit.c
 #, c-format
@@ -17818,6 +18216,11 @@
 
 #: commit.c
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "提交 %s 存在于提交图中,但不存在于对象数据库中"
+
+#: commit.c
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "提交 %s 有一个非可信的声称来自 %s 的 GPG 签名。"
 
@@ -18313,8 +18716,8 @@
 msgstr "错误的 zlib 压缩级别 %d"
 
 #: config.c
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar 应该是一个字符"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar 应该是一个 ASCII 编码的字符"
 
 #: config.c
 #, c-format
@@ -18360,11 +18763,6 @@
 msgstr "不能解析配置对象 '%s'"
 
 #: config.c
-#, c-format
-msgid "failed to parse %s"
-msgstr "无法解析 %s"
-
-#: config.c
 msgid "unable to parse command-line config"
 msgstr "无法解析命令行中的配置"
 
@@ -18454,6 +18852,11 @@
 
 #: config.c
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "拒绝支持内容过长的行,位于文件 '%s' 中的第 %<PRIuMAX> 行"
+
+#: config.c
+#, c-format
 msgid "missing value for '%s'"
 msgstr "%s 的取值缺失"
 
@@ -18927,10 +19330,6 @@
 msgstr "--merge-base 不适用于范围"
 
 #: diff-lib.c
-msgid "--merge-base only works with commits"
-msgstr "--merge-base 仅适用于提交"
-
-#: diff-lib.c
 msgid "unable to get HEAD"
 msgstr "不能解析 HEAD"
 
@@ -18943,6 +19342,14 @@
 msgstr "找到了多条合并基线"
 
 #: diff-no-index.c
+msgid "cannot compare stdin to a directory"
+msgstr "无法将标准输入和目录进行比较"
+
+#: diff-no-index.c
+msgid "cannot compare a named pipe to a directory"
+msgstr "无法将命名管道和目录进行比较"
+
+#: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<选项>] <路径> <路径>"
 
@@ -18992,6 +19399,11 @@
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 msgstr "配置变量 'diff.submodule' 未知的取值:'%s'"
 
+#: diff.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "配置 '%s' 未知的取值:%s"
+
 #: diff.c
 #, c-format
 msgid ""
@@ -19006,6 +19418,15 @@
 msgid "external diff died, stopping at %s"
 msgstr "外部 diff 退出,停止在 %s"
 
+#: diff.c
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow 明确要求只跟一个路径规格"
+
+#: diff.c
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "指定 --follow: %s 的同时,不支持在路径规格中使用神奇前缀"
+
 #: diff.c parse-options.c
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -19023,10 +19444,6 @@
 msgstr "选项 '%1$s'、'%2$s' 不能同时使用,与 '%4$s' 和 '%5$s' 一起使用 '%3$s'"
 
 #: diff.c
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow 明确要求只跟一个路径规格"
-
-#: diff.c
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "无效的 --stat 值:%s"
@@ -19081,13 +19498,6 @@
 msgstr "--color-moved-ws 中的无效模式 '%s' "
 
 #: diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"diff-algorithm 选项有 \"myers\"、\"minimal\"、\"patience\" 和 \"histogram\""
-
-#: diff.c
 #, c-format
 msgid "invalid argument to %s"
 msgstr "%s 的参数无效"
@@ -19144,8 +19554,8 @@
 msgstr "只输出 --stat 的最后一行"
 
 #: diff.c
-msgid "<param1,param2>..."
-msgstr "<参数1,参数2>..."
+msgid "<param1>,<param2>..."
+msgstr "<参数1>,<参数2>..."
 
 #: diff.c
 msgid ""
@@ -19157,8 +19567,8 @@
 msgstr "和 --dirstat=cumulative 同义"
 
 #: diff.c
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "是 --dirstat=files,param1,param2... 的同义词"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "是 --dirstat=files,<参数1>,<参数2>... 的同义词"
 
 #: diff.c
 msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19263,6 +19673,10 @@
 msgstr "不显示任何源和目标前缀"
 
 #: diff.c
+msgid "use default prefixes a/ and b/"
+msgstr "使用 a/ 和 b/ 作为默认前缀"
+
+#: diff.c
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "显示指定行数的差异块间的上下文"
 
@@ -19377,14 +19791,6 @@
 msgstr "使用 \"histogram diff\" 算法生成差异"
 
 #: diff.c
-msgid "<algorithm>"
-msgstr "<算法>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "选择一个差异算法"
-
-#: diff.c
 msgid "<text>"
 msgstr "<文本>"
 
@@ -19631,6 +20037,16 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "提示:等待您的编辑器关闭文件...%c"
 
+#: editor.c sequencer.c wrapper.c
+#, c-format
+msgid "could not write to '%s'"
+msgstr "不能写入 '%s'"
+
+#: editor.c
+#, c-format
+msgid "could not edit '%s'"
+msgstr "不能编辑 '%s'"
+
 #: entry.c
 msgid "Filtering content"
 msgstr "过滤内容"
@@ -19999,6 +20415,11 @@
 
 #: git.c
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "没有为 --attr-source 提供属性来源\n"
+
+#: git.c
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "未知选项:%s\n"
 
@@ -20621,12 +21042,12 @@
 "%s"
 
 #: merge-ort.c merge-recursive.c
-msgid "Failed to execute internal merge"
+msgid "failed to execute internal merge"
 msgstr "无法执行内部合并"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
-msgid "Unable to add %s to database"
+msgid "unable to add %s to database"
 msgstr "不能添加 %s 至对象库"
 
 #: merge-ort.c merge-recursive.c
@@ -21122,7 +21543,21 @@
 
 #: midx.c
 msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "多包索引的对象ID扇出表大小错误"
+msgstr "多包索引的对象 ID 扇出表大小错误"
+
+#: midx.c
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr "对象 ID 扇出失序:fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+#: midx.c
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "多包索引的对象 ID 查询块大小错误"
+
+#: midx.c
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "多包索引的对象 ID 偏移块大小错误"
 
 #: midx.c
 #, c-format
@@ -21145,20 +21580,24 @@
 msgstr "多包索引哈希版本 %u 和版本 %u 不匹配"
 
 #: midx.c
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "多包索引缺少必需的包名块"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "多包索引必需的包名块缺失或损坏"
 
 #: midx.c
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "多包索引缺少必需的对象 ID 扇出块"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr "多包索引必需的对象 ID 扇出块缺失或损坏"
 
 #: midx.c
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "多包索引缺少必需的对象 ID 查询块"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "多包索引必需的对象 ID 查询块缺失或损坏"
 
 #: midx.c
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "多包索引缺少必需的对象偏移块"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr "多包索引必需的对象偏移块缺少或损坏"
+
+#: midx.c
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "多包索引包名块过短"
 
 #: midx.c
 #, c-format
@@ -21171,10 +21610,23 @@
 msgstr "错的 pack-int-id:%u(共有 %u 个包)"
 
 #: midx.c
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "多包索引中未包含 BTMP 块"
+
+#: midx.c
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "不能打开已被位图索引的包 %<PRIu32>"
+
+#: midx.c
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "多包索引存储一个64位偏移,但是 off_t 太小"
 
 #: midx.c
+msgid "multi-pack-index large offset out of bounds"
+msgstr "多包索引大偏移区越界"
+
+#: midx.c
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "无法添加包文件 '%s'"
@@ -21274,12 +21726,6 @@
 msgstr "正在查找引用的包文件"
 
 #: midx.c
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "对象 ID 扇出无序:fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
-#: midx.c
 msgid "the midx contains no oid"
 msgstr "midx 不包含 oid"
 
@@ -21908,6 +22354,10 @@
 msgid "could not open pack %s"
 msgstr "不能打开包 %s"
 
+#: pack-bitmap.c t/helper/test-read-midx.c
+msgid "could not determine MIDX preferred pack"
+msgstr "不能确定多包索引的首选包"
+
 #: pack-bitmap.c
 #, c-format
 msgid "preferred pack (%s) is invalid"
@@ -21933,6 +22383,11 @@
 
 #: pack-bitmap.c
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "无法打开包:'%s',禁用包重用"
+
+#: pack-bitmap.c
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "对象 %s 为在类型位图中找到"
 
@@ -21978,6 +22433,11 @@
 msgid "unable to get disk usage of '%s'"
 msgstr "无法得到 '%s' 的磁盘使用量"
 
+#: pack-bitmap.c
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "位图文件 '%s' 有无效的校验码"
+
 #: pack-mtimes.c
 #, c-format
 msgid "mtimes file %s is too small"
@@ -22028,6 +22488,23 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "反向索引文件 %s 有不支持的哈希 ID %<PRIu32>"
 
+#: pack-revindex.c
+msgid "invalid checksum"
+msgstr "无效的校验码 %s"
+
+#: pack-revindex.c
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "位于 %<PRIu64> 的无效的反向索引:%<PRIu32> != %<PRIu32>"
+
+#: pack-revindex.c
+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 "无法同时写入和校验反向索引"
@@ -22093,16 +22570,6 @@
 
 #: parse-options.c
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s 与 %s 不兼容"
-
-#: parse-options.c
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s:和其它的不兼容"
-
-#: parse-options.c
-#, c-format
 msgid "%s takes no value"
 msgstr "%s 不取值"
 
@@ -22201,6 +22668,11 @@
 msgid "-NUM"
 msgstr "-数字"
 
+#: parse-options.c
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "与 --no-%s 相反"
+
 #: parse-options.h
 msgid "expiry-date"
 msgstr "到期时间"
@@ -22238,6 +22710,16 @@
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr "使用 --pathspec-from-file,路径表达式用空字符分隔"
 
+#: parse.c
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "对于 '%2$s' 的错误的布尔环境取值 '%1$s'"
+
+#: parse.c
+#, c-format
+msgid "failed to parse %s"
+msgstr "无法解析 %s"
+
 #: path.c
 #, c-format
 msgid "Could not make %s writable by group"
@@ -22296,6 +22778,11 @@
 
 #: pathspec.c
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "'%s' 位于目录树之外"
+
+#: pathspec.c
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s:'%s' 在位于 '%s' 的仓库之外"
 
@@ -22452,6 +22939,15 @@
 msgid "could not parse log for '%s'"
 msgstr "不能解析 '%s' 的日志"
 
+#: reachable.c
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "无效的额外废弃提交版本:'%s'"
+
+#: reachable.c
+msgid "unable to enumerate additional recent objects"
+msgstr "无法枚举额外的近期对象"
+
 #: read-cache.c
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
@@ -22478,11 +22974,6 @@
 
 #: read-cache.c
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "无法对 %s 执行 stat"
-
-#: read-cache.c
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "'%s' 看起来既是文件又是目录"
 
@@ -22616,11 +23107,6 @@
 
 #: read-cache.c
 #, c-format
-msgid "could not stat '%s'"
-msgstr "不能对 '%s' 调用 stat"
-
-#: read-cache.c
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "不能打开 git 目录:%s"
 
@@ -22639,6 +23125,16 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s:不能落到暂存区 #0"
 
+#: read-cache.c
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "意外的差异状态 %c"
+
+#: read-cache.c
+#, c-format
+msgid "remove '%s'\n"
+msgstr "删除 '%s'\n"
+
 #: rebase-interactive.c
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
@@ -22854,6 +23350,26 @@
 
 #: ref-filter.c
 #, c-format
+msgid "argument expected for %s"
+msgstr "预期参数 %s"
+
+#: ref-filter.c
+#, c-format
+msgid "positive value expected %s=%s"
+msgstr "预期正数参数值 %s=%s"
+
+#: ref-filter.c
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "不能完整解析 %s=%s"
+
+#: ref-filter.c
+#, c-format
+msgid "value expected %s="
+msgstr "预期值 %s="
+
+#: ref-filter.c
+#, c-format
 msgid "positive value expected '%s' in %%(%s)"
 msgstr "期望 %%(%2$s) 中的 '%1$s' 是一个正数"
 
@@ -22884,6 +23400,11 @@
 
 #: ref-filter.c
 #, c-format
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "期望的格式:%%(ahead-behind:<提交号>)"
+
+#: ref-filter.c
+#, c-format
 msgid "malformed field name: %.*s"
 msgstr "格式错误的字段名:%.*s"
 
@@ -22939,6 +23460,10 @@
 msgstr "--format=%.*s 不能和 --python、--shell、--tcl 同时使用"
 
 #: ref-filter.c
+msgid "failed to run 'describe'"
+msgstr "无法运行 'describe'"
+
+#: ref-filter.c
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(非分支,正变基 %s)"
@@ -23015,6 +23540,10 @@
 msgid "field name to sort on"
 msgstr "排序的字段名"
 
+#: ref-filter.h
+msgid "exclude refs which match pattern"
+msgstr "排除与 <模式> 相匹配的引用"
+
 #: reflog.c
 #, c-format
 msgid "not a reflog: %s"
@@ -23117,17 +23646,12 @@
 msgid "cannot process '%s' and '%s' at the same time"
 msgstr "无法同时处理 '%s' 和 '%s'"
 
-#: refs/files-backend.c
-#, c-format
-msgid "could not remove reference %s"
-msgstr "无法删除引用 %s"
-
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
 #, c-format
 msgid "could not delete reference %s: %s"
 msgstr "无法删除引用 %s:%s"
 
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
 #, c-format
 msgid "could not delete references: %s"
 msgstr "无法删除引用:%s"
@@ -23544,8 +24068,9 @@
 
 #  译者:注意保持前导空格
 #: remote.c
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (使用 \"git pull\" 来合并远程分支)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr "  (如果您想将远程分支与您的更改合并,请使用 \"git pull\")\n"
 
 #: remote.c
 #, c-format
@@ -23637,11 +24162,6 @@
 
 #: rerere.c
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "不能删除 '%s'"
-
-#: rerere.c
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "已为 '%s' 更新 preimage"
 
@@ -23691,6 +24211,11 @@
 msgstr "不再支持 --unpacked=<packfile>"
 
 #: revision.c
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "在 --stdin 模式下的无效选项:'%s'"
+
+#: revision.c
 msgid "your current branch appears to be broken"
 msgstr "您的当前分支好像被损坏"
 
@@ -23801,8 +24326,16 @@
 msgstr "只下载要检出的分支的元信息"
 
 #: scalar.c
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<选项>] [--] <仓库> [<目录>]"
+msgid "create repository within 'src' directory"
+msgstr "在 'src' 目录中创建仓库"
+
+#: scalar.c
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<登记>]"
 
 #: scalar.c
 #, c-format
@@ -23868,13 +24401,32 @@
 
 #: scalar.c
 #, c-format
-msgid "removing stale scalar.repo '%s'"
-msgstr "正在删除过期的 scalar.repo '%s'"
+msgid "removed stale scalar.repo '%s'"
+msgstr "已删除过期的 scalar.repo '%s'"
 
 #: scalar.c
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "在 '%s' 的 git 仓库已消失"
+msgid "repository at '%s' has different owner"
+msgstr "位于 '%s' 处的仓库有不同的所有者"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "位于 '%s' 处的仓库存在格式问题"
+
+#: scalar.c
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "在 '%s' 中找不到仓库"
+
+#: scalar.c
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"若希望从 Scalar 注销该仓库,执行\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 #: scalar.c
 msgid ""
@@ -24052,11 +24604,6 @@
 msgid "could not lock '%s'"
 msgstr "不能锁定 '%s'"
 
-#: sequencer.c strbuf.c wrapper.c
-#, c-format
-msgid "could not write to '%s'"
-msgstr "不能写入 '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not write eol to '%s'"
@@ -24259,7 +24806,7 @@
 msgid "corrupt author: missing date information"
 msgstr "损坏的作者:缺失日期信息"
 
-#: sequencer.c t/helper/test-fast-rebase.c
+#: sequencer.c
 #, c-format
 msgid "could not update %s"
 msgstr "不能更新 %s"
@@ -24356,11 +24903,6 @@
 
 #: sequencer.c
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "不能将 '%s' 重命名为 '%s'"
-
-#: sequencer.c
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "不能还原 %s... %s"
 
@@ -24495,10 +25037,6 @@
 msgstr "不能创建序列目录 '%s'"
 
 #: sequencer.c
-msgid "could not lock HEAD"
-msgstr "不能锁定 HEAD"
-
-#: sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "拣选或还原操作并未进行"
 
@@ -24612,21 +25150,21 @@
 "\n"
 
 #: sequencer.c
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "并且修改索引和/或工作区\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "并且修改索引和/或工作区。\n"
 
 #: sequencer.c
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "执行成功:%s\n"
-"但是在索引和/或工作区中存在变更\n"
+"但是在索引和/或工作区中存在变更。\n"
 "提交或贮藏修改,然后运行\n"
 "\n"
 "  git rebase --continue\n"
@@ -24759,6 +25297,10 @@
 msgstr "自动贮藏已经存在;正在创建一个新的贮藏条目。"
 
 #: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "自动贮藏的引用是一个符号引用"
+
+#: sequencer.c
 msgid "could not detach HEAD"
 msgstr "不能分离头指针"
 
@@ -24794,13 +25336,13 @@
 
 #: sequencer.c
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "正在变基(%d/%d)%s"
+msgid "Stopped at %s...  %.*s\n"
+msgstr "停止在 %s... %.*s\n"
 
 #: sequencer.c
 #, c-format
-msgid "Stopped at %s...  %.*s\n"
-msgstr "停止在 %s... %.*s\n"
+msgid "Rebasing (%d/%d)%s"
+msgstr "正在变基(%d/%d)%s"
 
 #: sequencer.c
 #, c-format
@@ -25087,6 +25629,100 @@
 msgid "setsid failed"
 msgstr "setsid 失败"
 
+#: setup.c
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "不能对模版 '%s' 调用 stat"
+
+#: setup.c
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "不能打开目录 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "不能读取链接 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "不能自 '%s' 到 '%s' 创建符号链接"
+
+#: setup.c
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "不能拷贝 '%s' 至 '%s'"
+
+#: setup.c
+#, c-format
+msgid "ignoring template %s"
+msgstr "忽略模版 %s"
+
+#: setup.c
+#, c-format
+msgid "templates not found in %s"
+msgstr "没有在 %s 中找到模版"
+
+#: setup.c
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "没有从 '%s' 复制模版:%s"
+
+#: setup.c
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "无效的初始分支名:'%s'"
+
+#: setup.c
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init:已忽略 --initial-branch=%s"
+
+#: setup.c
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "不能处理 %d 类型的文件"
+
+#: setup.c
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "不能移动 %s 至 %s"
+
+#: setup.c
+msgid "attempt to reinitialize repository with different hash"
+msgstr "尝试用不同的哈希算法重新初始化仓库"
+
+#: setup.c
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "尝试使用不同的引用存储格式重新初始化仓库"
+
+#: setup.c
+#, c-format
+msgid "%s already exists"
+msgstr "%s 已经存在"
+
+#: setup.c
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "重新初始化已存在的共享 Git 仓库于 %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "已重新初始化已存在的 Git 仓库于 %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "已初始化空的共享 Git 仓库于 %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "已初始化空的 Git 仓库于 %s%s\n"
+
 #: sparse-index.c
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -25148,11 +25784,6 @@
 msgstr[0] "%u 字节/秒"
 msgstr[1] "%u 字节/秒"
 
-#: strbuf.c
-#, c-format
-msgid "could not edit '%s'"
-msgstr "不能编辑 '%s'"
-
 #: submodule-config.c
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
@@ -25388,14 +26019,6 @@
 msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr "缓存树中无效化的条目数量(默认 0)"
 
-#: t/helper/test-fast-rebase.c
-msgid "unhandled options"
-msgstr "未处理的选项"
-
-#: t/helper/test-fast-rebase.c
-msgid "error preparing revisions"
-msgstr "准备版本时错误"
-
 #: t/helper/test-reach.c
 #, c-format
 msgid "commit %s is not marked reachable"
@@ -25589,10 +26212,6 @@
 msgid "invalid remote service path"
 msgstr "无效的远程服务路径"
 
-#: transport-helper.c transport.c
-msgid "operation not supported by protocol"
-msgstr "协议不支持该操作"
-
 #: transport-helper.c
 #, c-format
 msgid "can't connect to subservice %s"
@@ -25758,11 +26377,6 @@
 
 #: transport.c
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "配置 '%s' 的取值未知:%s"
-
-#: transport.c
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "传输 '%s' 不允许"
 
@@ -25820,6 +26434,10 @@
 msgid "could not retrieve server-advertised bundle-uri list"
 msgstr "无法获取服务器公布的 bundle-uri 列表"
 
+#: transport.c
+msgid "operation not supported by protocol"
+msgstr "协议不支持该操作"
+
 #: tree-walk.c
 msgid "too-short tree object"
 msgstr "太短的树对象"
@@ -26272,6 +26890,10 @@
 msgid "unable to get current working directory"
 msgstr "不能获取当前工作目录"
 
+#: wrapper.c
+msgid "unable to get random bytes"
+msgstr "无法获取随机字节"
+
 #: wt-status.c
 msgid "Unmerged paths:"
 msgstr "未合并的路径:"
@@ -26844,6 +27466,11 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "不能%s:您的索引中包含未提交的变更。"
 
+#: xdiff-interface.c
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "'%2$s' 的未知风格取值 '%1$s'"
+
 #: git-merge-octopus.sh git-merge-resolve.sh
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
@@ -27060,13 +27687,13 @@
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "无法打开 %s: %s"
+msgid "Failed to open %s.final: %s"
+msgstr "无法打开 %s.final: %s"
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s.final: %s"
-msgstr "无法打开 %s.final: %s"
+msgid "Failed to open %s: %s"
+msgstr "无法打开 %s: %s"
 
 #: git-send-email.perl
 msgid "Summary email is empty, skipping it\n"
@@ -27244,8 +27871,8 @@
 
 #: git-send-email.perl
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) 添加 %s: %s 自:'%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) 非法的输出信息,来自于: '%s'"
 
 #: git-send-email.perl
 #, perl-format
@@ -27253,6 +27880,11 @@
 msgstr "(%s) 无法关闭管道至 '%s'"
 
 #: git-send-email.perl
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) 添加 %s: %s 自:'%s'\n"
+
+#: git-send-email.perl
 msgid "cannot send message as 7bit"
 msgstr "不能以 7bit 形式发送信息"
 
diff --git a/po/zh_TW.po b/po/zh_TW.po
index aa59a8e..312dd12 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -1,7 +1,7 @@
 # Chinese (traditional) translations for Git package
 # Git 套裝軟體的繁體中文翻譯。
 # Copyright (C) 2012-2021 Jiang Xin <worldhello.net AT gmail.com>
-# Copyright (C) 2019-2022 Yi-Jyun Pan <pan93412@gmail.com>
+# Copyright (C) 2019-2023 Yi-Jyun Pan <pan93412@gmail.com>
 # This file is distributed under the same license as the Git package.
 #
 # The glossary can be found on https://github.com/l10n-tw/git-glossary
@@ -19,14 +19,15 @@
 # - Yichao Yu <yyc1992 AT gmail.com>
 # - Zhuang Ya <zhuangya AT me.com>
 #
-# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023, 2024.
 # Kaiyang Wu <self@origincode.me>, 2022.
+# lumynou5 <lumynou5.tw@gmail.com>, 2023, 2024.
 msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2022-12-11 00:28+0800\n"
-"PO-Revision-Date: 2022-12-10 17:12+0000\n"
+"POT-Creation-Date: 2024-02-18 20:48+0800\n"
+"PO-Revision-Date: 2024-02-18 20:50+0800\n"
 "Last-Translator: Yi-Jyun Pan <pan93412@gmail.com>\n"
 "Language-Team: Chinese (Traditional) <http://weblate.slat.org/projects/git-"
 "po/git-cli/zh_Hant/>\n"
@@ -35,7 +36,7 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.14.2\n"
+"X-Generator: Poedit 3.4.2\n"
 "X-ZhConverter: 繁化姬 dict-f4bc617e-r910 @ 2019/11/16 20:23:12 | https://"
 "zhconvert.org\n"
 
@@ -48,19 +49,19 @@
 msgid "could not read index"
 msgstr "無法讀取索引"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "binary"
 msgstr "二進位"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "nothing"
 msgstr "無"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unchanged"
 msgstr "未變更"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Update"
 msgstr "更新"
 
@@ -73,14 +74,14 @@
 msgid "could not write index"
 msgstr "無法寫入索引"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "updated %d path\n"
 msgid_plural "updated %d paths\n"
 msgstr[0] "已更新 %d 個路徑\n"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "note: %s is untracked now.\n"
 msgstr "註:現已不再追蹤 %s。\n"
 
@@ -89,7 +90,7 @@
 msgid "make_cache_entry failed for path '%s'"
 msgstr "對 “%s” 路徑執行 make_cache_entry 失敗"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Revert"
 msgstr "還原"
 
@@ -97,23 +98,23 @@
 msgid "Could not parse HEAD^{tree}"
 msgstr "無法解析 HEAD^{tree}"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "reverted %d path\n"
 msgid_plural "reverted %d paths\n"
 msgstr[0] "已還原 %d 個路徑\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 #, c-format
 msgid "No untracked files.\n"
 msgstr "沒有未追蹤的檔案。\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Add untracked"
 msgstr "加入未追蹤項目"
 
-#: add-interactive.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-interactive.c
+#, c-format
 msgid "added %d path\n"
 msgid_plural "added %d paths\n"
 msgstr[0] "已加入 %d 個路徑\n"
@@ -123,21 +124,21 @@
 msgid "ignoring unmerged: %s"
 msgstr "忽略未合併項目:%s"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "Only binary files changed.\n"
-msgstr "只變更二進位檔案。\n"
+msgstr "只有二進位檔案更動了。\n"
 
-#: add-interactive.c add-patch.c git-add--interactive.perl
+#: add-interactive.c add-patch.c
 #, c-format
 msgid "No changes.\n"
 msgstr "沒有更動。\n"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Patch update"
 msgstr "修補檔更新"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "Review diff"
 msgstr "檢閱差異"
 
@@ -205,25 +206,25 @@
 msgid "(empty) select nothing"
 msgstr "(空)全部不選取"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "*** Commands ***"
 msgstr "*** 命令 ***"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 msgid "What now"
 msgstr "請選擇"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "staged"
 msgstr "已暫存"
 
-#: add-interactive.c git-add--interactive.perl
+#: add-interactive.c
 msgid "unstaged"
 msgstr "未暫存"
 
 #: add-interactive.c apply.c builtin/am.c builtin/bugreport.c builtin/clone.c
-#: builtin/diagnose.c builtin/fetch.c builtin/merge.c builtin/pull.c
-#: builtin/submodule--helper.c git-add--interactive.perl
+#: builtin/diagnose.c builtin/fetch.c builtin/hook.c builtin/merge.c
+#: builtin/pull.c builtin/submodule--helper.c
 msgid "path"
 msgstr "路徑"
 
@@ -231,28 +232,28 @@
 msgid "could not refresh index"
 msgstr "無法重新整理索引"
 
-#: add-interactive.c builtin/clean.c git-add--interactive.perl
+#: add-interactive.c builtin/clean.c
 #, c-format
 msgid "Bye.\n"
 msgstr "再見。\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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 git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage deletion [y,n,q,a,d%s,?]? "
 msgstr "暫存刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage addition [y,n,q,a,d%s,?]? "
 msgstr "暫存加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stage this hunk [y,n,q,a,d%s,?]? "
 msgstr "暫存此區塊 [y,n,q,a,d%s,?]? "
 
@@ -276,23 +277,23 @@
 "a - 暫存此區塊和本檔案中後面的全部區塊\n"
 "d - 不暫存此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash mode change [y,n,q,a,d%s,?]? "
 msgstr "貯存模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash deletion [y,n,q,a,d%s,?]? "
 msgstr "貯存刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash addition [y,n,q,a,d%s,?]? "
 msgstr "貯存加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Stash this hunk [y,n,q,a,d%s,?]? "
 msgstr "貯存此區塊 [y,n,q,a,d%s,?]? "
 
@@ -316,23 +317,23 @@
 "a - 貯存此區塊和本檔案中後面的全部區塊\n"
 "d - 不貯存此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage mode change [y,n,q,a,d%s,?]? "
 msgstr "取消暫存模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage deletion [y,n,q,a,d%s,?]? "
 msgstr "取消暫存刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage addition [y,n,q,a,d%s,?]? "
 msgstr "取消暫存加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
 msgstr "取消暫存此區塊 [y,n,q,a,d%s,?]? "
 
@@ -356,23 +357,23 @@
 "a - 不暫存此區塊和本檔案中後面的全部區塊\n"
 "d - 不要不暫存此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
 msgstr "將模式更動套用到索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
 msgstr "將刪除動作套用至索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to index [y,n,q,a,d%s,?]? "
 msgstr "將加入動作套用至索引 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
 msgstr "將此區塊套用到索引 [y,n,q,a,d%s,?]? "
 
@@ -396,23 +397,23 @@
 "a - 套用此區塊和本檔案中後面的全部區塊\n"
 "d - 不要套用此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
 msgstr "從工作區捨棄模式更動 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
 msgstr "從工作區捨棄刪除動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard addition from worktree [y,n,q,a,d%s,?]? "
 msgstr "從工作區捨棄加入動作 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
 msgstr "從工作區捨棄此區塊 [y,n,q,a,d%s,?]? "
 
@@ -436,23 +437,23 @@
 "a - 捨棄此區塊和本檔案中後面的全部區塊\n"
 "d - 不要捨棄此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
@@ -470,23 +471,23 @@
 "a - 捨棄此區塊和本檔案中後面的全部區塊\n"
 "d - 不要捨棄此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: 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,?]? "
 
@@ -504,23 +505,23 @@
 "a - 套用此區塊和本檔案中後面的全部區塊\n"
 "d - 不要套用此區塊和本檔案中後面的全部區塊\n"
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? "
 msgstr "將模式更動套用到工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply deletion to worktree [y,n,q,a,d%s,?]? "
 msgstr "將刪除動作套用到工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply addition to worktree [y,n,q,a,d%s,?]? "
 msgstr "將加入動作套用到工作區 [y,n,q,a,d%s,?]? "
 
-#: add-patch.c git-add--interactive.perl
-#, c-format, perl-format
+#: add-patch.c
+#, c-format
 msgid "Apply this hunk to worktree [y,n,q,a,d%s,?]? "
 msgstr "將此區塊套用到工作區 [y,n,q,a,d%s,?]? "
 
@@ -590,7 +591,7 @@
 "\t結尾不是:\n"
 "%.*s"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
 msgstr "手動區塊編輯模式——檢視底部的快速指引。\n"
 
@@ -607,9 +608,7 @@
 "要刪除 “%c” 開頭的列,請直接刪除。\n"
 "開頭是 %c 的列將會被移除。\n"
 
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "If it does not apply cleanly, you will be given an opportunity to\n"
 "edit again.  If all lines of the hunk are removed, then the edit is\n"
@@ -627,21 +626,13 @@
 msgid "'git apply --cached' failed"
 msgstr "“git apply --cached” 失敗"
 
-#. #-#-#-#-#  add-patch.c.po  #-#-#-#-#
 #. TRANSLATORS: do not translate [y/n]
 #. The program will only accept that input at this point.
 #. Consider translating (saying "no" discards!) as
 #. (saying "n" for "no" discards!) if the translation
 #. of the word "no" does not start with n.
 #.
-#. #-#-#-#-#  git-add--interactive.perl.po  #-#-#-#-#
-#. TRANSLATORS: do not translate [y/n]
-#. The program will only accept that input
-#. at this point.
-#. Consider translating (saying "no" discards!) as
-#. (saying "n" for "no" discards!) if the translation
-#. of the word "no" does not start with n.
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid ""
 "Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
 msgstr "未套用您編輯的區塊。是否重新編輯(輸入 “no” 捨棄!) [y/n]? "
@@ -650,11 +641,11 @@
 msgid "The selected hunks do not apply to the index!"
 msgstr "選取的區塊無法套用至索引!"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Apply them to the worktree anyway? "
 msgstr "無論如何都要套用到工作區嗎? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "Nothing was applied.\n"
 msgstr "未套用。\n"
 
@@ -692,11 +683,11 @@
 msgid "No other hunks to goto"
 msgstr "沒有其它可以跳轉的區塊"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk (<ret> to see more)? "
 msgstr "要跳轉到哪個區塊(<ret> 檢視更多)? "
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "go to which hunk? "
 msgstr "跳轉到哪個區塊? "
 
@@ -715,7 +706,7 @@
 msgid "No other hunks to search"
 msgstr "沒有其它可以尋找的區塊"
 
-#: add-patch.c git-add--interactive.perl
+#: add-patch.c
 msgid "search for regex? "
 msgstr "使用常規表示式搜尋? "
 
@@ -780,9 +771,8 @@
 msgstr "無法還原提交,有未合併的檔案。"
 
 #: advice.c
-#, c-format
-msgid "It is not possible to %s because you have unmerged files."
-msgstr "無法 %s,有未合併的檔案。"
+msgid "Rebasing is not possible because you have unmerged files."
+msgstr "無法重定基底,有未合併的檔案。"
 
 #: advice.c
 msgid ""
@@ -809,6 +799,24 @@
 msgstr "存在未完成的合併,離開。"
 
 #: advice.c
+msgid ""
+"Diverging branches can't be fast-forwarded, you need to either:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"or:\n"
+"\n"
+"\tgit rebase\n"
+msgstr ""
+"岔開的分支不能快轉,您得執行:\n"
+"\n"
+"\tgit merge --no-ff\n"
+"\n"
+"或者:\n"
+"\n"
+"\tgit rebase\n"
+
+#: advice.c
 msgid "Not possible to fast-forward, aborting."
 msgstr "無法快轉,中止。"
 
@@ -902,7 +910,7 @@
 msgstr "未閉合的引號"
 
 #: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
+#: builtin/receive-pack.c builtin/tag.c t/helper/test-pkt-line.c
 msgid "too many arguments"
 msgstr "引數過多"
 
@@ -916,14 +924,16 @@
 msgid "unrecognized whitespace ignore option '%s'"
 msgstr "空白字元忽略選項 “%s” 無法識別"
 
-#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout.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.c builtin/pack-objects.c builtin/push.c
-#: builtin/rebase.c builtin/repack.c builtin/reset.c builtin/rev-list.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/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/pack-objects.c builtin/rebase.c
+#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.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 "無法同時使用 “%s” 和 “%s” 選項"
@@ -934,6 +944,14 @@
 msgstr "“%s” 在版本庫之外"
 
 #: apply.c
+msgid "failed to read patch"
+msgstr "無法讀取修補檔"
+
+#: apply.c
+msgid "patch too large"
+msgstr "修補檔過大"
+
+#: apply.c
 #, c-format
 msgid "Cannot prepare timestamp regexp %s"
 msgstr "無法準備時間戳常規表示式 %s"
@@ -1223,12 +1241,12 @@
 #: apply.c
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
-msgstr "%2$s 的新模式 (%1$o) 和舊模式 (%3$o) 不符"
+msgstr "%2$s 的新模式(%1$o)和舊模式(%3$o)不符"
 
 #: apply.c
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
-msgstr "%2$s 的新模式 (%1$o) 和 %4$s 的舊模式 (%3$o) 不符"
+msgstr "%2$s 的新模式(%1$o)和 %4$s 的舊模式(%3$o)不符"
 
 #: apply.c
 #, c-format
@@ -1295,7 +1313,7 @@
 msgid "unable to add cache entry for %s"
 msgstr "無法為 %s 加入快取項目"
 
-#: apply.c builtin/bisect--helper.c builtin/gc.c
+#: apply.c builtin/bisect.c builtin/gc.c
 #, c-format
 msgid "failed to write to '%s'"
 msgstr "無法寫入 “%s”"
@@ -1335,6 +1353,11 @@
 msgid "cannot open %s"
 msgstr "無法開啟 %s"
 
+#: apply.c rerere.c
+#, c-format
+msgid "cannot unlink '%s'"
+msgstr "無法刪除 “%s”"
+
 #: apply.c
 #, c-format
 msgid "Hunk #%d applied cleanly."
@@ -1381,7 +1404,7 @@
 msgid_plural "%d lines applied after fixing whitespace errors."
 msgstr[0] "修正空白誤用後,套用了 %d 列。"
 
-#: apply.c builtin/add.c builtin/mv.c builtin/rm.c
+#: apply.c builtin/mv.c builtin/rm.c
 msgid "Unable to write new index file"
 msgstr "無法寫入新索引檔案"
 
@@ -1569,6 +1592,11 @@
 msgid "cannot read '%s'"
 msgstr "無法讀取 “%s”"
 
+#: archive.c
+#, c-format
+msgid "pathspec '%s' matches files outside the current directory"
+msgstr "符合路徑規格 “%s” 的檔案在目前目錄之外"
+
 #: archive.c builtin/add.c builtin/rm.c
 #, c-format
 msgid "pathspec '%s' did not match any files"
@@ -1590,10 +1618,6 @@
 msgstr "非樹狀物件:%s"
 
 #: archive.c
-msgid "current working directory is untracked"
-msgstr "尚未追蹤目前的工作目錄"
-
-#: archive.c
 #, c-format
 msgid "File not found: %s"
 msgstr "找不到檔案:%s"
@@ -1626,7 +1650,7 @@
 msgid "archive format"
 msgstr "封存格式"
 
-#: archive.c builtin/log.c
+#: archive.c builtin/log.c parse-options.h
 msgid "prefix"
 msgstr "前綴"
 
@@ -1660,6 +1684,15 @@
 msgid "report archived files on stderr"
 msgstr "在 stderr 上回報封存的檔案"
 
+#: archive.c builtin/clone.c builtin/fetch.c builtin/pack-objects.c
+#: builtin/pull.c
+msgid "time"
+msgstr "time"
+
+#: archive.c
+msgid "set modification time of archive entries"
+msgstr "設定封存項目的修改時間"
+
 #: archive.c
 msgid "set compression level"
 msgstr "設定壓縮級別"
@@ -1702,6 +1735,11 @@
 
 #: archive.c
 #, c-format
+msgid "extra command line parameter '%s'"
+msgstr "多出命令列參數 “%s”"
+
+#: archive.c
+#, c-format
 msgid "Unknown archive format '%s'"
 msgstr "封存格式 “%s” 未知"
 
@@ -1716,6 +1754,15 @@
 msgstr "%.*s 不是有效的屬性名稱"
 
 #: attr.c
+msgid "unable to add additional attribute"
+msgstr "無法加入其他屬性"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly long attributes line %d"
+msgstr "忽略過長的屬性列 (第 %d 列)"
+
+#: attr.c
 #, c-format
 msgid "%s not allowed: %s:%d"
 msgstr "不允許 %s:%s:%d"
@@ -1728,6 +1775,36 @@
 "git attributes 會忽略反向模式\n"
 "當字串確定要以驚嘆號開始時,請使用 “\\!”。"
 
+#: attr.c
+#, c-format
+msgid "cannot fstat gitattributes file '%s'"
+msgstr "無法 fstat gitattributes 檔案 “%s”"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes file '%s'"
+msgstr "忽略過大的 gitattributes 檔案 “%s”"
+
+#: attr.c
+#, c-format
+msgid "ignoring overly large gitattributes blob '%s'"
+msgstr "忽略過大的 gitattributes 資料物件 “%s”"
+
+#: attr.c
+msgid "bad --attr-source or GIT_ATTR_SOURCE"
+msgstr "無效的 --attr-source 或 GIT_ATTR_SOURCE"
+
+#: attr.c read-cache.c
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "無法對 %s 執行 stat"
+
+#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
+#: builtin/pack-objects.c combine-diff.c rerere.c
+#, c-format
+msgid "unable to read %s"
+msgstr "不能讀 %s"
+
 #: bisect.c
 #, c-format
 msgid "Badly quoted content in file '%s': %s"
@@ -1850,16 +1927,12 @@
 msgstr "--contents 和 --reverse 不能混用。"
 
 #: blame.c
-msgid "cannot use --contents with final commit object name"
-msgstr "無法將 --contents 與最終的提交物件名稱共用"
-
-#: blame.c
 msgid "--reverse and --first-parent together require specified latest commit"
 msgstr "--reverse 和 --first-parent 共用,需要指定最新的提交"
 
 #: blame.c builtin/commit.c builtin/log.c builtin/merge.c
-#: builtin/pack-objects.c builtin/shortlog.c bundle.c midx.c pack-bitmap.c
-#: ref-filter.c remote.c sequencer.c submodule.c
+#: builtin/pack-objects.c builtin/shortlog.c midx.c pack-bitmap.c remote.c
+#: sequencer.c submodule.c
 msgid "revision walk setup failed"
 msgstr "修訂版遍歷設定失敗"
 
@@ -1934,12 +2007,10 @@
 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
@@ -1985,8 +2056,8 @@
 
 #: branch.c
 #, c-format
-msgid "cannot force update the branch '%s' checked out at '%s'"
-msgstr "無法強制更新在 “%2$s” 簽出的 “%1$s” 分支"
+msgid "cannot force update the branch '%s' used by worktree at '%s'"
+msgstr "無法強制更新被位於 “%2$s” 的工作區使用的 “%1$s” 分支"
 
 #: branch.c
 #, c-format
@@ -2040,11 +2111,11 @@
 #: branch.c
 #, c-format
 msgid ""
-"You may try updating the submodules using 'git checkout %s && git submodule "
-"update --init'"
+"You may try updating the submodules using 'git checkout --no-recurse-"
+"submodules %s && git submodule update --init'"
 msgstr ""
-"您可以嘗試使用 “git checkout %s && git submodule update --init” 命令,更新子"
-"模組"
+"您可以使用 “git checkout --no-recurse-submodules %s && git submodule update "
+"--init” 命令嘗試更新子模組"
 
 #: branch.c
 #, c-format
@@ -2053,13 +2124,8 @@
 
 #: branch.c
 #, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "“%s” 已在 “%s” 點簽出"
-
-#: branch.c
-#, c-format
-msgid "HEAD of working tree %s is not updated"
-msgstr "%s 工作區的 HEAD 指針未被更新"
+msgid "'%s' is already used by worktree at '%s'"
+msgstr "“%s” 已被位於 “%s” 的工作區使用"
 
 #: builtin/add.c
 msgid "git add [<options>] [--] <pathspec>..."
@@ -2071,47 +2137,37 @@
 msgstr "無法 chmod %cx '%s'"
 
 #: builtin/add.c
-#, c-format
-msgid "unexpected diff status %c"
-msgstr "非預期的 diff 狀態 %c"
-
-#: builtin/add.c builtin/commit.c
-msgid "updating files failed"
-msgstr "更新檔案失敗"
-
-#: builtin/add.c
-#, c-format
-msgid "remove '%s'\n"
-msgstr "移除 “%s”\n"
-
-#: builtin/add.c
 msgid "Unstaged changes after refreshing the index:"
 msgstr "重新整理索引之後,尚未被暫存的更動:"
 
-#: builtin/add.c builtin/rev-parse.c
-msgid "Could not read the index"
-msgstr "無法讀取索引"
+#: builtin/add.c
+msgid ""
+"the add.interactive.useBuiltin setting has been removed!\n"
+"See its entry in 'git help config' for details."
+msgstr ""
+"add.interactive.useBuiltin 設定已被移除!\n"
+"深入了解請參閱 “git help config” 中的對應條目。"
 
 #: builtin/add.c
-msgid "Could not write patch"
-msgstr "無法寫入修補檔"
+msgid "could not read the index"
+msgstr "無法讀取索引"
 
 #: builtin/add.c
 msgid "editing patch failed"
 msgstr "編輯修補檔失敗"
 
-#: builtin/add.c
+#: builtin/add.c read-cache.c
 #, c-format
-msgid "Could not stat '%s'"
-msgstr "不能對 “%s” 執行 stat"
+msgid "could not stat '%s'"
+msgstr "不能對 '%s' 呼叫 stat"
 
 #: builtin/add.c
-msgid "Empty patch. Aborted."
-msgstr "修補檔空白。中止。"
+msgid "empty patch. aborted"
+msgstr "修補檔空白。中止"
 
 #: builtin/add.c
 #, c-format
-msgid "Could not apply '%s'"
+msgid "could not apply '%s'"
 msgstr "無法套用 “%s”"
 
 #: builtin/add.c
@@ -2273,14 +2329,19 @@
 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
+msgid "unable to write new index file"
+msgstr "無法寫入新的索引檔案"
+
 #: builtin/am.c builtin/mailinfo.c mailinfo.c
 #, c-format
 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 diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
-#: sequencer.c setup.c
+#: builtin/pull.c config.c diff-merges.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c
 #, c-format
 msgid "invalid value for '%s': '%s'"
 msgstr "“%s” 的值無效:“%s”"
@@ -2323,7 +2384,7 @@
 msgid "could not open '%s' for reading"
 msgstr "無法開啟 “%s” 進行讀取"
 
-#: builtin/am.c builtin/rebase.c sequencer.c strbuf.c wrapper.c
+#: builtin/am.c builtin/rebase.c editor.c sequencer.c wrapper.c
 #, c-format
 msgid "could not open '%s' for writing"
 msgstr "無法開啟 “%s” 進行寫入"
@@ -2433,8 +2494,7 @@
 msgid "applying to an empty history"
 msgstr "正在套用至空白歷史記錄上"
 
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
-#: t/helper/test-fast-rebase.c
+#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
 msgid "failed to write commit object"
 msgstr "無法寫入提交物件"
 
@@ -2523,11 +2583,6 @@
 "您應該對已經解決衝突的每一個檔案執行 `git add`,標記為已經完成。\n"
 "你可以對「由他們刪除」的檔案,執行 `git rm` 指令。"
 
-#: builtin/am.c builtin/checkout.c builtin/clone.c builtin/stash.c merge.c
-#: rerere.c
-msgid "unable to write new index file"
-msgstr "無法寫入新的索引檔案"
-
 #: builtin/am.c builtin/reset.c
 #, c-format
 msgid "Could not parse object '%s'."
@@ -2545,17 +2600,12 @@
 "您似乎在上一次 “am” 失敗後移動了 HEAD。\n"
 "未倒轉回 ORIG_HEAD"
 
-#: builtin/am.c builtin/bisect--helper.c worktree.c
+#: builtin/am.c builtin/bisect.c worktree.c
 #, c-format
 msgid "failed to read '%s'"
 msgstr "無法讀取 “%s”"
 
 #: builtin/am.c
-#, c-format
-msgid "options '%s=%s' and '%s=%s' cannot be used together"
-msgstr "“%s=%s” 和 “%s=%s” 選項不得同時使用"
-
-#: builtin/am.c
 msgid "git am [<options>] [(<mbox> | <Maildir>)...]"
 msgstr "git am [<options>] [(<mbox> | <Maildir>)...]"
 
@@ -2568,6 +2618,10 @@
 msgstr "互動式執行"
 
 #: builtin/am.c
+msgid "bypass pre-applypatch and applypatch-msg hooks"
+msgstr "繞過 pre-applypatch 和 applypatch-msg 掛鉤"
+
+#: builtin/am.c
 msgid "historical option -- no-op"
 msgstr "歷史遺留選項——無作用"
 
@@ -2586,7 +2640,7 @@
 
 #: builtin/am.c
 msgid "recode into utf8 (default)"
-msgstr "使用 utf8 字元集(預設)"
+msgstr "使用 utf8 字元集(預設值)"
 
 #: builtin/am.c
 msgid "pass -k flag to git-mailinfo"
@@ -2605,10 +2659,6 @@
 msgstr "若為 mbox 格式,向 git-mailsplit 傳入 --keep-cr 標記"
 
 #: builtin/am.c
-msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
-msgstr "不向 git-mailsplit 傳入 --keep-cr 標記,無視 am.keepcr 的設定"
-
-#: builtin/am.c
 msgid "strip everything before a scissors line"
 msgstr "截掉裁切線前的所有內容"
 
@@ -2627,8 +2677,9 @@
 msgstr "n"
 
 #: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
-#: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.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/replace.c builtin/tag.c
+#: builtin/verify-tag.c
 msgid "format"
 msgstr "format"
 
@@ -2756,111 +2807,105 @@
 msgid "git archive: expected a flush"
 msgstr "git archive:預期收到 flush 封包"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-reset [<commit>]"
-msgstr "git bisect--helper --bisect-reset [<commit>]"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 msgstr ""
-"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}"
-"=<term>] [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] "
-"[<paths>...]"
+"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]    [--no-"
+"checkout] [--first-parent] [<bad> [<good>...]] [--]    [<pathspec>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (bad|new) [<rev>]"
-msgstr "git bisect--helper --bisect-state (bad|new) [<rev>]"
+#: builtin/bisect.c
+msgid "git bisect (good|bad) [<rev>...]"
+msgstr "git bisect (good|bad) [<rev>...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-state (good|old) [<rev>...]"
-msgstr "git bisect--helper --bisect-state (good|old) [<rev>...]"
+#: builtin/bisect.c
+msgid "git bisect skip [(<rev>|<range>)...]"
+msgstr "git bisect skip [(<rev>|<range>)...]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-replay <filename>"
-msgstr "git bisect--helper --bisect-replay <filename>"
+#: builtin/bisect.c
+msgid "git bisect reset [<commit>]"
+msgstr "git bisect reset [<commit>]"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
-msgstr "git bisect--helper --bisect-skip [(<rev>|<range>)...]"
+#: builtin/bisect.c
+msgid "git bisect replay <logfile>"
+msgstr "git bisect replay <logfile>"
 
-#: builtin/bisect--helper.c
-msgid "git bisect--helper --bisect-run <cmd>..."
-msgstr "git bisect--helper --bisect-run <cmd>..."
+#: builtin/bisect.c
+msgid "git bisect run <cmd> [<arg>...]"
+msgstr "git bisect run <cmd> [<arg>...]"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' in mode '%s'"
 msgstr "無法以 “%2$s” 模式開啟 “%1$s” 檔案"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not write to file '%s'"
 msgstr "無法寫入 “%s” 檔案"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for reading"
 msgstr "無法開啟 “%s” 檔案進行讀取"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid term"
 msgstr "“%s” 不是有效術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't use the builtin command '%s' as a term"
 msgstr "不能將內建命令 “%s” 當作術語使用"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "can't change the meaning of the term '%s'"
 msgstr "不能變更術語 “%s” 的含義"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "please use two different terms"
 msgstr "請使用兩個不同的術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "We are not bisecting.\n"
 msgstr "我們沒有在二分搜尋。\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' is not a valid commit"
 msgstr "“%s” 不是有效的提交"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
 msgstr "不能簽出原始 HEAD “%s”。請嘗試 “git bisect reset <commit>”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad bisect_write argument: %s"
 msgstr "bisect_write 引數無效:%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't get the oid of the rev '%s'"
 msgstr "無法取得修訂版 “%s” 的物件 ID"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "couldn't open the file '%s'"
 msgstr "無法開啟檔案 “%s”"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Invalid command: you're currently in a %s/%s bisect"
 msgstr "命令無效:您目前正處於二分搜尋 %s/%s 的狀態"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to give me at least one %s and %s revision.\n"
@@ -2869,7 +2914,7 @@
 "需指定至少一個 %s 和一個 %s 修訂版。\n"
 "為此您可以用 “git bisect %s” 和 “git bisect %s”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "You need to start by \"git bisect start\".\n"
@@ -2880,7 +2925,7 @@
 "接著提供至少一個 %s 和一個 %s 修訂版。\n"
 "為此您可以用 “git bisect %s” 和 “git bisect %s”。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bisecting only with a %s commit"
 msgstr "在只有一個 %s 提交的情況下二分搜尋"
@@ -2890,29 +2935,29 @@
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Are you sure [Y/n]? "
 msgstr "是否確定 [Y/n]? "
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for both good and bad commits\n"
 msgstr "狀態:正在等待好和壞的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "status: waiting for bad commit, %d good commit known\n"
 msgid_plural "status: waiting for bad commit, %d good commits known\n"
 msgstr[0] "狀態:正在等待壞的提交,已知有 %d 個好的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "status: waiting for good commit(s), bad commit known\n"
 msgstr "狀態:正在等待好的提交,已知有壞的提交\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "no terms defined"
 msgstr "未定義術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "Your current terms are %s for the old state\n"
@@ -2921,7 +2966,7 @@
 "您目前針對舊狀態的術語是 %s;\n"
 "對新狀態的術語是 %s。\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid ""
 "invalid argument %s for 'git bisect terms'.\n"
@@ -2930,52 +2975,48 @@
 "傳入 “git bisect terms” 的 %s 引數無效。\n"
 "支援的選項有:--term-good|--term-old 和 --term-bad|--term-new。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "revision walk setup failed\n"
 msgstr "修訂版遍歷設定失敗\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "could not open '%s' for appending"
 msgstr "無法開啟 “%s” 進行附加"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "'' is not a valid term"
 msgstr "“ ” 不是有效術語"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "unrecognized option: '%s'"
 msgstr "無法識別選項:“%s”"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s' does not appear to be a valid revision"
 msgstr "“%s” 似乎不是有效修訂版"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - I need a HEAD"
 msgstr "HEAD 無效 — 需要一個 HEAD"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "checking out '%s' failed. Try 'git bisect start <valid-branch>'."
 msgstr "簽出 “%s” 失敗。請嘗試 “git bisect start <valid-branch>”。"
 
-#: builtin/bisect--helper.c
-msgid "won't bisect on cg-seek'ed tree"
-msgstr "不會在做了 cg-seek 的樹狀物件上進行二分搜尋"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bad HEAD - strange symbolic ref"
 msgstr "HEAD 無效 — 異常符號引用"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "invalid ref: '%s'"
 msgstr "引用無效:“%s”"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "You need to start by \"git bisect start\"\n"
 msgstr "要開始,請執行 “git bisect start”\n"
 
@@ -2984,151 +3025,159 @@
 #. translation. The program will only accept English input
 #. at this point.
 #.
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Do you want me to do it for you [Y/n]? "
 msgstr "是否要這麼做 [Y/n]? "
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "Please call `--bisect-state` with at least one argument"
 msgstr "要呼叫 `--bisect-state`,請傳入一個以上的引數"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'git bisect %s' can take only one argument."
 msgstr "“git bisect %s” 只取一個引數。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input: %s"
 msgstr "rev 輸入格式錯誤:%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "Bad rev input (not a commit): %s"
 msgstr "rev 輸入有誤(不是提交):%s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "We are not bisecting."
 msgstr "我們沒有在二分搜尋。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "'%s'?? what are you talking about?"
 msgstr "「%s」??您在說什麼?"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot read file '%s' for replaying"
 msgstr "無法讀取「%s」檔案進行重放"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "running %s\n"
 msgstr "正在執行 %s\n"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run failed: no command provided."
 msgstr "二分搜尋執行失敗:沒有提供命令。"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "unable to verify '%s' on good revision"
-msgstr "無法在好的修訂版上驗證 “%s”"
+msgid "unable to verify %s on good revision"
+msgstr "無法在好的修訂版上驗證 %s"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "bogus exit code %d for good revision"
 msgstr "好的修訂版回傳偽造的錯誤碼 %d"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid "bisect run failed: exit code %d from '%s' is < 0 or >= 128"
-msgstr "二分搜尋執行失敗:“%2$s” 回傳的離開碼 %1$d 小於 0 或 >= 128"
+msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
+msgstr "二分搜尋執行失敗:%2$s 回傳的結束代碼 %1$d 小於 0 或大於 128"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
 msgid "cannot open file '%s' for writing"
 msgstr "無法開啟 “%s” 檔案進行寫入"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "bisect run cannot continue any more"
 msgstr "二分搜尋不能繼續執行"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect run success"
 msgstr "二分搜尋執行成功"
 
-#: builtin/bisect--helper.c
-#, c-format
+#: builtin/bisect.c
 msgid "bisect found first bad commit"
 msgstr "二分搜尋發現到第一個有問題的提交"
 
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 #, c-format
-msgid ""
-"bisect run failed: 'git bisect--helper --bisect-state %s' exited with error "
-"code %d"
-msgstr ""
-"二分搜尋執行失敗:“git bisect--helper --bisect-state %s” 以錯誤碼 %d 離開"
+msgid "bisect run failed: 'git bisect %s' exited with error code %d"
+msgstr "二分搜尋執行失敗:“git bisect %s” 以錯誤碼 %d 離開"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-reset requires either no argument or a commit"
-msgstr "--bisect-reset 可以不需要引數,或者得傳入一個提交"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires either no argument or a commit"
+msgstr "“%s” 不需要引數,或者得傳入一個提交"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-terms requires 0 or 1 argument"
-msgstr "--bisect-terms 需要 0 或 1 個引數"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 or 1 argument"
+msgstr "“%s” 需要 0 或 1 個引數"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-next requires 0 arguments"
-msgstr "--bisect-next 需要 0 個引數"
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' requires 0 arguments"
+msgstr "“%s” 不需引數"
 
-#: builtin/bisect--helper.c
-msgid "--bisect-log requires 0 arguments"
-msgstr "--bisect-log 需要 0 個引數"
-
-#: builtin/bisect--helper.c
+#: builtin/bisect.c
 msgid "no logfile given"
 msgstr "未提供日誌檔案"
 
+#: builtin/bisect.c
+#, c-format
+msgid "'%s' failed: no command provided."
+msgstr "“%s” 失敗:沒有提供命令。"
+
+#: builtin/bisect.c
+msgid "need a command"
+msgstr "需要提供命令"
+
+#: builtin/bisect.c builtin/cat-file.c
+#, c-format
+msgid "unknown command: '%s'"
+msgstr "未知命令:“%s”"
+
 #: builtin/blame.c
 msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
-msgstr "git blame [<選項>] [<版本選項>] [<版本>] [--] <檔案>"
+msgstr "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
 
 #: builtin/blame.c
 msgid "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
-msgstr "git annotate [<選項>] [<版本選項>] [<版本>] [--] <檔案>"
+msgstr "git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"
 
 #: builtin/blame.c
 msgid "<rev-opts> are documented in git-rev-list(1)"
-msgstr "<版本選項> 的檔案記錄在 git-rev-list(1) 中"
+msgstr "<rev-opts> 的文件在 git-rev-list(1)"
 
 #: builtin/blame.c
 #, c-format
 msgid "expecting a color: %s"
-msgstr "期望一個顏色:%s"
+msgstr "預期是個顏色:%s"
 
 #: builtin/blame.c
 msgid "must end with a color"
-msgstr "必須以一個顏色結尾"
+msgstr "結尾必須是一個顏色"
 
 #: builtin/blame.c
 #, c-format
 msgid "cannot find revision %s to ignore"
-msgstr "不能找到要忽略的版本 %s"
+msgstr "不能找到要忽略的 %s 修訂版"
 
 #: builtin/blame.c
 msgid "show blame entries as we find them, incrementally"
-msgstr "增量式顯示發現的 blame 條目"
+msgstr "漸進式顯示發現的溯源項目"
 
 #: builtin/blame.c
 msgid "do not show object names of boundary commits (Default: off)"
-msgstr "不要顯示邊界提交的物件名稱(預設值: off)"
+msgstr "不要顯示邊界提交的物件名稱(預設值:off)"
 
 #: builtin/blame.c
 msgid "do not treat root commits as boundaries (Default: off)"
-msgstr "不將根提交看作邊界(預設值:off)"
+msgstr "不把根提交當作邊界(預設值:off)"
 
 #: builtin/blame.c
 msgid "show work cost statistics"
@@ -3142,7 +3191,7 @@
 
 #: builtin/blame.c
 msgid "show output score for blame entries"
-msgstr "顯示判斷 blame 條目位移的得分診斷訊息"
+msgstr "顯示溯源項目的輸出得分"
 
 #: builtin/blame.c
 msgid "show original filename (Default: auto)"
@@ -3150,7 +3199,7 @@
 
 #: builtin/blame.c
 msgid "show original linenumber (Default: off)"
-msgstr "顯示原始檔案名稱(預設值:off)"
+msgstr "顯示原始列碼(預設值:off)"
 
 #: builtin/blame.c
 msgid "show in a format designed for machine consumption"
@@ -3158,7 +3207,7 @@
 
 #: builtin/blame.c
 msgid "show porcelain format with per-line commit information"
-msgstr "顯示每一列適合機器的提交說明"
+msgstr "顯示包含每一列提交資訊,適合機器讀取的格式"
 
 #: builtin/blame.c
 msgid "use the same output mode as git-annotate (Default: off)"
@@ -3186,15 +3235,15 @@
 
 #: builtin/blame.c builtin/log.c
 msgid "rev"
-msgstr "版本"
+msgstr "rev"
 
 #: builtin/blame.c
 msgid "ignore <rev> when blaming"
-msgstr "在執行 blame 動作時忽略 <修訂版>"
+msgstr "在執行溯源動作時忽略 <rev>"
 
 #: builtin/blame.c
 msgid "ignore revisions from <file>"
-msgstr "忽略 <檔案> 中的修訂版"
+msgstr "忽略 <file> 中的修訂版"
 
 #: builtin/blame.c
 msgid "color redundant metadata from previous line differently"
@@ -3202,31 +3251,31 @@
 
 #: builtin/blame.c
 msgid "color lines by age"
-msgstr "依據時間著色"
+msgstr "根據時間著色"
 
 #: builtin/blame.c
 msgid "spend extra cycles to find better match"
-msgstr "循環更多次以找到更佳符合"
+msgstr "循環更多次來找到更佳符合項目"
 
 #: builtin/blame.c
 msgid "use revisions from <file> instead of calling git-rev-list"
-msgstr "使用來自 <檔案> 的修訂集而不是呼叫 git-rev-list"
+msgstr "使用來自 <file> 的修訂集而不是呼叫 git-rev-list"
 
 #: builtin/blame.c
 msgid "use <file>'s contents as the final image"
-msgstr "將 <檔案> 的內容當成是最終 image"
+msgstr "將 <file> 的內容當成是最終印象"
 
 #: builtin/blame.c
 msgid "score"
-msgstr "得分"
+msgstr "score"
 
 #: builtin/blame.c
 msgid "find line copies within and across files"
-msgstr "找到檔案內及跨檔案的複製列"
+msgstr "找到檔案內及跨檔案的列拷貝動作"
 
 #: builtin/blame.c
 msgid "find line movements within and across files"
-msgstr "找到檔案內及跨檔案的移動列"
+msgstr "找到檔案內及跨檔案的列移動動作"
 
 #: builtin/blame.c
 msgid "range"
@@ -3256,83 +3305,86 @@
 #, c-format
 msgid "file %s has only %lu line"
 msgid_plural "file %s has only %lu lines"
-msgstr[0] "檔案 %s 只有 %lu 行"
+msgstr[0] "檔案 %s 只有 %lu 列"
 
 #: builtin/blame.c
 msgid "Blaming lines"
-msgstr "追蹤程式碼行"
+msgstr "溯源文字列"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
-msgstr "git branch [<選項>] [-r | -a] [--merged] [--no-merged]"
+msgstr "git branch [<options>] [-r | -a] [--merged] [--no-merged]"
 
 #: builtin/branch.c
 msgid ""
 "git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
 "point>]"
-msgstr "git branch [<選項>] [-f] [--recurse-submodules] <分支名> [<起始點>]"
+msgstr ""
+"git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-"
+"point>]"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-l] [<pattern>...]"
-msgstr "git branch [<選項>] [-l] [<模式>...]"
+msgstr "git branch [<options>] [-l] [<pattern>...]"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r] (-d | -D) <branch-name>..."
-msgstr "git branch [<選項>] [-r] (-d | -D) <分支名>..."
+msgstr "git branch [<options>] [-r] (-d | -D) <branch-name>..."
 
 #: builtin/branch.c
 msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
-msgstr "git branch [<選項>] (-m | -M) [<舊分支>] <新分支>"
+msgstr "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"
 
 #: builtin/branch.c
 msgid "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
-msgstr "git branch [<選項>] (-c | -C) [<老分支>] <新分支>"
+msgstr "git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r | -a] [--points-at]"
-msgstr "git branch [<選項>] [-r | -a] [--points-at]"
+msgstr "git branch [<options>] [-r | -a] [--points-at]"
 
 #: builtin/branch.c
 msgid "git branch [<options>] [-r | -a] [--format]"
-msgstr "git branch [<選項>] [-r | -a] [--format]"
+msgstr "git branch [<options>] [-r | -a] [--format]"
 
 #  譯者:保持原換行格式,在輸出時 %s 的替代內容會讓字串變長
 #: builtin/branch.c
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+"         '%s', but not yet merged to HEAD"
 msgstr ""
-"將要刪除的分支 '%s' 已經被合併到\n"
-"         '%s',但未合併到 HEAD。"
+"將要刪除的 “%s” 分支已經被合併到\n"
+"         “%s”,但尚未合併到 HEAD"
 
 #  譯者:保持原換行格式,在輸出時 %s 的替代內容會讓字串變長
 #: builtin/branch.c
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+"         '%s', even though it is merged to HEAD"
 msgstr ""
-"並未刪除分支 '%s', 雖然它已經合併到 HEAD,\n"
-"         然而卻尚未被合併到分支 '%s' 。"
+"並未刪除分支 “%s”, 雖然已經合併到 HEAD,\n"
+"         卻尚未被合併至 “%s” 分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
-msgstr "無法查詢 '%s' 指向的提交物件"
+msgid "couldn't look up commit object for '%s'"
+msgstr "無法查詢 “%s” 指向的提交物件"
 
 #: builtin/branch.c
 #, c-format
-msgid ""
-"The branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr ""
-"分支 '%s' 沒有完全合併。\n"
-"如果您確認要刪除它,執行 'git branch -D %s'。"
+msgid "the branch '%s' is not fully merged"
+msgstr "分支 “%s” 沒有完全合併"
 
 #: builtin/branch.c
-msgid "Update of config-file failed"
-msgstr "更新設定檔案失敗"
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "如果確定要刪除它,請執行 “git branch -D %s”"
+
+#: builtin/branch.c
+msgid "update of config-file failed"
+msgstr "更新組態檔案失敗"
 
 #: builtin/branch.c
 msgid "cannot use -a with -d"
@@ -3340,18 +3392,27 @@
 
 #: builtin/branch.c
 #, c-format
-msgid "Cannot delete branch '%s' checked out at '%s'"
-msgstr "無法刪除在「%2$s」簽出的「%1$s」分支"
+msgid "cannot delete branch '%s' used by worktree at '%s'"
+msgstr "無法刪除被位於 “%2$s” 的工作區使用的 “%1$s” 分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "remote-tracking branch '%s' not found."
-msgstr "未能找到遠端追蹤分支 '%s'。"
+msgid "remote-tracking branch '%s' not found"
+msgstr "找不到 “%s” 遠端追蹤分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "branch '%s' not found."
-msgstr "分支 '%s' 未發現。"
+msgid ""
+"branch '%s' not found.\n"
+"Did you forget --remote?"
+msgstr ""
+"找不到 “%s” 分支。\n"
+"您可能要加上 --remote?"
+
+#: builtin/branch.c
+#, c-format
+msgid "branch '%s' not found"
+msgstr "找不到 “%s” 分支"
 
 #: builtin/branch.c
 #, c-format
@@ -3365,72 +3426,77 @@
 
 #: builtin/branch.c builtin/tag.c
 msgid "unable to parse format string"
-msgstr "不能解析格式化字串"
+msgstr "無法解析格式化字串"
 
 #: builtin/branch.c
 msgid "could not resolve HEAD"
-msgstr "不能解析 HEAD 提交"
+msgstr "無法解析 HEAD 指針"
 
 #: builtin/branch.c
 #, c-format
 msgid "HEAD (%s) points outside of refs/heads/"
-msgstr "HEAD (%s) 指向 refs/heads/ 之外"
+msgstr "HEAD 指針 (%s) 指向 refs/heads/ 之外"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being rebased at %s"
-msgstr "分支 %s 正被重定基底到 %s"
+msgid "branch %s is being rebased at %s"
+msgstr "%s 分支正在重定基底至 %s"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch %s is being bisected at %s"
-msgstr "分支 %s 正被二分搜尋於 %s"
+msgid "branch %s is being bisected at %s"
+msgstr "%s 分支正於 %s 進行二分搜尋"
 
 #: builtin/branch.c
 #, c-format
-msgid "Invalid branch name: '%s'"
-msgstr "無效的分支名:'%s'"
+msgid "HEAD of working tree %s is not updated"
+msgstr "%s 工作區的 HEAD 指針未被更新"
 
 #: builtin/branch.c
 #, c-format
-msgid "No commit on branch '%s' yet."
-msgstr "分支 '%s' 尚無提交。"
+msgid "invalid branch name: '%s'"
+msgstr "分支名稱無效:“%s”"
 
 #: builtin/branch.c
 #, c-format
-msgid "No branch named '%s'."
-msgstr "沒有分支 '%s'。"
+msgid "no commit on branch '%s' yet"
+msgstr "分支 “%s” 尚無提交"
 
 #: builtin/branch.c
-msgid "Branch rename failed"
+#, c-format
+msgid "no branch named '%s'"
+msgstr "沒有名為 “%s” 的分支"
+
+#: builtin/branch.c
+msgid "branch rename failed"
 msgstr "分支重新命名失敗"
 
 #: builtin/branch.c
-msgid "Branch copy failed"
-msgstr "分支複製失敗"
+msgid "branch copy failed"
+msgstr "分支拷貝失敗"
 
 #: builtin/branch.c
 #, c-format
-msgid "Created a copy of a misnamed branch '%s'"
-msgstr "已為錯誤命名的分支 '%s' 建立了一個副本"
+msgid "created a copy of a misnamed branch '%s'"
+msgstr "已為誤命名的 “%s” 分支建立拷貝"
 
 #: builtin/branch.c
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
-msgstr "已將錯誤命名的分支 '%s' 重新命名"
+msgid "renamed a misnamed branch '%s' away"
+msgstr "已更改誤命名的 “%s” 分支的名稱"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "分支重新命名為 %s,但 HEAD 沒有更新!"
+msgid "branch renamed to %s, but HEAD is not updated"
+msgstr "分支已重新命名為 %s,但 HEAD 指針尚未更新"
 
 #: builtin/branch.c
-msgid "Branch is renamed, but update of config-file failed"
-msgstr "分支被重新命名,但更新設定檔案失敗"
+msgid "branch is renamed, but update of config-file failed"
+msgstr "分支已重新命名,但無法更新組態檔案"
 
 #: builtin/branch.c
-msgid "Branch is copied, but update of config-file failed"
-msgstr "分支已複製,但更新設定檔案失敗"
+msgid "branch is copied, but update of config-file failed"
+msgstr "分支已拷貝,但無法更新組態檔案"
 
 #: builtin/branch.c
 #, c-format
@@ -3439,41 +3505,41 @@
 "  %s\n"
 "Lines starting with '%c' will be stripped.\n"
 msgstr ""
-"請編輯分支的描述\n"
+"請編輯下述分支的描述\n"
 "  %s\n"
-"以 '%c' 開頭的行將被過濾。\n"
+"開頭是 “%c” 的列將被刪除。\n"
 
 #: builtin/branch.c
 msgid "Generic options"
-msgstr "通用選項"
+msgstr "一般性選項"
 
 #: builtin/branch.c
 msgid "show hash and subject, give twice for upstream branch"
-msgstr "顯示雜湊值和主題,若參數出現兩次則顯示上游分支"
+msgstr "顯示雜湊值和主旨,若傳入兩次則顯示上游分支"
 
 #: builtin/branch.c
 msgid "suppress informational messages"
-msgstr "不顯示訊息"
+msgstr "不顯示資訊訊息"
 
 #: builtin/branch.c builtin/checkout.c builtin/submodule--helper.c
 msgid "set branch tracking configuration"
-msgstr "設定分支追蹤設定"
+msgstr "設定分支追蹤組態"
 
 #: builtin/branch.c
 msgid "do not use"
-msgstr "不要使用"
+msgstr "請勿使用"
 
 #: builtin/branch.c
 msgid "upstream"
-msgstr "上游"
+msgstr "upstream"
 
 #: builtin/branch.c
 msgid "change the upstream info"
-msgstr "改變上游訊息"
+msgstr "變更上游資訊"
 
 #: builtin/branch.c
 msgid "unset the upstream info"
-msgstr "取消上游資訊設定"
+msgstr "取消設定上游資訊"
 
 #: builtin/branch.c
 msgid "use colored output"
@@ -3485,19 +3551,19 @@
 
 #: builtin/branch.c
 msgid "print only branches that contain the commit"
-msgstr "只列印包含該提交的分支"
+msgstr "只輸出包含此提交的分支"
 
 #: builtin/branch.c
 msgid "print only branches that don't contain the commit"
-msgstr "只列印不包含該提交的分支"
+msgstr "只輸出不包含此提交的分支"
 
 #: builtin/branch.c
 msgid "Specific git-branch actions:"
-msgstr "具體的 git-branch 動作:"
+msgstr "特定的 git-branch 動作:"
 
 #: builtin/branch.c
 msgid "list both remote-tracking and local branches"
-msgstr "列出遠端追蹤及本機分支"
+msgstr "列出遠端追蹤和本機分支"
 
 #: builtin/branch.c
 msgid "delete fully merged branch"
@@ -3505,31 +3571,35 @@
 
 #: builtin/branch.c
 msgid "delete branch (even if not merged)"
-msgstr "刪除分支(即使沒有合併)"
+msgstr "刪除分支(即使尚未合併)"
 
 #: builtin/branch.c
 msgid "move/rename a branch and its reflog"
-msgstr "移動/重新命名一個分支,以及它的引用日誌"
+msgstr "移動或重新命名分支及其引用日誌"
 
 #: builtin/branch.c
 msgid "move/rename a branch, even if target exists"
-msgstr "移動/重新命名一個分支,即使目標已存在"
+msgstr "即使目標已存在,仍移動或重新命名分支"
+
+#: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
+msgid "do not output a newline after empty formatted refs"
+msgstr "在格式化引用結果為空之後,不要輸出換列符號"
 
 #: builtin/branch.c
 msgid "copy a branch and its reflog"
-msgstr "複製分支及其引用日誌"
+msgstr "拷貝分支及其引用日誌"
 
 #: builtin/branch.c
 msgid "copy a branch, even if target exists"
-msgstr "複製一個分支,即使目標已存在"
+msgstr "即使目標已存在仍拷貝分支"
 
 #: builtin/branch.c
 msgid "list branch names"
-msgstr "列出分支名"
+msgstr "列出分支名稱"
 
 #: builtin/branch.c
 msgid "show current branch name"
-msgstr "顯示目前分支名"
+msgstr "顯示目前分支名稱"
 
 #: builtin/branch.c builtin/submodule--helper.c
 msgid "create the branch's reflog"
@@ -3537,7 +3607,7 @@
 
 #: builtin/branch.c
 msgid "edit the description for the branch"
-msgstr "標記分支的描述"
+msgstr "編輯分支的描述"
 
 #: builtin/branch.c
 msgid "force creation, move/rename, deletion"
@@ -3545,27 +3615,27 @@
 
 #: builtin/branch.c
 msgid "print only branches that are merged"
-msgstr "只列印已經合併的分支"
+msgstr "只輸出已經合併的分支"
 
 #: builtin/branch.c
 msgid "print only branches that are not merged"
-msgstr "只列印尚未合併的分支"
+msgstr "只輸出尚未合併的分支"
 
 #: builtin/branch.c
 msgid "list branches in columns"
-msgstr "以列的方式顯示分支"
+msgstr "以行的形式列出分支"
 
 #: builtin/branch.c builtin/for-each-ref.c builtin/notes.c builtin/tag.c
 msgid "object"
-msgstr "物件"
+msgstr "object"
 
 #: builtin/branch.c
 msgid "print only branches of the object"
-msgstr "只列印指向該物件的分支"
+msgstr "只輸出指向此物件的分支"
 
 #: builtin/branch.c builtin/for-each-ref.c builtin/tag.c
 msgid "sorting and filtering are case insensitive"
-msgstr "排序和過濾屬於大小寫不敏感"
+msgstr "排序和過濾不區分大小寫"
 
 #: builtin/branch.c builtin/ls-files.c
 msgid "recurse through submodules"
@@ -3576,20 +3646,20 @@
 msgid "format to use for the output"
 msgstr "輸出格式"
 
-#: builtin/branch.c builtin/submodule--helper.c submodule.c
-msgid "Failed to resolve HEAD as a valid ref."
-msgstr "無法將 HEAD 解析為有效引用。"
+#: builtin/branch.c
+msgid "failed to resolve HEAD as a valid ref"
+msgstr "無法將 HEAD 解析為有效引用"
 
 #: builtin/branch.c builtin/clone.c
 msgid "HEAD not found below refs/heads!"
-msgstr "HEAD 沒有位於 /refs/heads 之下!"
+msgstr "HEAD 指針不在 /refs/heads 中!"
 
 #: builtin/branch.c
 msgid ""
 "branch with --recurse-submodules can only be used if submodule."
 "propagateBranches is enabled"
 msgstr ""
-"有 --recurse-submodules 的分支只能在啟用 submodule.propagateBranches 時使用"
+"有 --recurse-submodules 的分支,只能在啟用 submodule.propagateBranches 時使用"
 
 #: builtin/branch.c
 msgid "--recurse-submodules can only be used to create branches"
@@ -3597,79 +3667,78 @@
 
 #: builtin/branch.c
 msgid "branch name required"
-msgstr "必須提供分支名"
+msgstr "必須提供分支名稱"
 
 #: builtin/branch.c
-msgid "Cannot give description to detached HEAD"
-msgstr "不能向分離開頭指標提供描述"
+msgid "cannot give description to detached HEAD"
+msgstr "無法向分離 HEAD 指針提供描述"
 
 #: builtin/branch.c
 msgid "cannot edit description of more than one branch"
-msgstr "不能為一個以上的分支編輯描述"
+msgstr "無法編輯超過一個分支的描述"
 
 #: builtin/branch.c
-msgid "cannot copy the current branch while not on any."
-msgstr "無法複製目前分支因為不處於任何分支上。"
+msgid "cannot copy the current branch while not on any"
+msgstr "不在任何分支上,無法拷貝目前分支"
 
 #: builtin/branch.c
-msgid "cannot rename the current branch while not on any."
-msgstr "無法重新命名目前分支因為不處於任何分支上。"
+msgid "cannot rename the current branch while not on any"
+msgstr "不在任何分支上,無法重新命名目前分支"
 
 #: builtin/branch.c
 msgid "too many branches for a copy operation"
-msgstr "為複製動作提供了太多的分支名"
+msgstr "要進行拷貝動作的分支太多"
 
 #: builtin/branch.c
 msgid "too many arguments for a rename operation"
-msgstr "為重新命名動作提供了太多的參數"
+msgstr "傳入重新命名動作的引數太多"
 
 #: builtin/branch.c
 msgid "too many arguments to set new upstream"
-msgstr "為設定新上游提供了太多的參數"
+msgstr "要設定新上游的引數太多"
 
 #: builtin/branch.c
 #, c-format
 msgid ""
-"could not set upstream of HEAD to %s when it does not point to any branch."
-msgstr "無法設定 HEAD 的上游為 %s,因為 HEAD 沒有指向任何分支。"
+"could not set upstream of HEAD to %s when it does not point to any branch"
+msgstr "無法將 HEAD 的上游設為 %s:其未指向任何分支"
 
 #: builtin/branch.c
 #, c-format
 msgid "no such branch '%s'"
-msgstr "沒有此分支 '%s'"
+msgstr "無 “%s” 分支"
 
 #: builtin/branch.c
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "分支 '%s' 不存在"
+msgstr "沒有 “%s” 分支"
 
 #: builtin/branch.c
 msgid "too many arguments to unset upstream"
-msgstr "為取消上游設定動作提供了太多的參數"
+msgstr "要取消設定上游的引數太多"
 
 #: builtin/branch.c
-msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr "在 HEAD 的上游未指向任何分支時無法取消設定。"
+msgid "could not unset upstream of HEAD when it does not point to any branch"
+msgstr "無法取消設定 HEAD 的上游:其未指向任何分支"
 
 #: builtin/branch.c
 #, c-format
-msgid "Branch '%s' has no upstream information"
-msgstr "分支 '%s' 沒有上游訊息"
+msgid "branch '%s' has no upstream information"
+msgstr "分支 “%s” 沒有上游資訊"
 
 #: builtin/branch.c
 msgid ""
-"The -a, and -r, options to 'git branch' do not take a branch name.\n"
+"the -a, and -r, options to 'git branch' do not take a branch name.\n"
 "Did you mean to use: -a|-r --list <pattern>?"
 msgstr ""
-"'git branch' 的 -a 和 -r 選項不帶一個分支名。\n"
-"您是否想要使用:-a|-r --list <模式>?"
+"“git branch” 的 -a 和 -r 選項不取分支名稱。\n"
+"您是想使用:-a|-r --list <pattern> 嗎?"
 
 #: builtin/branch.c
 msgid ""
 "the '--set-upstream' option is no longer supported. Please use '--track' or "
-"'--set-upstream-to' instead."
-msgstr ""
-"不再支援選項 '--set-upstream'。請使用 '--track' 或 '--set-upstream-to'。"
+"'--set-upstream-to' instead"
+msgstr "不再支援選項 “--set-upstream”。請改用 “--track” 或 “--set-upstream-to”"
 
 #: builtin/bugreport.c
 msgid "git version:\n"
@@ -3678,7 +3747,7 @@
 #: builtin/bugreport.c
 #, c-format
 msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() 失敗,錯誤:「%s」(%d)\n"
+msgstr "uname() 失敗,錯誤:“%s” (%d)\n"
 
 #: builtin/bugreport.c
 msgid "compiler info: "
@@ -3690,7 +3759,7 @@
 
 #: builtin/bugreport.c
 msgid "not run from a git repository - no hooks to show\n"
-msgstr "不是從 git 版本庫執行 - 沒有可顯示的掛鉤\n"
+msgstr "不是在 git 版本庫執行 — 沒有可顯示的掛鉤\n"
 
 #: builtin/bugreport.c
 msgid ""
@@ -3719,15 +3788,15 @@
 "You can delete any lines you don't wish to share.\n"
 msgstr ""
 "感謝您填寫 Git 臭蟲報告!\n"
-"請回答下列問題,以讓我們能夠了解您的問題。\n"
+"請回答下列詢問,協助我們了解您的問題。\n"
 "\n"
 "臭蟲發生前,您做了什麼?(重現問題的步驟)\n"
 "\n"
-"你原本期望發生什麼?(期望行為)\n"
+"原本您預期發生什麼?(預期行為)\n"
 "\n"
 "那實際上發生了什麼?(實際行為)\n"
 "\n"
-"期望行為跟實際發生的行為有什麼不同?\n"
+"預期發生的行為,跟實際發生的行為有什麼不同?\n"
 "\n"
 "其他您想加入的:\n"
 "\n"
@@ -3737,25 +3806,30 @@
 #: builtin/bugreport.c builtin/commit.c builtin/fast-export.c builtin/rebase.c
 #: parse-options.h
 msgid "mode"
-msgstr "模式"
+msgstr "mode"
 
 #: builtin/bugreport.c
 msgid ""
 "create an additional zip archive of detailed diagnostics (default 'stats')"
-msgstr "建立詳細診斷資訊的額外 zip 封存檔案(預設值是 “stats”)"
+msgstr "另外建立有詳細診斷資訊的 ZIP 封存檔(預設值 “stats”)"
 
 #: builtin/bugreport.c
 msgid "specify a destination for the bugreport file(s)"
-msgstr "請指定臭蟲報告檔案的目的地"
+msgstr "指定臭蟲報告檔案的目的地"
 
 #: builtin/bugreport.c
 msgid "specify a strftime format suffix for the filename(s)"
-msgstr "指定檔名,strftime 格式的後綴"
+msgstr "指定用於檔名的 strftime 格式後綴"
+
+#: builtin/bugreport.c
+#, c-format
+msgid "unknown argument `%s'"
+msgstr "未知引數 “%s”"
 
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
 msgid "could not create leading directories for '%s'"
-msgstr "無法建立 '%s' 的前置目錄"
+msgstr "無法建立 “%s” 的前置目錄"
 
 #: builtin/bugreport.c builtin/diagnose.c
 #, c-format
@@ -3778,16 +3852,14 @@
 #: builtin/bugreport.c
 #, c-format
 msgid "Created new report at '%s'.\n"
-msgstr "已在「%s」建立新報告。\n"
+msgstr "已在 “%s” 建立新報告。\n"
 
 #: builtin/bundle.c
 msgid ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 msgstr ""
-"git bundle create [-q | --quiet | --progress | --all-progress] [--all-"
-"progress-implied]\n"
+"git bundle create [-q | --quiet | --progress]\n"
 "                  [--version=<version>] <file> <git-rev-list-args>"
 
 #: builtin/bundle.c
@@ -3796,27 +3868,31 @@
 
 #: builtin/bundle.c
 msgid "git bundle list-heads <file> [<refname>...]"
-msgstr "git bundle list-heads <檔案> [<引用名稱>...]"
+msgstr "git bundle list-heads <file> [<refname>...]"
 
 #: builtin/bundle.c
 msgid "git bundle unbundle [--progress] <file> [<refname>...]"
 msgstr "git bundle unbundle [--progress] <file> [<refname>...]"
 
+#: builtin/bundle.c
+msgid "need a <file> argument"
+msgstr "需要提供 <file> 引數"
+
 #: builtin/bundle.c builtin/pack-objects.c
 msgid "do not show progress meter"
-msgstr "不顯示進度表"
+msgstr "不顯示進度列"
 
 #: builtin/bundle.c builtin/pack-objects.c
 msgid "show progress meter"
-msgstr "顯示進度表"
+msgstr "顯示進度列"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "show progress meter during object writing phase"
-msgstr "在物件寫入階段顯示進度表"
+#: builtin/bundle.c
+msgid "historical; same as --progress"
+msgstr "歷史遺留選項;和 --progress 相同"
 
-#: builtin/bundle.c builtin/pack-objects.c
-msgid "similar to --all-progress when progress meter is shown"
-msgstr "當進度表顯示時類似於 --all-progress"
+#: builtin/bundle.c
+msgid "historical; does nothing"
+msgstr "歷史遺留選項;沒有作用"
 
 #: builtin/bundle.c
 msgid "specify bundle format version"
@@ -3841,12 +3917,12 @@
 
 #: builtin/bundle.c
 msgid "Unbundling objects"
-msgstr "正在解包物件"
+msgstr "正在拆分物件"
 
 #: builtin/cat-file.c merge-recursive.c
 #, c-format
 msgid "cannot read object %s '%s'"
-msgstr "不能讀取物件 %s '%s'"
+msgstr "無法讀取物件 %s “%s”"
 
 #: builtin/cat-file.c
 msgid "flush is only for --buffer mode"
@@ -3859,7 +3935,7 @@
 #: builtin/cat-file.c
 #, c-format
 msgid "whitespace before command: '%s'"
-msgstr "命令前空格:「%s」"
+msgstr "命令前有空格:“%s”"
 
 #: builtin/cat-file.c
 #, c-format
@@ -3872,11 +3948,6 @@
 msgstr "%s 不取引數"
 
 #: builtin/cat-file.c
-#, c-format
-msgid "unknown command: '%s'"
-msgstr "未知命令:「%s」"
-
-#: builtin/cat-file.c
 msgid "only one batch option may be specified"
 msgstr "只能指定一個批次處理選項"
 
@@ -3894,23 +3965,23 @@
 
 #: builtin/cat-file.c
 msgid ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 msgstr ""
-"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
-"objects]\n"
-"             [--buffer] [--follow-symlinks] [--unordered]\n"
-"             [--textconv | --filters] [-z]"
+"git cat-file (--textconv | --filters)\n"
+"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
 
 #: builtin/cat-file.c
 msgid ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
 msgstr ""
-"git cat-file (--textconv | --filters)\n"
-"             [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"
+"git cat-file (--batch | --batch-check | --batch-command) [--batch-all-"
+"objects]\n"
+"             [--buffer] [--follow-symlinks] [--unordered]\n"
+"             [--textconv | --filters] [-Z]"
 
 #: builtin/cat-file.c
 msgid "Check object existence or emit object contents"
@@ -3926,7 +3997,7 @@
 
 #: builtin/cat-file.c
 msgid "Emit [broken] object attributes"
-msgstr "輸出 [損壞的] 物件屬性"
+msgstr "輸出 [損壞的] ([broken]) 物件屬性"
 
 #: builtin/cat-file.c
 msgid "show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"
@@ -3958,15 +4029,19 @@
 
 #: builtin/cat-file.c
 msgid "stdin is NUL-terminated"
-msgstr "標準輸入以 NUL 字元終止"
+msgstr "stdin 以 NUL 字元終止"
+
+#: builtin/cat-file.c
+msgid "stdin and stdout is NUL-terminated"
+msgstr "stdin 和 stdout 以 NUL 字元終止"
 
 #: builtin/cat-file.c
 msgid "read commands from stdin"
-msgstr "從標準輸入讀取命令"
+msgstr "從 stdin 讀取命令"
 
 #: builtin/cat-file.c
 msgid "with --batch[-check]: ignores stdin, batches all known objects"
-msgstr "傳入 --batch[-check]:忽略標準輸入,批次處理所有已知物件"
+msgstr "傳入 --batch[-check]:忽略 stdin,批次處理所有已知物件"
 
 #: builtin/cat-file.c
 msgid "Change or optimize batch output"
@@ -3978,21 +4053,21 @@
 
 #: builtin/cat-file.c
 msgid "follow in-tree symlinks"
-msgstr "追蹤樹中的符號連結"
+msgstr "追蹤樹狀物件中的符號連結"
 
 #: builtin/cat-file.c
 msgid "do not order objects before emitting them"
-msgstr "不要在輸出物件前排序"
+msgstr "不要在輸出前排序物件"
 
 #: builtin/cat-file.c
 msgid ""
 "Emit object (blob or tree) with conversion or filter (stand-alone, or with "
 "batch)"
-msgstr "轉換或過濾後輸出物件(blob 或樹)(單獨或批次處理)"
+msgstr "轉換或過濾後輸出物件 (blob 或 tree)(單獨或批次處理)"
 
 #: builtin/cat-file.c
 msgid "run textconv on object's content"
-msgstr "在物件內容執行 textconv"
+msgstr "對物件內容進行文字轉換 (textconv)"
 
 #: builtin/cat-file.c
 msgid "run filters on object's content"
@@ -4009,7 +4084,7 @@
 #: builtin/cat-file.c
 #, c-format
 msgid "'%s=<%s>' needs '%s' or '%s'"
-msgstr "「%s=<%s>」需要「%s」或「%s」"
+msgstr "“%s=<%s>” 需要 “%s” 或 “%s”"
 
 #: builtin/cat-file.c
 msgid "path|tree-ish"
@@ -4018,12 +4093,12 @@
 #: builtin/cat-file.c
 #, c-format
 msgid "'%s' requires a batch mode"
-msgstr "「%s」需要批次處理模式"
+msgstr "“%s” 需要批次處理模式"
 
 #: builtin/cat-file.c
 #, c-format
 msgid "'-%c' is incompatible with batch mode"
-msgstr "「-%c」與批次處理模式不相容"
+msgstr "“-%c” 與批次處理模式不相容"
 
 #: builtin/cat-file.c
 msgid "batch modes take no arguments"
@@ -4032,12 +4107,12 @@
 #: builtin/cat-file.c
 #, c-format
 msgid "<rev> required with '%s'"
-msgstr "<rev> 需要搭配「%s」"
+msgstr "“%s” 需要 <rev>"
 
 #: builtin/cat-file.c
 #, c-format
 msgid "<object> required with '-%c'"
-msgstr "<object> 需要搭配「-%c」"
+msgstr "“-%c” 需要 <object>"
 
 #: builtin/cat-file.c
 #, c-format
@@ -4045,16 +4120,22 @@
 msgstr "只允許在 <type> <object> 模式傳入兩個引數,但傳了 %d 個"
 
 #: builtin/check-attr.c
-msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
-msgstr "git check-attr [-a | --all | <屬性>...] [--] <路徑名>..."
+msgid ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
+msgstr ""
+"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
+"<pathname>..."
 
 #: builtin/check-attr.c
-msgid "git check-attr --stdin [-z] [-a | --all | <attr>...]"
-msgstr "git check-attr --stdin [-z] [-a | --all | <屬性>...]"
+msgid ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
+msgstr ""
+"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
 
 #: builtin/check-attr.c
 msgid "report all attributes set on file"
-msgstr "報告設定在檔案上的所有屬性"
+msgstr "回報設定在檔案上的所有屬性"
 
 #: builtin/check-attr.c
 msgid "use .gitattributes only from the index"
@@ -4062,11 +4143,19 @@
 
 #: builtin/check-attr.c builtin/check-ignore.c builtin/hash-object.c
 msgid "read file names from stdin"
-msgstr "從標準輸入讀出檔案名"
+msgstr "從 stdin 讀出檔案名稱"
 
 #: builtin/check-attr.c builtin/check-ignore.c
 msgid "terminate input and output records by a NUL character"
-msgstr "輸入和輸出的紀錄使用 NUL 字元終結"
+msgstr "輸入和輸出的紀錄使用 NUL 字元終止"
+
+#: builtin/check-attr.c
+msgid "<tree-ish>"
+msgstr "<tree-ish>"
+
+#: builtin/check-attr.c
+msgid "which tree-ish to check attributes at"
+msgstr "要用哪一個樹狀物件檢查屬性"
 
 #: builtin/check-ignore.c builtin/checkout.c builtin/gc.c builtin/worktree.c
 msgid "suppress progress reporting"
@@ -4074,7 +4163,7 @@
 
 #: builtin/check-ignore.c
 msgid "show non-matching input paths"
-msgstr "顯示未符合的輸入路徑"
+msgstr "顯示不符合的輸入路徑"
 
 #: builtin/check-ignore.c
 msgid "ignore index when checking"
@@ -4082,11 +4171,11 @@
 
 #: builtin/check-ignore.c
 msgid "cannot specify pathnames with --stdin"
-msgstr "不能同時指定路徑及 --stdin 參數"
+msgstr "無法同時指定路徑名稱及 --stdin"
 
 #: builtin/check-ignore.c
 msgid "-z only makes sense with --stdin"
-msgstr "-z 需要和 --stdin 參數共用才有意義"
+msgstr "-z 需要和 --stdin 同時使用才有意義"
 
 #: builtin/check-ignore.c
 msgid "no path specified"
@@ -4094,11 +4183,11 @@
 
 #: builtin/check-ignore.c
 msgid "--quiet is only valid with a single pathname"
-msgstr "參數 --quiet 只在提供一個路徑名時有效"
+msgstr "引數 --quiet 只在提供一個路徑名稱時有效"
 
 #: builtin/check-ignore.c
 msgid "cannot have both --quiet and --verbose"
-msgstr "不能同時提供 --quiet 和 --verbose 參數"
+msgstr "無法同時傳入 --quiet 和 --verbose"
 
 #: builtin/check-ignore.c
 msgid "--non-matching is only valid with --verbose"
@@ -4106,41 +4195,41 @@
 
 #: builtin/check-mailmap.c
 msgid "git check-mailmap [<options>] <contact>..."
-msgstr "git check-mailmap [<選項>] <聯繫位址>..."
+msgstr "git check-mailmap [<options>] <contact>..."
 
 #: builtin/check-mailmap.c
 msgid "also read contacts from stdin"
-msgstr "還從標準輸入讀取聯繫位址"
+msgstr "亦從 stdin 讀取聯絡地址"
 
 #: builtin/check-mailmap.c
 #, c-format
 msgid "unable to parse contact: %s"
-msgstr "不能解析聯繫位址:%s"
+msgstr "無法解析聯絡地址:%s"
 
 #: builtin/check-mailmap.c
 msgid "no contacts specified"
-msgstr "未指定聯繫位址"
+msgstr "未指定聯絡地址"
 
 #: builtin/checkout--worker.c
 msgid "git checkout--worker [<options>]"
-msgstr "git checkout--worker [<選項>]"
+msgstr "git checkout--worker [<options>]"
 
 #: builtin/checkout--worker.c builtin/checkout-index.c builtin/column.c
 #: builtin/submodule--helper.c builtin/worktree.c
 msgid "string"
-msgstr "字串"
+msgstr "string"
 
 #: builtin/checkout--worker.c builtin/checkout-index.c
 msgid "when creating files, prepend <string>"
-msgstr "在建立檔案時,在前面加上 <字串>"
+msgstr "建立檔案時,在前面加上 <string>"
 
 #: builtin/checkout-index.c
 msgid "git checkout-index [<options>] [--] [<file>...]"
-msgstr "git checkout-index [<選項>] [--] [<檔案>...]"
+msgstr "git checkout-index [<options>] [--] [<file>...]"
 
 #: builtin/checkout-index.c
 msgid "stage should be between 1 and 3 or all"
-msgstr "索引值應該取值 1 到 3 或者 all"
+msgstr "stage 應該是 1 到 3 的值,或者是 all"
 
 #: builtin/checkout-index.c
 msgid "check out all files in the index"
@@ -4164,11 +4253,11 @@
 
 #: builtin/checkout-index.c
 msgid "update stat information in the index file"
-msgstr "更新索引中檔案的狀態訊息"
+msgstr "更新索引檔案的狀態訊息"
 
 #: builtin/checkout-index.c
 msgid "read list of paths from the standard input"
-msgstr "從標準輸入讀取路徑列表"
+msgstr "從標準輸入讀取路徑清單"
 
 #: builtin/checkout-index.c
 msgid "write the content to temporary files"
@@ -4180,49 +4269,49 @@
 
 #: builtin/checkout.c
 msgid "git checkout [<options>] <branch>"
-msgstr "git checkout [<選項>] <分支>"
+msgstr "git checkout [<options>] <branch>"
 
 #: builtin/checkout.c
 msgid "git checkout [<options>] [<branch>] -- <file>..."
-msgstr "git checkout [<選項>] [<分支>] -- <檔案>..."
+msgstr "git checkout [<options>] [<branch>] -- <file>..."
 
 #: builtin/checkout.c
 msgid "git switch [<options>] [<branch>]"
-msgstr "git switch [<選項>] [<分支>]"
+msgstr "git switch [<options>] [<branch>]"
 
 #: builtin/checkout.c
 msgid "git restore [<options>] [--source=<branch>] <file>..."
-msgstr "git restore [<選項>] [--source=<分支>] <檔案>..."
+msgstr "git restore [<options>] [--source=<branch>] <file>..."
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have our version"
-msgstr "路徑 '%s' 沒有我們的版本"
+msgstr "“%s” 路徑沒有我們的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have their version"
-msgstr "路徑 '%s' 沒有他們的版本"
+msgstr "“%s” 路徑沒有他們的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have all necessary versions"
-msgstr "路徑 '%s' 沒有全部必需的版本"
+msgstr "“%s” 路徑沒有所有必須的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' does not have necessary versions"
-msgstr "路徑 '%s' 沒有必需的版本"
+msgstr "“%s” 路徑沒有必須的版本"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s': cannot merge"
-msgstr "path '%s':無法合併"
+msgstr "path “%s”:無法合併"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Unable to add merge result for '%s'"
-msgstr "無法為 '%s' 新增合併結果"
+msgstr "無法為 “%s” 加上合併結果"
 
 #: builtin/checkout.c
 #, c-format
@@ -4240,41 +4329,46 @@
 #, c-format
 msgid "Updated %d path from the index"
 msgid_plural "Updated %d paths from the index"
-msgstr[0] "從索引區更新了 %d 個路徑"
+msgstr[0] "已從索引區更新 %d 個路徑"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with updating paths"
-msgstr "'%s' 不能在更新路徑時使用"
+msgstr "無法在更新路徑時使用 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
-msgstr "不能同時更新路徑並切換到分支'%s'。"
+msgstr "無法同時更新路徑和切換至 “%s” 分支。"
 
 #: builtin/checkout.c
 #, c-format
 msgid "neither '%s' or '%s' is specified"
-msgstr "'%s' 或 '%s' 都沒有指定"
+msgstr "“%s” 或 “%s” 皆未指定"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' must be used when '%s' is not specified"
-msgstr "未指定 '%2$s' 時,必須使用 '%1$s'"
+msgstr "未指定 “%2$s” 時,必須使用 “%1$s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' or '%s' cannot be used with %s"
-msgstr "'%s' 或 '%s' 不能和 %s 一起使用"
+msgstr "“%s” 或 “%s” 無法與 %s 一起使用"
+
+#: builtin/checkout.c
+#, c-format
+msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree"
+msgstr "“%s”、“%s” 或 “%s” 無法在簽出樹狀物件時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "path '%s' is unmerged"
-msgstr "路徑 '%s' 未合併"
+msgstr "路徑 “%s” 未合併"
 
 #: builtin/checkout.c
 msgid "you need to resolve your current index first"
-msgstr "您需要先解決目前索引的衝突"
+msgstr "您需要先解決目前索引區的衝突"
 
 #: builtin/checkout.c
 #, c-format
@@ -4282,52 +4376,52 @@
 "cannot continue with staged changes in the following files:\n"
 "%s"
 msgstr ""
-"不能繼續,下列檔案有暫存的修改:\n"
+"無法繼續,下列檔案有暫存的更動:\n"
 "%s"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Can not do reflog for '%s': %s\n"
-msgstr "不能對 '%s' 執行 reflog 動作:%s\n"
+msgstr "無法對 “%s” 執行 reflog 動作:%s\n"
 
 #: builtin/checkout.c
 msgid "HEAD is now at"
 msgstr "HEAD 目前位於"
 
-#: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
+#: builtin/checkout.c builtin/clone.c
 msgid "unable to update HEAD"
-msgstr "不能更新 HEAD"
+msgstr "無法更新 HEAD"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Reset branch '%s'\n"
-msgstr "重設分支 '%s'\n"
+msgstr "重設分支 “%s”\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Already on '%s'\n"
-msgstr "已經位於 '%s'\n"
+msgstr "已經位於 “%s”\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
-msgstr "切換並重設分支 '%s'\n"
+msgstr "已切換並重設分支 “%s”\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Switched to a new branch '%s'\n"
-msgstr "切換到一個新分支 '%s'\n"
+msgstr "已切換至新分支 “%s”\n"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Switched to branch '%s'\n"
-msgstr "切換到分支 '%s'\n"
+msgstr "已切換至分支 “%s”\n"
 
 #  譯者:請維持前導空格
 #: builtin/checkout.c
 #, c-format
 msgid " ... and %d more.\n"
-msgstr " ... 及其它 %d 個。\n"
+msgstr " …… 及其它 %d 個。\n"
 
 #: builtin/checkout.c
 #, c-format
@@ -4342,7 +4436,8 @@
 "\n"
 "%s\n"
 msgstr[0] ""
-"警告:您正丟下 %d 個提交,未和任何分支關聯:\n"
+"警告:您正丟下 %d 個\n"
+"未和任何分支關聯的提交:\n"
 "\n"
 "%s\n"
 
@@ -4361,23 +4456,23 @@
 " git branch <new-branch-name> %s\n"
 "\n"
 msgstr[0] ""
-"如果您想要透過建立新分支儲存它,這可能是一個好時候。\n"
-"如下動作:\n"
+"如果您想要透過建立新分支留下這些提交,\n"
+"現在很適合執行:\n"
 "\n"
-" git branch <新分支名> %s\n"
+" git branch <新分支名稱> %s\n"
 "\n"
 
 #: builtin/checkout.c
 msgid "internal error in revision walk"
-msgstr "在版本遍歷時遇到內部錯誤"
+msgstr "在修訂版遍歷時遇到內部錯誤"
 
 #: builtin/checkout.c
 msgid "Previous HEAD position was"
-msgstr "之前的 HEAD 位置是"
+msgstr "之前的 HEAD 指針位置在"
 
 #: builtin/checkout.c
 msgid "You are on a branch yet to be born"
-msgstr "您位於一個尚未初始化的分支"
+msgstr "您正位於一個尚未初始化的分支"
 
 #: builtin/checkout.c
 #, c-format
@@ -4385,7 +4480,7 @@
 "'%s' could be both a local file and a tracking branch.\n"
 "Please use -- (and optionally --no-guess) to disambiguate"
 msgstr ""
-"'%s' 既可以是一個本機檔案,也可以是一個追蹤分支。\n"
+"“%s” 既可以是本機檔案,也可以是追蹤分支。\n"
 "請使用 --(和可選的 --no-guess)來消除歧義"
 
 #: builtin/checkout.c
@@ -4399,18 +4494,19 @@
 "one remote, e.g. the 'origin' remote, consider setting\n"
 "checkout.defaultRemote=origin in your config."
 msgstr ""
-"如果您想要簽出一個遠端追蹤分支,例如 'origin',您可以\n"
-"使用 --track 選項寫出全名:\n"
+"如果您想要簽出遠端追蹤分支,例如 “origin”,\n"
+"您可以使用 --track 選項寫出全名:\n"
 "\n"
 "    git checkout --track origin/<name>\n"
 "\n"
-"如果您總是喜歡使用模糊的簡短分支名 <name>,而不喜歡如 'origin' 的遠端\n"
-"版本庫名,可以在設定中設定 checkout.defaultRemote=origin。"
+"如果您平時比較想要使用模糊的簡短分支名稱 <name>,\n"
+"而不太想寫如 “origin” 的遠端版本庫名稱,\n"
+"可以在組態中設定 checkout.defaultRemote=origin。"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' matched multiple (%d) remote tracking branches"
-msgstr "'%s' 符合多個 (%d) 遠端追蹤分支"
+msgstr "“%s” 符合多個 (%d) 遠端追蹤分支"
 
 #: builtin/checkout.c
 msgid "only one reference expected"
@@ -4419,7 +4515,7 @@
 #: builtin/checkout.c
 #, c-format
 msgid "only one reference expected, %d given."
-msgstr "應只有一個引用,卻提供了 %d 個。"
+msgstr "預期只有一個引用,卻提供了 %d 個。"
 
 #: builtin/checkout.c builtin/worktree.c
 #, c-format
@@ -4429,112 +4525,112 @@
 #: builtin/checkout.c
 #, c-format
 msgid "reference is not a tree: %s"
-msgstr "引用不是一個樹:%s"
+msgstr "引用不是樹狀物件:%s"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got tag '%s'"
-msgstr "期望一個分支,得到標籤 '%s'"
+msgstr "預期收到分支,卻收到標籤 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got remote branch '%s'"
-msgstr "期望一個分支,得到遠端分支 '%s'"
+msgstr "預期收到分支,卻收到遠端分支 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got '%s'"
-msgstr "期望一個分支,得到 '%s'"
+msgstr "預期收到分支,卻收到 “%s”"
 
 #: builtin/checkout.c
 #, c-format
 msgid "a branch is expected, got commit '%s'"
-msgstr "期望一個分支,得到提交 '%s'"
+msgstr "預期收到分支,卻收到提交 “%s”"
 
 #: builtin/checkout.c
 msgid ""
 "If you want to detach HEAD at the commit, try again with the --detach option."
-msgstr "若您想要分離提交的 HEAD,請傳入 --detach 選項重試。"
+msgstr "若您想要在此提交分離 HEAD 指針,請傳入 --detach 選項重試。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while merging\n"
 "Consider \"git merge --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在合併時切換分支\n"
-"考慮使用 \"git merge --quit\" 或 \"git worktree add\"。"
+"無法在合併時切換分支\n"
+"請試試 “git merge --quit” 或 “git worktree add”。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch in the middle of an am session\n"
 "Consider \"git am --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在 am 工作階段期間切換分支\n"
-"考慮使用 \"git am --quit\" 或 \"git worktree add\"。"
+"無法在 am 工作階段期間時切換分支\n"
+"請試試 “git am --quit” 或 “git worktree add”。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while rebasing\n"
 "Consider \"git rebase --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在重定基底時切換分支\n"
-"考慮使用 \"git rebase --quit\" 或 \"git worktree add\"。"
+"無法在重定基底時切換分支\n"
+"請試試 “git rebase --quit” 或 “git worktree add”。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while cherry-picking\n"
 "Consider \"git cherry-pick --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在揀選時切換分支\n"
-"考慮使用 \"git cherry-pick --quit\" 或 \"git worktree add\"。"
+"無法在揀選時切換分支\n"
+"請試試 “git cherry-pick --quit” 或 “git worktree add”。"
 
 #: builtin/checkout.c
 msgid ""
 "cannot switch branch while reverting\n"
 "Consider \"git revert --quit\" or \"git worktree add\"."
 msgstr ""
-"不能在還原時切換分支\n"
-"考慮使用 \"git revert --quit\" 或 \"git worktree add\"。"
+"無法在還原時切換分支\n"
+"請試試 “git revert --quit” 或 “git worktree add”。"
 
 #: builtin/checkout.c
 msgid "you are switching branch while bisecting"
-msgstr "您在執行二分搜尋時切換分支"
+msgstr "您在進行二分搜尋時切換分支"
 
 #: builtin/checkout.c
 msgid "paths cannot be used with switching branches"
-msgstr "路徑不能和切換分支同時使用"
+msgstr "路徑不能與切換分支同時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with switching branches"
-msgstr "'%s' 不能和切換分支同時使用"
+msgstr "“%s” 不能與切換分支同時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot be used with '%s'"
-msgstr "'%s' 不能和 '%s' 同時使用"
+msgstr "“%s” 不能與 “%s” 同時使用"
 
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' cannot take <start-point>"
-msgstr "'%s' 不帶 <起始點>"
+msgstr "“%s” 不取 <start-point>"
 
 #: builtin/checkout.c
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
-msgstr "不能切換分支到一個非提交 '%s'"
+msgstr "無法將分支切換至非提交項目 “%s”"
 
 #: builtin/checkout.c
 msgid "missing branch or commit argument"
-msgstr "缺少分支或提交參數"
+msgstr "缺少分支或提交引數"
 
 #: builtin/checkout.c
 msgid "perform a 3-way merge with the new branch"
-msgstr "和新的分支執行三方合併"
+msgstr "和新分支進行三方合併"
 
 #: builtin/checkout.c builtin/log.c parse-options.h
 msgid "style"
-msgstr "風格"
+msgstr "style"
 
 #: builtin/checkout.c
 msgid "conflict style (merge, diff3, or zdiff3)"
@@ -4542,7 +4638,7 @@
 
 #: builtin/checkout.c builtin/worktree.c
 msgid "detach HEAD at named commit"
-msgstr "HEAD 從指定的提交分離"
+msgstr "自指定提交分離 HEAD 指針"
 
 #: builtin/checkout.c
 msgid "force checkout (throw away local modifications)"
@@ -4550,19 +4646,19 @@
 
 #: builtin/checkout.c
 msgid "new-branch"
-msgstr "新分支"
+msgstr "new-branch"
 
 #: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "新的沒有父提交的分支"
+msgid "new unborn branch"
+msgstr "新的未誕生分支"
 
 #: builtin/checkout.c builtin/merge.c
 msgid "update ignored files (default)"
-msgstr "更新忽略的檔案(預設)"
+msgstr "更新忽略的檔案(預設值)"
 
 #: builtin/checkout.c
 msgid "do not check if another worktree is holding the given ref"
-msgstr "不檢查指定的引用是否被其他工作區所占用"
+msgstr "不檢查其他工作區是否正在佔用指定的引用"
 
 #: builtin/checkout.c
 msgid "checkout our version for unmerged files"
@@ -4574,16 +4670,16 @@
 
 #: builtin/checkout.c
 msgid "do not limit pathspecs to sparse entries only"
-msgstr "對路徑不做稀疏簽出的限制"
+msgstr "對路徑規格不做稀疏簽出的限制"
 
 #: builtin/checkout.c
 #, c-format
 msgid "options '-%c', '-%c', and '%s' cannot be used together"
-msgstr "「-%c」、「-%c」和「%s」選項不得同時使用"
+msgstr "“-%c”、“-%c” 和 “%s” 選項不得同時使用"
 
 #: builtin/checkout.c
 msgid "--track needs a branch name"
-msgstr "--track 需要一個分支名"
+msgstr "--track 需要分支名稱"
 
 #: builtin/checkout.c
 #, c-format
@@ -4602,60 +4698,61 @@
 #: builtin/checkout.c
 #, c-format
 msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
-msgstr "'%s' 不是一個提交,不能基於它建立分支 '%s'"
+msgstr "“%s” 不是提交,因此不能以這為基礎建立 “%s” 分支"
 
 #: builtin/checkout.c
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
-msgstr "git checkout:--detach 不能接收路徑參數 '%s'"
+msgstr "git checkout:--detach 不取路徑引數 “%s”"
 
 #: builtin/checkout.c
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
 msgstr ""
-"git checkout:在從索引簽出時,--ours/--theirs、--force 和 --merge 不相容。"
+"git checkout:在從索引簽出時,--ours/--theirs、--force\n"
+"和 --merge 不相容。"
 
 #: builtin/checkout.c
 msgid "you must specify path(s) to restore"
-msgstr "您必須指定一個要復原的路徑"
+msgstr "您必須指定要還原的路徑"
 
-#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
 #: builtin/submodule--helper.c builtin/worktree.c
 msgid "branch"
-msgstr "分支"
+msgstr "branch"
 
 #: builtin/checkout.c
 msgid "create and checkout a new branch"
-msgstr "建立並簽出一個新的分支"
+msgstr "建立並簽出新分支"
 
 #: builtin/checkout.c
 msgid "create/reset and checkout a branch"
-msgstr "建立/重設並簽出一個分支"
+msgstr "建立/重設並簽出分支"
 
 #: builtin/checkout.c
 msgid "create reflog for new branch"
-msgstr "為新的分支建立引用日誌"
+msgstr "為新分支建立引用日誌"
 
 #: builtin/checkout.c
 msgid "second guess 'git checkout <no-such-branch>' (default)"
-msgstr "二次猜測 'git checkout <無此分支>'(預設)"
+msgstr "二次猜測 “git checkout <無此分支>”(預設值)"
 
 #: builtin/checkout.c
 msgid "use overlay mode (default)"
-msgstr "使用疊加模式(預設)"
+msgstr "使用疊加模式(預設值)"
 
 #: builtin/checkout.c
 msgid "create and switch to a new branch"
-msgstr "建立並切換一個新分支"
+msgstr "建立並切換至新分支"
 
 #: builtin/checkout.c
 msgid "create/reset and switch to a branch"
-msgstr "建立/重設並切換一個分支"
+msgstr "建立/重設並切換至指定分支"
 
 #: builtin/checkout.c
 msgid "second guess 'git switch <no-such-branch>'"
-msgstr "二次猜測 'git switch <無此分支>'"
+msgstr "二次猜測 “git switch <無此分支>”"
 
 #: builtin/checkout.c
 msgid "throw away local modifications"
@@ -4663,19 +4760,19 @@
 
 #: builtin/checkout.c
 msgid "which tree-ish to checkout from"
-msgstr "要簽出哪一個樹"
+msgstr "要自哪一個樹狀物件指示元簽出"
 
 #: builtin/checkout.c
 msgid "restore the index"
-msgstr "復原索引"
+msgstr "還原索引"
 
 #: builtin/checkout.c
 msgid "restore the working tree (default)"
-msgstr "復原工作區(預設)"
+msgstr "還原工作區(預設值)"
 
 #: builtin/checkout.c
 msgid "ignore unmerged entries"
-msgstr "忽略未合併條目"
+msgstr "忽略未合併項目"
 
 #: builtin/checkout.c
 msgid "use overlay mode"
@@ -4686,17 +4783,18 @@
 "git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
 "[<pathspec>...]"
 msgstr ""
-"git clean [-d] [-f] [-i] [-n] [-q] [-e <模式>] [-x | -X] [--] [<路徑規格>...]"
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] "
+"[<pathspec>...]"
 
 #: builtin/clean.c
 #, c-format
 msgid "Removing %s\n"
-msgstr "正刪除 %s\n"
+msgstr "正在刪除 %s\n"
 
 #: builtin/clean.c
 #, c-format
 msgid "Would remove %s\n"
-msgstr "將刪除 %s\n"
+msgstr "將會刪除 %s\n"
 
 #: builtin/clean.c
 #, c-format
@@ -4711,12 +4809,12 @@
 #: builtin/clean.c midx.c
 #, c-format
 msgid "failed to remove %s"
-msgstr "刪除 %s 失敗"
+msgstr "無法移除 %s"
 
 #: builtin/clean.c
 #, c-format
 msgid "could not lstat %s\n"
-msgstr "不能對 %s 呼叫 lstat\n"
+msgstr "不能對 %s 進行 lstat\n"
 
 #: builtin/clean.c
 msgid "Refusing to remove current working directory\n"
@@ -4726,7 +4824,7 @@
 msgid "Would refuse to remove current working directory\n"
 msgstr "會拒絕移除目前的工作目錄\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4734,12 +4832,12 @@
 "foo        - select item based on unique prefix\n"
 "           - (empty) select nothing\n"
 msgstr ""
-"協助:\n"
-"1          - 透過編號選擇一個選項\n"
-"foo        - 透過唯一前綴選擇一個選項\n"
+"提示說明:\n"
+"1          - 透過編號選擇項目\n"
+"foo        - 根據獨特前綴選擇項目\n"
 "           - (空)什麼也不選擇\n"
 
-#: builtin/clean.c git-add--interactive.perl
+#: builtin/clean.c
 #, c-format
 msgid ""
 "Prompt help:\n"
@@ -4751,39 +4849,39 @@
 "*          - choose all items\n"
 "           - (empty) finish selecting\n"
 msgstr ""
-"協助:\n"
-"1          - 選擇一個選項\n"
-"3-5        - 選擇一個範圍內的所有選項\n"
-"2-3,6-9    - 選擇多個範圍內的所有選項\n"
-"foo        - 透過唯一前綴選擇一個選項\n"
-"-...       - 反選特定的選項\n"
-"*          - 選擇所有選項\n"
-"           - (空)結束選擇\n"
+"提示說明:\n"
+"1          - 選擇一個項目\n"
+"3-5        - 選擇一個項目範圍\n"
+"2-3,6-9    - 選擇多個項目範圍\n"
+"foo        - 根據獨特前綴選擇項目\n"
+"-...       - 取消選取指定項目\n"
+"*          - 選擇所有項目\n"
+"           - (空)完成選擇\n"
 
-#: builtin/clean.c git-add--interactive.perl
-#, c-format, perl-format
+#: builtin/clean.c
+#, c-format
 msgid "Huh (%s)?\n"
-msgstr "嗯(%s)?\n"
+msgstr "嗯(%s)?\n"
 
 #: builtin/clean.c
 #, c-format
 msgid "Input ignore patterns>> "
-msgstr "輸入範本以排除條目>> "
+msgstr "輸入要忽略的模式項目>> "
 
 #: builtin/clean.c
 #, c-format
 msgid "WARNING: Cannot find items matched by: %s"
-msgstr "警告:無法找到和 %s 符合的條目"
+msgstr "警告:無法找到和 %s 符合的項目"
 
 #: builtin/clean.c
 msgid "Select items to delete"
-msgstr "選擇要刪除的條目"
+msgstr "選擇要刪除的項目"
 
 #. TRANSLATORS: Make sure to keep [y/N] as is
 #: builtin/clean.c
 #, c-format
 msgid "Remove %s [y/N]? "
-msgstr "移除 %s [y/N]? "
+msgstr "移除 %s [y/N]? "
 
 #: builtin/clean.c
 msgid ""
@@ -4798,15 +4896,15 @@
 "clean               - 開始清理\n"
 "filter by pattern   - 透過範本排除要刪除的條目\n"
 "select by numbers   - 透過數字選擇要刪除的條目\n"
-"ask each            - 針對刪除逐一詢問(就像 \"rm -i\")\n"
+"ask each            - 針對刪除逐一詢問(就像 “rm -i”)\n"
 "quit                - 停止刪除並離開\n"
-"help                - 顯示本協助\n"
-"?                   - 顯示如何在提示符下選擇的協助"
+"help                - 顯示本輔助說明\n"
+"?                   - 顯示如何在提示下選擇的協助"
 
 #: builtin/clean.c
 msgid "Would remove the following item:"
 msgid_plural "Would remove the following items:"
-msgstr[0] "將刪除如下條目:"
+msgstr[0] "將移除以下項目:"
 
 #: builtin/clean.c
 msgid "No more files to clean, exiting."
@@ -4814,11 +4912,11 @@
 
 #: builtin/clean.c
 msgid "do not print names of files removed"
-msgstr "不列印刪除檔案的名稱"
+msgstr "不輸出移除的檔案的名稱"
 
 #: builtin/clean.c
 msgid "force"
-msgstr "強制"
+msgstr "force"
 
 #: builtin/clean.c
 msgid "interactive cleaning"
@@ -4826,24 +4924,25 @@
 
 #: builtin/clean.c
 msgid "remove whole directories"
-msgstr "刪除整個目錄"
+msgstr "移除整個目錄"
 
 #: builtin/clean.c builtin/describe.c builtin/grep.c builtin/log.c
-#: builtin/ls-files.c builtin/name-rev.c builtin/show-ref.c
+#: builtin/ls-files.c builtin/name-rev.c builtin/pack-refs.c builtin/show-ref.c
+#: ref-filter.h
 msgid "pattern"
-msgstr "模式"
+msgstr "pattern"
 
 #: builtin/clean.c
 msgid "add <pattern> to ignore rules"
-msgstr "新增 <模式> 到忽略規則"
+msgstr "將 <pattern> 加到忽略規則"
 
 #: builtin/clean.c
 msgid "remove ignored files, too"
-msgstr "也刪除忽略的檔案"
+msgstr "亦移除忽略的檔案"
 
 #: builtin/clean.c
 msgid "remove only ignored files"
-msgstr "只刪除忽略的檔案"
+msgstr "只移除忽略的檔案"
 
 #: builtin/clean.c
 msgid ""
@@ -4859,29 +4958,25 @@
 msgstr ""
 "clean.requireForce 預設為 true 且未提供 -i、-n 或 -f 選項,拒絕執行清理動作"
 
-#: builtin/clean.c
-msgid "-x and -X cannot be used together"
-msgstr "-x 和 -X 不能同時使用"
-
 #: builtin/clone.c
 msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<選項>] [--] <版本庫> [<路徑>]"
+msgstr "git clone [<options>] [--] <repo> [<dir>]"
 
 #: builtin/clone.c
 msgid "don't clone shallow repository"
-msgstr "不要複製淺版本庫"
+msgstr "不要複製淺層版本庫"
 
 #: builtin/clone.c
 msgid "don't create a checkout"
-msgstr "不建立一個簽出"
+msgstr "不要建立簽出"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "create a bare repository"
-msgstr "建立一個純版本庫"
+msgstr "建立裸版本庫"
 
 #: builtin/clone.c
 msgid "create a mirror repository (implies bare)"
-msgstr "建立一個鏡像版本庫(也是純版本庫)"
+msgstr "建立鏡像版本庫(亦即裸版本庫)"
 
 #: builtin/clone.c
 msgid "to clone from a local repository"
@@ -4889,7 +4984,7 @@
 
 #: builtin/clone.c
 msgid "don't use local hardlinks, always copy"
-msgstr "不使用本機硬連結,始終複製"
+msgstr "不使用本機硬連結,始終拷貝"
 
 #: builtin/clone.c
 msgid "setup as shared repository"
@@ -4905,15 +5000,15 @@
 
 #: builtin/clone.c
 msgid "number of submodules cloned in parallel"
-msgstr "並行複製的子模組數"
+msgstr "平行複製的子模組數量"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "template-directory"
-msgstr "範本目錄"
+msgstr "模板目錄"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "directory from which templates will be used"
-msgstr "範本目錄將被使用"
+msgstr "將被使用的模板目錄"
 
 #: builtin/clone.c builtin/submodule--helper.c
 msgid "reference repository"
@@ -4921,49 +5016,46 @@
 
 #: builtin/clone.c builtin/submodule--helper.c
 msgid "use --reference only while cloning"
-msgstr "僅在複製時參考 --reference 指向的本機版本庫"
+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/submodule--helper.c t/helper/test-simple-ipc.c
 msgid "name"
-msgstr "名稱"
+msgstr "name"
 
 #: builtin/clone.c
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "使用 <名稱> 而不是 'origin' 去追蹤上游"
+msgstr "使用 <name> 而不是 “origin” 追蹤上游"
 
 #: builtin/clone.c
 msgid "checkout <branch> instead of the remote's HEAD"
-msgstr "簽出 <分支> 而不是遠端 HEAD"
+msgstr "簽出 <branch> 而不是遠端 HEAD"
 
 #: builtin/clone.c
 msgid "path to git-upload-pack on the remote"
 msgstr "遠端 git-upload-pack 路徑"
 
-#: builtin/clone.c builtin/fetch.c builtin/grep.c builtin/pull.c
+#: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "depth"
-msgstr "深度"
+msgstr "depth"
 
 #: builtin/clone.c
 msgid "create a shallow clone of that depth"
-msgstr "建立一個指定深度的淺複製"
-
-#: builtin/clone.c builtin/fetch.c builtin/pack-objects.c builtin/pull.c
-msgid "time"
-msgstr "時間"
+msgstr "建立指定深度的淺層複製"
 
 #: builtin/clone.c
 msgid "create a shallow clone since a specific time"
-msgstr "建立從指定時間到現在的淺複製"
+msgstr "建立從指定時間到現在的淺層複製"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
 msgid "revision"
-msgstr "修訂版"
+msgstr "revision"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c
 msgid "deepen history of shallow clone, excluding rev"
-msgstr "取得更多淺複製的過去歷史記錄,除了特定版本"
+msgstr "取得更多淺層複製的過去歷史記錄,除了特定修訂版"
 
 #: builtin/clone.c builtin/submodule--helper.c
 msgid "clone only one branch, HEAD or --branch"
@@ -4979,11 +5071,15 @@
 
 #: builtin/clone.c builtin/init-db.c
 msgid "gitdir"
-msgstr "git目錄"
+msgstr "gitdir"
 
 #: builtin/clone.c builtin/init-db.c
 msgid "separate git dir from working tree"
-msgstr "git目錄和工作區分離"
+msgstr "git 目錄和工作區分離"
+
+#: builtin/clone.c builtin/init-db.c
+msgid "specify the reference format to use"
+msgstr "指定要使用的引用格式"
 
 #: builtin/clone.c
 msgid "key=value"
@@ -5003,14 +5099,6 @@
 msgid "option to transmit"
 msgstr "傳輸選項"
 
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv4 addresses only"
-msgstr "只使用 IPv4 位址"
-
-#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/push.c
-msgid "use IPv6 addresses only"
-msgstr "只使用 IPv6 位址"
-
 #: builtin/clone.c
 msgid "apply partial clone filters to submodules"
 msgstr "將部分複製過濾器套用至子模組"
@@ -5048,6 +5136,11 @@
 
 #: builtin/clone.c
 #, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "“%s” 是個符號連結,故不能使用 --local 複製"
+
+#: builtin/clone.c
+#, c-format
 msgid "failed to start iterator over '%s'"
 msgstr "無法在 '%s' 上啟動疊代器"
 
@@ -5059,7 +5152,7 @@
 #: builtin/clone.c compat/precompose_utf8.c
 #, c-format
 msgid "failed to unlink '%s'"
-msgstr "刪除 '%s' 失敗"
+msgstr "無法刪除 “%s”"
 
 #: builtin/clone.c
 #, c-format
@@ -5127,7 +5220,7 @@
 
 #: builtin/clone.c
 msgid "cannot unlink temporary alternates file"
-msgstr "無法刪除暫時的 alternates 檔案"
+msgstr "無法刪除暫存 alternates 檔案"
 
 #: builtin/clone.c
 msgid "Too many arguments."
@@ -5137,11 +5230,10 @@
 msgid "You must specify a repository to clone."
 msgstr "您必須指定要複製的版本庫。"
 
-#: builtin/clone.c
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr "--bundle-uri 與 --depth、--shallow-since 和 --shallow-exclude 不相容"
+#: builtin/clone.c builtin/init-db.c setup.c
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "未知的引用儲存格式 “%s”"
 
 #: builtin/clone.c
 #, c-format
@@ -5241,6 +5333,10 @@
 msgstr "無法從套件包 URL “%s” 抓取物件"
 
 #: builtin/clone.c
+msgid "failed to fetch advertised bundles"
+msgstr "無法抓取公佈的套件包"
+
+#: builtin/clone.c
 msgid "remote transport reported error"
 msgstr "遠端傳輸回報錯誤"
 
@@ -5298,14 +5394,14 @@
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 msgstr ""
 "git commit-graph write [--object-dir <dir>] [--append]\n"
 "                       [--split[=<strategy>]] [--reachable | --stdin-packs | "
 "--stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <n>] [--"
 "[no-]progress]\n"
-"                       <split options>"
+"                       <split-options>"
 
 #: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
 msgid "dir"
@@ -5326,6 +5422,11 @@
 
 #: builtin/commit-graph.c
 #, c-format
+msgid "could not open commit-graph chain '%s'"
+msgstr "無法開啟提交圖鏈 “%s”"
+
+#: builtin/commit-graph.c
+#, c-format
 msgid "unrecognized --split argument, %s"
 msgstr "無法識別的 --split 參數,%s"
 
@@ -5536,6 +5637,10 @@
 "    git cherry-pick --skip\n"
 "\n"
 
+#: builtin/commit.c read-cache.c
+msgid "updating files failed"
+msgstr "更新檔案失敗"
+
 #: builtin/commit.c
 msgid "failed to unpack HEAD tree object"
 msgstr "解包 HEAD 樹狀物件失敗"
@@ -5561,10 +5666,6 @@
 msgstr "不能更新樹的主快取"
 
 #: builtin/commit.c
-msgid "unable to write new_index file"
-msgstr "無法寫 new_index 檔案"
-
-#: builtin/commit.c
 msgid "cannot do a partial commit during a merge."
 msgstr "在合併過程中不能做部分提交。"
 
@@ -5609,10 +5710,10 @@
 "in the current commit message"
 msgstr "無法選擇一個未被目前提交說明使用的備註字元"
 
-#: builtin/commit.c
+#: builtin/commit.c builtin/merge-tree.c
 #, c-format
-msgid "could not lookup commit %s"
-msgstr "不能查詢提交 %s"
+msgid "could not lookup commit '%s'"
+msgstr "無法查詢提交 “%s”"
 
 #: builtin/commit.c builtin/shortlog.c
 #, c-format
@@ -5834,13 +5935,13 @@
 msgid "version"
 msgstr "版本"
 
-#: builtin/commit.c builtin/push.c builtin/worktree.c
+#: builtin/commit.c builtin/fetch.c builtin/push.c builtin/worktree.c
 msgid "machine-readable output"
 msgstr "機器可讀的輸出"
 
 #: builtin/commit.c
 msgid "show status in long format (default)"
-msgstr "以長格式顯示狀態(預設)"
+msgstr "以長格式顯示狀態(預設值)"
 
 #: builtin/commit.c
 msgid "terminate entries with NUL"
@@ -5917,7 +6018,7 @@
 msgid "override date for commit"
 msgstr "提交時覆蓋日期"
 
-#: builtin/commit.c parse-options.h ref-filter.h
+#: builtin/commit.c builtin/merge-tree.c parse-options.h ref-filter.h
 msgid "commit"
 msgstr "提交"
 
@@ -6058,17 +6159,17 @@
 #: builtin/commit.c
 msgid ""
 "repository has been updated, but unable to write\n"
-"new_index file. Check that disk is not full and quota is\n"
+"new index file. Check that disk is not full and quota is\n"
 "not exceeded, and then \"git restore --staged :/\" to recover."
 msgstr ""
-"版本庫已更新,但無法寫 new_index 檔案。檢查是否磁碟已滿或\n"
-"磁碟配額已耗盡,然後執行 \"git restore --staged :/\" 復原。"
+"版本庫已更新,但無法寫入新的索引檔案。請檢查磁碟是否\n"
+"已滿或磁碟配額已耗盡,然後執行 “git restore --staged :/” 復原。"
 
 #: builtin/config.c
 msgid "git config [<options>]"
 msgstr "git config [<選項>]"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 #, c-format
 msgid "unrecognized --type argument, %s"
 msgstr "無法識別的 --type 參數,%s"
@@ -6167,7 +6268,7 @@
 
 #: builtin/config.c
 msgid "find the color configured: slot [default]"
-msgstr "獲得設定的顏色:設定 [預設]"
+msgstr "獲得設定的顏色:設定 [預設值]"
 
 #: builtin/config.c
 msgid "find the color setting: slot [stdout-is-tty]"
@@ -6177,11 +6278,11 @@
 msgid "Type"
 msgstr "類型"
 
-#: builtin/config.c builtin/env--helper.c builtin/hash-object.c
+#: builtin/config.c builtin/hash-object.c
 msgid "type"
 msgstr "類型"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value is given this type"
 msgstr "取值為該類型"
 
@@ -6235,7 +6336,7 @@
 "顯示設定檔的作用域 (工作區 worktree、本機 local、全域 global、系統 system、指"
 "令 command)"
 
-#: builtin/config.c builtin/env--helper.c
+#: builtin/config.c
 msgid "value"
 msgstr "取值"
 
@@ -6770,30 +6871,6 @@
 msgid "no <cmd> given for --extcmd=<cmd>"
 msgstr "沒有為 --extcmd=<命令> 參數提供 <命令>"
 
-#: builtin/env--helper.c
-msgid "git env--helper --type=[bool|ulong] <options> <env-var>"
-msgstr "git env--helper --type=[bool|ulong] <選項> <環境變數>"
-
-#: builtin/env--helper.c
-msgid "default for git_env_*(...) to fall back on"
-msgstr "git_env_*(...) 的預設值"
-
-#: builtin/env--helper.c
-msgid "be quiet only use git_env_*() value as exit code"
-msgstr "安靜模式,只使用 git_env_*() 的值作為離開碼"
-
-#: builtin/env--helper.c
-#, c-format
-msgid "option `--default' expects a boolean value with `--type=bool`, not `%s`"
-msgstr "選項「--default」預期收到「--type=bool」的布林值,而非「%s」"
-
-#: builtin/env--helper.c
-#, c-format
-msgid ""
-"option `--default' expects an unsigned long value with `--type=ulong`, not "
-"`%s`"
-msgstr "選項「--default」預期收到「--type=ulong」的無號 long 數值,而非「%s」"
-
 #: builtin/fast-export.c
 msgid "git fast-export [<rev-list-opts>]"
 msgstr "git fast-export [<rev-list-opts>]"
@@ -6936,6 +7013,213 @@
 msgid "fetch.parallel cannot be negative"
 msgstr "fetch.parallel 不能為負數"
 
+#: builtin/fetch.c
+msgid "couldn't find remote ref HEAD"
+msgstr "找不到遠端 HEAD 引用"
+
+#: builtin/fetch.c
+#, c-format
+msgid "From %.*s\n"
+msgstr "來自 %.*s\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "object %s not found"
+msgstr "物件 %s 未發現"
+
+#: builtin/fetch.c
+msgid "[up to date]"
+msgstr "[最新]"
+
+#: builtin/fetch.c
+msgid "[rejected]"
+msgstr "[已拒絕]"
+
+#: builtin/fetch.c
+msgid "can't fetch into checked-out branch"
+msgstr "無法抓取至已簽出分支"
+
+#: builtin/fetch.c
+msgid "[tag update]"
+msgstr "[標籤更新]"
+
+#: builtin/fetch.c
+msgid "unable to update local ref"
+msgstr "不能更新本機引用"
+
+#: builtin/fetch.c
+msgid "would clobber existing tag"
+msgstr "會破壞現有的標籤"
+
+#: builtin/fetch.c
+msgid "[new tag]"
+msgstr "[新標籤]"
+
+#: builtin/fetch.c
+msgid "[new branch]"
+msgstr "[新分支]"
+
+#: builtin/fetch.c
+msgid "[new ref]"
+msgstr "[新引用]"
+
+#: builtin/fetch.c
+msgid "forced update"
+msgstr "強制更新"
+
+#: builtin/fetch.c
+msgid "non-fast-forward"
+msgstr "非快轉"
+
+#: builtin/fetch.c builtin/grep.c sequencer.c
+#, c-format
+msgid "cannot open '%s'"
+msgstr "不能開啟 '%s'"
+
+#: builtin/fetch.c
+msgid ""
+"fetch normally indicates which branches had a forced update,\n"
+"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+"flag or run 'git config fetch.showForcedUpdates true'"
+msgstr ""
+"取得 (fetch) 動作通常會指示哪些分支發生了強制更新,但該檢查已被停用。\n"
+"要重新啟用,請使用「--show-forced-updates」選項,或執行\n"
+"「git config fetch.showForcedUpdates true」"
+
+#: builtin/fetch.c
+#, c-format
+msgid ""
+"it took %.2f seconds to check forced updates; you can use\n"
+"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
+"false'\n"
+"to avoid this check\n"
+msgstr ""
+"花了 %.2f 秒檢查強制更新。您可以使用「--no-show-forced-updates」\n"
+"或執行「git config fetch.showForcedUpdates false」避免此項檢查\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s 未傳送所有必需的物件\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "rejected %s because shallow roots are not allowed to be updated"
+msgstr "已拒絕 %s,不允許更新淺複製"
+
+#: builtin/fetch.c
+#, c-format
+msgid ""
+"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' 來刪除舊的、有衝突的分支"
+
+#  譯者:請維持前導空格
+#: builtin/fetch.c
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr "   (%s 將成為懸空狀態)"
+
+#  譯者:請維持前導空格
+#: builtin/fetch.c
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr "   (%s 已成為懸空狀態)"
+
+#: builtin/fetch.c
+msgid "[deleted]"
+msgstr "[已刪除]"
+
+#: builtin/fetch.c builtin/remote.c
+msgid "(none)"
+msgstr "(無)"
+
+#: builtin/fetch.c
+#, c-format
+msgid "refusing to fetch into branch '%s' checked out at '%s'"
+msgstr "拒絕取得在「%2$s」簽出的「%1$s」分支"
+
+#: builtin/fetch.c
+#, c-format
+msgid "option \"%s\" value \"%s\" is not valid for %s"
+msgstr "選項「%s」的值「%s」對 %s 無效"
+
+#: builtin/fetch.c
+#, c-format
+msgid "option \"%s\" is ignored for %s\n"
+msgstr "選項「%s」被 %s 忽略\n"
+
+#: builtin/fetch.c object-file.c
+#, c-format
+msgid "%s is not a valid object"
+msgstr "%s 不是一個有效的物件"
+
+#: builtin/fetch.c
+#, c-format
+msgid "the object %s does not exist"
+msgstr "%s 物件不存在"
+
+#: builtin/fetch.c
+msgid "multiple branches detected, incompatible with --set-upstream"
+msgstr "檢測到多分支,和 --set-upstream 不相容"
+
+#: builtin/fetch.c
+#, c-format
+msgid ""
+"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
+"any branch."
+msgstr ""
+"無法將 HEAD 的上游從「%2$s」設定為「%1$s」,因為 HEAD 沒有指向任何分支。"
+
+#: builtin/fetch.c
+msgid "not setting upstream for a remote remote-tracking branch"
+msgstr "沒有為一個遠端追蹤分支設定上游"
+
+#: builtin/fetch.c
+msgid "not setting upstream for a remote tag"
+msgstr "沒有為一個遠端標籤設定上游"
+
+#: builtin/fetch.c
+msgid "unknown branch type"
+msgstr "未知的分支類型"
+
+#: builtin/fetch.c
+msgid ""
+"no source branch found;\n"
+"you need to specify exactly one branch with the --set-upstream option"
+msgstr ""
+"找不到來源分支。\n"
+"您得使用 --set-upstream 選項明確指定一個分支"
+
+#: builtin/fetch.c
+#, c-format
+msgid "Fetching %s\n"
+msgstr "正在取得 %s\n"
+
+#: builtin/fetch.c
+#, c-format
+msgid "could not fetch %s"
+msgstr "不能取得 %s"
+
+#: builtin/fetch.c
+#, c-format
+msgid "could not fetch '%s' (exit code: %d)\n"
+msgstr "無法取得 '%s'(離開碼:%d)\n"
+
+#: builtin/fetch.c
+msgid ""
+"no remote repository specified; please specify either a URL or a\n"
+"remote name from which new revisions should be fetched"
+msgstr ""
+"未指定遠端版本庫。請指定用以取得新修訂版本之\n"
+"來源 URL 或者遠端版本庫名稱"
+
+#: builtin/fetch.c
+msgid "you need to specify a tag name"
+msgstr "您需要指定標籤名稱"
+
 #: builtin/fetch.c builtin/pull.c
 msgid "fetch from all remotes"
 msgstr "從所有的遠端抓取"
@@ -7072,213 +7356,6 @@
 msgstr "從標準輸入中接受引用規格"
 
 #: builtin/fetch.c
-msgid "couldn't find remote ref HEAD"
-msgstr "找不到遠端 HEAD 引用"
-
-#: builtin/fetch.c
-#, c-format
-msgid "object %s not found"
-msgstr "物件 %s 未發現"
-
-#: builtin/fetch.c
-msgid "[up to date]"
-msgstr "[最新]"
-
-#: builtin/fetch.c
-msgid "[rejected]"
-msgstr "[已拒絕]"
-
-#: builtin/fetch.c
-msgid "can't fetch into checked-out branch"
-msgstr "無法抓取至已簽出分支"
-
-#: builtin/fetch.c
-msgid "[tag update]"
-msgstr "[標籤更新]"
-
-#: builtin/fetch.c
-msgid "unable to update local ref"
-msgstr "不能更新本機引用"
-
-#: builtin/fetch.c
-msgid "would clobber existing tag"
-msgstr "會破壞現有的標籤"
-
-#: builtin/fetch.c
-msgid "[new tag]"
-msgstr "[新標籤]"
-
-#: builtin/fetch.c
-msgid "[new branch]"
-msgstr "[新分支]"
-
-#: builtin/fetch.c
-msgid "[new ref]"
-msgstr "[新引用]"
-
-#: builtin/fetch.c
-msgid "forced update"
-msgstr "強制更新"
-
-#: builtin/fetch.c
-msgid "non-fast-forward"
-msgstr "非快轉"
-
-#: builtin/fetch.c builtin/grep.c sequencer.c
-#, c-format
-msgid "cannot open '%s'"
-msgstr "不能開啟 '%s'"
-
-#: builtin/fetch.c
-msgid ""
-"fetch normally indicates which branches had a forced update,\n"
-"but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
-"flag or run 'git config fetch.showForcedUpdates true'"
-msgstr ""
-"取得 (fetch) 動作通常會指示哪些分支發生了強制更新,但該檢查已被停用。\n"
-"要重新啟用,請使用「--show-forced-updates」選項,或執行\n"
-"「git config fetch.showForcedUpdates true」"
-
-#: builtin/fetch.c
-#, c-format
-msgid ""
-"it took %.2f seconds to check forced updates; you can use\n"
-"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates "
-"false'\n"
-"to avoid this check\n"
-msgstr ""
-"花了 %.2f 秒檢查強制更新。您可以使用「--no-show-forced-updates」\n"
-"或執行「git config fetch.showForcedUpdates false」避免此項檢查\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid "%s did not send all necessary objects\n"
-msgstr "%s 未傳送所有必需的物件\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid "rejected %s because shallow roots are not allowed to be updated"
-msgstr "已拒絕 %s,不允許更新淺複製"
-
-#: builtin/fetch.c
-#, c-format
-msgid "From %.*s\n"
-msgstr "來自 %.*s\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid ""
-"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' 來刪除舊的、有衝突的分支"
-
-#  譯者:請維持前導空格
-#: builtin/fetch.c
-#, c-format
-msgid "   (%s will become dangling)"
-msgstr "   (%s 將成為懸空狀態)"
-
-#  譯者:請維持前導空格
-#: builtin/fetch.c
-#, c-format
-msgid "   (%s has become dangling)"
-msgstr "   (%s 已成為懸空狀態)"
-
-#: builtin/fetch.c
-msgid "[deleted]"
-msgstr "[已刪除]"
-
-#: builtin/fetch.c builtin/remote.c
-msgid "(none)"
-msgstr "(無)"
-
-#: builtin/fetch.c
-#, c-format
-msgid "refusing to fetch into branch '%s' checked out at '%s'"
-msgstr "拒絕取得在「%2$s」簽出的「%1$s」分支"
-
-#: builtin/fetch.c
-#, c-format
-msgid "option \"%s\" value \"%s\" is not valid for %s"
-msgstr "選項「%s」的值「%s」對 %s 無效"
-
-#: builtin/fetch.c
-#, c-format
-msgid "option \"%s\" is ignored for %s\n"
-msgstr "選項「%s」被 %s 忽略\n"
-
-#: builtin/fetch.c object-file.c
-#, c-format
-msgid "%s is not a valid object"
-msgstr "%s 不是一個有效的物件"
-
-#: builtin/fetch.c
-#, c-format
-msgid "the object %s does not exist"
-msgstr "%s 物件不存在"
-
-#: builtin/fetch.c
-msgid "multiple branches detected, incompatible with --set-upstream"
-msgstr "檢測到多分支,和 --set-upstream 不相容"
-
-#: builtin/fetch.c
-#, c-format
-msgid ""
-"could not set upstream of HEAD to '%s' from '%s' when it does not point to "
-"any branch."
-msgstr ""
-"無法將 HEAD 的上游從「%2$s」設定為「%1$s」,因為 HEAD 沒有指向任何分支。"
-
-#: builtin/fetch.c
-msgid "not setting upstream for a remote remote-tracking branch"
-msgstr "沒有為一個遠端追蹤分支設定上游"
-
-#: builtin/fetch.c
-msgid "not setting upstream for a remote tag"
-msgstr "沒有為一個遠端標籤設定上游"
-
-#: builtin/fetch.c
-msgid "unknown branch type"
-msgstr "未知的分支類型"
-
-#: builtin/fetch.c
-msgid ""
-"no source branch found;\n"
-"you need to specify exactly one branch with the --set-upstream option"
-msgstr ""
-"找不到來源分支。\n"
-"您得使用 --set-upstream 選項明確指定一個分支"
-
-#: builtin/fetch.c
-#, c-format
-msgid "Fetching %s\n"
-msgstr "正在取得 %s\n"
-
-#: builtin/fetch.c
-#, c-format
-msgid "could not fetch %s"
-msgstr "不能取得 %s"
-
-#: builtin/fetch.c
-#, c-format
-msgid "could not fetch '%s' (exit code: %d)\n"
-msgstr "無法取得 '%s'(離開碼:%d)\n"
-
-#: builtin/fetch.c
-msgid ""
-"no remote repository specified; please specify either a URL or a\n"
-"remote name from which new revisions should be fetched"
-msgstr ""
-"未指定遠端版本庫。請指定用以取得新修訂版本之\n"
-"來源 URL 或者遠端版本庫名稱"
-
-#: builtin/fetch.c
-msgid "you need to specify a tag name"
-msgstr "您需要指定標籤名稱"
-
-#: builtin/fetch.c
 msgid "--negotiate-only needs one or more --negotiation-tip=*"
 msgstr "--negotiate-only 需要一或多個 --negotiation-tip=*"
 
@@ -7291,6 +7368,11 @@
 msgstr "對於一個完整的版本庫,參數 --unshallow 沒有意義"
 
 #: builtin/fetch.c
+#, c-format
+msgid "failed to fetch bundles from '%s'"
+msgstr "無法自「%s」抓取套件包"
+
+#: builtin/fetch.c
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all 不能帶一個版本庫參數"
 
@@ -7418,6 +7500,14 @@
 msgid "print only refs which don't contain the commit"
 msgstr "只列印不包含該提交的引用"
 
+#: builtin/for-each-ref.c
+msgid "read reference patterns from stdin"
+msgstr "從 stdin 讀取引用模式"
+
+#: builtin/for-each-ref.c
+msgid "unknown arguments supplied with --stdin"
+msgstr "為 --stdin 提供的引數未知"
+
 #: builtin/for-each-repo.c
 msgid "git for-each-repo --config=<config> [--] <arguments>"
 msgstr "git for-each-repo --config=<config> [--] <arguments>"
@@ -7434,6 +7524,11 @@
 msgid "missing --config=<config>"
 msgstr "缺少 --config=<設定>"
 
+#: builtin/for-each-repo.c
+#, c-format
+msgid "got bad config --config=%s"
+msgstr "收到無效的組態 --config=%s"
+
 #: builtin/fsck.c
 msgid "unknown"
 msgstr "未知"
@@ -7619,13 +7714,14 @@
 msgstr "注意:%s 指向一個尚未誕生的分支(%s)"
 
 #: builtin/fsck.c
-msgid "Checking cache tree"
-msgstr "正在檢查快取樹"
+#, c-format
+msgid "Checking cache tree of %s"
+msgstr "正在檢查 %s 的快取樹"
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in cache-tree"
-msgstr "%s:cache-tree 中無效的 sha1 指標"
+msgid "%s: invalid sha1 pointer in cache-tree of %s"
+msgstr "%s:%s 的 cache-tree 中的 sha1 指標無效"
 
 #: builtin/fsck.c
 msgid "non-tree in cache-tree"
@@ -7633,8 +7729,18 @@
 
 #: builtin/fsck.c
 #, c-format
-msgid "%s: invalid sha1 pointer in resolve-undo"
-msgstr "%s:resolve-undo 的 sha1 指針無效"
+msgid "%s: invalid sha1 pointer in resolve-undo of %s"
+msgstr "%s:%s 的 resolve-undo 中的 sha1 指標無效"
+
+#: builtin/fsck.c
+#, c-format
+msgid "unable to load rev-index for pack '%s'"
+msgstr "無法讀取 “%s” 封裝的修訂版索引 (rev-index)"
+
+#: builtin/fsck.c
+#, c-format
+msgid "invalid rev-index for pack '%s'"
+msgstr "“%s” 封裝的修訂版索引 (rev-index) 無效"
 
 #: builtin/fsck.c
 msgid ""
@@ -7670,7 +7776,7 @@
 
 #: builtin/fsck.c
 msgid "make reflogs head nodes (default)"
-msgstr "將引用日誌作為檢查的 HEAD 節點(預設)"
+msgstr "將引用日誌作為檢查的 HEAD 節點(預設值)"
 
 #: builtin/fsck.c
 msgid "also consider packs and alternate objects"
@@ -7841,7 +7947,7 @@
 msgid "failed to parse '%s' value '%s'"
 msgstr "無法解析 '%s' 值 '%s'"
 
-#: builtin/gc.c builtin/init-db.c
+#: builtin/gc.c setup.c
 #, c-format
 msgid "cannot stat '%s'"
 msgstr "不能對 '%s' 呼叫 stat"
@@ -7868,6 +7974,10 @@
 msgid "pack unreferenced objects separately"
 msgstr "獨立封裝無引用物件"
 
+#: builtin/gc.c builtin/repack.c
+msgid "with --cruft, limit the size of new cruft packs"
+msgstr "搭配 --cruft,限制新廢棄封裝的大小"
+
 #: builtin/gc.c
 msgid "be more thorough (increased runtime)"
 msgstr "更徹底(增加執行時間)"
@@ -8082,14 +8192,6 @@
 msgid "'crontab' died"
 msgstr "“crontab” 結束運作"
 
-#: builtin/gc.c
-msgid "failed to start systemctl"
-msgstr "無法啟動 systemctl"
-
-#: builtin/gc.c
-msgid "failed to run systemctl"
-msgstr "無法執行 systemctl"
-
 #: builtin/gc.c builtin/worktree.c
 #, c-format
 msgid "failed to delete '%s'"
@@ -8101,6 +8203,14 @@
 msgstr "排清 '%s' 失敗"
 
 #: builtin/gc.c
+msgid "failed to start systemctl"
+msgstr "無法啟動 systemctl"
+
+#: builtin/gc.c
+msgid "failed to run systemctl"
+msgstr "無法執行 systemctl"
+
+#: builtin/gc.c
 #, c-format
 msgid "unrecognized --scheduler argument '%s'"
 msgstr "無法識別的 --scheduler 引數 '%s'"
@@ -8131,6 +8241,10 @@
 msgstr "要觸發 git maintenance run 的排程器"
 
 #: builtin/gc.c
+msgid "failed to set up maintenance schedule"
+msgstr "無法設定維護排程"
+
+#: builtin/gc.c
 msgid "failed to add repo to global config"
 msgstr "無法將版本庫加至全域設定"
 
@@ -8152,7 +8266,6 @@
 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
@@ -8169,6 +8282,11 @@
 
 #: builtin/grep.c
 #, c-format
+msgid "unable to read tree %s"
+msgstr "無法讀取 %s 樹狀物件"
+
+#: builtin/grep.c
+#, c-format
 msgid "unable to grep from object of type %s"
 msgstr "無法抓取來自於 %s 類型的物件"
 
@@ -8224,11 +8342,11 @@
 
 #: builtin/grep.c
 msgid "search in subdirectories (default)"
-msgstr "在子目錄中尋找(預設)"
+msgstr "在子目錄中尋找(預設值)"
 
 #: builtin/grep.c
-msgid "descend at most <depth> levels"
-msgstr "最多以指定的深度向下尋找"
+msgid "descend at most <n> levels"
+msgstr "最多向下尋找 <n> 層"
 
 #: builtin/grep.c
 msgid "use extended POSIX regular expressions"
@@ -8236,7 +8354,7 @@
 
 #: builtin/grep.c
 msgid "use basic POSIX regular expressions (default)"
-msgstr "使用基本的 POSIX 常規表示式(預設)"
+msgstr "使用基本的 POSIX 常規表示式(預設值)"
 
 #: builtin/grep.c
 msgid "interpret patterns as fixed strings"
@@ -8581,13 +8699,21 @@
 msgstr "'git help config' 取得更多訊息"
 
 #: builtin/hook.c
-msgid "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
-msgstr "git hook run [--ignore-missing] <hook-name> [-- <hook-args>]"
+msgid ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
+msgstr ""
+"git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-"
+"args>]"
 
 #: builtin/hook.c
 msgid "silently ignore missing requested <hook-name>"
 msgstr "靜默忽略不存在而請求的 <hook-name>"
 
+#: builtin/hook.c
+msgid "file to read into hooks' stdin"
+msgstr "要讀進掛鉤 stdin 的檔案"
+
 #: builtin/index-pack.c
 #, c-format
 msgid "object type mismatch at %s"
@@ -8681,11 +8807,6 @@
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "發現 %s 出現 SHA1 衝突!"
 
-#: builtin/index-pack.c builtin/pack-objects.c
-#, c-format
-msgid "unable to read %s"
-msgstr "不能讀 %s"
-
 #: builtin/index-pack.c
 #, c-format
 msgid "cannot read existing object info %s"
@@ -8838,7 +8959,7 @@
 msgid "bad %s"
 msgstr "錯誤選項 %s"
 
-#: builtin/index-pack.c builtin/init-db.c
+#: builtin/index-pack.c builtin/init-db.c setup.c
 #, c-format
 msgid "unknown hash algorithm '%s'"
 msgstr "未知的「%s」雜湊算法"
@@ -8856,103 +8977,16 @@
 msgstr "在打包物件中 fsck 檢查發生錯誤"
 
 #: builtin/init-db.c
-#, c-format
-msgid "cannot stat template '%s'"
-msgstr "不能對範本 '%s' 呼叫 stat"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot opendir '%s'"
-msgstr "不能開啟目錄 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot readlink '%s'"
-msgstr "不能讀取連結 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot symlink '%s' '%s'"
-msgstr "不能自 '%s' 到 '%s' 建立符號連結"
-
-#: builtin/init-db.c
-#, c-format
-msgid "cannot copy '%s' to '%s'"
-msgstr "不能複製 '%s' 至 '%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "ignoring template %s"
-msgstr "忽略範本 %s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "templates not found in %s"
-msgstr "沒有在 %s 中找到範本"
-
-#: builtin/init-db.c
-#, c-format
-msgid "not copying templates from '%s': %s"
-msgstr "沒有從 '%s' 複製範本:%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "invalid initial branch name: '%s'"
-msgstr "無效的初始分支名稱:'%s'"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to handle file type %d"
-msgstr "不能處理 %d 類型的檔案"
-
-#: builtin/init-db.c
-#, c-format
-msgid "unable to move %s to %s"
-msgstr "不能移動 %s 至 %s"
-
-#: builtin/init-db.c
-msgid "attempt to reinitialize repository with different hash"
-msgstr "嘗試以不同的雜湊值重新初始化版本庫"
-
-#: builtin/init-db.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已經存在"
-
-#: builtin/init-db.c
-#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: 忽略 --initial-branch=%s"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing shared Git repository in %s%s\n"
-msgstr "重新初始化已存在的共享 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Reinitialized existing Git repository in %s%s\n"
-msgstr "重新初始化已存在的 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty shared Git repository in %s%s\n"
-msgstr "已初始化空的共享 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
-#, c-format
-msgid "Initialized empty Git repository in %s%s\n"
-msgstr "已初始化空的 Git 版本庫於 %s%s\n"
-
-#: builtin/init-db.c
 msgid ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 msgstr ""
 "git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
 "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+"         [--ref-format=<format>]\n"
 "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
 "         [--shared[=<permissions>]] [<directory>]"
 
@@ -9005,11 +9039,11 @@
 #: builtin/interpret-trailers.c
 msgid ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer <token>[(=|:)<value>])...]\n"
+"                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
 "                       [--parse] [<file>...]"
 
 #: builtin/interpret-trailers.c
@@ -9021,6 +9055,10 @@
 msgstr "刪除空的尾部署名"
 
 #: builtin/interpret-trailers.c
+msgid "placement"
+msgstr "placement"
+
+#: builtin/interpret-trailers.c
 msgid "where to place the new trailer"
 msgstr "在哪裡放置新的尾部署名"
 
@@ -9037,20 +9075,20 @@
 msgstr "只輸出尾部署名"
 
 #: builtin/interpret-trailers.c
-msgid "do not apply config rules"
-msgstr "不要套用組態設定規則"
+msgid "do not apply trailer.* configuration variables"
+msgstr "不套用 trailer.* 組態變數"
 
 #: builtin/interpret-trailers.c
-msgid "join whitespace-continued values"
-msgstr "連線空白折行的值"
+msgid "reformat multiline trailer values as single-line values"
+msgstr "將多列尾注值 (trailer values) 重新格式化為單列值"
 
 #: builtin/interpret-trailers.c
-msgid "set parsing options"
-msgstr "設定解析選項"
+msgid "alias for --only-trailers --only-input --unfold"
+msgstr "--only-trailers --only-input --unfold 的別名"
 
 #: builtin/interpret-trailers.c
-msgid "do not treat --- specially"
-msgstr "不要對 --- 特殊處理"
+msgid "do not treat \"---\" as the end of input"
+msgstr "不要把 “---” 當作輸入結尾"
 
 #: builtin/interpret-trailers.c
 msgid "trailer(s) to add"
@@ -9107,7 +9145,7 @@
 "<file>"
 msgstr "追蹤 <開始>,<結束> 範圍中橫列或 <檔案> 中> :<函數名稱> 的變化史"
 
-#: builtin/log.c builtin/shortlog.c bundle.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "無法識別的參數:%s"
@@ -9163,6 +9201,11 @@
 msgstr "不是一個範圍"
 
 #: builtin/log.c
+#, c-format
+msgid "unable to read branch description file '%s'"
+msgstr "無法讀取分支描述檔 “%s”"
+
+#: builtin/log.c
 msgid "cover letter needs email format"
 msgstr "附函需要信件位址格式"
 
@@ -9289,6 +9332,10 @@
 msgstr "基於分支描述產生部分附函"
 
 #: builtin/log.c
+msgid "use branch description from file"
+msgstr "從檔案讀取分支描述並使用"
+
+#: builtin/log.c
 msgid "use [<prefix>] instead of [PATCH]"
 msgstr "使用 [<前綴>] 代替 [PATCH]"
 
@@ -9314,7 +9361,7 @@
 
 #: builtin/log.c
 msgid "show patch format instead of default (patch + stat)"
-msgstr "顯示純修補檔格式而非預設的(修補檔+狀態)"
+msgstr "顯示純修補檔格式而非預設值(修補檔+狀態)"
 
 #: builtin/log.c
 msgid "Messaging"
@@ -9330,7 +9377,7 @@
 
 #: builtin/log.c
 msgid "email"
-msgstr "信件位址"
+msgstr "信箱"
 
 #: builtin/log.c
 msgid "add To: header"
@@ -9491,6 +9538,11 @@
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "不能找到追蹤的遠端分支,請手動指定 <上游>。\n"
 
+#: builtin/ls-files.c builtin/ls-tree.c
+#, c-format
+msgid "could not get object info about '%s'"
+msgstr "無法取得「%s」相關的物件資訊"
+
 #: builtin/ls-files.c
 #, c-format
 msgid "bad ls-files format: element '%s' does not start with '('"
@@ -9528,7 +9580,7 @@
 
 #: builtin/ls-files.c
 msgid "show cached files in the output (default)"
-msgstr "顯示快取的檔案(預設)"
+msgstr "顯示快取的檔案(預設值)"
 
 #: builtin/ls-files.c
 msgid "show deleted files in the output"
@@ -9629,11 +9681,11 @@
 msgid ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
 "              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [--symref] [<repository> [<patterns>...]]"
 msgstr ""
 "git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
 "              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
-"              [--symref] [<repository> [<refs>...]]"
+"              [--symref] [<repository> [<patterns>...]]"
 
 #: builtin/ls-remote.c
 msgid "do not print remote URL"
@@ -9677,11 +9729,6 @@
 
 #: builtin/ls-tree.c
 #, c-format
-msgid "could not get object info about '%s'"
-msgstr "無法取得「%s」相關的物件資訊"
-
-#: builtin/ls-tree.c
-#, c-format
 msgid "bad ls-tree format: element '%s' does not start with '('"
 msgstr "無效的 ls-tree 格式:「%s」元素的開頭不是「(」"
 
@@ -9841,11 +9888,22 @@
 "git merge-file [<選項>] [-L <檔案1> [-L <初始> [-L <名字2>]]] <檔案1> <初始文"
 "件> <檔案2>"
 
+#: builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"diff-algorithm 選項有 \"myers\"、\"minimal\"、\"patience\" 和 \"histogram\""
+
 #: builtin/merge-file.c
 msgid "send results to standard output"
 msgstr "將結果傳送到標準輸出"
 
 #: builtin/merge-file.c
+msgid "use object IDs instead of filenames"
+msgstr "使用物件 ID 取代檔名"
+
+#: builtin/merge-file.c
 msgid "use a diff3 based merge"
 msgstr "使用基於 diff3 的合併"
 
@@ -9865,6 +9923,14 @@
 msgid "for conflicts, use a union version"
 msgstr "如果衝突,使用聯合版本"
 
+#: builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<演算法>"
+
+#: builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "選擇一個差異演算法"
+
 #: builtin/merge-file.c
 msgid "for conflicts, use this marker size"
 msgstr "如果衝突,使用指定長度的標記"
@@ -9877,6 +9943,15 @@
 msgid "set labels for file1/orig-file/file2"
 msgstr "為 檔案1/初始檔案/檔案2 設定標籤"
 
+#: builtin/merge-file.c
+#, c-format
+msgid "object '%s' does not exist"
+msgstr "物件 “%s” 不存在"
+
+#: builtin/merge-file.c
+msgid "Could not write object file"
+msgstr "無法寫入物件檔案"
+
 #: builtin/merge-recursive.c
 #, c-format
 msgid "unknown option %s"
@@ -9952,9 +10027,26 @@
 msgstr "執行多次合併,一次執行輸入一列"
 
 #: builtin/merge-tree.c
+msgid "specify a merge-base for the merge"
+msgstr "指定用來合併的合併基底"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option=value"
+msgstr "option=value"
+
+#: builtin/merge-tree.c builtin/merge.c builtin/pull.c
+msgid "option for selected merge strategy"
+msgstr "所選的合併策略的選項"
+
+#: builtin/merge-tree.c
 msgid "--trivial-merge is incompatible with all other options"
 msgstr "--trivial-merge 和其他所有選項都不相容"
 
+#: builtin/merge-tree.c builtin/merge.c
+#, c-format
+msgid "unknown strategy option: -X%s"
+msgstr "未知的策略選項:-X%s"
+
 #: builtin/merge-tree.c builtin/notes.c
 #, c-format
 msgid "malformed input line: '%s'."
@@ -10015,7 +10107,7 @@
 
 #: builtin/merge.c builtin/pull.c
 msgid "perform a commit if the merge succeeds (default)"
-msgstr "如果合併成功,執行一次提交(預設)"
+msgstr "如果合併成功,執行一次提交(預設值)"
 
 #: builtin/merge.c builtin/pull.c
 msgid "edit message before committing"
@@ -10023,7 +10115,7 @@
 
 #: builtin/merge.c
 msgid "allow fast-forward (default)"
-msgstr "允許快轉(預設)"
+msgstr "允許快轉(預設值)"
 
 #: builtin/merge.c builtin/pull.c
 msgid "abort if fast-forward is not possible"
@@ -10042,14 +10134,6 @@
 msgid "merge strategy to use"
 msgstr "要使用的合併策略"
 
-#: builtin/merge.c builtin/pull.c
-msgid "option=value"
-msgstr "option=value"
-
-#: builtin/merge.c builtin/pull.c
-msgid "option for selected merge strategy"
-msgstr "所選的合併策略的選項"
-
 #: builtin/merge.c
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "合併的提交說明(針對非快轉式合併)"
@@ -10121,7 +10205,7 @@
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "壞的 branch.%s.mergeoptions 字串:%s"
 
-#: builtin/merge.c builtin/stash.c merge-recursive.c
+#: builtin/merge.c merge-recursive.c
 msgid "Unable to write index."
 msgstr "不能寫入索引。"
 
@@ -10131,11 +10215,6 @@
 
 #: builtin/merge.c
 #, c-format
-msgid "unknown strategy option: -X%s"
-msgstr "未知的策略選項:-X%s"
-
-#: builtin/merge.c t/helper/test-fast-rebase.c
-#, c-format
 msgid "unable to write %s"
 msgstr "不能寫 %s"
 
@@ -10206,7 +10285,7 @@
 msgid "Bad value '%s' in environment '%s'"
 msgstr "環境 '%2$s' 中存在壞的取值 '%1$s'"
 
-#: builtin/merge.c read-cache.c strbuf.c wrapper.c
+#: builtin/merge.c editor.c read-cache.c wrapper.c
 #, c-format
 msgid "could not close '%s'"
 msgstr "不能關閉 '%s'"
@@ -10492,8 +10571,8 @@
 msgstr "不能將目錄移動到自身"
 
 #: builtin/mv.c
-msgid "cannot move directory over file"
-msgstr "不能將目錄移動到檔案"
+msgid "destination already exists"
+msgstr "目的地已存在"
 
 #: builtin/mv.c
 msgid "source directory is empty"
@@ -10585,7 +10664,7 @@
 
 #: builtin/name-rev.c
 msgid "allow to print `undefined` names (default)"
-msgstr "允許列印 `未定義` 的名稱(預設)"
+msgstr "允許列印 `未定義` 的名稱(預設值)"
 
 #: builtin/name-rev.c
 msgid "dereference tags in the input (internal use)"
@@ -10597,11 +10676,13 @@
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> "
-"| (-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <註解引用>] add [-f] [--allow-empty] [-m <說明> | -F <檔案> "
-"| (-c | -C) <物件>] [<物件>]"
+"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
@@ -10609,11 +10690,13 @@
 
 #: builtin/notes.c
 msgid ""
-"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | "
-"(-c | -C) <object>] [<object>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 msgstr ""
-"git notes [--ref <註解引用>] append [--allow-empty] [-m <說明> | -F <檔案> | "
-"(-c | -C) <物件>] [<物件>]"
+"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--"
+"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c "
+"| -C) <object>] [<object>]"
 
 #: builtin/notes.c
 msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
@@ -10782,6 +10865,18 @@
 msgstr "取代已存在的註解"
 
 #: builtin/notes.c
+msgid "<paragraph-break>"
+msgstr "<paragraph-break>"
+
+#: builtin/notes.c
+msgid "insert <paragraph-break> between paragraphs"
+msgstr "在段落間插入 <paragraph-break>"
+
+#: builtin/notes.c
+msgid "remove unnecessary whitespace"
+msgstr "移除不必要的空白字元"
+
+#: builtin/notes.c
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -11039,7 +11134,7 @@
 msgid "wrote %<PRIu32> objects while expecting %<PRIu32>"
 msgstr "寫入 %<PRIu32> 個物件而預期 %<PRIu32> 個"
 
-#: builtin/pack-objects.c
+#: builtin/pack-objects.c builtin/repack.c
 msgid "disabling bitmap writing, as some objects are not being packed"
 msgstr "停用 bitmap 寫入,因為一些物件將不會被打包"
 
@@ -11106,6 +11201,11 @@
 
 #: builtin/pack-objects.c
 #, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "無效的 pack.allowPackReuse 值:“%s”"
+
+#: builtin/pack-objects.c
+#, c-format
 msgid ""
 "value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
 "hash> <uri>' (got '%s')"
@@ -11206,6 +11306,14 @@
 msgstr "壞的索引版本 '%s'"
 
 #: builtin/pack-objects.c
+msgid "show progress meter during object writing phase"
+msgstr "在物件寫入階段顯示進度列"
+
+#: builtin/pack-objects.c
+msgid "similar to --all-progress when progress meter is shown"
+msgstr "顯示進度列時類似 --all-progress"
+
+#: builtin/pack-objects.c
 msgid "<version>[,<offset>]"
 msgstr "<版本>[,<位移>]"
 
@@ -11397,10 +11505,6 @@
 msgstr "--thin 不能用於建立一個可索引包"
 
 #: builtin/pack-objects.c
-msgid "cannot use --filter without --stdout"
-msgstr "不能在沒有 --stdout 的情況下使用 --filter"
-
-#: builtin/pack-objects.c
 msgid "cannot use --filter with --stdin-packs"
 msgstr "無法將 --filter 及 --stdin-packs 結合使用"
 
@@ -11417,10 +11521,6 @@
 msgstr "無法將 --stdin-packs 與 --cruft 組合使用"
 
 #: builtin/pack-objects.c
-msgid "cannot use --max-pack-size with --cruft"
-msgstr "無法將 --max-pack-size 與 --cruft 組合使用"
-
-#: builtin/pack-objects.c
 msgid "Enumerating objects"
 msgstr "枚舉物件"
 
@@ -11428,10 +11528,10 @@
 #, c-format
 msgid ""
 "Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
 msgstr ""
 "總共 %<PRIu32> (差異 %<PRIu32>),復用 %<PRIu32> (差異 %<PRIu32>),重用包 "
-"%<PRIu32>"
+"%<PRIu32> (總共 %<PRIuMAX>)"
 
 #: builtin/pack-redundant.c
 msgid ""
@@ -11447,9 +11547,17 @@
 "<git@vger.kernel.org> 讓我們知道您還在使用。\n"
 "感謝。\n"
 
+#: builtin/pack-redundant.c
+msgid "refusing to run without --i-still-use-this"
+msgstr "傳入 --i-still-use-this 前拒絕執行"
+
 #: builtin/pack-refs.c
-msgid "git pack-refs [--all] [--no-prune]"
-msgstr "git pack-refs [--all] [--no-prune]"
+msgid ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
+msgstr ""
+"git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude "
+"<pattern>]"
 
 #: builtin/pack-refs.c
 msgid "pack everything"
@@ -11457,7 +11565,15 @@
 
 #: builtin/pack-refs.c
 msgid "prune loose refs (default)"
-msgstr "剪除鬆散引用(預設)"
+msgstr "剪除鬆散引用(預設值)"
+
+#: builtin/pack-refs.c
+msgid "references to include"
+msgstr "要包含的引用"
+
+#: builtin/pack-refs.c
+msgid "references to exclude"
+msgstr "要排除的引用"
 
 #: builtin/patch-id.c
 msgid "git patch-id [--stable | --unstable | --verbatim]"
@@ -11535,6 +11651,14 @@
 msgid "number of submodules pulled in parallel"
 msgstr "並行拉取的子模組數量"
 
+#: builtin/pull.c parse-options.h
+msgid "use IPv4 addresses only"
+msgstr "只使用 IPv4 位址"
+
+#: builtin/pull.c parse-options.h
+msgid "use IPv6 addresses only"
+msgstr "只使用 IPv6 位址"
+
 #: builtin/pull.c
 msgid ""
 "There is no candidate for rebasing against among the refs that you just "
@@ -11650,9 +11774,9 @@
 msgid "pull with rebase"
 msgstr "重定基底式拉取"
 
-#: builtin/pull.c
-msgid "please commit or stash them."
-msgstr "請提交或貯存它們。"
+#: builtin/pull.c builtin/rebase.c
+msgid "Please commit or stash them."
+msgstr "請提交或貯存修改。"
 
 #: builtin/pull.c
 #, c-format
@@ -11819,37 +11943,37 @@
 #: builtin/push.c
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Integrate the remote changes (e.g.\n"
-"'git pull ...') before pushing again.\n"
+"its remote counterpart. If you want to integrate the remote changes,\n"
+"use 'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒絕,因為您目前分支的最新提交落後於其對應的遠端分支。\n"
-"再次推送前,先與遠端變更合併(如 'git pull ...')。詳見\n"
-"'git push --help' 中的 'Note about fast-forwards' 小節。"
+"如果您想要整合遠端更動,請在再次推送前使用 “git pull”。詳見\n"
+"“git push --help” 中的〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and integrate the remote changes\n"
-"(e.g. 'git pull ...') before pushing again.\n"
+"counterpart. If you want to integrate the remote changes, use 'git pull'\n"
+"before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"更新被拒絕,因為推送的一個分支的最新提交落後於其對應的遠端分支。\n"
-"簽出該分支並整合遠端變更(如 'git pull ...'),然後再推送。詳見\n"
-"'git push --help' 中的 'Note about fast-forwards' 小節。"
+"更新被拒絕,因為推送的某分支的最新提交落後於其對應的遠端分支。\n"
+"如果您想要整合遠端更動,請在再次推送前使用 “git pull”。詳見\n"
+"“git push --help” 中的〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the remote contains work that you do\n"
-"not have locally. This is usually caused by another repository pushing\n"
-"to the same ref. You may want to first integrate the remote changes\n"
-"(e.g., 'git pull ...') before pushing again.\n"
+"Updates were rejected because the remote contains work that you do not\n"
+"have locally. This is usually caused by another repository pushing to\n"
+"the same ref. If you want to integrate the remote changes, use\n"
+"'git pull' before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"因為遠端版本庫包含您本機尚不存在的提交,而導致更新被拒絕。這通常是因為另外\n"
-"一個版本庫已向該引用進行了推送。再次推送前,您可能需要先整合遠端變更\n"
-"(如 'git pull ...')。\n"
-"詳見 'git push --help' 中的 'Note about fast-forwards' 小節。"
+"更新被拒絕,因為遠端包含您本機沒有的提交。這通常是因為\n"
+"另一個版本庫有推送更動到同個引用。如果您想要整合遠端更動,\n"
+"請在再次推送前使用 “git pull”。詳見 “git push --help” 中的\n"
+"〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 msgid "Updates were rejected because the tag already exists in the remote."
@@ -11866,14 +11990,14 @@
 
 #: builtin/push.c
 msgid ""
-"Updates were rejected because the tip of the remote-tracking\n"
-"branch has been updated since the last checkout. You may want\n"
-"to integrate those changes locally (e.g., 'git pull ...')\n"
-"before forcing an update.\n"
+"Updates were rejected because the tip of the remote-tracking branch has\n"
+"been updated since the last checkout. If you want to integrate the\n"
+"remote changes, use 'git pull' before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"更新被拒,因為遠端追蹤分支的最新指針繼上次簽出後有更新。\n"
-"您可能會希望先將這些變更整合至本地(例如:‘git pull …’)\n"
-"最後才強制更新。\n"
+"更新被拒絕,因為遠端追蹤分支的最新提交自上次簽出後有改變。\n"
+"如果您想要整合遠端更動,請在再次推送前使用 “git pull”。\n"
+"詳見 “git push --help” 中的〈Note about fast-forwards〉小節。"
 
 #: builtin/push.c
 #, c-format
@@ -11900,9 +12024,9 @@
 msgid "repository"
 msgstr "版本庫"
 
-#: builtin/push.c builtin/send-pack.c
-msgid "push all refs"
-msgstr "推送所有引用"
+#: builtin/push.c
+msgid "push all branches"
+msgstr "推送所有分支"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "mirror all refs"
@@ -11913,8 +12037,8 @@
 msgstr "刪除引用"
 
 #: builtin/push.c
-msgid "push tags (can't be used with --all or --mirror)"
-msgstr "推送標籤(不能使用 --all or --mirror)"
+msgid "push tags (can't be used with --all or --branches or --mirror)"
+msgstr "推送標籤(不能和 --all、--branches 或 --mirror 一起使用)"
 
 #: builtin/push.c builtin/send-pack.c
 msgid "force updates"
@@ -11972,7 +12096,7 @@
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete 未接任何引用沒有意義"
 
-#: builtin/push.c
+#: builtin/push.c t/helper/test-bundle-uri.c
 #, c-format
 msgid "bad repository '%s'"
 msgstr "壞的版本庫 '%s'"
@@ -12239,10 +12363,19 @@
 
 #: builtin/rebase.c
 #, c-format
+msgid "Unknown rebase-merges mode: %s"
+msgstr "未知的 rebase-merges 模式:%s"
+
+#: builtin/rebase.c
+#, c-format
 msgid "could not switch to %s"
 msgstr "無法切換到 %s"
 
 #: builtin/rebase.c
+msgid "apply options and merge options cannot be used together"
+msgstr "套用選項與合併選項不得同時使用"
+
+#: builtin/rebase.c
 #, c-format
 msgid ""
 "unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and "
@@ -12250,6 +12383,15 @@
 msgstr "無法識別的 '%s' 空類型;有效的數值有 \"drop\"、\"keep\" 跟 \"ask\"。"
 
 #: builtin/rebase.c
+msgid ""
+"--rebase-merges with an empty string argument is deprecated and will stop "
+"working in a future version of Git. Use --rebase-merges without an argument "
+"instead, which does the same thing."
+msgstr ""
+"有字串引數的 --rebase-merges 已被棄用,而且會在未來的 Git 版本停止運作。請改"
+"用無引數的 --rebase-merges,功能相同。"
+
+#: builtin/rebase.c
 #, c-format
 msgid ""
 "%s\n"
@@ -12468,7 +12610,7 @@
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr "動作 --edit-todo 只能用在互動式重定基底過程中。"
 
-#: builtin/rebase.c t/helper/test-fast-rebase.c
+#: builtin/rebase.c
 msgid "Cannot read HEAD"
 msgstr "不能讀取 HEAD"
 
@@ -12513,17 +12655,16 @@
 msgstr "開關 `C' 期望一個數字值"
 
 #: builtin/rebase.c
-#, c-format
-msgid "Unknown mode: %s"
-msgstr "未知模式:%s"
+msgid ""
+"apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
+"no-rebase-merges"
+msgstr "apply 選項與 rebase.rebaseMerges 不相容。請考慮加上 --no-rebase-merges"
 
 #: builtin/rebase.c
-msgid "--strategy requires --merge or --interactive"
-msgstr "--strategy 需要 --merge 或 --interactive"
-
-#: builtin/rebase.c
-msgid "apply options and merge options cannot be used together"
-msgstr "套用選項與合併選項不得同時使用"
+msgid ""
+"apply options are incompatible with rebase.updateRefs.  Consider adding --no-"
+"update-refs"
+msgstr "apply 選項與 rebase.updateRefs 不相容。請考慮加上 --no-update-refs"
 
 #: builtin/rebase.c
 #, c-format
@@ -12573,10 +12714,6 @@
 msgstr "沒有指向一個有效的提交 '%s'"
 
 #: builtin/rebase.c
-msgid "Please commit or stash them."
-msgstr "請提交或貯存修改。"
-
-#: builtin/rebase.c
 msgid "HEAD is up to date."
 msgstr "HEAD 是最新的。"
 
@@ -12879,12 +13016,12 @@
 msgstr "抓取遠端的分支"
 
 #: builtin/remote.c
-msgid "import all tags and associated objects when fetching"
-msgstr "抓取時匯入所有的標籤和關聯物件"
-
-#: builtin/remote.c
-msgid "or do not fetch any tag at all (--no-tags)"
-msgstr "或不抓取任何標籤(--no-tags)"
+msgid ""
+"import all tags and associated objects when fetching\n"
+"or do not fetch any tag at all (--no-tags)"
+msgstr ""
+"抓取時匯入所有的標籤和關聯物件\n"
+"或者是完全不抓取所有標籤 (--no-tags)"
 
 #: builtin/remote.c
 msgid "branch(es) to track"
@@ -13002,7 +13139,7 @@
 msgid_plural ""
 "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
 "to delete them, use:"
-msgstr[0] "注意:ref/remotes 層級之外的一個分支未被移除。要刪除它,使用:"
+msgstr[0] "注意:refs/remotes/ 層級之外的一個分支未被移除。要刪除它,使用:"
 
 #: builtin/remote.c
 #, c-format
@@ -13359,6 +13496,11 @@
 msgstr "無法移除過時位圖:%s"
 
 #: builtin/repack.c
+#, c-format
+msgid "pack prefix %s does not begin with objdir %s"
+msgstr "封包前綴 %s 不以 objdir %s 開頭"
+
+#: builtin/repack.c
 msgid "pack everything in a single pack"
 msgstr "所有內容打包到一個包檔案中"
 
@@ -13375,8 +13517,8 @@
 msgstr "近似日期"
 
 #: builtin/repack.c
-msgid "with -C, expire objects older than this"
-msgstr "搭配 -C 會將早於此的物件設為過期"
+msgid "with --cruft, expire objects older than this"
+msgstr "搭配 --cruft 會將早於此的物件標為過期"
 
 #: builtin/repack.c
 msgid "remove redundant packs, and run git-prune-packed"
@@ -13459,17 +13601,21 @@
 msgstr "封裝前綴,儲存為包含過時物件的套件包"
 
 #: builtin/repack.c
+msgid "pack prefix to store a pack containing filtered out objects"
+msgstr "將前綴進行包裝,儲存為包含已過濾物件的封裝"
+
+#: builtin/repack.c
 msgid "cannot delete packs in a precious-objects repo"
 msgstr "不能刪除珍品版本庫中的封包"
 
 #: builtin/repack.c
-msgid "Nothing new to pack."
-msgstr "沒有新的要打包。"
+#, c-format
+msgid "option '%s' can only be used along with '%s'"
+msgstr "“%s” 選項只能與 “%s” 一起使用"
 
 #: builtin/repack.c
-#, c-format
-msgid "pack prefix %s does not begin with objdir %s"
-msgstr "封包前綴 %s 不以 objdir %s 開頭"
+msgid "Nothing new to pack."
+msgstr "沒有新的要打包。"
 
 #: builtin/repack.c
 #, c-format
@@ -13484,7 +13630,7 @@
 #: builtin/repack.c sequencer.c
 #, c-format
 msgid "could not unlink: %s"
-msgstr "無法取消連結:%s"
+msgstr "無法刪除:%s"
 
 #: builtin/replace.c
 msgid "git replace [-f] <object> <replacement>"
@@ -13722,6 +13868,86 @@
 msgid "only one pattern can be given with -l"
 msgstr "只能為 -l 提供一個模式"
 
+#: builtin/replay.c
+msgid "need some commits to replay"
+msgstr "需要一些提交才能重放"
+
+#: builtin/replay.c
+msgid "--onto and --advance are incompatible"
+msgstr "--onto 和 --advance 不相容"
+
+#: 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
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr "無法假設這是 --advance 還是 --onto 動作"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr "無法由多個來源分支演進目的地,以免無法確定排序"
+
+#: builtin/replay.c
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "無法假設 --onto 的正確基底"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(實驗性功能!)git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "在指定分支上進行重放演進"
+
+#: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "重放到指定提交"
+
+#: builtin/replay.c
+msgid "advance all branches contained in revision-range"
+msgstr "演進所有包含在 revision-range 中的分支"
+
+#: builtin/replay.c
+msgid "option --onto or --advance is mandatory"
+msgstr "必須傳入 --onto 或 --advance 選項"
+
+#: builtin/replay.c
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr "將覆寫部分修訂版遍歷選項,強制使用 “struct rev_info” 的 “%s” 位元"
+
+#: builtin/replay.c
+msgid "error preparing revisions"
+msgstr "無法準備修訂集"
+
+#: builtin/replay.c
+msgid "replaying down to root commit is not supported yet!"
+msgstr "尚不支援重放到根提交!"
+
+#: builtin/replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "尚不支援重放合併提交!"
+
 #: builtin/rerere.c
 msgid ""
 "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
@@ -13984,23 +14210,15 @@
 msgid "unknown mode for --abbrev-ref: %s"
 msgstr "--abbrev-ref 的模式未知:%s"
 
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden 無法與 --branches 同時使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden 無法與 --tags 同時使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden 無法與 --remotes 同時使用"
-
 #: builtin/rev-parse.c setup.c
 msgid "this operation must be run in a work tree"
 msgstr "該動作必須在一個工作區中執行"
 
 #: builtin/rev-parse.c
+msgid "Could not read the index"
+msgstr "無法讀取索引"
+
+#: builtin/rev-parse.c
 #, c-format
 msgid "unknown mode for --show-object-format: %s"
 msgstr "--show-object-format 的模式未知:%s"
@@ -14209,6 +14427,10 @@
 msgstr "遠端名稱"
 
 #: builtin/send-pack.c
+msgid "push all refs"
+msgstr "推送所有引用"
+
+#: builtin/send-pack.c
 msgid "use stateless RPC protocol"
 msgstr "使用無狀態的 RPC 協定"
 
@@ -14416,19 +14638,41 @@
 
 #: builtin/show-ref.c
 msgid ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 msgstr ""
-"git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n"
+"git show-ref [--head] [-d | --dereference]\n"
 "             [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n"
 "             [--heads] [--] [<pattern>...]"
 
 #: builtin/show-ref.c
+msgid ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+msgstr ""
+"git show-ref --verify [-q | --quiet] [-d | --dereference]\n"
+"             [-s | --hash[=<n>]] [--abbrev[=<n>]]\n"
+"             [--] [<ref>...]"
+
+#: builtin/show-ref.c
 msgid "git show-ref --exclude-existing[=<pattern>]"
 msgstr "git show-ref --exclude-existing[=<模式>]"
 
 #: builtin/show-ref.c
+msgid "git show-ref --exists <ref>"
+msgstr "git show-ref --exists <ref>"
+
+#: builtin/show-ref.c
+msgid "reference does not exist"
+msgstr "引用不存在"
+
+#: builtin/show-ref.c
+msgid "failed to look up reference"
+msgstr "無法查詢引用"
+
+#: builtin/show-ref.c
 msgid "only show tags (can be combined with heads)"
 msgstr "只顯示標籤(可以和頭共用)"
 
@@ -14437,6 +14681,10 @@
 msgstr "只顯示頭(可以和標籤共用)"
 
 #: builtin/show-ref.c
+msgid "check for reference existence without resolving"
+msgstr "檢查引用是否存在但不解析"
+
+#: builtin/show-ref.c
 msgid "stricter reference checking, requires exact ref path"
 msgstr "更嚴格的引用檢測,需要精確的引用路徑"
 
@@ -14462,9 +14710,11 @@
 
 #: builtin/sparse-checkout.c
 msgid ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 msgstr ""
-"git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"
+"git sparse-checkout (init | list | set | add | reapply | disable | check-"
+"rules) [<options>]"
 
 #: builtin/sparse-checkout.c
 msgid "this worktree is not sparse"
@@ -14605,6 +14855,27 @@
 msgid "error while refreshing working directory"
 msgstr "重新整理工作目錄時發生錯誤"
 
+#: builtin/sparse-checkout.c
+msgid ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+msgstr ""
+"git sparse-checkout check-rules [-z] [--skip-checks][--[no-]cone] [--rules-"
+"file <file>]"
+
+#: builtin/sparse-checkout.c
+msgid "terminate input and output files by a NUL character"
+msgstr "使用 NUL 字元終止輸入和輸出檔案"
+
+#: builtin/sparse-checkout.c
+msgid "when used with --rules-file interpret patterns as cone mode patterns"
+msgstr ""
+"與 --rules-file 搭配使用時,將 patterns (模式) 解釋為 cone 模式的 patterns"
+
+#: builtin/sparse-checkout.c
+msgid "use patterns in <file> instead of the current ones."
+msgstr "使用 <file> 中的(而非目前使用的)模式。"
+
 #: builtin/stash.c
 msgid "git stash list [<log-options>]"
 msgstr "git stash list [<log-options>]"
@@ -15240,6 +15511,11 @@
 
 #: builtin/submodule--helper.c
 #, c-format
+msgid "cannot clone submodule '%s' without a URL"
+msgstr "無法在沒有網址的情況下複製 “%s” 子模組"
+
+#: builtin/submodule--helper.c
+#, c-format
 msgid "Failed to clone '%s'. Retry scheduled"
 msgstr "複製 '%s' 失敗。已排程重試作業"
 
@@ -15396,6 +15672,10 @@
 "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] "
 "[--] [<path>...]"
 
+#: builtin/submodule--helper.c submodule.c
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "無法將 HEAD 解析為有效引用。"
+
 #: builtin/submodule--helper.c
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<options>] [<path>...]"
@@ -15562,11 +15842,6 @@
 msgid "git submodule--helper <command>"
 msgstr "git submodule--helper <command>"
 
-#: builtin/submodule--helper.c git.c
-#, c-format
-msgid "%s doesn't support --super-prefix"
-msgstr "%s 不支援 --super-prefix"
-
 #: builtin/symbolic-ref.c
 msgid "git symbolic-ref [-m <reason>] <name> <ref>"
 msgstr "git symbolic-ref [-m <reason>] <name> <ref>"
@@ -15977,6 +16252,10 @@
 msgstr "以這種格式寫入索引區"
 
 #: builtin/update-index.c
+msgid "report on-disk index format version"
+msgstr "回報磁碟上索引格式的版本"
+
+#: builtin/update-index.c
 msgid "enable or disable split index"
 msgstr "啟用或停用索引分割"
 
@@ -16009,6 +16288,16 @@
 msgstr "清除 fsmonitor 有效位"
 
 #: builtin/update-index.c
+#, c-format
+msgid "%d\n"
+msgstr "%d\n"
+
+#: builtin/update-index.c
+#, c-format
+msgid "index-version: was %d, set to %d"
+msgstr "index-version:曾是 %d,已設為 %d"
+
+#: builtin/update-index.c
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
 "enable split index"
@@ -16153,10 +16442,10 @@
 #: builtin/worktree.c
 msgid ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 msgstr ""
 "git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n"
-"                 [-b <new-branch>] <path> [<commit-ish>]"
+"                 [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]"
 
 #: builtin/worktree.c
 msgid "git worktree list [-v | --porcelain [-z]]"
@@ -16187,6 +16476,40 @@
 msgstr "git worktree unlock <worktree>"
 
 #: builtin/worktree.c
+msgid "No possible source branch, inferring '--orphan'"
+msgstr "沒有可能的來源分支,推測為 “--orphan”"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+msgstr ""
+"如果您是想要在這個版本庫建立一個工作區,裡面包含一個\n"
+"未誕生分支(即沒有提交的分支),可以使用 --orphan 達到\n"
+"這個效果:\n"
+"\n"
+"    git worktree add --orphan -b %s %s\n"
+
+#: builtin/worktree.c
+#, c-format
+msgid ""
+"If you meant to create a worktree containing a new unborn branch\n"
+"(branch with no commits) for this repository, you can do so\n"
+"using the --orphan flag:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+msgstr ""
+"如果您是想要在這個版本庫建立一個工作區,裡面包含一個\n"
+"未誕生分支(即沒有提交的分支),可以使用 --orphan 達到\n"
+"這個效果:\n"
+"\n"
+"    git worktree add --orphan %s\n"
+
+#: builtin/worktree.c
 #, c-format
 msgid "Removing %s/%s: %s"
 msgstr "移除 %s/%s: %s"
@@ -16253,6 +16576,11 @@
 
 #: builtin/worktree.c
 #, c-format
+msgid "could not find created worktree '%s'"
+msgstr "找不到建立的工作區「%s」"
+
+#: builtin/worktree.c
+#, c-format
 msgid "Preparing worktree (new branch '%s')"
 msgstr "準備工作區(新分支 '%s')"
 
@@ -16268,10 +16596,34 @@
 
 #: builtin/worktree.c
 #, c-format
+msgid "unreachable: invalid reference: %s"
+msgstr "不可達:無效引用:%s"
+
+#: builtin/worktree.c
+#, c-format
 msgid "Preparing worktree (detached HEAD %s)"
 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”"
+
+#: builtin/worktree.c
+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"
+"故停止。使用 “add -f” 先覆蓋或抓取遠端"
+
+#: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "簽出 <分支>,即使已經被簽出到其它工作區"
 
@@ -16284,6 +16636,10 @@
 msgstr "建立或重設一個分支"
 
 #: builtin/worktree.c
+msgid "create unborn branch"
+msgstr "建立未誕生分支"
+
+#: builtin/worktree.c
 msgid "populate the new working tree"
 msgstr "生成新的工作區"
 
@@ -16309,6 +16665,11 @@
 msgstr "「%s」、「%s」和「%s」選項不得同時使用"
 
 #: builtin/worktree.c
+#, c-format
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "“%s” 選項和提交號不得同時使用"
+
+#: builtin/worktree.c
 msgid "added with --lock"
 msgstr "已使用 --lock 加入"
 
@@ -16470,6 +16831,11 @@
 
 #: bundle-uri.c
 #, c-format
+msgid "could not parse bundle list key %s with value '%s'"
+msgstr "無法解析套件包清單鍵 %s 的值 “%s”"
+
+#: bundle-uri.c
+#, c-format
 msgid "bundle list at '%s' has no mode"
 msgstr "位於 “%s” 的套件包清單沒有模式"
 
@@ -16483,6 +16849,15 @@
 
 #: bundle-uri.c
 #, c-format
+msgid "file downloaded from '%s' is not a bundle"
+msgstr "從 “%s” 下載的檔案不是套件包"
+
+#: bundle-uri.c
+msgid "failed to store maximum creation token"
+msgstr "無法儲存最大的建立權杖"
+
+#: bundle-uri.c
+#, c-format
 msgid "unrecognized bundle mode from URI '%s'"
 msgstr "無法識別從 URI “%s” 取回的套件包模式"
 
@@ -16502,6 +16877,15 @@
 msgstr "位於 URI “%s” 的檔案不是套件包或套件包清單"
 
 #: bundle-uri.c
+#, c-format
+msgid "bundle-uri: unexpected argument: '%s'"
+msgstr "bundle-uri: 非預期的引數:“%s”"
+
+#: bundle-uri.c
+msgid "bundle-uri: expected flush after arguments"
+msgstr "bundle-uri: 引數後應該有一個 flush 包"
+
+#: bundle-uri.c
 msgid "bundle-uri: got an empty line"
 msgstr "bundle-uri: 收到空白列"
 
@@ -16542,6 +16926,12 @@
 msgstr "需要版本庫驗證套件包"
 
 #: bundle.c
+msgid ""
+"some prerequisite commits exist in the object store, but are not connected "
+"to the repository's history"
+msgstr "一些前提提交存在於物件儲存區,但未連接到版本庫的歷史記錄"
+
+#: bundle.c
 #, c-format
 msgid "The bundle contains this ref:"
 msgid_plural "The bundle contains these %<PRIuMAX> refs:"
@@ -16558,6 +16948,16 @@
 msgstr[0] "這個套件包需要這 %<PRIuMAX> 個引用:"
 
 #: bundle.c
+#, c-format
+msgid "The bundle uses this hash algorithm: %s"
+msgstr "本套件包採用此雜湊演算法:%s"
+
+#: bundle.c
+#, c-format
+msgid "The bundle uses this filter: %s"
+msgstr "本套件包使用此過濾器:%s"
+
+#: bundle.c
 msgid "unable to dup bundle descriptor"
 msgstr "無法複製套件包描述元"
 
@@ -16603,6 +17003,11 @@
 
 #: chunk-format.c
 #, c-format
+msgid "chunk id %<PRIx32> not %d-byte aligned"
+msgstr "區塊 ID %<PRIx32> 沒有以 %d 位元組為單位對齊"
+
+#: chunk-format.c
+#, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
 msgstr "不正確的區塊偏移 %<PRIx64> 及 %<PRIx64>"
 
@@ -16670,8 +17075,8 @@
 msgstr "透過歸檔移動物件和引用"
 
 #: command-list.h
-msgid "Provide content or type and size information for repository objects"
-msgstr "提供版本庫物件的內容、類型或大小"
+msgid "Provide contents or details of repository objects"
+msgstr "提供版本庫物件的內容或詳細資訊"
 
 #: command-list.h
 msgid "Display gitattributes information"
@@ -16739,7 +17144,7 @@
 
 #: command-list.h
 msgid "Count unpacked number of objects and their disk consumption"
-msgstr "計算未打包物件的數量和磁碟空間占用"
+msgstr "計算未打包物件的數量和磁碟空間佔用"
 
 #: command-list.h
 msgid "Retrieve and store user credentials"
@@ -16854,8 +17259,8 @@
 msgstr "一個便攜的 Git 圖形用戶端"
 
 #: command-list.h
-msgid "Compute object ID and optionally creates a blob from a file"
-msgstr "從一個檔案計算物件 ID,並可以建立 blob 資料物件"
+msgid "Compute object ID and optionally create an object from a file"
+msgstr "從一個檔案計算物件 ID,也能順帶建立一個物件"
 
 #: command-list.h
 msgid "Display help information about Git"
@@ -17051,6 +17456,10 @@
 msgstr "建立、列出、刪除物件取代引用"
 
 #: command-list.h
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr "實驗性功能:在新的基底重放提交,亦支援裸版本庫"
+
+#: command-list.h
 msgid "Generates a summary of pending changes"
 msgstr "生成待定更改的摘要"
 
@@ -17212,7 +17621,7 @@
 msgstr "顯示 Git 的版本資訊"
 
 #: command-list.h
-msgid "Show logs with difference each commit introduces"
+msgid "Show logs with differences each commit introduces"
 msgstr "顯示每一個提交引入的差異日誌"
 
 #: command-list.h
@@ -17368,6 +17777,37 @@
 msgstr "提交圖形檔案太小"
 
 #: commit-graph.c
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "提交圖形 OID 扇出區塊大小有誤"
+
+#: commit-graph.c
+msgid "commit-graph fanout values out of order"
+msgstr "提交圖形扇出的數值失序"
+
+#: commit-graph.c
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "提交圖形 OID 查詢區塊的大小有誤"
+
+#: commit-graph.c
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "提交圖形的提交資料區塊大小有誤"
+
+#: commit-graph.c
+msgid "commit-graph generations chunk is wrong size"
+msgstr "提交圖形的世代區塊大小有誤"
+
+#: commit-graph.c
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "提交圖形的更動路徑索引區塊過小"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr "忽略提交圖形檔案中過小的更動路徑區塊 (%<PRIuMAX> < %<PRIuMAX>)"
+
+#: commit-graph.c
 #, c-format
 msgid "commit-graph signature %X does not match signature %X"
 msgstr "提交圖形簽名 %X 和簽名 %X 不符合"
@@ -17385,20 +17825,45 @@
 #: commit-graph.c
 #, c-format
 msgid "commit-graph file is too small to hold %u chunks"
-msgstr "commit-graph 檔案不夠放置 %u 個區塊"
+msgstr "提交圖形檔案不夠放置 %u 個區塊"
+
+#: commit-graph.c
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "提交圖形需要的 OID 扇出區塊遺失或損壞"
+
+#: commit-graph.c
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "提交圖形需要的 OID 查詢區塊遺失或損壞"
+
+#: commit-graph.c
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "提交圖形需要的提交資料區塊遺失或損壞"
 
 #: commit-graph.c
 msgid "commit-graph has no base graphs chunk"
 msgstr "提交圖形沒有基礎圖形區塊"
 
 #: commit-graph.c
+msgid "commit-graph base graphs chunk is too small"
+msgstr "提交圖形的基礎圖形區塊過小"
+
+#: commit-graph.c
 msgid "commit-graph chain does not match"
 msgstr "提交圖形鏈不符合"
 
 #: commit-graph.c
 #, c-format
+msgid "commit count in base graph too high: %<PRIuMAX>"
+msgstr "基礎圖 (base graph) 中的提交數過多:%<PRIuMAX>"
+
+#: commit-graph.c
+msgid "commit-graph chain file too small"
+msgstr "提交圖形鏈檔案過小"
+
+#: commit-graph.c
+#, c-format
 msgid "invalid commit-graph chain: line '%s' not a hash"
-msgstr "無效的提交圖形鏈:行 '%s' 不是一個雜湊值"
+msgstr "無效的提交圖形鏈:「%s」列不是雜湊值"
 
 #: commit-graph.c
 msgid "unable to find all commit-graph files"
@@ -17418,6 +17883,14 @@
 msgstr "提交圖需要比目前更多的世代資料,但沒有相關資料"
 
 #: commit-graph.c
+msgid "commit-graph overflow generation data is too small"
+msgstr "提交圖形的溢出世代資料過小"
+
+#: commit-graph.c
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "提交圖形的延伸邊界指針超出範圍"
+
+#: commit-graph.c
 msgid "Loading known commits in commit graph"
 msgstr "正在載入提交圖中的已知提交"
 
@@ -17501,6 +17974,16 @@
 msgstr "無法重新命名暫時提交圖形檔案"
 
 #: commit-graph.c
+#, c-format
+msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
+msgstr "無法將圖與 %<PRIuMAX>, %<PRIuMAX> 個提交進行合併"
+
+#: commit-graph.c
+#, c-format
+msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
+msgstr "無法合併 %s 圖,太多提交:%<PRIuMAX>"
+
+#: commit-graph.c
 msgid "Scanning merged commits"
 msgstr "正在掃描合併提交"
 
@@ -17536,10 +18019,6 @@
 msgstr "無法從提交圖形中解析提交 %s"
 
 #: commit-graph.c
-msgid "Verifying commits in commit graph"
-msgstr "正在驗證提交圖中的提交"
-
-#: commit-graph.c
 #, c-format
 msgid "failed to parse commit %s from object database for commit-graph"
 msgstr "無法從提交圖形的物件庫中解析提交 %s"
@@ -17566,18 +18045,6 @@
 
 #: commit-graph.c
 #, c-format
-msgid ""
-"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
-msgstr "提交圖形中提交 %s 的世代號是零,但其它地方非零"
-
-#: commit-graph.c
-#, c-format
-msgid ""
-"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
-msgstr "提交圖形中提交 %s 的世代號非零,但其它地方是零"
-
-#: commit-graph.c
-#, c-format
 msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
 msgstr "提交 %s 的提交圖形處於 %<PRIuMAX> < %<PRIuMAX> 世代"
 
@@ -17586,6 +18053,17 @@
 msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
 msgstr "提交圖形中提交 %s 的提交日期是 %<PRIuMAX> != %<PRIuMAX>"
 
+#: commit-graph.c
+#, c-format
+msgid ""
+"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
+"'%s')"
+msgstr "提交圖形中包含 0 和非 0 兩個世代號(例如 “%s” 和 “%s” 提交)"
+
+#: commit-graph.c
+msgid "Verifying commits in commit graph"
+msgstr "正在驗證提交圖中的提交"
+
 #: commit.c
 #, c-format
 msgid "%s %s is not a commit!"
@@ -17613,6 +18091,11 @@
 
 #: commit.c
 #, c-format
+msgid "commit %s exists in commit-graph but not in the object database"
+msgstr "%s 提交在提交圖形中,但不在物件資料庫中"
+
+#: commit.c
+#, c-format
 msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
 msgstr "提交 %s 有一個非可信的聲稱來自 %s 的 GPG 簽名。"
 
@@ -18108,8 +18591,8 @@
 msgstr "錯誤的 zlib 壓縮級別 %d"
 
 #: config.c
-msgid "core.commentChar should only be one character"
-msgstr "core.commentChar 應該是一個字元"
+msgid "core.commentChar should only be one ASCII character"
+msgstr "core.commentChar 應該是一個 ASCII 字元"
 
 #: config.c
 #, c-format
@@ -18155,11 +18638,6 @@
 msgstr "不能解析設定物件 '%s'"
 
 #: config.c
-#, c-format
-msgid "failed to parse %s"
-msgstr "解析 %s 失敗"
-
-#: config.c
 msgid "unable to parse command-line config"
 msgstr "無法解析命令列中的設定"
 
@@ -18249,6 +18727,11 @@
 
 #: config.c
 #, c-format
+msgid "refusing to work with overly long line in '%s' on line %<PRIuMAX>"
+msgstr "因為第 %2$<PRIuMAX> 列中 “%1$s” 的文字列太長,故拒絕運作"
+
+#: config.c
+#, c-format
 msgid "missing value for '%s'"
 msgstr "%s 的取值缺少"
 
@@ -18315,6 +18798,19 @@
 
 #: connect.c
 #, c-format
+msgid "error on bundle-uri response line %d: %s"
+msgstr "在 bundle-uri 回應的第 %d 列發現錯誤:%s"
+
+#: connect.c
+msgid "expected flush after bundle-uri listing"
+msgstr "在 bundle-uri 清單後應該有一個 flush 包"
+
+#: connect.c
+msgid "expected response end packet after ref listing"
+msgstr "在引用列表後預期要有回應結束封包"
+
+#: connect.c
+#, c-format
 msgid "invalid ls-refs response: %s"
 msgstr "無效的 ls-refs 回應:%s"
 
@@ -18323,10 +18819,6 @@
 msgstr "在引用列表之後應該有一個 flush 包"
 
 #: connect.c
-msgid "expected response end packet after ref listing"
-msgstr "在引用列表後預期要有回應結束封包"
-
-#: connect.c
 #, c-format
 msgid "protocol '%s' is not supported"
 msgstr "不支援 '%s' 協定"
@@ -18703,10 +19195,6 @@
 msgstr "--merge-base 跟範圍無法搭配運作"
 
 #: diff-lib.c
-msgid "--merge-base only works with commits"
-msgstr "--merge-base 只能跟提交搭配才能運作"
-
-#: diff-lib.c
 msgid "unable to get HEAD"
 msgstr "不能取得 HEAD"
 
@@ -18719,6 +19207,14 @@
 msgstr "找到多個合併基底"
 
 #: diff-no-index.c
+msgid "cannot compare stdin to a directory"
+msgstr "無法比對 stdin 和目錄"
+
+#: diff-no-index.c
+msgid "cannot compare a named pipe to a directory"
+msgstr "無法比對命名管線 (pipe) 和目錄"
+
+#: diff-no-index.c
 msgid "git diff --no-index [<options>] <path> <path>"
 msgstr "git diff --no-index [<選項>] <路徑> <路徑>"
 
@@ -18768,6 +19264,11 @@
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 msgstr "設定變數 'diff.submodule' 未知的取值:'%s'"
 
+#: diff.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "設定 '%s' 的取值未知:%s"
+
 #: diff.c
 #, c-format
 msgid ""
@@ -18782,6 +19283,15 @@
 msgid "external diff died, stopping at %s"
 msgstr "外部 diff 離開,停止在 %s"
 
+#: diff.c
+msgid "--follow requires exactly one pathspec"
+msgstr "--follow 明確要求只跟一個路徑規格"
+
+#: diff.c
+#, c-format
+msgid "pathspec magic not supported by --follow: %s"
+msgstr "--follow 不支援路徑規格魔法:%s"
+
 #: diff.c parse-options.c
 #, c-format
 msgid "options '%s', '%s', '%s', and '%s' cannot be used together"
@@ -18799,10 +19309,6 @@
 msgstr "「%s」和「%s」選項不得同時使用,請使用「%s」搭配「%s」、「%s」"
 
 #: diff.c
-msgid "--follow requires exactly one pathspec"
-msgstr "--follow 明確要求只跟一個路徑規格"
-
-#: diff.c
 #, c-format
 msgid "invalid --stat value: %s"
 msgstr "無效的 --stat 值:%s"
@@ -18857,13 +19363,6 @@
 msgstr "--color-moved-ws 中的無效模式 '%s'"
 
 #: diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"diff-algorithm 選項有 \"myers\"、\"minimal\"、\"patience\" 和 \"histogram\""
-
-#: diff.c
 #, c-format
 msgid "invalid argument to %s"
 msgstr "%s 的參數無效"
@@ -18920,21 +19419,22 @@
 msgstr "只輸出 --stat 的最後一行"
 
 #: diff.c
-msgid "<param1,param2>..."
-msgstr "<參數1,參數2>..."
+#| msgid "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
 
 #: diff.c
 msgid ""
 "output the distribution of relative amount of changes for each sub-directory"
-msgstr "輸出每個子目錄相對變更的分布"
+msgstr "輸出每個子目錄相對變更的分佈"
 
 #: diff.c
 msgid "synonym for --dirstat=cumulative"
 msgstr "和 --dirstat=cumulative 同義"
 
 #: diff.c
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "是 --dirstat=files,param1,param2... 的同義詞"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "是 --dirstat=files,<param1>,<param2>... 的同義詞"
 
 #: diff.c
 msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19039,6 +19539,10 @@
 msgstr "不顯示任何來源和目的地前綴"
 
 #: diff.c
+msgid "use default prefixes a/ and b/"
+msgstr "使用預設的前置名稱 a/ 和 b/"
+
+#: diff.c
 msgid "show context between diff hunks up to the specified number of lines"
 msgstr "顯示指定行數的差異區塊間的上下文"
 
@@ -19153,14 +19657,6 @@
 msgstr "使用 \"histogram diff\" 演算法生成差異"
 
 #: diff.c
-msgid "<algorithm>"
-msgstr "<演算法>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "選擇一個差異演算法"
-
-#: diff.c
 msgid "<text>"
 msgstr "<文字>"
 
@@ -19407,6 +19903,16 @@
 msgid "hint: Waiting for your editor to close the file...%c"
 msgstr "提示:等待您的編輯器關閉檔案...%c"
 
+#: editor.c sequencer.c wrapper.c
+#, c-format
+msgid "could not write to '%s'"
+msgstr "不能寫入 '%s'"
+
+#: editor.c
+#, c-format
+msgid "could not edit '%s'"
+msgstr "無法編輯 '%s'"
+
 #: entry.c
 msgid "Filtering content"
 msgstr "過濾內容"
@@ -19727,16 +20233,14 @@
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 msgstr ""
 "git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
 "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--"
 "bare]\n"
 "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-"           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-"           <command> [<args>]"
+"           [--config-env=<name>=<envvar>] <command> [<args>]"
 
 #: git.c
 msgid ""
@@ -19767,11 +20271,6 @@
 
 #: git.c
 #, c-format
-msgid "no prefix given for --super-prefix\n"
-msgstr "沒有為 --super-prefix 提供前綴\n"
-
-#: git.c
-#, c-format
 msgid "-c expects a configuration string\n"
 msgstr "應為 -c 提供一個設定字串\n"
 
@@ -19782,6 +20281,11 @@
 
 #: git.c
 #, c-format
+msgid "no attribute source given for --attr-source\n"
+msgstr "沒有為 --attr-source 提供屬性來源\n"
+
+#: git.c
+#, c-format
 msgid "unknown option: %s\n"
 msgstr "未知選項:%s\n"
 
@@ -19904,8 +20408,13 @@
 msgstr "gpg.ssh.defaultKeyCommand 執行失敗:%s %s"
 
 #: gpg-interface.c
-msgid "gpg failed to sign the data"
-msgstr "gpg 無法為資料簽名"
+#, c-format
+msgid ""
+"gpg failed to sign the data:\n"
+"%s"
+msgstr ""
+"gpg 無法簽名資料:\n"
+"%s"
 
 #: gpg-interface.c
 msgid "user.signingKey needs to be set for ssh signing"
@@ -20395,13 +20904,13 @@
 "%s"
 
 #: merge-ort.c merge-recursive.c
-msgid "Failed to execute internal merge"
+msgid "failed to execute internal merge"
 msgstr "無法執行內部合併"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
-msgid "Unable to add %s to database"
-msgstr "不能新增 %s 至物件庫"
+msgid "unable to add %s to database"
+msgstr "無法將 %s 加進資料庫"
 
 #: merge-ort.c merge-recursive.c
 #, c-format
@@ -20755,7 +21264,7 @@
 
 #: merge-recursive.c
 msgid "renamed"
-msgstr "重新命名"
+msgstr "已重新命名"
 
 #: merge-recursive.c
 #, c-format
@@ -20902,6 +21411,20 @@
 
 #: midx.c
 #, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr "物件 ID 扇出無序:fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+#: midx.c
+msgid "multi-pack-index OID lookup chunk is the wrong size"
+msgstr "多包索引 OID 查詢區塊的大小有誤"
+
+#: midx.c
+msgid "multi-pack-index object offset chunk is the wrong size"
+msgstr "多包索引的物件偏移區塊大小有誤"
+
+#: midx.c
+#, c-format
 msgid "multi-pack-index file %s is too small"
 msgstr "多包索引檔案 %s 太小"
 
@@ -20921,20 +21444,24 @@
 msgstr "multi-pack-index 雜湊版本 %u 與版本 %u 不符合"
 
 #: midx.c
-msgid "multi-pack-index missing required pack-name chunk"
-msgstr "多包索引缺少必需的包名區塊"
+msgid "multi-pack-index required pack-name chunk missing or corrupted"
+msgstr "多包索引所需的封裝名稱區塊不存在或損壞"
 
 #: midx.c
-msgid "multi-pack-index missing required OID fanout chunk"
-msgstr "多包索引缺少必需的物件 ID fanout 區塊"
+msgid "multi-pack-index required OID fanout chunk missing or corrupted"
+msgstr "多包索引所需的 OID fanout 區塊不存在或損壞"
 
 #: midx.c
-msgid "multi-pack-index missing required OID lookup chunk"
-msgstr "多包索引缺少必需的物件 ID 查詢區塊"
+msgid "multi-pack-index required OID lookup chunk missing or corrupted"
+msgstr "多包索引所需的 OID 查詢區塊不存在或損壞"
 
 #: midx.c
-msgid "multi-pack-index missing required object offsets chunk"
-msgstr "多包索引缺少必需的物件位移區塊"
+msgid "multi-pack-index required object offsets chunk missing or corrupted"
+msgstr "多包索引所需的物件偏移區塊不存在或損壞"
+
+#: midx.c
+msgid "multi-pack-index pack-name chunk is too short"
+msgstr "多包索引的封裝名稱區塊過短"
 
 #: midx.c
 #, c-format
@@ -20947,10 +21474,23 @@
 msgstr "錯的 pack-int-id:%u(共有 %u 個包)"
 
 #: midx.c
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX 未包含 BTMP 區塊"
+
+#: midx.c
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "無法載入位圖化 (bitmapped) 的封裝 %<PRIu32>"
+
+#: midx.c
 msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
 msgstr "多包索引儲存一個64位位移,但是 off_t 太小"
 
 #: midx.c
+msgid "multi-pack-index large offset out of bounds"
+msgstr "多包索引的最大偏移超出邊界"
+
+#: midx.c
 #, c-format
 msgid "failed to add packfile '%s'"
 msgstr "新增 packfile '%s' 失敗"
@@ -21050,12 +21590,6 @@
 msgstr "正在尋找引用的 packfile"
 
 #: midx.c
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "物件 ID 扇出無序:fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
-#: midx.c
 msgid "the midx contains no oid"
 msgstr "midx 沒有 oid"
 
@@ -21250,6 +21784,11 @@
 
 #: object-file.c
 #, c-format
+msgid "unable to open loose object %s"
+msgstr "無法打開鬆散物件 %s"
+
+#: object-file.c
+#, c-format
 msgid "unable to parse %s header"
 msgstr "無法解析 %s 頭部"
 
@@ -21269,8 +21808,8 @@
 
 #: object-file.c
 #, c-format
-msgid "failed to read object %s"
-msgstr "讀取物件 %s 失敗"
+msgid "loose object %s (stored in %s) is corrupt"
+msgstr "鬆散物件 %s(儲存在 %s)已損壞"
 
 #: object-file.c
 #, c-format
@@ -21279,11 +21818,6 @@
 
 #: object-file.c
 #, c-format
-msgid "loose object %s (stored in %s) is corrupt"
-msgstr "鬆散物件 %s(儲存在 %s)已損壞"
-
-#: object-file.c
-#, c-format
 msgid "packed object %s (stored in %s) is corrupt"
 msgstr "打包物件 %s(儲存在 %s)已損壞"
 
@@ -21298,10 +21832,6 @@
 msgstr "無法為 '%s' 設定權限"
 
 #: object-file.c
-msgid "file write error"
-msgstr "檔案寫錯誤"
-
-#: object-file.c
 msgid "error when closing loose object file"
 msgstr "關閉鬆散物件檔案時發生錯誤"
 
@@ -21359,12 +21889,13 @@
 msgstr "不能讀取物件 %s"
 
 #: object-file.c
-msgid "corrupt commit"
-msgstr "損壞的提交"
+#, c-format
+msgid "object fails fsck: %s"
+msgstr "物件 fsck 失敗:%s"
 
 #: object-file.c
-msgid "corrupt tag"
-msgstr "損壞的標籤"
+msgid "refusing to create malformed object"
+msgstr "拒絕建立格式錯誤的物件"
 
 #: object-file.c
 #, c-format
@@ -21676,11 +22207,6 @@
 msgstr "無法 fstat 位圖檔案"
 
 #: pack-bitmap.c
-#, c-format
-msgid "ignoring extra bitmap file: '%s'"
-msgstr "忽略多出來的位圖檔案:「%s」"
-
-#: pack-bitmap.c
 msgid "checksum doesn't match in MIDX and bitmap"
 msgstr "總和檢查碼在 MIDX 和位圖中無符合項目"
 
@@ -21693,6 +22219,10 @@
 msgid "could not open pack %s"
 msgstr "無法開啟封包 %s"
 
+#: pack-bitmap.c t/helper/test-read-midx.c
+msgid "could not determine MIDX preferred pack"
+msgstr "無法確定 MIDX 偏好的封裝"
+
 #: pack-bitmap.c
 #, c-format
 msgid "preferred pack (%s) is invalid"
@@ -21718,6 +22248,11 @@
 
 #: pack-bitmap.c
 #, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "無法載入「%s」封裝,停用 pack-reuse"
+
+#: pack-bitmap.c
+#, c-format
 msgid "object '%s' not found in type bitmaps"
 msgstr "在類型位圖中,找不到「%s」物件"
 
@@ -21763,6 +22298,11 @@
 msgid "unable to get disk usage of '%s'"
 msgstr "無法取得「%s」的磁碟用量"
 
+#: pack-bitmap.c
+#, c-format
+msgid "bitmap file '%s' has invalid checksum"
+msgstr "“%s” 位圖檔案的總和檢查碼無效"
+
 #: pack-mtimes.c
 #, c-format
 msgid "mtimes file %s is too small"
@@ -21813,6 +22353,23 @@
 msgid "reverse-index file %s has unsupported hash id %<PRIu32>"
 msgstr "倒排索引檔案 %s 有不支援的雜湊 ID %<PRIu32>"
 
+#: pack-revindex.c
+msgid "invalid checksum"
+msgstr "無效的總和檢查碼"
+
+#: pack-revindex.c
+#, c-format
+msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "%<PRIu64> 位置的修訂版索引 (rev-index) 無效:%<PRIu32> != %<PRIu32>"
+
+#: pack-revindex.c
+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 "無法同時寫入和驗證倒排索引"
@@ -21878,16 +22435,6 @@
 
 #: parse-options.c
 #, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s 與 %s 不相容"
-
-#: parse-options.c
-#, c-format
-msgid "%s : incompatible with something else"
-msgstr "%s:和其它的不相容"
-
-#: parse-options.c
-#, c-format
 msgid "%s takes no value"
 msgstr "%s 不取值"
 
@@ -21986,6 +22533,11 @@
 msgid "-NUM"
 msgstr "-數字"
 
+#: parse-options.c
+#, c-format
+msgid "opposite of --no-%s"
+msgstr "--no-%s 的相反行為"
+
 #: parse-options.h
 msgid "expiry-date"
 msgstr "到期時間"
@@ -22007,6 +22559,10 @@
 msgstr "用 <n> 位數字顯示物件名稱"
 
 #: parse-options.h
+msgid "prefixed path to initial superproject"
+msgstr "初始父專案的前綴路徑"
+
+#: parse-options.h
 msgid "how to strip spaces and #comments from message"
 msgstr "設定如何刪除提交說明裡的空格和 #備註"
 
@@ -22019,6 +22575,16 @@
 "with --pathspec-from-file, pathspec elements are separated with NUL character"
 msgstr "如使用 --pathspec-from-file,則 <路徑規格> 元件會使用 NUL 字元分隔"
 
+#: parse.c
+#, c-format
+msgid "bad boolean environment value '%s' for '%s'"
+msgstr "「%2$s」的「%1$s」布林環境值無效"
+
+#: parse.c
+#, c-format
+msgid "failed to parse %s"
+msgstr "解析 %s 失敗"
+
 #: path.c
 #, c-format
 msgid "Could not make %s writable by group"
@@ -22077,6 +22643,11 @@
 
 #: pathspec.c
 #, c-format
+msgid "'%s' is outside the directory tree"
+msgstr "“%s” 在目錄樹之外"
+
+#: pathspec.c
+#, c-format
 msgid "%s: '%s' is outside repository at '%s'"
 msgstr "%s:'%s' 在位於 '%s' 的版本庫之外"
 
@@ -22233,6 +22804,15 @@
 msgid "could not parse log for '%s'"
 msgstr "不能解析 '%s' 的日誌"
 
+#: reachable.c
+#, c-format
+msgid "invalid extra cruft tip: '%s'"
+msgstr "無效的額外廢棄提交修訂版:“%s”"
+
+#: reachable.c
+msgid "unable to enumerate additional recent objects"
+msgstr "無法列舉多出來的近期物件"
+
 #: read-cache.c
 #, c-format
 msgid "will not add file alias '%s' ('%s' already exists in index)"
@@ -22259,11 +22839,6 @@
 
 #: read-cache.c
 #, c-format
-msgid "unable to stat '%s'"
-msgstr "無法對 %s 執行 stat"
-
-#: read-cache.c
-#, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "'%s' 看起來既是檔案又是目錄"
 
@@ -22398,11 +22973,6 @@
 
 #: read-cache.c
 #, c-format
-msgid "could not stat '%s'"
-msgstr "不能對 '%s' 呼叫 stat"
-
-#: read-cache.c
-#, c-format
 msgid "unable to open git dir: %s"
 msgstr "不能開啟 git 目錄:%s"
 
@@ -22421,6 +22991,16 @@
 msgid "%s: cannot drop to stage #0"
 msgstr "%s:不能落到暫存區 #0"
 
+#: read-cache.c
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "非預期的 diff 狀態 %c"
+
+#: read-cache.c
+#, c-format
+msgid "remove '%s'\n"
+msgstr "移除 “%s”\n"
+
 #: rebase-interactive.c
 msgid ""
 "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --"
@@ -22589,6 +23169,16 @@
 
 #: ref-filter.c
 #, c-format
+msgid "%%(%.*s) does not take arguments"
+msgstr "%%(%.*s) 不取引數"
+
+#: ref-filter.c
+#, c-format
+msgid "unrecognized %%(%.*s) argument: %s"
+msgstr "無法識別的 %%(%.*s) 引數:%s"
+
+#: ref-filter.c
+#, c-format
 msgid "expected format: %%(color:<color>)"
 msgstr "期望的格式:%%(color:<顏色>)"
 
@@ -22609,26 +23199,6 @@
 
 #: ref-filter.c
 #, c-format
-msgid "unrecognized %%(%s) argument: %s"
-msgstr "無法識別的 %%(%s) 參數:%s"
-
-#: ref-filter.c
-#, c-format
-msgid "%%(objecttype) does not take arguments"
-msgstr "%%(objecttype) 不帶參數"
-
-#: ref-filter.c
-#, c-format
-msgid "%%(deltabase) does not take arguments"
-msgstr "%%(deltabase) 不帶參數"
-
-#: ref-filter.c
-#, c-format
-msgid "%%(body) does not take arguments"
-msgstr "%%(body) 不帶參數"
-
-#: ref-filter.c
-#, c-format
 msgid "expected %%(trailers:key=<value>)"
 msgstr "預期是 %%(trailers:key=<value>)"
 
@@ -22644,13 +23214,28 @@
 
 #: ref-filter.c
 #, c-format
-msgid "positive value expected '%s' in %%(%s)"
-msgstr "%%(%2$s) 中的 '%1$s' 預期是正數值"
+msgid "argument expected for %s"
+msgstr "引數預期 %s"
 
 #: ref-filter.c
 #, c-format
-msgid "unrecognized email option: %s"
-msgstr "無法識別的 email 選項:%s"
+msgid "positive value expected %s=%s"
+msgstr "期望一個正數 %s=%s"
+
+#: ref-filter.c
+#, c-format
+msgid "cannot fully parse %s=%s"
+msgstr "無法完全解析 %s=%s"
+
+#: ref-filter.c
+#, c-format
+msgid "value expected %s="
+msgstr "數值預期 %s="
+
+#: ref-filter.c
+#, c-format
+msgid "positive value expected '%s' in %%(%s)"
+msgstr "%%(%2$s) 中的 '%1$s' 預期是正數值"
 
 #: ref-filter.c
 #, c-format
@@ -22669,13 +23254,18 @@
 
 #: ref-filter.c
 #, c-format
+msgid "unrecognized %%(%s) argument: %s"
+msgstr "無法識別的 %%(%s) 參數:%s"
+
+#: ref-filter.c
+#, c-format
 msgid "positive width expected with the %%(align) atom"
 msgstr "元素 %%(align) 需要一個正數的寬度"
 
 #: ref-filter.c
 #, c-format
-msgid "%%(rest) does not take arguments"
-msgstr "%%(rest) 未取引數"
+msgid "expected format: %%(ahead-behind:<committish>)"
+msgstr "預期格式:%%(ahead-behind:<committish>)"
 
 #: ref-filter.c
 #, c-format
@@ -22734,6 +23324,10 @@
 msgstr "--format=%.*s 不能和 --python、--shell、--tcl 一起使用"
 
 #: ref-filter.c
+msgid "failed to run 'describe'"
+msgstr "無法執行 “describe”"
+
+#: ref-filter.c
 #, c-format
 msgid "(no branch, rebasing %s)"
 msgstr "(無分支,重定 %s 的基底)"
@@ -22810,6 +23404,10 @@
 msgid "field name to sort on"
 msgstr "排序的欄位名"
 
+#: ref-filter.h
+msgid "exclude refs which match pattern"
+msgstr "排除符合模式的引用"
+
 #: reflog.c
 #, c-format
 msgid "not a reflog: %s"
@@ -22913,17 +23511,12 @@
 msgid "cannot process '%s' and '%s' at the same time"
 msgstr "無法同時處理 '%s' 和 '%s'"
 
-#: refs/files-backend.c
-#, c-format
-msgid "could not remove reference %s"
-msgstr "無法刪除引用 %s"
-
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
 #, c-format
 msgid "could not delete reference %s: %s"
 msgstr "無法刪除引用 %s:%s"
 
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
 #, c-format
 msgid "could not delete references: %s"
 msgstr "無法刪除引用:%s"
@@ -23339,8 +23932,9 @@
 
 #  譯者:請維持前導空格
 #: remote.c
-msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
-msgstr "  (使用 \"git pull\" 來合併遠端分支)\n"
+msgid ""
+"  (use \"git pull\" if you want to integrate the remote branch with yours)\n"
+msgstr "  (使用 “git pull” 來將遠端分支整合進您的分支)\n"
 
 #: remote.c
 #, c-format
@@ -23413,7 +24007,7 @@
 #: rerere.c
 #, c-format
 msgid "cannot unlink stray '%s'"
-msgstr "不能刪除 stray '%s'"
+msgstr "無法刪除失散檔案 “%s”"
 
 #: rerere.c
 #, c-format
@@ -23432,11 +24026,6 @@
 
 #: rerere.c
 #, c-format
-msgid "cannot unlink '%s'"
-msgstr "不能刪除 '%s'"
-
-#: rerere.c
-#, c-format
 msgid "Updated preimage for '%s'"
 msgstr "已為 '%s' 更新 preimage"
 
@@ -23486,6 +24075,11 @@
 msgstr "--unpacked=<packfile> 已不受支援"
 
 #: revision.c
+#, c-format
+msgid "invalid option '%s' in --stdin mode"
+msgstr "在 --stdin 模式下,“%s” 選項無效"
+
+#: revision.c
 msgid "your current branch appears to be broken"
 msgstr "您的目前分支好像被損壞"
 
@@ -23596,8 +24190,16 @@
 msgstr "只下載會簽出的分支中介資料"
 
 #: scalar.c
-msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-msgstr "scalar clone [<options>] [--] <repo> [<dir>]"
+msgid "create repository within 'src' directory"
+msgstr "在 “src” 目錄建立版本庫"
+
+#: scalar.c
+msgid ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
+msgstr ""
+"scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+"\t[--[no-]src] <url> [<enlistment>]"
 
 #: scalar.c
 #, c-format
@@ -23663,13 +24265,32 @@
 
 #: scalar.c
 #, c-format
-msgid "removing stale scalar.repo '%s'"
-msgstr "正在移除過時的 scalar.repo “%s”"
+msgid "removed stale scalar.repo '%s'"
+msgstr "已移除過時的 scalar.repo “%s”"
 
 #: scalar.c
 #, c-format
-msgid "git repository gone in '%s'"
-msgstr "git 版本庫在「%s」遺失"
+msgid "repository at '%s' has different owner"
+msgstr "位於 “%s” 的版本庫有不同的擁有者"
+
+#: scalar.c
+#, c-format
+msgid "repository at '%s' has a format issue"
+msgstr "位於 “%s” 的版本庫有格式問題"
+
+#: scalar.c
+#, c-format
+msgid "repository not found in '%s'"
+msgstr "版本庫不在 “%s”"
+
+#: scalar.c
+#, c-format
+msgid ""
+"to unregister this repository from Scalar, run\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
+msgstr ""
+"如果要從 Scalar 解除這個版本庫的註冊,請執行\n"
+"\tgit config --global --unset --fixed-value scalar.repo \"%s\""
 
 #: scalar.c
 msgid ""
@@ -23851,11 +24472,6 @@
 msgid "could not lock '%s'"
 msgstr "不能鎖定 '%s'"
 
-#: sequencer.c strbuf.c wrapper.c
-#, c-format
-msgid "could not write to '%s'"
-msgstr "不能寫入 '%s'"
-
 #: sequencer.c
 #, c-format
 msgid "could not write eol to '%s'"
@@ -24058,7 +24674,7 @@
 msgid "corrupt author: missing date information"
 msgstr "作者資訊損壞:缺少日期資訊"
 
-#: sequencer.c t/helper/test-fast-rebase.c
+#: sequencer.c
 #, c-format
 msgid "could not update %s"
 msgstr "不能更新 %s"
@@ -24155,11 +24771,6 @@
 
 #: sequencer.c
 #, c-format
-msgid "could not rename '%s' to '%s'"
-msgstr "不能將 '%s' 重新命名為 '%s'"
-
-#: sequencer.c
-#, c-format
 msgid "could not revert %s... %s"
 msgstr "不能還原 %s... %s"
 
@@ -24185,6 +24796,26 @@
 
 #: sequencer.c
 #, c-format
+msgid "'%s' is not a valid label"
+msgstr "“%s” 不是有效的標籤"
+
+#: 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
+#, c-format
+msgid "invalid command '%.*s'"
+msgstr "無效的命令 “%.*s”"
+
+#: sequencer.c
+#, c-format
 msgid "%s does not accept arguments: '%s'"
 msgstr "%s 不接受參數:'%s'"
 
@@ -24274,10 +24905,6 @@
 msgstr "不能建立序列目錄 '%s'"
 
 #: sequencer.c
-msgid "could not lock HEAD"
-msgstr "不能鎖定 HEAD"
-
-#: sequencer.c
 msgid "no cherry-pick or revert in progress"
 msgstr "揀選或還原動作並未進行"
 
@@ -24392,22 +25019,22 @@
 "\n"
 
 #: sequencer.c
-msgid "and made changes to the index and/or the working tree\n"
-msgstr "並且修改索引和/或工作區\n"
+msgid "and made changes to the index and/or the working tree.\n"
+msgstr "並且更改索引和(或)工作區。\n"
 
 #: sequencer.c
 #, c-format
 msgid ""
 "execution succeeded: %s\n"
-"but left changes to the index and/or the working tree\n"
+"but left changes to the index and/or the working tree.\n"
 "Commit or stash your changes, and then run\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
 msgstr ""
 "執行成功:%s\n"
-"但是在索引和/或工作區中存在變更\n"
-"提交或貯存修改,然後執行\n"
+"但是在索引和(或)工作區中存在變更。\n"
+"請提交或貯存修改,然後執行\n"
 "\n"
 "  git rebase --continue\n"
 "\n"
@@ -24539,6 +25166,10 @@
 msgstr "已有自動貯存;建立新貯存項目。"
 
 #: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "autostash 引用是符號引用"
+
+#: sequencer.c
 msgid "could not detach HEAD"
 msgstr "不能分離開頭指標"
 
@@ -24574,13 +25205,13 @@
 
 #: sequencer.c
 #, c-format
-msgid "Rebasing (%d/%d)%s"
-msgstr "正在重定基底 (%d/%d)%s"
+msgid "Stopped at %s...  %.*s\n"
+msgstr "停止在 %s... %.*s\n"
 
 #: sequencer.c
 #, c-format
-msgid "Stopped at %s...  %.*s\n"
-msgstr "停止在 %s... %.*s\n"
+msgid "Rebasing (%d/%d)%s"
+msgstr "正在重定基底 (%d/%d)%s"
 
 #: sequencer.c
 #, c-format
@@ -24865,6 +25496,100 @@
 msgid "setsid failed"
 msgstr "setsid 失敗"
 
+#: setup.c
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "不能對範本 '%s' 呼叫 stat"
+
+#: setup.c
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "不能開啟目錄 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "不能讀取連結 '%s'"
+
+#: setup.c
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "不能自 '%s' 到 '%s' 建立符號連結"
+
+#: setup.c
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "不能複製 '%s' 至 '%s'"
+
+#: setup.c
+#, c-format
+msgid "ignoring template %s"
+msgstr "忽略範本 %s"
+
+#: setup.c
+#, c-format
+msgid "templates not found in %s"
+msgstr "沒有在 %s 中找到範本"
+
+#: setup.c
+#, c-format
+msgid "not copying templates from '%s': %s"
+msgstr "沒有從 '%s' 複製範本:%s"
+
+#: setup.c
+#, c-format
+msgid "invalid initial branch name: '%s'"
+msgstr "無效的初始分支名稱:'%s'"
+
+#: setup.c
+#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: 忽略 --initial-branch=%s"
+
+#: setup.c
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "不能處理 %d 類型的檔案"
+
+#: setup.c
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "不能移動 %s 至 %s"
+
+#: setup.c
+msgid "attempt to reinitialize repository with different hash"
+msgstr "嘗試以不同的雜湊值重新初始化版本庫"
+
+#: setup.c
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "嘗試以不同的引用儲存格式重新初始化版本庫"
+
+#: setup.c
+#, c-format
+msgid "%s already exists"
+msgstr "%s 已經存在"
+
+#: setup.c
+#, c-format
+msgid "Reinitialized existing shared Git repository in %s%s\n"
+msgstr "重新初始化已存在的共享 Git 版本庫於 %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Reinitialized existing Git repository in %s%s\n"
+msgstr "重新初始化已存在的 Git 版本庫於 %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Initialized empty shared Git repository in %s%s\n"
+msgstr "已初始化空的共享 Git 版本庫於 %s%s\n"
+
+#: setup.c
+#, c-format
+msgid "Initialized empty Git repository in %s%s\n"
+msgstr "已初始化空的 Git 版本庫於 %s%s\n"
+
 #: sparse-index.c
 #, c-format
 msgid "index entry is a directory, but not sparse (%08x)"
@@ -24924,11 +25649,6 @@
 msgid_plural "%u bytes/s"
 msgstr[0] "%u 位元組/秒"
 
-#: strbuf.c
-#, c-format
-msgid "could not edit '%s'"
-msgstr "無法編輯 '%s'"
-
 #: submodule-config.c
 #, c-format
 msgid "ignoring suspicious submodule name: %s"
@@ -25139,6 +25859,19 @@
 msgid "failed to lstat '%s'"
 msgstr "無法 lstat “%s”"
 
+#: t/helper/test-bundle-uri.c
+msgid "no remote configured to get bundle URIs from"
+msgstr "沒有設定可以用來取得套件包 URIs 的遠端"
+
+#: t/helper/test-bundle-uri.c
+#, c-format
+msgid "remote '%s' has no configured URL"
+msgstr "“%s” 遠端未設定 URL"
+
+#: t/helper/test-bundle-uri.c
+msgid "could not get the bundle-uri list"
+msgstr "無法取得 bundle-uri 清單"
+
 #: t/helper/test-cache-tree.c
 msgid "test-tool cache-tree <options> (control|prime|update)"
 msgstr "test-tool cache-tree <options> (control|prime|update)"
@@ -25151,14 +25884,6 @@
 msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr "在快取樹狀物件中,要使失效的項目數量(預設值為 0)"
 
-#: t/helper/test-fast-rebase.c
-msgid "unhandled options"
-msgstr "未處理選項"
-
-#: t/helper/test-fast-rebase.c
-msgid "error preparing revisions"
-msgstr "準備修訂版本時發生錯誤"
-
 #: t/helper/test-reach.c
 #, c-format
 msgid "commit %s is not marked reachable"
@@ -25352,10 +26077,6 @@
 msgid "invalid remote service path"
 msgstr "無效的遠端服務路徑"
 
-#: transport-helper.c transport.c
-msgid "operation not supported by protocol"
-msgstr "協定不支援該動作"
-
 #: transport-helper.c
 #, c-format
 msgid "can't connect to subservice %s"
@@ -25521,11 +26242,6 @@
 
 #: transport.c
 #, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "設定 '%s' 的取值未知:%s"
-
-#: transport.c
-#, c-format
 msgid "transport '%s' not allowed"
 msgstr "傳輸 '%s' 不允許"
 
@@ -25575,6 +26291,18 @@
 msgid "failed to push all needed submodules"
 msgstr "不能推送全部需要的子模組"
 
+#: transport.c
+msgid "bundle-uri operation not supported by protocol"
+msgstr "通訊協定不支援 bundle-uri 動作"
+
+#: transport.c
+msgid "could not retrieve server-advertised bundle-uri list"
+msgstr "無法取得伺服器公佈的 bundle-uri 清單"
+
+#: transport.c
+msgid "operation not supported by protocol"
+msgstr "協定不支援該動作"
+
 #: tree-walk.c
 msgid "too-short tree object"
 msgstr "太短的樹狀物件"
@@ -26027,6 +26755,10 @@
 msgid "unable to get current working directory"
 msgstr "不能取得目前工作目錄"
 
+#: wrapper.c
+msgid "unable to get random bytes"
+msgstr "無法取得隨機位元組"
+
 #: wt-status.c
 msgid "Unmerged paths:"
 msgstr "未合併的路徑:"
@@ -26489,12 +27221,20 @@
 #: wt-status.c
 #, c-format
 msgid ""
-"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-"may speed it up, but you have to be careful not to forget to add\n"
-"new files yourself (see 'git help status')."
+"It took %.2f seconds to enumerate untracked files,\n"
+"but the results were cached, and subsequent runs may be faster."
 msgstr ""
-"耗費了 %.2f 秒以枚舉未追蹤的檔案。'status -uno' 也許能提高速度,\n"
-"但您需要小心不要忘了新增新檔案(參見 'git help status')。"
+"列舉未追蹤檔案花費 %.2f 秒,\n"
+"不過已經快取結果,之後執行的速度或許能比較快。"
+
+#: wt-status.c
+#, c-format
+msgid "It took %.2f seconds to enumerate untracked files."
+msgstr "列舉未追蹤檔案花費 %.2f 秒。"
+
+#: wt-status.c
+msgid "See 'git help status' for information on how to improve this."
+msgstr "請參閱 “git help status” 深入了解如何改善。"
 
 #: wt-status.c
 #, c-format
@@ -26591,6 +27331,11 @@
 msgid "cannot %s: Your index contains uncommitted changes."
 msgstr "不能%s:您的索引中包含未提交的變更。"
 
+#: xdiff-interface.c
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "給予「%2$s」的「%1$s」樣式未知"
+
 #: git-merge-octopus.sh git-merge-resolve.sh
 msgid ""
 "Error: Your local changes to the following files would be overwritten by "
@@ -26670,297 +27415,6 @@
 msgid "Unable to determine absolute path of git directory"
 msgstr "不能確定 git 目錄的絕對路徑"
 
-#. TRANSLATORS: you can adjust this to align "git add -i" status menu
-#: git-add--interactive.perl
-#, perl-format
-msgid "%12s %12s %s"
-msgstr "%12s %12s %s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "touched %d path\n"
-msgid_plural "touched %d paths\n"
-msgstr[0] "建立了 %d 個路徑\n"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for staging."
-msgstr "如果修補檔能完全套用,編輯區塊將立即標記為暫存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for stashing."
-msgstr "如果修補檔能完全套用,編輯區塊將立即標記為貯存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for unstaging."
-msgstr "如果修補檔能完全套用,編輯區塊將立即標記為未暫存。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for applying."
-msgstr "如果修補檔能完全套用,編輯區塊將立即標記為套用。"
-
-#: git-add--interactive.perl
-msgid ""
-"If the patch applies cleanly, the edited hunk will immediately be\n"
-"marked for discarding."
-msgstr "如果修補檔能完全套用,編輯塊將立即標記為捨棄。"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for writing: %s"
-msgstr "為寫入開啟區塊編輯檔案失敗:%s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid ""
-"---\n"
-"To remove '%s' lines, make them ' ' lines (context).\n"
-"To remove '%s' lines, delete them.\n"
-"Lines starting with %s will be removed.\n"
-msgstr ""
-"---\n"
-"要刪除 '%s' 開始的行,使其成為 ' ' 開始的行(上下文)。\n"
-"要刪除 '%s' 開始的行,刪除它們。\n"
-"以 %s 開始的行將被刪除。\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "failed to open hunk edit file for reading: %s"
-msgstr "無法讀取區塊編輯檔案:%s"
-
-#: git-add--interactive.perl
-msgid ""
-"y - stage this hunk\n"
-"n - do not stage this hunk\n"
-"q - quit; do not stage this hunk or any of the remaining ones\n"
-"a - stage this hunk and all later hunks in the file\n"
-"d - do not stage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 暫存此區塊\n"
-"n - 不要暫存此區塊\n"
-"q - 離開。不暫存此區塊及後面的全部區塊\n"
-"a - 暫存此區塊和本檔案中後面的全部區塊\n"
-"d - 不暫存此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - stash this hunk\n"
-"n - do not stash this hunk\n"
-"q - quit; do not stash this hunk or any of the remaining ones\n"
-"a - stash this hunk and all later hunks in the file\n"
-"d - do not stash this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 貯存此區塊\n"
-"n - 不要貯存此區塊\n"
-"q - 離開。不貯存此區塊及後面的全部區塊\n"
-"a - 貯存此區塊和本檔案中後面的全部區塊\n"
-"d - 不貯存此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - unstage this hunk\n"
-"n - do not unstage this hunk\n"
-"q - quit; do not unstage this hunk or any of the remaining ones\n"
-"a - unstage this hunk and all later hunks in the file\n"
-"d - do not unstage this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 不暫存此區塊\n"
-"n - 不要不暫存此區塊\n"
-"q - 離開。不要不暫存此區塊及後面的全部區塊\n"
-"a - 不暫存此區塊和本檔案中後面的全部區塊\n"
-"d - 不要不暫存此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index\n"
-"n - do not apply this hunk to index\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引中套用此區塊\n"
-"n - 不要在索引中套用此區塊\n"
-"q - 離開。不要套用此區塊及後面的全部區塊\n"
-"a - 套用此區塊和本檔案中後面的全部區塊\n"
-"d - 不要套用此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from worktree\n"
-"n - do not discard this hunk from worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在工作區中捨棄此區塊\n"
-"n - 不要在工作區中捨棄此區塊\n"
-"q - 離開。不要捨棄此區塊及後面的全部區塊\n"
-"a - 捨棄此區塊和本檔案中後面的全部區塊\n"
-"d - 不要捨棄此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - discard this hunk from index and worktree\n"
-"n - do not discard this hunk from index and worktree\n"
-"q - quit; do not discard this hunk or any of the remaining ones\n"
-"a - discard this hunk and all later hunks in the file\n"
-"d - do not discard this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引和工作區中捨棄此區塊\n"
-"n - 不要在索引和工作區中捨棄此區塊\n"
-"q - 離開。不要捨棄此區塊及後面的全部區塊\n"
-"a - 捨棄此區塊和本檔案中後面的全部區塊\n"
-"d - 不要捨棄此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to index and worktree\n"
-"n - do not apply this hunk to index and worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在索引和工作區中套用此區塊\n"
-"n - 不要在索引和工作區中套用此區塊\n"
-"q - 離開。不要套用此區塊及後面的全部區塊\n"
-"a - 套用此區塊和本檔案中後面的全部區塊\n"
-"d - 不要套用此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"y - apply this hunk to worktree\n"
-"n - do not apply this hunk to worktree\n"
-"q - quit; do not apply this hunk or any of the remaining ones\n"
-"a - apply this hunk and all later hunks in the file\n"
-"d - do not apply this hunk or any of the later hunks in the file"
-msgstr ""
-"y - 在工作區中套用此區塊\n"
-"n - 不要在工作區中套用此區塊\n"
-"q - 離開。不要套用此區塊及後面的全部區塊\n"
-"a - 套用此區塊和本檔案中後面的全部區塊\n"
-"d - 不要套用此區塊和本檔案中後面的全部區塊"
-
-#: git-add--interactive.perl
-msgid ""
-"g - select a hunk to go to\n"
-"/ - search for a hunk matching the given regex\n"
-"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"
-"s - split the current hunk into smaller hunks\n"
-"e - manually edit the current hunk\n"
-"? - print help\n"
-msgstr ""
-"g - 選擇跳轉到一個區塊\n"
-"/ - 尋找和提供常規表示式符合的區塊\n"
-"j - 維持此區塊未決狀態,檢視下一個未決定區塊\n"
-"J - 維持此區塊未決狀態,檢視下一個區塊\n"
-"k - 維持此區塊未決狀態,檢視上一個未決定區塊\n"
-"K - 維持此區塊未決狀態,檢視上一個區塊\n"
-"s - 分割目前區塊為更小的區塊\n"
-"e - 手動編輯目前區塊\n"
-"? - 顯示說明\n"
-
-#: git-add--interactive.perl
-msgid "The selected hunks do not apply to the index!\n"
-msgstr "選取區塊不能套用到索引!\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "ignoring unmerged: %s\n"
-msgstr "忽略未套用的:%s\n"
-
-#: git-add--interactive.perl
-msgid "No other hunks to goto\n"
-msgstr "沒有其它可供跳轉的區塊\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Invalid number: '%s'\n"
-msgstr "無效數字:'%s'\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Sorry, only %d hunk available.\n"
-msgid_plural "Sorry, only %d hunks available.\n"
-msgstr[0] "對不起,只有 %d 個可用區塊。\n"
-
-#: git-add--interactive.perl
-msgid "No other hunks to search\n"
-msgstr "沒有其它可供尋找的區塊\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Malformed search regexp %s: %s\n"
-msgstr "錯誤的常規表示式 %s:%s\n"
-
-#: git-add--interactive.perl
-msgid "No hunk matches the given pattern\n"
-msgstr "沒有和提供模式相符合的區塊\n"
-
-#: git-add--interactive.perl
-msgid "No previous hunk\n"
-msgstr "沒有上一個區塊\n"
-
-#: git-add--interactive.perl
-msgid "No next hunk\n"
-msgstr "沒有下一個區塊\n"
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot split this hunk\n"
-msgstr "對不起,不能分割這個區塊\n"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "Split into %d hunk.\n"
-msgid_plural "Split into %d hunks.\n"
-msgstr[0] "分割為 %d 塊。\n"
-
-#: git-add--interactive.perl
-msgid "Sorry, cannot edit this hunk\n"
-msgstr "對不起,不能編輯這個區塊\n"
-
-#. TRANSLATORS: please do not translate the command names
-#. 'status', 'update', 'revert', etc.
-#: git-add--interactive.perl
-msgid ""
-"status        - show paths with changes\n"
-"update        - add working tree state to the staged set of changes\n"
-"revert        - revert staged set of changes back to the HEAD version\n"
-"patch         - pick hunks and update selectively\n"
-"diff          - view diff between HEAD and index\n"
-"add untracked - add contents of untracked files to the staged set of "
-"changes\n"
-msgstr ""
-"status        - 顯示含變更的路徑\n"
-"update        - 新增工作區狀態至暫存列表\n"
-"revert        - 還原修改的暫存集至 HEAD 版本\n"
-"patch         - 挑選區塊並且有選擇地更新\n"
-"diff          - 顯示 HEAD 和索引間差異\n"
-"add untracked - 新增未追蹤檔案的內容至暫存列表\n"
-
-#: git-add--interactive.perl
-msgid "missing --"
-msgstr "缺少 --"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "unknown --patch mode: %s"
-msgstr "未知的 --patch 模式:%s"
-
-#: git-add--interactive.perl
-#, perl-format
-msgid "invalid argument %s, expecting --"
-msgstr "無效的參數 %s,期望是 --"
-
 #: git-send-email.perl
 msgid "local zone differs from GMT by a non-minute interval\n"
 msgstr "本機時間和 GMT 有不到一分鐘間隔\n"
@@ -27098,13 +27552,13 @@
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s: %s"
-msgstr "無法開啟 %s: %s"
+msgid "Failed to open %s.final: %s"
+msgstr "無法開啟 %s.final: %s"
 
 #: git-send-email.perl
 #, perl-format
-msgid "Failed to open %s.final: %s"
-msgstr "無法開啟 %s.final: %s"
+msgid "Failed to open %s: %s"
+msgstr "無法開啟 %s: %s"
 
 #: git-send-email.perl
 msgid "Summary email is empty, skipping it\n"
@@ -27282,8 +27736,8 @@
 
 #: git-send-email.perl
 #, perl-format
-msgid "(%s) Adding %s: %s from: '%s'\n"
-msgstr "(%s) 新增 %s: %s 自:'%s'\n"
+msgid "(%s) Malformed output from '%s'"
+msgstr "(%s) 從 “%s” 讀到格式錯誤的輸出"
 
 #: git-send-email.perl
 #, perl-format
@@ -27291,6 +27745,11 @@
 msgstr "(%s) 無法關閉管道至 '%s'"
 
 #: git-send-email.perl
+#, perl-format
+msgid "(%s) Adding %s: %s from: '%s'\n"
+msgstr "(%s) 新增 %s: %s 自:'%s'\n"
+
+#: git-send-email.perl
 msgid "cannot send message as 7bit"
 msgstr "不能以 7bit 形式傳送訊息"
 
@@ -27334,146 +27793,54 @@
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "您真的要傳送 %s?[y|N]: "
 
-#, c-format
-#~ msgid "unable to normalize object directory: %s"
-#~ msgstr "無法規範化物件目錄: %s"
-
-#~ msgid "reset the bisection state"
-#~ msgstr "清除二分搜尋狀態"
-
-#~ msgid "check whether bad or good terms exist"
-#~ msgstr "檢查壞的或好的術語是否存在"
-
-#~ msgid "print out the bisect terms"
-#~ msgstr "列印二分搜尋術語"
-
-#~ msgid "start the bisect session"
-#~ msgstr "啟動二分搜尋過程"
-
-#~ msgid "find the next bisection commit"
-#~ msgstr "尋找下一個二分搜尋提交"
-
-#~ msgid "mark the state of ref (or refs)"
-#~ msgstr "標記 ref (或 refs) 的狀態"
-
-#~ msgid "list the bisection steps so far"
-#~ msgstr "列出迄今的二分搜尋步驟"
-
-#~ msgid "replay the bisection process from the given file"
-#~ msgstr "從指定檔案重放二分搜尋過程"
-
-#~ msgid "skip some commits for checkout"
-#~ msgstr "略過要簽出的部分提交"
-
-#~ msgid "visualize the bisection"
-#~ msgstr "視覺化二分搜尋過程"
-
-#~ msgid "use <cmd>... to automatically bisect"
-#~ msgstr "使用 <cmd>... 自動進行二分搜尋"
-
-#~ msgid "no log for BISECT_WRITE"
-#~ msgstr "BISECT_WRITE 無日誌"
-
-#~ msgid "Couldn't look up commit object for HEAD"
-#~ msgstr "無法查詢 HEAD 指向的提交物件"
-
-#~ msgid "git bundle create [<options>] <file> <git-rev-list args>"
-#~ msgstr "git bundle create [<選項>] <檔案> <git-rev-list 參數>"
-
-#, c-format
-#~ msgid "options '%s' and '%s %s' cannot be used together"
-#~ msgstr "「%s」和「%s %s」選項不得同時使用"
-
-#~ msgid "git commit [<options>] [--] <pathspec>..."
-#~ msgstr "git commit [<選項>] [--] <路徑規格>..."
-
-#~ msgid "git fsck [<options>] [<object>...]"
-#~ msgstr "git fsck [<選項>] [<物件>...]"
-
-#~ msgid "git fsmonitor--daemon stop"
-#~ msgstr "git fsmonitor--daemon stop"
-
-#~ msgid "git fsmonitor--daemon status"
-#~ msgstr "git fsmonitor--daemon status"
-
-#~ msgid "failed to run 'git config'"
-#~ msgstr "無法執行 ‘git config’"
-
-#, c-format
-#~ msgid "could not get 'onto': '%s'"
-#~ msgstr "無法取得 'onto':'%s'"
-
-#~ msgid "Could not resolve HEAD to a revision"
-#~ msgstr "無法將 HEAD 解析為一個版本"
-
-#, c-format
-#~ msgid "missing required file: %s"
-#~ msgstr "缺少必要檔案:%s"
-
-#~ msgid "git revert [<options>] <commit-ish>..."
-#~ msgstr "git revert [<選項>] <提交號>..."
-
-#~ msgid "git revert <subcommand>"
-#~ msgstr "git revert <子指令>"
-
-#~ msgid "git cherry-pick [<options>] <commit-ish>..."
-#~ msgstr "git cherry-pick [<選項>] <提交號>..."
-
-#~ msgid "git cherry-pick <subcommand>"
-#~ msgstr "git cherry-pick <子指令>"
-
-#~ msgid "git rm [<options>] [--] <file>..."
-#~ msgstr "git rm [<選項>] [--] <檔案>..."
-
-#~ msgid "using --group=trailer with stdin is not supported"
-#~ msgstr "不支援在標準輸入使用 --group=trailer"
-
-#~ msgid "git stash show [<options>] [<stash>]"
-#~ msgstr "git stash show [<選項>] [<stash>]"
-
-#~ msgid "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
-#~ msgstr "git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"
+#~ msgid "-x and -X cannot be used together"
+#~ msgstr "-x 和 -X 不能同時使用"
 
 #~ msgid ""
-#~ "git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-#~ "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
-#~ "          [--] [<pathspec>...]]"
+#~ "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+#~ "exclude"
 #~ msgstr ""
-#~ "git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-#~ "          [-u|--include-untracked] [-a|--all] [-m|--message <消息>]\n"
-#~ "          [--] [<路徑規格>...]]"
+#~ "--bundle-uri 與 --depth、--shallow-since 和 --shallow-exclude 不相容"
+
+#~ msgid "--merge-base is incompatible with --stdin"
+#~ msgstr "--merge-base 與 --stdin 不相容"
 
 #~ msgid ""
-#~ "git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-#~ "               [-u|--include-untracked] [-a|--all] [<message>]"
-#~ msgstr ""
-#~ "git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
-#~ "               [-u|--include-untracked] [-a|--all] [<消息>]"
+#~ "apply options are incompatible with rebase.autoSquash.  Consider adding --"
+#~ "no-autosquash"
+#~ msgstr "apply 選項與 rebase.autoSquash 不相容。請考慮加上 --no-autosquash"
 
-#~ msgid "path into the working tree"
-#~ msgstr "到工作區的路徑"
+#~ msgid "--exclude-hidden cannot be used together with --branches"
+#~ msgstr "--exclude-hidden 無法與 --branches 同時使用"
 
-#~ msgid "recurse into submodules"
-#~ msgstr "在子模組中遞迴"
+#~ msgid "--exclude-hidden cannot be used together with --tags"
+#~ msgstr "--exclude-hidden 無法與 --tags 同時使用"
 
-#~ msgid "check if it is safe to write to the .gitmodules file"
-#~ msgstr "檢查寫入 .gitmodules 檔案是否安全"
-
-#~ msgid "unset the config in the .gitmodules file"
-#~ msgstr "取消 .gitmodules 檔案中的設定"
-
-#~ msgid "git submodule--helper config --unset <name>"
-#~ msgstr "git submodule--helper config --unset <名稱>"
+#~ msgid "--exclude-hidden cannot be used together with --remotes"
+#~ msgstr "--exclude-hidden 無法與 --remotes 同時使用"
 
 #, c-format
-#~ msgid "'%s' is not a valid submodule--helper subcommand"
-#~ msgstr "'%s' 不是一個有效的 submodule--helper 子指令"
+#~ msgid "only one of '%s', '%s' or '%s' can be given"
+#~ msgstr "只能傳入 “%s”、“%s” 或 “%s”"
 
-#~ msgid "git upload-pack [<options>] <dir>"
-#~ msgstr "git upload-pack [<選項>] <目錄>"
+#, c-format
+#~ msgid "'%s' and '%s' cannot be used together"
+#~ msgstr "無法同時使用 “%s” 和 “%s”"
 
-#~ msgid "git worktree add [<options>] <path> [<commit-ish>]"
-#~ msgstr "git worktree add [<選項>] <路徑> [<提交>]"
+#, c-format
+#~ msgid "options '%s', and '%s' cannot be used together"
+#~ msgstr "無法同時使用 “%s” 和 “%s” 選項"
 
-#~ msgid "git worktree lock [<options>] <path>"
-#~ msgstr "git worktree lock [<選項>] <路徑>"
+#~ msgid "<commit-ish>"
+#~ msgstr "<提交指示元>"
+
+#, c-format
+#~ msgid "%s is incompatible with %s"
+#~ msgstr "%s 與 %s 不相容"
+
+#, c-format
+#~ msgid "could not remove reference %s"
+#~ msgstr "無法刪除引用 %s"
+
+#~ msgid "unhandled options"
+#~ msgstr "未處理選項"
diff --git a/preload-index.c b/preload-index.c
index 100f7a3..63fd35d 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -1,14 +1,20 @@
 /*
  * Copyright (C) 2008 Linus Torvalds
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "environment.h"
 #include "fsmonitor.h"
-#include "config.h"
+#include "gettext.h"
+#include "parse.h"
+#include "preload-index.h"
 #include "progress.h"
+#include "read-cache.h"
 #include "thread-utils.h"
 #include "repository.h"
+#include "symlinks.h"
+#include "trace2.h"
 
 /*
  * Mostly randomly chosen maximum thread counts: we
diff --git a/preload-index.h b/preload-index.h
new file mode 100644
index 0000000..251b1ed
--- /dev/null
+++ b/preload-index.h
@@ -0,0 +1,15 @@
+#ifndef PRELOAD_INDEX_H
+#define PRELOAD_INDEX_H
+
+struct index_state;
+struct pathspec;
+struct repository;
+
+void preload_index(struct index_state *index,
+		   const struct pathspec *pathspec,
+		   unsigned int refresh_flags);
+int repo_read_index_preload(struct repository *,
+			    const struct pathspec *pathspec,
+			    unsigned refresh_flags);
+
+#endif /* PRELOAD_INDEX_H */
diff --git a/pretty.c b/pretty.c
index 1e1e218..2faf651 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1,8 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "utf8.h"
 #include "diff.h"
+#include "pager.h"
 #include "revision.h"
 #include "string-list.h"
 #include "mailmap.h"
@@ -13,6 +18,7 @@
 #include "gpg-interface.h"
 #include "trailer.h"
 #include "run-command.h"
+#include "object-name.h"
 
 /*
  * The limit for formatting directives, which enable the caller to append
@@ -51,6 +57,7 @@
 }
 
 static int git_pretty_formats_config(const char *var, const char *value,
+				     const struct config_context *ctx UNUSED,
 				     void *cb UNUSED)
 {
 	struct cmt_fmt_map *commit_format = NULL;
@@ -140,7 +147,7 @@
 	for (i = 0; i < commit_formats_len; i++) {
 		size_t match_len;
 
-		if (!starts_with(commit_formats[i].name, sought))
+		if (!istarts_with(commit_formats[i].name, sought))
 			continue;
 
 		match_len = strlen(commit_formats[i].name);
@@ -719,7 +726,7 @@
 		 * Otherwise, we still want to munge the encoding header in the
 		 * result, which will be done by modifying the buffer. If we
 		 * are using a fresh copy, we can reuse it. But if we are using
-		 * the cached copy from get_commit_buffer, we need to duplicate it
+		 * the cached copy from repo_get_commit_buffer, we need to duplicate it
 		 * to avoid munging the cached copy.
 		 */
 		if (msg == get_cached_commit_buffer(r, commit, NULL))
@@ -1245,6 +1252,27 @@
 	return 0;
 }
 
+static struct strbuf *expand_string_arg(struct strbuf *sb,
+					const char *argval, size_t arglen)
+{
+	char *fmt = xstrndup(argval, arglen);
+	const char *format = fmt;
+
+	strbuf_reset(sb);
+	while (strbuf_expand_step(sb, &format)) {
+		size_t len;
+
+		if (skip_prefix(format, "%", &format))
+			strbuf_addch(sb, '%');
+		else if ((len = strbuf_expand_literal(sb, format)))
+			format += len;
+		else
+			strbuf_addch(sb, '%');
+	}
+	free(fmt);
+	return sb;
+}
+
 int format_set_trailers_options(struct process_trailer_options *opts,
 				struct string_list *filter_list,
 				struct strbuf *sepbuf,
@@ -1273,21 +1301,9 @@
 			opts->filter_data = filter_list;
 			opts->only_trailers = 1;
 		} else if (match_placeholder_arg_value(*arg, "separator", arg, &argval, &arglen)) {
-			char *fmt;
-
-			strbuf_reset(sepbuf);
-			fmt = xstrndup(argval, arglen);
-			strbuf_expand(sepbuf, fmt, strbuf_expand_literal_cb, NULL);
-			free(fmt);
-			opts->separator = sepbuf;
+			opts->separator = expand_string_arg(sepbuf, argval, arglen);
 		} else if (match_placeholder_arg_value(*arg, "key_value_separator", arg, &argval, &arglen)) {
-			char *fmt;
-
-			strbuf_reset(kvsepbuf);
-			fmt = xstrndup(argval, arglen);
-			strbuf_expand(kvsepbuf, fmt, strbuf_expand_literal_cb, NULL);
-			free(fmt);
-			opts->key_value_separator = kvsepbuf;
+			opts->key_value_separator = expand_string_arg(kvsepbuf, argval, arglen);
 		} else if (!match_placeholder_bool_arg(*arg, "only", arg, &opts->only_trailers) &&
 			   !match_placeholder_bool_arg(*arg, "unfold", arg, &opts->unfold) &&
 			   !match_placeholder_bool_arg(*arg, "keyonly", arg, &opts->key_only) &&
@@ -1368,6 +1384,44 @@
 	return arg - start;
 }
 
+
+static int parse_decoration_option(const char **arg,
+				   const char *name,
+				   char **opt)
+{
+	const char *argval;
+	size_t arglen;
+
+	if (match_placeholder_arg_value(*arg, name, arg, &argval, &arglen)) {
+		struct strbuf sb = STRBUF_INIT;
+
+		expand_string_arg(&sb, argval, arglen);
+		*opt = strbuf_detach(&sb, NULL);
+		return 1;
+	}
+	return 0;
+}
+
+static void parse_decoration_options(const char **arg,
+				     struct decoration_options *opts)
+{
+	while (parse_decoration_option(arg, "prefix", &opts->prefix) ||
+	       parse_decoration_option(arg, "suffix", &opts->suffix) ||
+	       parse_decoration_option(arg, "separator", &opts->separator) ||
+	       parse_decoration_option(arg, "pointer", &opts->pointer) ||
+	       parse_decoration_option(arg, "tag", &opts->tag))
+		;
+}
+
+static void free_decoration_options(const struct decoration_options *opts)
+{
+	free(opts->prefix);
+	free(opts->suffix);
+	free(opts->separator);
+	free(opts->pointer);
+	free(opts->tag);
+}
+
 static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 				const char *placeholder,
 				void *context)
@@ -1381,7 +1435,7 @@
 	char **slot;
 
 	/* these are independent of the commit */
-	res = strbuf_expand_literal_cb(sb, placeholder, NULL);
+	res = strbuf_expand_literal(sb, placeholder);
 	if (res)
 		return res;
 
@@ -1521,11 +1575,18 @@
 		strbuf_addstr(sb, get_revision_mark(NULL, commit));
 		return 1;
 	case 'd':
-		format_decorations(sb, commit, c->auto_color);
+		format_decorations(sb, commit, c->auto_color, NULL);
 		return 1;
 	case 'D':
-		format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
-		return 1;
+		{
+			const struct decoration_options opts = {
+				.prefix = "",
+				.suffix = ""
+			};
+
+			format_decorations(sb, commit, c->auto_color, &opts);
+			return 1;
+		}
 	case 'S':		/* tag/branch like --source */
 		if (!(c->pretty_ctx->rev && c->pretty_ctx->rev->sources))
 			return 0;
@@ -1622,6 +1683,23 @@
 		return 2;
 	}
 
+	if (skip_prefix(placeholder, "(decorate", &arg)) {
+		struct decoration_options opts = { NULL };
+		size_t ret = 0;
+
+		if (*arg == ':') {
+			arg++;
+			parse_decoration_options(&arg, &opts);
+		}
+		if (*arg == ')') {
+			format_decorations(sb, commit, c->auto_color, &opts);
+			ret = arg - placeholder + 1;
+		}
+
+		free_decoration_options(&opts);
+		return ret;
+	}
+
 	/* For the rest we have to parse the commit header. */
 	if (!c->commit_header_parsed) {
 		msg = c->message =
@@ -1681,7 +1759,7 @@
 				goto trailer_out;
 		}
 		if (*arg == ')') {
-			format_trailers_from_commit(sb, msg + c->subject_off, &opts);
+			format_trailers_from_commit(&opts, msg + c->subject_off, sb);
 			ret = arg - placeholder + 1;
 		}
 	trailer_out:
@@ -1799,7 +1877,7 @@
 
 static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
 				 const char *placeholder,
-				 void *context)
+				 struct format_commit_context *context)
 {
 	size_t consumed, orig_len;
 	enum {
@@ -1838,10 +1916,10 @@
 	}
 
 	orig_len = sb->len;
-	if (((struct format_commit_context *)context)->flush_type != no_flush)
-		consumed = format_and_pad_commit(sb, placeholder, context);
-	else
+	if (context->flush_type == no_flush)
 		consumed = format_commit_one(sb, placeholder, context);
+	else
+		consumed = format_and_pad_commit(sb, placeholder, context);
 	if (magic == NO_MAGIC)
 		return consumed;
 
@@ -1857,40 +1935,38 @@
 	return consumed + 1;
 }
 
-static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
-				   void *context)
-{
-	struct userformat_want *w = context;
-
-	if (*placeholder == '+' || *placeholder == '-' || *placeholder == ' ')
-		placeholder++;
-
-	switch (*placeholder) {
-	case 'N':
-		w->notes = 1;
-		break;
-	case 'S':
-		w->source = 1;
-		break;
-	case 'd':
-	case 'D':
-		w->decorate = 1;
-		break;
-	}
-	return 0;
-}
-
 void userformat_find_requirements(const char *fmt, struct userformat_want *w)
 {
-	struct strbuf dummy = STRBUF_INIT;
-
 	if (!fmt) {
 		if (!user_format)
 			return;
 		fmt = user_format;
 	}
-	strbuf_expand(&dummy, fmt, userformat_want_item, w);
-	strbuf_release(&dummy);
+	while ((fmt = strchr(fmt, '%'))) {
+		fmt++;
+		if (skip_prefix(fmt, "%", &fmt))
+			continue;
+
+		if (*fmt == '+' || *fmt == '-' || *fmt == ' ')
+			fmt++;
+
+		switch (*fmt) {
+		case 'N':
+			w->notes = 1;
+			break;
+		case 'S':
+			w->source = 1;
+			break;
+		case 'd':
+		case 'D':
+			w->decorate = 1;
+			break;
+		case '(':
+			if (starts_with(fmt + 1, "decorate"))
+				w->decorate = 1;
+			break;
+		}
+	}
 }
 
 void repo_format_commit_message(struct repository *r,
@@ -1907,7 +1983,16 @@
 	const char *output_enc = pretty_ctx->output_encoding;
 	const char *utf8 = "UTF-8";
 
-	strbuf_expand(sb, format, format_commit_item, &context);
+	while (strbuf_expand_step(sb, &format)) {
+		size_t len;
+
+		if (skip_prefix(format, "%", &format))
+			strbuf_addch(sb, '%');
+		else if ((len = format_commit_item(sb, format, &context)))
+			format += len;
+		else
+			strbuf_addch(sb, '%');
+	}
 	rewrap_message_tail(sb, &context, 0, 0, 0);
 
 	/*
@@ -1992,11 +2077,11 @@
 	}
 }
 
-void pp_title_line(struct pretty_print_context *pp,
-		   const char **msg_p,
-		   struct strbuf *sb,
-		   const char *encoding,
-		   int need_8bit_cte)
+void pp_email_subject(struct pretty_print_context *pp,
+		      const char **msg_p,
+		      struct strbuf *sb,
+		      const char *encoding,
+		      int need_8bit_cte)
 {
 	static const int max_length = 78; /* per rfc2047 */
 	struct strbuf title;
@@ -2006,19 +2091,14 @@
 				pp->preserve_subject ? "\n" : " ");
 
 	strbuf_grow(sb, title.len + 1024);
-	if (pp->print_email_subject) {
-		if (pp->rev)
-			fmt_output_email_subject(sb, pp->rev);
-		if (pp->encode_email_headers &&
-		    needs_rfc2047_encoding(title.buf, title.len))
-			add_rfc2047(sb, title.buf, title.len,
-						encoding, RFC2047_SUBJECT);
-		else
-			strbuf_add_wrapped_bytes(sb, title.buf, title.len,
+	fmt_output_email_subject(sb, pp->rev);
+	if (pp->encode_email_headers &&
+	    needs_rfc2047_encoding(title.buf, title.len))
+		add_rfc2047(sb, title.buf, title.len,
+			    encoding, RFC2047_SUBJECT);
+	else
+		strbuf_add_wrapped_bytes(sb, title.buf, title.len,
 					 -last_line_length(sb), 1, max_length);
-	} else {
-		strbuf_addbuf(sb, &title);
-	}
 	strbuf_addch(sb, '\n');
 
 	if (need_8bit_cte == 0) {
@@ -2041,9 +2121,8 @@
 	if (pp->after_subject) {
 		strbuf_addstr(sb, pp->after_subject);
 	}
-	if (cmit_fmt_is_mail(pp->fmt)) {
-		strbuf_addch(sb, '\n');
-	}
+
+	strbuf_addch(sb, '\n');
 
 	if (pp->in_body_headers.nr) {
 		int i;
@@ -2199,12 +2278,14 @@
 	int need_8bit_cte = pp->need_8bit_cte;
 
 	if (pp->fmt == CMIT_FMT_USERFORMAT) {
-		format_commit_message(commit, user_format, sb, pp);
+		repo_format_commit_message(the_repository, commit,
+					   user_format, sb, pp);
 		return;
 	}
 
 	encoding = get_log_output_encoding();
-	msg = reencoded = logmsg_reencode(commit, NULL, encoding);
+	msg = reencoded = repo_logmsg_reencode(the_repository, commit, NULL,
+					       encoding);
 
 	if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
 		indent = 0;
@@ -2233,7 +2314,7 @@
 	}
 
 	pp_header(pp, encoding, commit, &msg, sb);
-	if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) {
+	if (pp->fmt != CMIT_FMT_ONELINE && !cmit_fmt_is_mail(pp->fmt)) {
 		strbuf_addch(sb, '\n');
 	}
 
@@ -2241,8 +2322,11 @@
 	msg = skip_blank_lines(msg);
 
 	/* These formats treat the title line specially. */
-	if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
-		pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
+	if (pp->fmt == CMIT_FMT_ONELINE) {
+		msg = format_subject(sb, msg, " ");
+		strbuf_addch(sb, '\n');
+	} else if (cmit_fmt_is_mail(pp->fmt))
+		pp_email_subject(pp, &msg, sb, encoding, need_8bit_cte);
 
 	beginning_of_body = sb->len;
 	if (pp->fmt != CMIT_FMT_ONELINE)
@@ -2261,7 +2345,7 @@
 	if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body)
 		strbuf_addch(sb, '\n');
 
-	unuse_commit_buffer(commit, reencoded);
+	repo_unuse_commit_buffer(the_repository, commit, reencoded);
 }
 
 void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
diff --git a/pretty.h b/pretty.h
index f34e24c..9cc9e5d 100644
--- a/pretty.h
+++ b/pretty.h
@@ -1,11 +1,11 @@
 #ifndef PRETTY_H
 #define PRETTY_H
 
-#include "cache.h"
 #include "date.h"
 #include "string-list.h"
 
 struct commit;
+struct repository;
 struct strbuf;
 struct process_trailer_options;
 
@@ -35,11 +35,10 @@
 	 */
 	enum cmit_fmt fmt;
 	int abbrev;
-	const char *after_subject;
+	char *after_subject;
 	int preserve_subject;
 	struct date_mode date_mode;
 	unsigned date_mode_explicit:1;
-	int print_email_subject;
 	int expand_tabs_in_log;
 	int need_8bit_cte;
 	char *notes_message;
@@ -96,13 +95,13 @@
 			const char *encoding);
 
 /*
- * Format title line of commit message taken from "msg_p" and
+ * Format subject line of commit message taken from "msg_p" and
  * put it into "sb".
  * First line of "msg_p" is also affected.
  */
-void pp_title_line(struct pretty_print_context *pp, const char **msg_p,
-			struct strbuf *sb, const char *encoding,
-			int need_8bit_cte);
+void pp_email_subject(struct pretty_print_context *pp, const char **msg_p,
+		      struct strbuf *sb, const char *encoding,
+		      int need_8bit_cte);
 
 /*
  * Get current state of commit message from "msg_p" and continue formatting
@@ -120,10 +119,6 @@
 			const struct commit *commit,
 			const char *format, struct strbuf *sb,
 			const struct pretty_print_context *context);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define format_commit_message(c, f, s, con) \
-	repo_format_commit_message(the_repository, c, f, s, con)
-#endif
 
 /*
  * Parse given arguments from "arg", check it for correctness and
@@ -153,6 +148,8 @@
 /* Make subject of commit message suitable for filename */
 void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);
 
+int has_non_ascii(const char *text);
+
 /*
  * Set values of fields in "struct process_trailer_options"
  * according to trailers arguments.
diff --git a/prio-queue.c b/prio-queue.c
index d31b48e..450775a 100644
--- a/prio-queue.c
+++ b/prio-queue.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "prio-queue.h"
 
 static inline int compare(struct prio_queue *queue, int i, int j)
diff --git a/progress.c b/progress.c
index 0cdd875..c83cb60 100644
--- a/progress.c
+++ b/progress.c
@@ -9,13 +9,15 @@
  */
 
 #define GIT_TEST_PROGRESS_ONLY
-#include "cache.h"
-#include "gettext.h"
+#include "git-compat-util.h"
+#include "pager.h"
 #include "progress.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "trace.h"
+#include "trace2.h"
 #include "utf8.h"
-#include "config.h"
+#include "parse.h"
 
 #define TP_IDX_MAX      8
 
@@ -59,7 +61,7 @@
 }
 
 
-static void progress_interval(int signum)
+static void progress_interval(int signum UNUSED)
 {
 	progress_update = 1;
 }
diff --git a/promisor-remote.c b/promisor-remote.c
index faa7612..ac3aa1e 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -1,7 +1,10 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "promisor-remote.h"
 #include "config.h"
+#include "trace2.h"
 #include "transport.h"
 #include "strvec.h"
 #include "packfile.h"
@@ -97,7 +100,9 @@
 	config->promisors_tail = &r->next;
 }
 
-static int promisor_remote_config(const char *var, const char *value, void *data)
+static int promisor_remote_config(const char *var, const char *value,
+				  const struct config_context *ctx UNUSED,
+				  void *data)
 {
 	struct promisor_remote_config *config = data;
 	const char *name;
diff --git a/promisor-remote.h b/promisor-remote.h
index df36eb0..2cb9eda 100644
--- a/promisor-remote.h
+++ b/promisor-remote.h
@@ -18,24 +18,9 @@
 };
 
 void repo_promisor_remote_reinit(struct repository *r);
-static inline void promisor_remote_reinit(void)
-{
-	repo_promisor_remote_reinit(the_repository);
-}
-
 void promisor_remote_clear(struct promisor_remote_config *config);
-
 struct promisor_remote *repo_promisor_remote_find(struct repository *r, const char *remote_name);
-static inline struct promisor_remote *promisor_remote_find(const char *remote_name)
-{
-	return repo_promisor_remote_find(the_repository, remote_name);
-}
-
 int repo_has_promisor_remote(struct repository *r);
-static inline int has_promisor_remote(void)
-{
-	return repo_has_promisor_remote(the_repository);
-}
 
 /*
  * Fetches all requested objects from all promisor remotes, trying them one at
diff --git a/prompt.c b/prompt.c
index 50df172..8935fe4 100644
--- a/prompt.c
+++ b/prompt.c
@@ -1,5 +1,6 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
+#include "parse.h"
+#include "environment.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "prompt.h"
diff --git a/protocol-caps.c b/protocol-caps.c
index bbde918..75f4cbb 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -1,11 +1,12 @@
 #include "git-compat-util.h"
 #include "protocol-caps.h"
 #include "gettext.h"
+#include "hex.h"
 #include "pkt-line.h"
-#include "strvec.h"
-#include "hash.h"
+#include "hash-ll.h"
+#include "hex.h"
 #include "object.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "string-list.h"
 #include "strbuf.h"
 
@@ -77,7 +78,7 @@
 
 int cap_object_info(struct repository *r, struct packet_reader *request)
 {
-	struct requested_info info;
+	struct requested_info info = { 0 };
 	struct packet_writer writer;
 	struct string_list oid_str_list = STRING_LIST_INIT_DUP;
 
diff --git a/protocol.c b/protocol.c
index c53f7df..079ba75 100644
--- a/protocol.c
+++ b/protocol.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
 #include "protocol.h"
+#include "trace2.h"
 
 static enum protocol_version parse_protocol_version(const char *value)
 {
diff --git a/protocol.h b/protocol.h
index cef1a4a..1e574bb 100644
--- a/protocol.h
+++ b/protocol.h
@@ -1,6 +1,27 @@
 #ifndef PROTOCOL_H
 #define PROTOCOL_H
 
+/*
+ * Intensive research over the course of many years has shown that
+ * port 9418 is totally unused by anything else. Or
+ *
+ *	Your search - "port 9418" - did not match any documents.
+ *
+ * as www.google.com puts it.
+ *
+ * This port has been properly assigned for git use by IANA:
+ * git (Assigned-9418) [I06-050728-0001].
+ *
+ *	git  9418/tcp   git pack transfer service
+ *	git  9418/udp   git pack transfer service
+ *
+ * with Linus Torvalds <torvalds@osdl.org> as the point of
+ * contact. September 2005.
+ *
+ * See https://www.iana.org/assignments/port-numbers
+ */
+#define DEFAULT_GIT_PORT 9418
+
 enum protocol_version {
 	protocol_unknown_version = -1,
 	protocol_v0 = 0,
diff --git a/prune-packed.c b/prune-packed.c
index 261520b..e54daf7 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -1,4 +1,7 @@
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "object-store-ll.h"
 #include "packfile.h"
 #include "progress.h"
 #include "prune-packed.h"
diff --git a/quote.c b/quote.c
index 26719d2..3c05194 100644
--- a/quote.c
+++ b/quote.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "path.h"
 #include "quote.h"
+#include "strbuf.h"
 #include "strvec.h"
 
 int quote_path_fully = 1;
diff --git a/quote.h b/quote.h
index 87ff458..0300c29 100644
--- a/quote.h
+++ b/quote.h
@@ -3,6 +3,8 @@
 
 struct strbuf;
 
+extern int quote_path_fully;
+
 /* Help to copy the thing properly quoted for the shell safety.
  * any single quote is replaced with '\'', any exclamation point
  * is replaced with '\!', and the whole thing is enclosed in a
diff --git a/range-diff.c b/range-diff.c
index 4bd65ab..c45b6d8 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -1,5 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
 #include "range-diff.h"
+#include "object-name.h"
 #include "string-list.h"
 #include "run-command.h"
 #include "strvec.h"
@@ -8,7 +11,9 @@
 #include "linear-assignment.h"
 #include "diffcore.h"
 #include "commit.h"
+#include "pager.h"
 #include "pretty.h"
+#include "repository.h"
 #include "userdiff.h"
 #include "apply.h"
 #include "revision.h"
@@ -55,7 +60,7 @@
 		     "--output-indicator-context=#",
 		     "--no-abbrev-commit",
 		     "--pretty=medium",
-		     "--notes",
+		     "--show-notes-by-default",
 		     NULL);
 	strvec_push(&cp.args, range);
 	if (other_arg)
@@ -94,7 +99,7 @@
 				strbuf_reset(&buf);
 			}
 			CALLOC_ARRAY(util, 1);
-			if (get_oid(p, &util->oid)) {
+			if (repo_get_oid(the_repository, p, &util->oid)) {
 				error(_("could not parse commit '%s'"), p);
 				FREE_AND_NULL(util);
 				string_list_clear(list, 1);
@@ -225,16 +230,19 @@
 }
 
 static int patch_util_cmp(const void *cmp_data UNUSED,
-			  const struct patch_util *a,
-			  const struct patch_util *b,
-			  const char *keydata)
+			  const struct hashmap_entry *ha,
+			  const struct hashmap_entry *hb,
+			  const void *keydata)
 {
+	const struct patch_util
+		*a = container_of(ha, const struct patch_util, e),
+		*b = container_of(hb, const struct patch_util, e);
 	return strcmp(a->diff, keydata ? keydata : b->diff);
 }
 
 static void find_exact_matches(struct string_list *a, struct string_list *b)
 {
-	struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL);
+	struct hashmap map = HASHMAP_INIT(patch_util_cmp, NULL);
 	int i;
 
 	/* First, add the patches of a to a hash map */
@@ -390,7 +398,7 @@
 
 	if (!dashes->len)
 		strbuf_addchars(dashes, '-',
-				strlen(find_unique_abbrev(oid, abbrev)));
+				strlen(repo_find_unique_abbrev(the_repository, oid, abbrev)));
 
 	if (!b_util) {
 		color = color_old;
@@ -412,7 +420,7 @@
 		strbuf_addf(buf, "%*s:  %s ", patch_no_width, "-", dashes->buf);
 	else
 		strbuf_addf(buf, "%*d:  %s ", patch_no_width, a_util->i + 1,
-			    find_unique_abbrev(&a_util->oid, abbrev));
+			    repo_find_unique_abbrev(the_repository, &a_util->oid, abbrev));
 
 	if (status == '!')
 		strbuf_addf(buf, "%s%s", color_reset, color);
@@ -424,7 +432,7 @@
 		strbuf_addf(buf, " %*s:  %s", patch_no_width, "-", dashes->buf);
 	else
 		strbuf_addf(buf, " %*d:  %s", patch_no_width, b_util->i + 1,
-			    find_unique_abbrev(&b_util->oid, abbrev));
+			    repo_find_unique_abbrev(the_repository, &b_util->oid, abbrev));
 
 	commit = lookup_commit_reference(the_repository, oid);
 	if (commit) {
@@ -485,7 +493,7 @@
 	if (range_diff_opts->diffopt)
 		memcpy(&opts, range_diff_opts->diffopt, sizeof(opts));
 	else
-		diff_setup(&opts);
+		repo_diff_setup(the_repository, &opts);
 
 	opts.no_free = 1;
 	if (!opts.output_format)
@@ -588,7 +596,7 @@
 	int i, positive = 0, negative = 0;
 	struct rev_info revs;
 
-	init_revisions(&revs, NULL);
+	repo_init_revisions(the_repository, &revs, NULL);
 	if (setup_revisions(3, argv, &revs, NULL) == 1) {
 		for (i = 0; i < revs.pending.nr; i++)
 			if (revs.pending.objects[i].item->flags & UNINTERESTING)
diff --git a/reachable.c b/reachable.c
index aba63eb..3b85add 100644
--- a/reachable.c
+++ b/reachable.c
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
-#include "tag.h"
 #include "commit.h"
 #include "blob.h"
 #include "diff.h"
@@ -11,9 +12,12 @@
 #include "list-objects.h"
 #include "packfile.h"
 #include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "pack-bitmap.h"
 #include "pack-mtimes.h"
+#include "config.h"
+#include "run-command.h"
+#include "sequencer.h"
 
 struct connectivity_progress {
 	struct progress *progress;
@@ -27,6 +31,52 @@
 		display_progress(cp->progress, cp->count);
 }
 
+static void add_one_file(const char *path, struct rev_info *revs)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id oid;
+	struct object *object;
+
+	if (!read_oneliner(&buf, path, READ_ONELINER_SKIP_IF_EMPTY)) {
+		strbuf_release(&buf);
+		return;
+	}
+	strbuf_trim(&buf);
+	if (!get_oid_hex(buf.buf, &oid)) {
+		object = parse_object_or_die(&oid, buf.buf);
+		add_pending_object(revs, object, "");
+	}
+	strbuf_release(&buf);
+}
+
+/* Mark objects recorded in rebase state files as reachable. */
+static void add_rebase_files(struct rev_info *revs)
+{
+	struct strbuf buf = STRBUF_INIT;
+	size_t len;
+	const char *path[] = {
+		"rebase-apply/autostash",
+		"rebase-apply/orig-head",
+		"rebase-merge/autostash",
+		"rebase-merge/orig-head",
+	};
+	struct worktree **worktrees = get_worktrees();
+
+	for (struct worktree **wt = worktrees; *wt; wt++) {
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, get_worktree_git_dir(*wt));
+		strbuf_complete(&buf, '/');
+		len = buf.len;
+		for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
+			strbuf_setlen(&buf, len);
+			strbuf_addstr(&buf, path[i]);
+			add_one_file(buf.buf, revs);
+		}
+	}
+	strbuf_release(&buf);
+	free_worktrees(worktrees);
+}
+
 static int add_one_ref(const char *path, const struct object_id *oid,
 		       int flag, void *cb_data)
 {
@@ -48,7 +98,9 @@
  * The traversal will have already marked us as SEEN, so we
  * only need to handle any progress reporting here.
  */
-static void mark_object(struct object *obj, const char *name, void *data)
+static void mark_object(struct object *obj UNUSED,
+			const char *name UNUSED,
+			void *data)
 {
 	update_progress(data);
 }
@@ -63,8 +115,77 @@
 	timestamp_t timestamp;
 	report_recent_object_fn *cb;
 	int ignore_in_core_kept_packs;
+
+	struct oidset extra_recent_oids;
+	int extra_recent_oids_loaded;
 };
 
+static int run_one_gc_recent_objects_hook(struct oidset *set,
+					    const char *args)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	FILE *out;
+	int ret = 0;
+
+	cmd.use_shell = 1;
+	cmd.out = -1;
+
+	strvec_push(&cmd.args, args);
+
+	if (start_command(&cmd))
+		return -1;
+
+	out = xfdopen(cmd.out, "r");
+	while (strbuf_getline(&buf, out) != EOF) {
+		struct object_id oid;
+		const char *rest;
+
+		if (parse_oid_hex(buf.buf, &oid, &rest) || *rest) {
+			ret = error(_("invalid extra cruft tip: '%s'"), buf.buf);
+			break;
+		}
+
+		oidset_insert(set, &oid);
+	}
+
+	fclose(out);
+	ret |= finish_command(&cmd);
+
+	strbuf_release(&buf);
+	return ret;
+}
+
+static void load_gc_recent_objects(struct recent_data *data)
+{
+	const struct string_list *programs;
+	int ret = 0;
+	size_t i;
+
+	data->extra_recent_oids_loaded = 1;
+
+	if (git_config_get_string_multi("gc.recentobjectshook", &programs))
+		return;
+
+	for (i = 0; i < programs->nr; i++) {
+		ret = run_one_gc_recent_objects_hook(&data->extra_recent_oids,
+						       programs->items[i].string);
+		if (ret)
+			die(_("unable to enumerate additional recent objects"));
+	}
+}
+
+static int obj_is_recent(const struct object_id *oid, timestamp_t mtime,
+			 struct recent_data *data)
+{
+	if (mtime > data->timestamp)
+		return 1;
+
+	if (!data->extra_recent_oids_loaded)
+		load_gc_recent_objects(data);
+	return oidset_contains(&data->extra_recent_oids, oid);
+}
+
 static void add_recent_object(const struct object_id *oid,
 			      struct packed_git *pack,
 			      off_t offset,
@@ -74,7 +195,7 @@
 	struct object *obj;
 	enum object_type type;
 
-	if (mtime <= data->timestamp)
+	if (!obj_is_recent(oid, mtime, data))
 		return;
 
 	/*
@@ -152,7 +273,8 @@
 }
 
 static int add_recent_packed(const struct object_id *oid,
-			     struct packed_git *p, uint32_t pos,
+			     struct packed_git *p,
+			     uint32_t pos,
 			     void *data)
 {
 	struct object *obj;
@@ -188,24 +310,32 @@
 	data.cb = cb;
 	data.ignore_in_core_kept_packs = ignore_in_core_kept_packs;
 
+	oidset_init(&data.extra_recent_oids, 0);
+	data.extra_recent_oids_loaded = 0;
+
 	r = for_each_loose_object(add_recent_loose, &data,
 				  FOR_EACH_OBJECT_LOCAL_ONLY);
 	if (r)
-		return r;
+		goto done;
 
 	flags = FOR_EACH_OBJECT_LOCAL_ONLY | FOR_EACH_OBJECT_PACK_ORDER;
 	if (ignore_in_core_kept_packs)
 		flags |= FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS;
 
-	return for_each_packed_object(add_recent_packed, &data, flags);
+	r = for_each_packed_object(add_recent_packed, &data, flags);
+
+done:
+	oidset_clear(&data.extra_recent_oids);
+
+	return r;
 }
 
 static int mark_object_seen(const struct object_id *oid,
 			     enum object_type type,
-			     int exclude,
-			     uint32_t name_hash,
-			     struct packed_git *found_pack,
-			     off_t found_offset)
+			     int exclude UNUSED,
+			     uint32_t name_hash UNUSED,
+			     struct packed_git *found_pack UNUSED,
+			     off_t found_offset UNUSED)
 {
 	struct object *obj = lookup_object_by_type(the_repository, oid, type);
 	if (!obj)
@@ -239,6 +369,9 @@
 	head_ref(add_one_ref, revs);
 	other_head_refs(add_one_ref, revs);
 
+	/* rebase autostash and orig-head */
+	add_rebase_files(revs);
+
 	/* Add all reflog info */
 	if (mark_reflog)
 		add_reflogs_to_pending(revs, 0);
diff --git a/read-cache-ll.h b/read-cache-ll.h
new file mode 100644
index 0000000..09414af
--- /dev/null
+++ b/read-cache-ll.h
@@ -0,0 +1,489 @@
+#ifndef READ_CACHE_LL_H
+#define READ_CACHE_LL_H
+
+#include "hash-ll.h"
+#include "hashmap.h"
+#include "statinfo.h"
+
+/*
+ * Basic data structures for the directory cache
+ */
+
+#define CACHE_SIGNATURE 0x44495243	/* "DIRC" */
+struct cache_header {
+	uint32_t hdr_signature;
+	uint32_t hdr_version;
+	uint32_t hdr_entries;
+};
+
+#define INDEX_FORMAT_LB 2
+#define INDEX_FORMAT_UB 4
+
+struct cache_entry {
+	struct hashmap_entry ent;
+	struct stat_data ce_stat_data;
+	unsigned int ce_mode;
+	unsigned int ce_flags;
+	unsigned int mem_pool_allocated;
+	unsigned int ce_namelen;
+	unsigned int index;	/* for link extension */
+	struct object_id oid;
+	char name[FLEX_ARRAY]; /* more */
+};
+
+#define CE_STAGEMASK (0x3000)
+#define CE_EXTENDED  (0x4000)
+#define CE_VALID     (0x8000)
+#define CE_STAGESHIFT 12
+
+/*
+ * Range 0xFFFF0FFF in ce_flags is divided into
+ * two parts: in-memory flags and on-disk ones.
+ * Flags in CE_EXTENDED_FLAGS will get saved on-disk
+ * if you want to save a new flag, add it in
+ * CE_EXTENDED_FLAGS
+ *
+ * In-memory only flags
+ */
+#define CE_UPDATE            (1 << 16)
+#define CE_REMOVE            (1 << 17)
+#define CE_UPTODATE          (1 << 18)
+#define CE_ADDED             (1 << 19)
+
+#define CE_HASHED            (1 << 20)
+#define CE_FSMONITOR_VALID   (1 << 21)
+#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
+#define CE_CONFLICTED        (1 << 23)
+
+#define CE_UNPACKED          (1 << 24)
+#define CE_NEW_SKIP_WORKTREE (1 << 25)
+
+/* used to temporarily mark paths matched by pathspecs */
+#define CE_MATCHED           (1 << 26)
+
+#define CE_UPDATE_IN_BASE    (1 << 27)
+#define CE_STRIP_NAME        (1 << 28)
+
+/*
+ * Extended on-disk flags
+ */
+#define CE_INTENT_TO_ADD     (1 << 29)
+#define CE_SKIP_WORKTREE     (1 << 30)
+/* CE_EXTENDED2 is for future extension */
+#define CE_EXTENDED2         (1U << 31)
+
+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
+
+/*
+ * Safeguard to avoid saving wrong flags:
+ *  - CE_EXTENDED2 won't get saved until its semantic is known
+ *  - Bits in 0x0000FFFF have been saved in ce_flags already
+ *  - Bits in 0x003F0000 are currently in-memory flags
+ */
+#if CE_EXTENDED_FLAGS & 0x803FFFFF
+#error "CE_EXTENDED_FLAGS out of range"
+#endif
+
+/* Forward structure decls */
+struct pathspec;
+struct tree;
+
+/*
+ * Copy the sha1 and stat state of a cache entry from one to
+ * another. But we never change the name, or the hash state!
+ */
+static inline void copy_cache_entry(struct cache_entry *dst,
+				    const struct cache_entry *src)
+{
+	unsigned int state = dst->ce_flags & CE_HASHED;
+	int mem_pool_allocated = dst->mem_pool_allocated;
+
+	/* Don't copy hash chain and name */
+	memcpy(&dst->ce_stat_data, &src->ce_stat_data,
+			offsetof(struct cache_entry, name) -
+			offsetof(struct cache_entry, ce_stat_data));
+
+	/* Restore the hash state */
+	dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
+
+	/* Restore the mem_pool_allocated flag */
+	dst->mem_pool_allocated = mem_pool_allocated;
+}
+
+static inline unsigned create_ce_flags(unsigned stage)
+{
+	return (stage << CE_STAGESHIFT);
+}
+
+#define ce_namelen(ce) ((ce)->ce_namelen)
+#define ce_size(ce) cache_entry_size(ce_namelen(ce))
+#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
+#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
+#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
+#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
+
+#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
+
+#define SOMETHING_CHANGED	(1 << 0) /* unclassified changes go here */
+#define CE_ENTRY_CHANGED	(1 << 1)
+#define CE_ENTRY_REMOVED	(1 << 2)
+#define CE_ENTRY_ADDED		(1 << 3)
+#define RESOLVE_UNDO_CHANGED	(1 << 4)
+#define CACHE_TREE_CHANGED	(1 << 5)
+#define SPLIT_INDEX_ORDERED	(1 << 6)
+#define UNTRACKED_CHANGED	(1 << 7)
+#define FSMONITOR_CHANGED	(1 << 8)
+
+struct split_index;
+struct untracked_cache;
+struct progress;
+struct pattern_list;
+
+enum sparse_index_mode {
+	/*
+	 * There are no sparse directories in the index at all.
+	 *
+	 * Repositories that don't use cone-mode sparse-checkout will
+	 * always have their indexes in this mode.
+	 */
+	INDEX_EXPANDED = 0,
+
+	/*
+	 * The index has already been collapsed to sparse directories
+	 * whereever possible.
+	 */
+	INDEX_COLLAPSED,
+
+	/*
+	 * The sparse directories that exist are outside the
+	 * sparse-checkout boundary, but it is possible that some file
+	 * entries could collapse to sparse directory entries.
+	 */
+	INDEX_PARTIALLY_SPARSE,
+};
+
+struct index_state {
+	struct cache_entry **cache;
+	unsigned int version;
+	unsigned int cache_nr, cache_alloc, cache_changed;
+	struct string_list *resolve_undo;
+	struct cache_tree *cache_tree;
+	struct split_index *split_index;
+	struct cache_time timestamp;
+	unsigned name_hash_initialized : 1,
+		 initialized : 1,
+		 drop_cache_tree : 1,
+		 updated_workdir : 1,
+		 updated_skipworktree : 1,
+		 fsmonitor_has_run_once : 1;
+	enum sparse_index_mode sparse_index;
+	struct hashmap name_hash;
+	struct hashmap dir_hash;
+	struct object_id oid;
+	struct untracked_cache *untracked;
+	char *fsmonitor_last_update;
+	struct ewah_bitmap *fsmonitor_dirty;
+	struct mem_pool *ce_mem_pool;
+	struct progress *progress;
+	struct repository *repo;
+	struct pattern_list *sparse_checkout_patterns;
+};
+
+/**
+ * A "struct index_state istate" must be initialized with
+ * INDEX_STATE_INIT or the corresponding index_state_init().
+ *
+ * If the variable won't be used again, use release_index() to free()
+ * its resources. If it needs to be used again use discard_index(),
+ * which does the same thing, but will use use index_state_init() at
+ * the end. The discard_index() will use its own "istate->repo" as the
+ * "r" argument to index_state_init() in that case.
+ */
+#define INDEX_STATE_INIT(r) { \
+	.repo = (r), \
+}
+void index_state_init(struct index_state *istate, struct repository *r);
+void release_index(struct index_state *istate);
+
+/* Cache entry creation and cleanup */
+
+/*
+ * Create cache_entry intended for use in the specified index. Caller
+ * is responsible for discarding the cache_entry with
+ * `discard_cache_entry`.
+ */
+struct cache_entry *make_cache_entry(struct index_state *istate,
+				     unsigned int mode,
+				     const struct object_id *oid,
+				     const char *path,
+				     int stage,
+				     unsigned int refresh_options);
+
+struct cache_entry *make_empty_cache_entry(struct index_state *istate,
+					   size_t name_len);
+
+/*
+ * Create a cache_entry that is not intended to be added to an index. If
+ * `ce_mem_pool` is not NULL, the entry is allocated within the given memory
+ * pool. Caller is responsible for discarding "loose" entries with
+ * `discard_cache_entry()` and the memory pool with
+ * `mem_pool_discard(ce_mem_pool, should_validate_cache_entries())`.
+ */
+struct cache_entry *make_transient_cache_entry(unsigned int mode,
+					       const struct object_id *oid,
+					       const char *path,
+					       int stage,
+					       struct mem_pool *ce_mem_pool);
+
+struct cache_entry *make_empty_transient_cache_entry(size_t len,
+						     struct mem_pool *ce_mem_pool);
+
+/*
+ * Discard cache entry.
+ */
+void discard_cache_entry(struct cache_entry *ce);
+
+/*
+ * Check configuration if we should perform extra validation on cache
+ * entries.
+ */
+int should_validate_cache_entries(void);
+
+/*
+ * Duplicate a cache_entry. Allocate memory for the new entry from a
+ * memory_pool. Takes into account cache_entry fields that are meant
+ * for managing the underlying memory allocation of the cache_entry.
+ */
+struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
+
+/*
+ * Validate the cache entries in the index.  This is an internal
+ * consistency check that the cache_entry structs are allocated from
+ * the expected memory pool.
+ */
+void validate_cache_entries(const struct index_state *istate);
+
+/*
+ * Bulk prefetch all missing cache entries that are not GITLINKs and that match
+ * the given predicate. This function should only be called if
+ * repo_has_promisor_remote() returns true.
+ */
+typedef int (*must_prefetch_predicate)(const struct cache_entry *);
+void prefetch_cache_entries(const struct index_state *istate,
+			    must_prefetch_predicate must_prefetch);
+
+/* Initialize and use the cache information */
+struct lock_file;
+int do_read_index(struct index_state *istate, const char *path,
+		  int must_exist); /* for testting only! */
+int read_index_from(struct index_state *, const char *path,
+		    const char *gitdir);
+int is_index_unborn(struct index_state *);
+
+/* For use with `write_locked_index()`. */
+#define COMMIT_LOCK		(1 << 0)
+#define SKIP_IF_UNCHANGED	(1 << 1)
+
+/*
+ * Write the index while holding an already-taken lock. Close the lock,
+ * and if `COMMIT_LOCK` is given, commit it.
+ *
+ * Unless a split index is in use, write the index into the lockfile.
+ *
+ * With a split index, write the shared index to a temporary file,
+ * adjust its permissions and rename it into place, then write the
+ * split index to the lockfile. If the temporary file for the shared
+ * index cannot be created, fall back to the behavior described in
+ * the previous paragraph.
+ *
+ * With `COMMIT_LOCK`, the lock is always committed or rolled back.
+ * Without it, the lock is closed, but neither committed nor rolled
+ * back.
+ *
+ * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
+ * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
+ */
+int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
+
+void discard_index(struct index_state *);
+void move_index_extensions(struct index_state *dst, struct index_state *src);
+int unmerged_index(const struct index_state *);
+
+/**
+ * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
+ * compares istate to HEAD.  If tree is NULL and on an unborn branch,
+ * returns 1 if there are entries in istate, 0 otherwise.  If an strbuf is
+ * provided, the space-separated list of files that differ will be appended
+ * to it.
+ */
+int repo_index_has_changes(struct repository *repo,
+			   struct tree *tree,
+			   struct strbuf *sb);
+
+int verify_path(const char *path, unsigned mode);
+int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
+
+/*
+ * Searches for an entry defined by name and namelen in the given index.
+ * If the return value is positive (including 0) it is the position of an
+ * exact match. If the return value is negative, the negated value minus 1
+ * is the position where the entry would be inserted.
+ * Example: The current index consists of these files and its stages:
+ *
+ *   b#0, d#0, f#1, f#3
+ *
+ * index_name_pos(&index, "a", 1) -> -1
+ * index_name_pos(&index, "b", 1) ->  0
+ * index_name_pos(&index, "c", 1) -> -2
+ * index_name_pos(&index, "d", 1) ->  1
+ * index_name_pos(&index, "e", 1) -> -3
+ * index_name_pos(&index, "f", 1) -> -3
+ * index_name_pos(&index, "g", 1) -> -5
+ */
+int index_name_pos(struct index_state *, const char *name, int namelen);
+
+/*
+ * Like index_name_pos, returns the position of an entry of the given name in
+ * the index if one exists, otherwise returns a negative value where the negated
+ * value minus 1 is the position where the index entry would be inserted. Unlike
+ * index_name_pos, however, a sparse index is not expanded to find an entry
+ * inside a sparse directory.
+ */
+int index_name_pos_sparse(struct index_state *, const char *name, int namelen);
+
+/*
+ * Determines whether an entry with the given name exists within the
+ * given index. The return value is 1 if an exact match is found, otherwise
+ * it is 0. Note that, unlike index_name_pos, this function does not expand
+ * the index if it is sparse. If an item exists within the full index but it
+ * is contained within a sparse directory (and not in the sparse index), 0 is
+ * returned.
+ */
+int index_entry_exists(struct index_state *, const char *name, int namelen);
+
+/*
+ * Some functions return the negative complement of an insert position when a
+ * precise match was not found but a position was found where the entry would
+ * need to be inserted. This helper protects that logic from any integer
+ * underflow.
+ */
+static inline int index_pos_to_insert_pos(uintmax_t pos)
+{
+	if (pos > INT_MAX)
+		die("overflow: -1 - %"PRIuMAX, pos);
+	return -1 - (int)pos;
+}
+
+#define ADD_CACHE_OK_TO_ADD 1		/* Ok to add */
+#define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
+#define ADD_CACHE_SKIP_DFCHECK 4	/* Ok to skip DF conflict checks */
+#define ADD_CACHE_JUST_APPEND 8		/* Append only */
+#define ADD_CACHE_NEW_ONLY 16		/* Do not replace existing ones */
+#define ADD_CACHE_KEEP_CACHE_TREE 32	/* Do not invalidate cache-tree */
+#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
+int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
+void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
+
+/* Remove entry, return true if there are more entries to go. */
+int remove_index_entry_at(struct index_state *, int pos);
+
+void remove_marked_cache_entries(struct index_state *istate, int invalidate);
+int remove_file_from_index(struct index_state *, const char *path);
+#define ADD_CACHE_VERBOSE 1
+#define ADD_CACHE_PRETEND 2
+#define ADD_CACHE_IGNORE_ERRORS	4
+#define ADD_CACHE_IGNORE_REMOVAL 8
+#define ADD_CACHE_INTENT 16
+/*
+ * These two are used to add the contents of the file at path
+ * to the index, marking the working tree up-to-date by storing
+ * the cached stat info in the resulting cache entry.  A caller
+ * that has already run lstat(2) on the path can call
+ * add_to_index(), and all others can call add_file_to_index();
+ * the latter will do necessary lstat(2) internally before
+ * calling the former.
+ */
+int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+int add_file_to_index(struct index_state *, const char *path, int flags);
+
+int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
+void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
+int index_name_is_other(struct index_state *, const char *, int);
+void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
+
+/* do stat comparison even if CE_VALID is true */
+#define CE_MATCH_IGNORE_VALID		01
+/* do not check the contents but report dirty on racily-clean entries */
+#define CE_MATCH_RACY_IS_DIRTY		02
+/* do stat comparison even if CE_SKIP_WORKTREE is true */
+#define CE_MATCH_IGNORE_SKIP_WORKTREE	04
+/* ignore non-existent files during stat update  */
+#define CE_MATCH_IGNORE_MISSING		0x08
+/* enable stat refresh */
+#define CE_MATCH_REFRESH		0x10
+/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
+#define CE_MATCH_IGNORE_FSMONITOR 0X20
+int is_racy_timestamp(const struct index_state *istate,
+		      const struct cache_entry *ce);
+int has_racy_timestamp(struct index_state *istate);
+int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+
+int match_stat_data_racy(const struct index_state *istate,
+			 const struct stat_data *sd, struct stat *st);
+
+void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
+
+/*
+ * Fill members of st by members of sd enough to convince match_stat()
+ * to consider that they match.  It should be usable as a replacement
+ * for lstat() for a tracked path that is known to be up-to-date via
+ * some out-of-line means (like fsmonitor).
+ */
+int fake_lstat(const struct cache_entry *ce, struct stat *st);
+
+#define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
+#define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
+#define REFRESH_QUIET                    (1 << 2) /* be quiet about it */
+#define REFRESH_IGNORE_MISSING           (1 << 3) /* ignore non-existent */
+#define REFRESH_IGNORE_SUBMODULES        (1 << 4) /* ignore submodules */
+#define REFRESH_IN_PORCELAIN             (1 << 5) /* user friendly output, not "needs update" */
+#define REFRESH_PROGRESS                 (1 << 6) /* show progress bar if stderr is tty */
+#define REFRESH_IGNORE_SKIP_WORKTREE     (1 << 7) /* ignore skip_worktree entries */
+int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
+/*
+ * Refresh the index and write it to disk.
+ *
+ * 'refresh_flags' is passed directly to 'refresh_index()', while
+ * 'COMMIT_LOCK | write_flags' is passed to 'write_locked_index()', so
+ * the lockfile is always either committed or rolled back.
+ *
+ * If 'gentle' is passed, errors locking the index are ignored.
+ *
+ * Return 1 if refreshing the index returns an error, -1 if writing
+ * the index to disk fails, 0 on success.
+ *
+ * Note that if refreshing the index returns an error, we still write
+ * out the index (unless locking fails).
+ */
+int repo_refresh_and_write_index(struct repository*, unsigned int refresh_flags, unsigned int write_flags, int gentle, const struct pathspec *, char *seen, const char *header_msg);
+
+struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
+
+void set_alternate_index_output(const char *);
+
+extern int verify_index_checksum;
+extern int verify_ce_order;
+
+int cmp_cache_name_compare(const void *a_, const void *b_);
+
+int add_files_to_cache(struct repository *repo, const char *prefix,
+		       const struct pathspec *pathspec, char *ps_matched,
+		       int include_sparse, int flags);
+
+void overlay_tree_on_index(struct index_state *istate,
+			   const char *tree_name, const char *prefix);
+
+#endif /* READ_CACHE_LL_H */
diff --git a/read-cache.c b/read-cache.c
index 35e5657..e1723ad 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3,24 +3,38 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "bulk-checkin.h"
 #include "config.h"
+#include "date.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "hex.h"
 #include "tempfile.h"
 #include "lockfile.h"
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
 #include "tree.h"
 #include "commit.h"
-#include "blob.h"
+#include "environment.h"
+#include "gettext.h"
+#include "mem-pool.h"
+#include "name-hash.h"
+#include "object-name.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
 #include "resolve-undo.h"
-#include "run-command.h"
+#include "revision.h"
 #include "strbuf.h"
+#include "trace2.h"
 #include "varint.h"
 #include "split-index.h"
+#include "symlinks.h"
 #include "utf8.h"
 #include "fsmonitor.h"
 #include "thread-utils.h"
@@ -163,61 +177,6 @@
 		add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
-void fill_stat_data(struct stat_data *sd, struct stat *st)
-{
-	sd->sd_ctime.sec = (unsigned int)st->st_ctime;
-	sd->sd_mtime.sec = (unsigned int)st->st_mtime;
-	sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
-	sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
-	sd->sd_dev = st->st_dev;
-	sd->sd_ino = st->st_ino;
-	sd->sd_uid = st->st_uid;
-	sd->sd_gid = st->st_gid;
-	sd->sd_size = st->st_size;
-}
-
-int match_stat_data(const struct stat_data *sd, struct stat *st)
-{
-	int changed = 0;
-
-	if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
-		changed |= MTIME_CHANGED;
-	if (trust_ctime && check_stat &&
-	    sd->sd_ctime.sec != (unsigned int)st->st_ctime)
-		changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
-	if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
-		changed |= MTIME_CHANGED;
-	if (trust_ctime && check_stat &&
-	    sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
-		changed |= CTIME_CHANGED;
-#endif
-
-	if (check_stat) {
-		if (sd->sd_uid != (unsigned int) st->st_uid ||
-			sd->sd_gid != (unsigned int) st->st_gid)
-			changed |= OWNER_CHANGED;
-		if (sd->sd_ino != (unsigned int) st->st_ino)
-			changed |= INODE_CHANGED;
-	}
-
-#ifdef USE_STDEV
-	/*
-	 * st_dev breaks on network filesystems where different
-	 * clients will have different views of what "device"
-	 * the filesystem is on
-	 */
-	if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
-			changed |= INODE_CHANGED;
-#endif
-
-	if (sd->sd_size != (unsigned int) st->st_size)
-		changed |= DATA_CHANGED;
-
-	return changed;
-}
-
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -236,6 +195,33 @@
 	}
 }
 
+static unsigned int st_mode_from_ce(const struct cache_entry *ce)
+{
+	extern int trust_executable_bit, has_symlinks;
+
+	switch (ce->ce_mode & S_IFMT) {
+	case S_IFLNK:
+		return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
+	case S_IFREG:
+		return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
+	case S_IFGITLINK:
+		return S_IFDIR | 0755;
+	case S_IFDIR:
+		return ce->ce_mode;
+	default:
+		BUG("unsupported ce_mode: %o", ce->ce_mode);
+	}
+}
+
+int fake_lstat(const struct cache_entry *ce, struct stat *st)
+{
+	fake_lstat_data(&ce->ce_stat_data, st);
+	st->st_mode = st_mode_from_ce(ce);
+
+	/* always succeed as lstat() replacement */
+	return 0;
+}
+
 static int ce_compare_data(struct index_state *istate,
 			   const struct cache_entry *ce,
 			   struct stat *st)
@@ -263,7 +249,7 @@
 	if (strbuf_readlink(&sb, ce->name, expected_size))
 		return -1;
 
-	buffer = read_object_file(&ce->oid, &type, &size);
+	buffer = repo_read_object_file(the_repository, &ce->oid, &type, &size);
 	if (buffer) {
 		if (size == sb.len)
 			match = memcmp(buffer, sb.buf, size);
@@ -488,75 +474,8 @@
 	return 0;
 }
 
-int base_name_compare(const char *name1, size_t len1, int mode1,
-		      const char *name2, size_t len2, int mode2)
-{
-	unsigned char c1, c2;
-	size_t len = len1 < len2 ? len1 : len2;
-	int cmp;
-
-	cmp = memcmp(name1, name2, len);
-	if (cmp)
-		return cmp;
-	c1 = name1[len];
-	c2 = name2[len];
-	if (!c1 && S_ISDIR(mode1))
-		c1 = '/';
-	if (!c2 && S_ISDIR(mode2))
-		c2 = '/';
-	return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-}
-
-/*
- * df_name_compare() is identical to base_name_compare(), except it
- * compares conflicting directory/file entries as equal. Note that
- * while a directory name compares as equal to a regular file, they
- * then individually compare _differently_ to a filename that has
- * a dot after the basename (because '\0' < '.' < '/').
- *
- * This is used by routines that want to traverse the git namespace
- * but then handle conflicting entries together when possible.
- */
-int df_name_compare(const char *name1, size_t len1, int mode1,
-		    const char *name2, size_t len2, int mode2)
-{
-	unsigned char c1, c2;
-	size_t len = len1 < len2 ? len1 : len2;
-	int cmp;
-
-	cmp = memcmp(name1, name2, len);
-	if (cmp)
-		return cmp;
-	/* Directories and files compare equal (same length, same name) */
-	if (len1 == len2)
-		return 0;
-	c1 = name1[len];
-	if (!c1 && S_ISDIR(mode1))
-		c1 = '/';
-	c2 = name2[len];
-	if (!c2 && S_ISDIR(mode2))
-		c2 = '/';
-	if (c1 == '/' && !c2)
-		return 0;
-	if (c2 == '/' && !c1)
-		return 0;
-	return c1 - c2;
-}
-
-int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
-{
-	size_t min_len = (len1 < len2) ? len1 : len2;
-	int cmp = memcmp(name1, name2, min_len);
-	if (cmp)
-		return cmp;
-	if (len1 < len2)
-		return -1;
-	if (len1 > len2)
-		return 1;
-	return 0;
-}
-
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
+static int cache_name_stage_compare(const char *name1, int len1, int stage1,
+				    const char *name2, int len2, int stage2)
 {
 	int cmp;
 
@@ -571,6 +490,16 @@
 	return 0;
 }
 
+int cmp_cache_name_compare(const void *a_, const void *b_)
+{
+	const struct cache_entry *ce1, *ce2;
+
+	ce1 = *((const struct cache_entry **)a_);
+	ce2 = *((const struct cache_entry **)b_);
+	return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
+				  ce2->name, ce2->ce_namelen, ce_stage(ce2));
+}
+
 static int index_name_stage_pos(struct index_state *istate,
 				const char *name, int namelen,
 				int stage,
@@ -2330,6 +2259,7 @@
 	if (fd < 0) {
 		if (!must_exist && errno == ENOENT) {
 			set_new_index_sparsity(istate);
+			istate->initialized = 1;
 			return 0;
 		}
 		die_errno(_("%s: index file open failed"), path);
@@ -2499,12 +2429,14 @@
 
 	base_oid_hex = oid_to_hex(&split_index->base_oid);
 	base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
-	trace2_region_enter_printf("index", "shared/do_read_index",
-				   the_repository, "%s", base_path);
-	ret = do_read_index(split_index->base, base_path, 0);
-	trace2_region_leave_printf("index", "shared/do_read_index",
-				   the_repository, "%s", base_path);
-	if (!ret) {
+	if (file_exists(base_path)) {
+		trace2_region_enter_printf("index", "shared/do_read_index",
+					the_repository, "%s", base_path);
+
+		ret = do_read_index(split_index->base, base_path, 0);
+		trace2_region_leave_printf("index", "shared/do_read_index",
+					the_repository, "%s", base_path);
+	} else {
 		char *path_copy = xstrdup(path);
 		char *base_path2 = xstrfmt("%s/sharedindex.%s",
 					   dirname(path_copy), base_oid_hex);
@@ -2628,7 +2560,7 @@
 
 	if (tree)
 		cmp = tree->object.oid;
-	if (tree || !get_oid_tree("HEAD", &cmp)) {
+	if (tree || !repo_get_oid_tree(repo, "HEAD", &cmp)) {
 		struct diff_options opt;
 
 		repo_diff_setup(repo, &opt);
@@ -2901,6 +2833,16 @@
 	return !git_config_get_index_threads(&val) && val != 1;
 }
 
+enum write_extensions {
+	WRITE_NO_EXTENSION =              0,
+	WRITE_SPLIT_INDEX_EXTENSION =     1<<0,
+	WRITE_CACHE_TREE_EXTENSION =      1<<1,
+	WRITE_RESOLVE_UNDO_EXTENSION =    1<<2,
+	WRITE_UNTRACKED_CACHE_EXTENSION = 1<<3,
+	WRITE_FSMONITOR_EXTENSION =       1<<4,
+};
+#define WRITE_ALL_EXTENSIONS ((enum write_extensions)-1)
+
 /*
  * On success, `tempfile` is closed. If it is the temporary file
  * of a `struct lock_file`, we will therefore effectively perform
@@ -2909,7 +2851,7 @@
  * rely on it.
  */
 static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
-			  int strip_extensions, unsigned flags)
+			  enum write_extensions write_extensions, unsigned flags)
 {
 	uint64_t start = getnanotime();
 	struct hashfile *f;
@@ -2947,7 +2889,7 @@
 	}
 
 	if (!istate->version)
-		istate->version = get_index_format_default(the_repository);
+		istate->version = get_index_format_default(r);
 
 	/* demote version 3 to version 2 when the latter suffices */
 	if (istate->version == 3 || istate->version == 2)
@@ -3082,8 +3024,8 @@
 			return -1;
 	}
 
-	if (!strip_extensions && istate->split_index &&
-	    !is_null_oid(&istate->split_index->base_oid)) {
+	if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
+	    istate->split_index) {
 		struct strbuf sb = STRBUF_INIT;
 
 		if (istate->sparse_index)
@@ -3097,7 +3039,8 @@
 		if (err)
 			return -1;
 	}
-	if (!strip_extensions && !drop_cache_tree && istate->cache_tree) {
+	if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
+	    !drop_cache_tree && istate->cache_tree) {
 		struct strbuf sb = STRBUF_INIT;
 
 		cache_tree_write(&sb, istate->cache_tree);
@@ -3107,7 +3050,8 @@
 		if (err)
 			return -1;
 	}
-	if (!strip_extensions && istate->resolve_undo) {
+	if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
+	    istate->resolve_undo) {
 		struct strbuf sb = STRBUF_INIT;
 
 		resolve_undo_write(&sb, istate->resolve_undo);
@@ -3118,7 +3062,8 @@
 		if (err)
 			return -1;
 	}
-	if (!strip_extensions && istate->untracked) {
+	if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
+	    istate->untracked) {
 		struct strbuf sb = STRBUF_INIT;
 
 		write_untracked_extension(&sb, istate->untracked);
@@ -3129,7 +3074,8 @@
 		if (err)
 			return -1;
 	}
-	if (!strip_extensions && istate->fsmonitor_last_update) {
+	if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
+	    istate->fsmonitor_last_update) {
 		struct strbuf sb = STRBUF_INIT;
 
 		write_fsmonitor_extension(&sb, istate);
@@ -3203,8 +3149,10 @@
 		return commit_lock_file(lk);
 }
 
-static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
-				 unsigned flags)
+static int do_write_locked_index(struct index_state *istate,
+				 struct lock_file *lock,
+				 unsigned flags,
+				 enum write_extensions write_extensions)
 {
 	int ret;
 	int was_full = istate->sparse_index == INDEX_EXPANDED;
@@ -3222,7 +3170,7 @@
 	 */
 	trace2_region_enter_printf("index", "do_write_index", the_repository,
 				   "%s", get_lock_file_path(lock));
-	ret = do_write_index(istate, lock->tempfile, 0, flags);
+	ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
 	trace2_region_leave_printf("index", "do_write_index", the_repository,
 				   "%s", get_lock_file_path(lock));
 
@@ -3251,7 +3199,7 @@
 {
 	int ret;
 	prepare_to_write_split_index(istate);
-	ret = do_write_locked_index(istate, lock, flags);
+	ret = do_write_locked_index(istate, lock, flags, WRITE_ALL_EXTENSIONS);
 	finish_writing_split_index(istate);
 	return ret;
 }
@@ -3326,7 +3274,7 @@
 
 	trace2_region_enter_printf("index", "shared/do_write_index",
 				   the_repository, "%s", get_tempfile_path(*temp));
-	ret = do_write_index(si->base, *temp, 1, flags);
+	ret = do_write_index(si->base, *temp, WRITE_NO_EXTENSION, flags);
 	trace2_region_leave_printf("index", "shared/do_write_index",
 				   the_repository, "%s", get_tempfile_path(*temp));
 
@@ -3403,9 +3351,8 @@
 	if ((!si && !test_split_index_env) ||
 	    alternate_index_output ||
 	    (istate->cache_changed & ~EXTMASK)) {
-		if (si)
-			oidclr(&si->base_oid);
-		ret = do_write_locked_index(istate, lock, flags);
+		ret = do_write_locked_index(istate, lock, flags,
+					    ~WRITE_SPLIT_INDEX_EXTENSION);
 		goto out;
 	}
 
@@ -3431,8 +3378,8 @@
 		/* Same initial permissions as the main .git/index file */
 		temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
 		if (!temp) {
-			oidclr(&si->base_oid);
-			ret = do_write_locked_index(istate, lock, flags);
+			ret = do_write_locked_index(istate, lock, flags,
+						    ~WRITE_SPLIT_INDEX_EXTENSION);
 			goto out;
 		}
 		ret = write_shared_index(istate, &temp, flags);
@@ -3553,7 +3500,8 @@
 	}
 	if (pos < 0)
 		return NULL;
-	data = read_object_file(&istate->cache[pos]->oid, &type, &sz);
+	data = repo_read_object_file(the_repository, &istate->cache[pos]->oid,
+				     &type, &sz);
 	if (!data || type != OBJ_BLOB) {
 		free(data);
 		return NULL;
@@ -3563,35 +3511,6 @@
 	return data;
 }
 
-void stat_validity_clear(struct stat_validity *sv)
-{
-	FREE_AND_NULL(sv->sd);
-}
-
-int stat_validity_check(struct stat_validity *sv, const char *path)
-{
-	struct stat st;
-
-	if (stat(path, &st) < 0)
-		return sv->sd == NULL;
-	if (!sv->sd)
-		return 0;
-	return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
-}
-
-void stat_validity_update(struct stat_validity *sv, int fd)
-{
-	struct stat st;
-
-	if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
-		stat_validity_clear(sv);
-	else {
-		if (!sv->sd)
-			CALLOC_ARRAY(sv->sd, 1);
-		fill_stat_data(sv->sd, &st);
-	}
-}
-
 void move_index_extensions(struct index_state *dst, struct index_state *src)
 {
 	dst->untracked = src->untracked;
@@ -3835,3 +3754,242 @@
 				   to_fetch.oid, to_fetch.nr);
 	oid_array_clear(&to_fetch);
 }
+
+static int read_one_entry_opt(struct index_state *istate,
+			      const struct object_id *oid,
+			      struct strbuf *base,
+			      const char *pathname,
+			      unsigned mode, int opt)
+{
+	int len;
+	struct cache_entry *ce;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	len = strlen(pathname);
+	ce = make_empty_cache_entry(istate, base->len + len);
+
+	ce->ce_mode = create_ce_mode(mode);
+	ce->ce_flags = create_ce_flags(1);
+	ce->ce_namelen = base->len + len;
+	memcpy(ce->name, base->buf, base->len);
+	memcpy(ce->name + base->len, pathname, len+1);
+	oidcpy(&ce->oid, oid);
+	return add_index_entry(istate, ce, opt);
+}
+
+static int read_one_entry(const struct object_id *oid, struct strbuf *base,
+			  const char *pathname, unsigned mode,
+			  void *context)
+{
+	struct index_state *istate = context;
+	return read_one_entry_opt(istate, oid, base, pathname,
+				  mode,
+				  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+}
+
+/*
+ * This is used when the caller knows there is no existing entries at
+ * the stage that will conflict with the entry being added.
+ */
+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
+				const char *pathname, unsigned mode,
+				void *context)
+{
+	struct index_state *istate = context;
+	return read_one_entry_opt(istate, oid, base, pathname,
+				  mode, ADD_CACHE_JUST_APPEND);
+}
+
+/*
+ * Read the tree specified with --with-tree option
+ * (typically, HEAD) into stage #1 and then
+ * squash them down to stage #0.  This is used for
+ * --error-unmatch to list and check the path patterns
+ * that were given from the command line.  We are not
+ * going to write this index out.
+ */
+void overlay_tree_on_index(struct index_state *istate,
+			   const char *tree_name, const char *prefix)
+{
+	struct tree *tree;
+	struct object_id oid;
+	struct pathspec pathspec;
+	struct cache_entry *last_stage0 = NULL;
+	int i;
+	read_tree_fn_t fn = NULL;
+	int err;
+
+	if (repo_get_oid(the_repository, tree_name, &oid))
+		die("tree-ish %s not found.", tree_name);
+	tree = parse_tree_indirect(&oid);
+	if (!tree)
+		die("bad tree-ish %s", tree_name);
+
+	/* Hoist the unmerged entries up to stage #3 to make room */
+	/* TODO: audit for interaction with sparse-index. */
+	ensure_full_index(istate);
+	for (i = 0; i < istate->cache_nr; i++) {
+		struct cache_entry *ce = istate->cache[i];
+		if (!ce_stage(ce))
+			continue;
+		ce->ce_flags |= CE_STAGEMASK;
+	}
+
+	if (prefix) {
+		static const char *(matchbuf[1]);
+		matchbuf[0] = NULL;
+		parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
+			       PATHSPEC_PREFER_CWD, prefix, matchbuf);
+	} else
+		memset(&pathspec, 0, sizeof(pathspec));
+
+	/*
+	 * See if we have cache entry at the stage.  If so,
+	 * do it the original slow way, otherwise, append and then
+	 * sort at the end.
+	 */
+	for (i = 0; !fn && i < istate->cache_nr; i++) {
+		const struct cache_entry *ce = istate->cache[i];
+		if (ce_stage(ce) == 1)
+			fn = read_one_entry;
+	}
+
+	if (!fn)
+		fn = read_one_entry_quick;
+	err = read_tree(the_repository, tree, &pathspec, fn, istate);
+	clear_pathspec(&pathspec);
+	if (err)
+		die("unable to read tree entries %s", tree_name);
+
+	/*
+	 * Sort the cache entry -- we need to nuke the cache tree, though.
+	 */
+	if (fn == read_one_entry_quick) {
+		cache_tree_free(&istate->cache_tree);
+		QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
+	}
+
+	for (i = 0; i < istate->cache_nr; i++) {
+		struct cache_entry *ce = istate->cache[i];
+		switch (ce_stage(ce)) {
+		case 0:
+			last_stage0 = ce;
+			/* fallthru */
+		default:
+			continue;
+		case 1:
+			/*
+			 * If there is stage #0 entry for this, we do not
+			 * need to show it.  We use CE_UPDATE bit to mark
+			 * such an entry.
+			 */
+			if (last_stage0 &&
+			    !strcmp(last_stage0->name, ce->name))
+				ce->ce_flags |= CE_UPDATE;
+		}
+	}
+}
+
+struct update_callback_data {
+	struct index_state *index;
+	int include_sparse;
+	int flags;
+	int add_errors;
+};
+
+static int fix_unmerged_status(struct diff_filepair *p,
+			       struct update_callback_data *data)
+{
+	if (p->status != DIFF_STATUS_UNMERGED)
+		return p->status;
+	if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
+		/*
+		 * This is not an explicit add request, and the
+		 * path is missing from the working tree (deleted)
+		 */
+		return DIFF_STATUS_DELETED;
+	else
+		/*
+		 * Either an explicit add request, or path exists
+		 * in the working tree.  An attempt to explicitly
+		 * add a path that does not exist in the working tree
+		 * will be caught as an error by the caller immediately.
+		 */
+		return DIFF_STATUS_MODIFIED;
+}
+
+static void update_callback(struct diff_queue_struct *q,
+			    struct diff_options *opt UNUSED, void *cbdata)
+{
+	int i;
+	struct update_callback_data *data = cbdata;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		const char *path = p->one->path;
+
+		if (!data->include_sparse &&
+		    !path_in_sparse_checkout(path, data->index))
+			continue;
+
+		switch (fix_unmerged_status(p, data)) {
+		default:
+			die(_("unexpected diff status %c"), p->status);
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_TYPE_CHANGED:
+			if (add_file_to_index(data->index, path, data->flags)) {
+				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+					die(_("updating files failed"));
+				data->add_errors++;
+			}
+			break;
+		case DIFF_STATUS_DELETED:
+			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+				break;
+			if (!(data->flags & ADD_CACHE_PRETEND))
+				remove_file_from_index(data->index, path);
+			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+				printf(_("remove '%s'\n"), path);
+			break;
+		}
+	}
+}
+
+int add_files_to_cache(struct repository *repo, const char *prefix,
+		       const struct pathspec *pathspec, char *ps_matched,
+		       int include_sparse, int flags)
+{
+	struct update_callback_data data;
+	struct rev_info rev;
+
+	memset(&data, 0, sizeof(data));
+	data.index = repo->index;
+	data.include_sparse = include_sparse;
+	data.flags = flags;
+
+	repo_init_revisions(repo, &rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+	if (pathspec) {
+		copy_pathspec(&rev.prune_data, pathspec);
+		rev.ps_matched = ps_matched;
+	}
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = update_callback;
+	rev.diffopt.format_callback_data = &data;
+	rev.diffopt.flags.override_submodule_config = 1;
+	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
+
+	/*
+	 * Use an ODB transaction to optimize adding multiple objects.
+	 * This function is invoked from commands other than 'add', which
+	 * may not have their own transaction active.
+	 */
+	begin_odb_transaction();
+	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+	end_odb_transaction();
+
+	release_revisions(&rev);
+	return !!data.add_errors;
+}
diff --git a/read-cache.h b/read-cache.h
new file mode 100644
index 0000000..043da1f
--- /dev/null
+++ b/read-cache.h
@@ -0,0 +1,45 @@
+#ifndef READ_CACHE_H
+#define READ_CACHE_H
+
+#include "read-cache-ll.h"
+#include "object.h"
+#include "pathspec.h"
+
+static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+					     unsigned int mode)
+{
+	extern int trust_executable_bit, has_symlinks;
+	if (!has_symlinks && S_ISREG(mode) &&
+	    ce && S_ISLNK(ce->ce_mode))
+		return ce->ce_mode;
+	if (!trust_executable_bit && S_ISREG(mode)) {
+		if (ce && S_ISREG(ce->ce_mode))
+			return ce->ce_mode;
+		return create_ce_mode(0666);
+	}
+	return create_ce_mode(mode);
+}
+
+static inline int ce_to_dtype(const struct cache_entry *ce)
+{
+	unsigned ce_mode = ntohl(ce->ce_mode);
+	if (S_ISREG(ce_mode))
+		return DT_REG;
+	else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
+		return DT_DIR;
+	else if (S_ISLNK(ce_mode))
+		return DT_LNK;
+	else
+		return DT_UNKNOWN;
+}
+
+static inline int ce_path_match(struct index_state *istate,
+				const struct cache_entry *ce,
+				const struct pathspec *pathspec,
+				char *seen)
+{
+	return match_pathspec(istate, pathspec, ce->name, ce_namelen(ce), 0, seen,
+			      S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
+}
+
+#endif /* READ_CACHE_H */
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 7407c59..c343e16 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,11 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
 #include "sequencer.h"
 #include "rebase-interactive.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "commit-slab.h"
 #include "config.h"
 #include "dir.h"
+#include "object-name.h"
 
 static const char edit_todo_list_advice[] =
 N_("You can fix this with 'git rebase --edit-todo' "
@@ -66,13 +71,14 @@
 
 	if (!edit_todo) {
 		strbuf_addch(buf, '\n');
-		strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
-					      "Rebase %s onto %s (%d commands)",
-					      command_count),
+		strbuf_commented_addf(buf, comment_line_str,
+				      Q_("Rebase %s onto %s (%d command)",
+					 "Rebase %s onto %s (%d commands)",
+					 command_count),
 				      shortrevisions, shortonto, command_count);
 	}
 
-	strbuf_add_commented_lines(buf, msg, strlen(msg));
+	strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
 
 	if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
 		msg = _("\nDo not remove any line. Use 'drop' "
@@ -81,7 +87,7 @@
 		msg = _("\nIf you remove a line here "
 			 "THAT COMMIT WILL BE LOST.\n");
 
-	strbuf_add_commented_lines(buf, msg, strlen(msg));
+	strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
 
 	if (edit_todo)
 		msg = _("\nYou are editing the todo file "
@@ -92,7 +98,7 @@
 		msg = _("\nHowever, if you remove everything, "
 			"the rebase will be aborted.\n\n");
 
-	strbuf_add_commented_lines(buf, msg, strlen(msg));
+	strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
 }
 
 int edit_todo_list(struct repository *r, struct todo_list *todo_list,
@@ -124,7 +130,7 @@
 	if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
 		return -2;
 
-	strbuf_stripspace(&new_todo->buf, 1);
+	strbuf_stripspace(&new_todo->buf, comment_line_str);
 	if (initial && new_todo->buf.len == 0)
 		return -3;
 
@@ -187,7 +193,7 @@
 		struct commit *commit = item->commit;
 		if (commit && !*commit_seen_at(&commit_seen, commit)) {
 			strbuf_addf(&missing, " - %s %.*s\n",
-				    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),
+				    repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV),
 				    item->arg_len,
 				    todo_item_get_arg(old_todo, item));
 			*commit_seen_at(&commit_seen, commit) = 1;
diff --git a/rebase.c b/rebase.c
index 6775cdd..69a1822 100644
--- a/rebase.c
+++ b/rebase.c
@@ -1,5 +1,6 @@
+#include "git-compat-util.h"
 #include "rebase.h"
-#include "config.h"
+#include "parse.h"
 #include "gettext.h"
 
 /*
diff --git a/ref-filter.c b/ref-filter.c
index f8203c6..03542d0 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1,11 +1,20 @@
-#include "builtin.h"
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "config.h"
+#include "gpg-interface.h"
+#include "hex.h"
 #include "parse-options.h"
+#include "run-command.h"
 #include "refs.h"
 #include "wildmatch.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
 #include "repository.h"
 #include "commit.h"
+#include "mailmap.h"
+#include "ident.h"
 #include "remote.h"
 #include "color.h"
 #include "tag.h"
@@ -13,16 +22,13 @@
 #include "ref-filter.h"
 #include "revision.h"
 #include "utf8.h"
-#include "git-compat-util.h"
-#include "version.h"
+#include "versioncmp.h"
 #include "trailer.h"
 #include "wt-status.h"
 #include "commit-slab.h"
-#include "commit-graph.h"
 #include "commit-reach.h"
 #include "worktree.h"
 #include "hashmap.h"
-#include "strvec.h"
 
 static struct ref_msg {
 	const char *gone;
@@ -140,10 +146,12 @@
 	ATOM_TAGGERDATE,
 	ATOM_CREATOR,
 	ATOM_CREATORDATE,
+	ATOM_DESCRIBE,
 	ATOM_SUBJECT,
 	ATOM_BODY,
 	ATOM_TRAILERS,
 	ATOM_CONTENTS,
+	ATOM_SIGNATURE,
 	ATOM_RAW,
 	ATOM_UPSTREAM,
 	ATOM_PUSH,
@@ -158,6 +166,7 @@
 	ATOM_THEN,
 	ATOM_ELSE,
 	ATOM_REST,
+	ATOM_AHEADBEHIND,
 };
 
 /*
@@ -205,9 +214,22 @@
 		struct {
 			enum { O_SIZE, O_SIZE_DISK } option;
 		} objectsize;
-		struct email_option {
-			enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;
+		struct {
+			enum { N_RAW, N_MAILMAP } option;
+		} name_option;
+		struct {
+			enum {
+				EO_RAW = 0,
+				EO_TRIM = 1<<0,
+				EO_LOCALPART = 1<<1,
+				EO_MAILMAP = 1<<2,
+			} option;
 		} email_option;
+		struct {
+			enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
+			       S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
+		} signature;
+		const char **describe_args;
 		struct refname_atom refname;
 		char *head;
 	} u;
@@ -244,6 +266,110 @@
 	return -1;
 }
 
+/*
+ * Parse option of name "candidate" in the option string "to_parse" of
+ * the form
+ *
+ *	"candidate1[=val1],candidate2[=val2],candidate3[=val3],..."
+ *
+ * The remaining part of "to_parse" is stored in "end" (if we are
+ * parsing the last candidate, then this is NULL) and the value of
+ * the candidate is stored in "valuestart" and its length in "valuelen",
+ * that is the portion after "=". Since it is possible for a "candidate"
+ * to not have a value, in such cases, "valuestart" is set to point to
+ * NULL and "valuelen" to 0.
+ *
+ * The function returns 1 on success. It returns 0 if we don't find
+ * "candidate" in "to_parse" or we find "candidate" but it is followed
+ * by more chars (for example, "candidatefoo"), that is, we don't find
+ * an exact match.
+ *
+ * This function only does the above for one "candidate" at a time. So
+ * it has to be called each time trying to parse a "candidate" in the
+ * option string "to_parse".
+ */
+static int match_atom_arg_value(const char *to_parse, const char *candidate,
+				const char **end, const char **valuestart,
+				size_t *valuelen)
+{
+	const char *atom;
+
+	if (!skip_prefix(to_parse, candidate, &atom))
+		return 0; /* definitely not "candidate" */
+
+	if (*atom == '=') {
+		/* we just saw "candidate=" */
+		*valuestart = atom + 1;
+		atom = strchrnul(*valuestart, ',');
+		*valuelen = atom - *valuestart;
+	} else if (*atom != ',' && *atom != '\0') {
+		/* key begins with "candidate" but has more chars */
+		return 0;
+	} else {
+		/* just "candidate" without "=val" */
+		*valuestart = NULL;
+		*valuelen = 0;
+	}
+
+	/* atom points at either the ',' or NUL after this key[=val] */
+	if (*atom == ',')
+		atom++;
+	else if (*atom)
+		BUG("Why is *atom not NULL yet?");
+
+	*end = atom;
+	return 1;
+}
+
+/*
+ * Parse boolean option of name "candidate" in the option list "to_parse"
+ * of the form
+ *
+ *	"candidate1[=bool1],candidate2[=bool2],candidate3[=bool3],..."
+ *
+ * The remaining part of "to_parse" is stored in "end" (if we are parsing
+ * the last candidate, then this is NULL) and the value (if given) is
+ * parsed and stored in "val", so "val" always points to either 0 or 1.
+ * If the value is not given, then "val" is set to point to 1.
+ *
+ * The boolean value is parsed using "git_parse_maybe_bool()", so the
+ * accepted values are
+ *
+ *	to set true  - "1", "yes", "true"
+ *	to set false - "0", "no", "false"
+ *
+ * This function returns 1 on success. It returns 0 when we don't find
+ * an exact match for "candidate" or when the boolean value given is
+ * not valid.
+ */
+static int match_atom_bool_arg(const char *to_parse, const char *candidate,
+				const char **end, int *val)
+{
+	const char *argval;
+	char *strval;
+	size_t arglen;
+	int v;
+
+	if (!match_atom_arg_value(to_parse, candidate, end, &argval, &arglen))
+		return 0;
+
+	if (!argval) {
+		*val = 1;
+		return 1;
+	}
+
+	strval = xstrndup(argval, arglen);
+	v = git_parse_maybe_bool(strval);
+	free(strval);
+
+	if (v == -1)
+		return 0;
+
+	*val = v;
+
+	return 1;
+}
+
 static int color_atom_parser(struct ref_format *format, struct used_atom *atom,
 			     const char *color_value, struct strbuf *err)
 {
@@ -282,7 +408,8 @@
 	return 0;
 }
 
-static int remote_ref_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int remote_ref_atom_parser(struct ref_format *format UNUSED,
+				  struct used_atom *atom,
 				  const char *arg, struct strbuf *err)
 {
 	struct string_list params = STRING_LIST_INIT_DUP;
@@ -329,7 +456,8 @@
 	return 0;
 }
 
-static int objecttype_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int objecttype_atom_parser(struct ref_format *format UNUSED,
+				  struct used_atom *atom,
 				  const char *arg, struct strbuf *err)
 {
 	if (arg)
@@ -341,7 +469,8 @@
 	return 0;
 }
 
-static int objectsize_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int objectsize_atom_parser(struct ref_format *format UNUSED,
+				  struct used_atom *atom,
 				  const char *arg, struct strbuf *err)
 {
 	if (!arg) {
@@ -361,7 +490,8 @@
 	return 0;
 }
 
-static int deltabase_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int deltabase_atom_parser(struct ref_format *format UNUSED,
+				 struct used_atom *atom,
 				 const char *arg, struct strbuf *err)
 {
 	if (arg)
@@ -373,7 +503,8 @@
 	return 0;
 }
 
-static int body_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int body_atom_parser(struct ref_format *format UNUSED,
+			    struct used_atom *atom,
 			    const char *arg, struct strbuf *err)
 {
 	if (arg)
@@ -382,7 +513,8 @@
 	return 0;
 }
 
-static int subject_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int subject_atom_parser(struct ref_format *format UNUSED,
+			       struct used_atom *atom,
 			       const char *arg, struct strbuf *err)
 {
 	if (!arg)
@@ -394,7 +526,38 @@
 	return 0;
 }
 
-static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int parse_signature_option(const char *arg)
+{
+	if (!arg)
+		return S_BARE;
+	else if (!strcmp(arg, "signer"))
+		return S_SIGNER;
+	else if (!strcmp(arg, "grade"))
+		return S_GRADE;
+	else if (!strcmp(arg, "key"))
+		return S_KEY;
+	else if (!strcmp(arg, "fingerprint"))
+		return S_FINGERPRINT;
+	else if (!strcmp(arg, "primarykeyfingerprint"))
+		return S_PRI_KEY_FP;
+	else if (!strcmp(arg, "trustlevel"))
+		return S_TRUST_LEVEL;
+	return -1;
+}
+
+static int signature_atom_parser(struct ref_format *format UNUSED,
+				 struct used_atom *atom,
+				 const char *arg, struct strbuf *err)
+{
+	int opt = parse_signature_option(arg);
+	if (opt < 0)
+		return err_bad_arg(err, "signature", arg);
+	atom->u.signature.option = opt;
+	return 0;
+}
+
+static int trailers_atom_parser(struct ref_format *format UNUSED,
+				struct used_atom *atom,
 				const char *arg, struct strbuf *err)
 {
 	atom->u.contents.trailer_opts.no_divider = 1;
@@ -427,9 +590,10 @@
 		atom->u.contents.option = C_BARE;
 	else if (!strcmp(arg, "body"))
 		atom->u.contents.option = C_BODY;
-	else if (!strcmp(arg, "size"))
+	else if (!strcmp(arg, "size")) {
+		atom->type = FIELD_ULONG;
 		atom->u.contents.option = C_LENGTH;
-	else if (!strcmp(arg, "signature"))
+	} else if (!strcmp(arg, "signature"))
 		atom->u.contents.option = C_SIG;
 	else if (!strcmp(arg, "subject"))
 		atom->u.contents.option = C_SUB;
@@ -448,19 +612,103 @@
 	return 0;
 }
 
-static int raw_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int describe_atom_option_parser(struct strvec *args, const char **arg,
+				       struct strbuf *err)
+{
+	const char *argval;
+	size_t arglen = 0;
+	int optval = 0;
+
+	if (match_atom_bool_arg(*arg, "tags", arg, &optval)) {
+		if (!optval)
+			strvec_push(args, "--no-tags");
+		else
+			strvec_push(args, "--tags");
+		return 1;
+	}
+
+	if (match_atom_arg_value(*arg, "abbrev", arg, &argval, &arglen)) {
+		char *endptr;
+
+		if (!arglen)
+			return strbuf_addf_ret(err, -1,
+					       _("argument expected for %s"),
+					       "describe:abbrev");
+		if (strtol(argval, &endptr, 10) < 0)
+			return strbuf_addf_ret(err, -1,
+					       _("positive value expected %s=%s"),
+					       "describe:abbrev", argval);
+		if (endptr - argval != arglen)
+			return strbuf_addf_ret(err, -1,
+					       _("cannot fully parse %s=%s"),
+					       "describe:abbrev", argval);
+
+		strvec_pushf(args, "--abbrev=%.*s", (int)arglen, argval);
+		return 1;
+	}
+
+	if (match_atom_arg_value(*arg, "match", arg, &argval, &arglen)) {
+		if (!arglen)
+			return strbuf_addf_ret(err, -1,
+					       _("value expected %s="),
+					       "describe:match");
+
+		strvec_pushf(args, "--match=%.*s", (int)arglen, argval);
+		return 1;
+	}
+
+	if (match_atom_arg_value(*arg, "exclude", arg, &argval, &arglen)) {
+		if (!arglen)
+			return strbuf_addf_ret(err, -1,
+					       _("value expected %s="),
+					       "describe:exclude");
+
+		strvec_pushf(args, "--exclude=%.*s", (int)arglen, argval);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int describe_atom_parser(struct ref_format *format UNUSED,
+				struct used_atom *atom,
 				const char *arg, struct strbuf *err)
 {
+	struct strvec args = STRVEC_INIT;
+
+	for (;;) {
+		int found = 0;
+		const char *bad_arg = arg;
+
+		if (!arg || !*arg)
+			break;
+
+		found = describe_atom_option_parser(&args, &arg, err);
+		if (found < 0)
+			return found;
+		if (!found)
+			return err_bad_arg(err, "describe", bad_arg);
+	}
+	atom->u.describe_args = strvec_detach(&args);
+	return 0;
+}
+
+static int raw_atom_parser(struct ref_format *format UNUSED,
+			   struct used_atom *atom,
+			   const char *arg, struct strbuf *err)
+{
 	if (!arg)
 		atom->u.raw_data.option = RAW_BARE;
-	else if (!strcmp(arg, "size"))
+	else if (!strcmp(arg, "size")) {
+		atom->type = FIELD_ULONG;
 		atom->u.raw_data.option = RAW_LENGTH;
-	else
+	} else
 		return err_bad_arg(err, "raw", arg);
 	return 0;
 }
 
-static int oid_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int oid_atom_parser(struct ref_format *format UNUSED,
+			   struct used_atom *atom,
 			   const char *arg, struct strbuf *err)
 {
 	if (!arg)
@@ -479,21 +727,57 @@
 	return 0;
 }
 
-static int person_email_atom_parser(struct ref_format *format, struct used_atom *atom,
-				    const char *arg, struct strbuf *err)
+static int person_name_atom_parser(struct ref_format *format UNUSED,
+				   struct used_atom *atom,
+				   const char *arg, struct strbuf *err)
 {
 	if (!arg)
-		atom->u.email_option.option = EO_RAW;
-	else if (!strcmp(arg, "trim"))
-		atom->u.email_option.option = EO_TRIM;
-	else if (!strcmp(arg, "localpart"))
-		atom->u.email_option.option = EO_LOCALPART;
+		atom->u.name_option.option = N_RAW;
+	else if (!strcmp(arg, "mailmap"))
+		atom->u.name_option.option = N_MAILMAP;
 	else
 		return err_bad_arg(err, atom->name, arg);
 	return 0;
 }
 
-static int refname_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int email_atom_option_parser(struct used_atom *atom,
+				    const char **arg, struct strbuf *err)
+{
+	if (!*arg)
+		return EO_RAW;
+	if (skip_prefix(*arg, "trim", arg))
+		return EO_TRIM;
+	if (skip_prefix(*arg, "localpart", arg))
+		return EO_LOCALPART;
+	if (skip_prefix(*arg, "mailmap", arg))
+		return EO_MAILMAP;
+	return -1;
+}
+
+static int person_email_atom_parser(struct ref_format *format UNUSED,
+				    struct used_atom *atom,
+				    const char *arg, struct strbuf *err)
+{
+	for (;;) {
+		int opt = email_atom_option_parser(atom, &arg, err);
+		const char *bad_arg = arg;
+
+		if (opt < 0)
+			return err_bad_arg(err, atom->name, bad_arg);
+		atom->u.email_option.option |= opt;
+
+		if (!arg || !*arg)
+			break;
+		if (*arg == ',')
+			arg++;
+		else
+			return err_bad_arg(err, atom->name, bad_arg);
+	}
+	return 0;
+}
+
+static int refname_atom_parser(struct ref_format *format UNUSED,
+			       struct used_atom *atom,
 			       const char *arg, struct strbuf *err)
 {
 	return refname_atom_parser_internal(&atom->u.refname, arg, atom->name, err);
@@ -510,7 +794,8 @@
 	return -1;
 }
 
-static int align_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int align_atom_parser(struct ref_format *format UNUSED,
+			     struct used_atom *atom,
 			     const char *arg, struct strbuf *err)
 {
 	struct align *align = &atom->u.align;
@@ -562,7 +847,8 @@
 	return 0;
 }
 
-static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int if_atom_parser(struct ref_format *format UNUSED,
+			  struct used_atom *atom,
 			  const char *arg, struct strbuf *err)
 {
 	if (!arg) {
@@ -577,16 +863,34 @@
 	return 0;
 }
 
-static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int rest_atom_parser(struct ref_format *format UNUSED,
+			    struct used_atom *atom UNUSED,
 			    const char *arg, struct strbuf *err)
 {
 	if (arg)
 		return err_no_arg(err, "rest");
-	format->use_rest = 1;
 	return 0;
 }
 
-static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
+static int ahead_behind_atom_parser(struct ref_format *format,
+				    struct used_atom *atom UNUSED,
+				    const char *arg, struct strbuf *err)
+{
+	struct string_list_item *item;
+
+	if (!arg)
+		return strbuf_addf_ret(err, -1, _("expected format: %%(ahead-behind:<committish>)"));
+
+	item = string_list_append(&format->bases, arg);
+	item->util = lookup_commit_reference_by_name(arg);
+	if (!item->util)
+		die("failed to find '%s'", arg);
+
+	return 0;
+}
+
+static int head_atom_parser(struct ref_format *format UNUSED,
+			    struct used_atom *atom,
 			    const char *arg, struct strbuf *err)
 {
 	if (arg)
@@ -614,23 +918,25 @@
 	[ATOM_TYPE] = { "type", SOURCE_OBJ },
 	[ATOM_TAG] = { "tag", SOURCE_OBJ },
 	[ATOM_AUTHOR] = { "author", SOURCE_OBJ },
-	[ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ },
+	[ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
 	[ATOM_AUTHOREMAIL] = { "authoremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
 	[ATOM_AUTHORDATE] = { "authordate", SOURCE_OBJ, FIELD_TIME },
 	[ATOM_COMMITTER] = { "committer", SOURCE_OBJ },
-	[ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ },
+	[ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
 	[ATOM_COMMITTEREMAIL] = { "committeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
 	[ATOM_COMMITTERDATE] = { "committerdate", SOURCE_OBJ, FIELD_TIME },
 	[ATOM_TAGGER] = { "tagger", SOURCE_OBJ },
-	[ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ },
+	[ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser },
 	[ATOM_TAGGEREMAIL] = { "taggeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
 	[ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME },
 	[ATOM_CREATOR] = { "creator", SOURCE_OBJ },
 	[ATOM_CREATORDATE] = { "creatordate", SOURCE_OBJ, FIELD_TIME },
+	[ATOM_DESCRIBE] = { "describe", SOURCE_OBJ, FIELD_STR, describe_atom_parser },
 	[ATOM_SUBJECT] = { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser },
 	[ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
 	[ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
 	[ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
+	[ATOM_SIGNATURE] = { "signature", SOURCE_OBJ, FIELD_STR, signature_atom_parser },
 	[ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser },
 	[ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
 	[ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
@@ -645,6 +951,7 @@
 	[ATOM_THEN] = { "then", SOURCE_NONE },
 	[ATOM_ELSE] = { "else", SOURCE_NONE },
 	[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
+	[ATOM_AHEADBEHIND] = { "ahead-behind", SOURCE_OTHER, FIELD_STR, ahead_behind_atom_parser },
 	/*
 	 * Please update $__git_ref_fieldlist in git-completion.bash
 	 * when you add new atoms
@@ -791,7 +1098,7 @@
 }
 
 static int append_atom(struct atom_value *v, struct ref_formatting_state *state,
-		       struct strbuf *unused_err)
+		       struct strbuf *err UNUSED)
 {
 	/*
 	 * Quote formatting is only done when the stack has a single
@@ -841,7 +1148,7 @@
 }
 
 static int align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
-			      struct strbuf *unused_err)
+			      struct strbuf *err UNUSED)
 {
 	struct ref_formatting_stack *new_stack;
 
@@ -888,7 +1195,7 @@
 }
 
 static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
-			   struct strbuf *unused_err)
+			   struct strbuf *err UNUSED)
 {
 	struct ref_formatting_stack *new_stack;
 	struct if_then_else *if_then_else = xcalloc(1,
@@ -915,7 +1222,8 @@
 	return cur == end;
  }
 
-static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+static int then_atom_handler(struct atom_value *atomv UNUSED,
+			     struct ref_formatting_state *state,
 			     struct strbuf *err)
 {
 	struct ref_formatting_stack *cur = state->stack;
@@ -952,7 +1260,8 @@
 	return 0;
 }
 
-static int else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+static int else_atom_handler(struct atom_value *atomv UNUSED,
+			     struct ref_formatting_state *state,
 			     struct strbuf *err)
 {
 	struct ref_formatting_stack *prev = state->stack;
@@ -973,7 +1282,8 @@
 	return 0;
 }
 
-static int end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+static int end_atom_handler(struct atom_value *atomv UNUSED,
+			    struct ref_formatting_state *state,
 			    struct strbuf *err)
 {
 	struct ref_formatting_stack *current = state->stack;
@@ -1075,9 +1385,11 @@
 	case O_FULL:
 		return oid_to_hex(oid);
 	case O_LENGTH:
-		return find_unique_abbrev(oid, atom->u.oid.length);
+		return repo_find_unique_abbrev(the_repository, oid,
+					       atom->u.oid.length);
 	case O_SHORT:
-		return find_unique_abbrev(oid, DEFAULT_ABBREV);
+		return repo_find_unique_abbrev(the_repository, oid,
+					       DEFAULT_ABBREV);
 	default:
 		BUG("unknown %%(%s) option", field);
 	}
@@ -1215,32 +1527,49 @@
 	return xstrdup("");
 }
 
+static const char *find_end_of_email(const char *email, int opt)
+{
+	const char *eoemail;
+
+	if (opt & EO_LOCALPART) {
+		eoemail = strchr(email, '@');
+		if (eoemail)
+			return eoemail;
+		return strchr(email, '>');
+	}
+
+	if (opt & EO_TRIM)
+		return strchr(email, '>');
+
+	/*
+	 * The option here is either the raw email option or the raw
+	 * mailmap option (that is EO_RAW or EO_MAILMAP). In such cases,
+	 * we directly grab the whole email including the closing
+	 * angle brackets.
+	 *
+	 * If EO_MAILMAP was set with any other option (that is either
+	 * EO_TRIM or EO_LOCALPART), we already grab the end of email
+	 * above.
+	 */
+	eoemail = strchr(email, '>');
+	if (eoemail)
+		eoemail++;
+	return eoemail;
+}
+
 static const char *copy_email(const char *buf, struct used_atom *atom)
 {
 	const char *email = strchr(buf, '<');
 	const char *eoemail;
+	int opt = atom->u.email_option.option;
+
 	if (!email)
 		return xstrdup("");
-	switch (atom->u.email_option.option) {
-	case EO_RAW:
-		eoemail = strchr(email, '>');
-		if (eoemail)
-			eoemail++;
-		break;
-	case EO_TRIM:
-		email++;
-		eoemail = strchr(email, '>');
-		break;
-	case EO_LOCALPART:
-		email++;
-		eoemail = strchr(email, '@');
-		if (!eoemail)
-			eoemail = strchr(email, '>');
-		break;
-	default:
-		BUG("unknown email option");
-	}
 
+	if (opt & (EO_LOCALPART | EO_TRIM))
+		email++;
+
+	eoemail = find_end_of_email(email, opt);
 	if (!eoemail)
 		return xstrdup("");
 	return xmemdupz(email, eoemail - email);
@@ -1282,6 +1611,12 @@
 	if (formatp) {
 		formatp++;
 		parse_date_format(formatp, &date_mode);
+
+		/*
+		 * If this is a sort field and a format was specified, we'll
+		 * want to compare formatted date by string value.
+		 */
+		v->atom->type = FIELD_STR;
 	}
 
 	if (!eoemail)
@@ -1301,16 +1636,23 @@
 	v->value = 0;
 }
 
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
 /* See grab_values */
 static void grab_person(const char *who, struct atom_value *val, int deref, void *buf)
 {
 	int i;
 	int wholen = strlen(who);
 	const char *wholine = NULL;
+	const char *headers[] = { "author ", "committer ",
+				  "tagger ", NULL };
 
 	for (i = 0; i < used_atom_cnt; i++) {
-		const char *name = used_atom[i].name;
+		struct used_atom *atom = &used_atom[i];
+		const char *name = atom->name;
 		struct atom_value *v = &val[i];
+		struct strbuf mailmap_buf = STRBUF_INIT;
+
 		if (!!deref != (*name == '*'))
 			continue;
 		if (deref)
@@ -1318,22 +1660,36 @@
 		if (strncmp(who, name, wholen))
 			continue;
 		if (name[wholen] != 0 &&
-		    strcmp(name + wholen, "name") &&
+		    !starts_with(name + wholen, "name") &&
 		    !starts_with(name + wholen, "email") &&
 		    !starts_with(name + wholen, "date"))
 			continue;
-		if (!wholine)
+
+		if ((starts_with(name + wholen, "name") &&
+		    (atom->u.name_option.option == N_MAILMAP)) ||
+		    (starts_with(name + wholen, "email") &&
+		    (atom->u.email_option.option & EO_MAILMAP))) {
+			if (!mailmap.items)
+				read_mailmap(&mailmap);
+			strbuf_addstr(&mailmap_buf, buf);
+			apply_mailmap_to_header(&mailmap_buf, headers, &mailmap);
+			wholine = find_wholine(who, wholen, mailmap_buf.buf);
+		} else {
 			wholine = find_wholine(who, wholen, buf);
+		}
+
 		if (!wholine)
 			return; /* no point looking for it */
 		if (name[wholen] == 0)
 			v->s = copy_line(wholine);
-		else if (!strcmp(name + wholen, "name"))
+		else if (starts_with(name + wholen, "name"))
 			v->s = copy_name(wholine);
 		else if (starts_with(name + wholen, "email"))
 			v->s = copy_email(wholine, &used_atom[i]);
 		else if (starts_with(name + wholen, "date"))
 			grab_date(wholine, v, name);
+
+		strbuf_release(&mailmap_buf);
 	}
 
 	/*
@@ -1362,6 +1718,92 @@
 	}
 }
 
+static void grab_signature(struct atom_value *val, int deref, struct object *obj)
+{
+	int i;
+	struct commit *commit = (struct commit *) obj;
+	struct signature_check sigc = { 0 };
+	int signature_checked = 0;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		struct used_atom *atom = &used_atom[i];
+		const char *name = atom->name;
+		struct atom_value *v = &val[i];
+		int opt;
+
+		if (!!deref != (*name == '*'))
+			continue;
+		if (deref)
+			name++;
+
+		if (!skip_prefix(name, "signature", &name) ||
+		    (*name && *name != ':'))
+			continue;
+		if (!*name)
+			name = NULL;
+		else
+			name++;
+
+		opt = parse_signature_option(name);
+		if (opt < 0)
+			continue;
+
+		if (!signature_checked) {
+			check_commit_signature(commit, &sigc);
+			signature_checked = 1;
+		}
+
+		switch (opt) {
+		case S_BARE:
+			v->s = xstrdup(sigc.output ? sigc.output: "");
+			break;
+		case S_SIGNER:
+			v->s = xstrdup(sigc.signer ? sigc.signer : "");
+			break;
+		case S_GRADE:
+			switch (sigc.result) {
+			case 'G':
+				switch (sigc.trust_level) {
+				case TRUST_UNDEFINED:
+				case TRUST_NEVER:
+					v->s = xstrfmt("%c", (char)'U');
+					break;
+				default:
+					v->s = xstrfmt("%c", (char)'G');
+					break;
+				}
+				break;
+			case 'B':
+			case 'E':
+			case 'N':
+			case 'X':
+			case 'Y':
+			case 'R':
+				v->s = xstrfmt("%c", (char)sigc.result);
+				break;
+			}
+			break;
+		case S_KEY:
+			v->s = xstrdup(sigc.key ? sigc.key : "");
+			break;
+		case S_FINGERPRINT:
+			v->s = xstrdup(sigc.fingerprint ?
+				       sigc.fingerprint : "");
+			break;
+		case S_PRI_KEY_FP:
+			v->s = xstrdup(sigc.primary_key_fingerprint ?
+				       sigc.primary_key_fingerprint : "");
+			break;
+		case S_TRUST_LEVEL:
+			v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level));
+			break;
+		}
+	}
+
+	if (signature_checked)
+		signature_check_clear(&sigc);
+}
+
 static void find_subpos(const char *buf,
 			const char **sub, size_t *sublen,
 			const char **body, size_t *bodylen,
@@ -1440,6 +1882,44 @@
 	}
 }
 
+static void grab_describe_values(struct atom_value *val, int deref,
+				 struct object *obj)
+{
+	struct commit *commit = (struct commit *)obj;
+	int i;
+
+	for (i = 0; i < used_atom_cnt; i++) {
+		struct used_atom *atom = &used_atom[i];
+		enum atom_type type = atom->atom_type;
+		const char *name = atom->name;
+		struct atom_value *v = &val[i];
+
+		struct child_process cmd = CHILD_PROCESS_INIT;
+		struct strbuf out = STRBUF_INIT;
+		struct strbuf err = STRBUF_INIT;
+
+		if (type != ATOM_DESCRIBE)
+			continue;
+
+		if (!!deref != (*name == '*'))
+			continue;
+
+		cmd.git_cmd = 1;
+		strvec_push(&cmd.args, "describe");
+		strvec_pushv(&cmd.args, atom->u.describe_args);
+		strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
+		if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
+			error(_("failed to run 'describe'"));
+			v->s = xstrdup("");
+			continue;
+		}
+		strbuf_rtrim(&out);
+		v->s = strbuf_detach(&out, NULL);
+
+		strbuf_release(&err);
+	}
+}
+
 /* See grab_values */
 static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data)
 {
@@ -1466,7 +1946,8 @@
 				v->s = xmemdupz(buf, buf_size);
 				v->s_size = buf_size;
 			} else if (atom->u.raw_data.option == RAW_LENGTH) {
-				v->s = xstrfmt("%"PRIuMAX, (uintmax_t)buf_size);
+				v->value = buf_size;
+				v->s = xstrfmt("%"PRIuMAX, v->value);
 			}
 			continue;
 		}
@@ -1492,9 +1973,10 @@
 			v->s = strbuf_detach(&sb, NULL);
 		} else if (atom->u.contents.option == C_BODY_DEP)
 			v->s = xmemdupz(bodypos, bodylen);
-		else if (atom->u.contents.option == C_LENGTH)
-			v->s = xstrfmt("%"PRIuMAX, (uintmax_t)strlen(subpos));
-		else if (atom->u.contents.option == C_BODY)
+		else if (atom->u.contents.option == C_LENGTH) {
+			v->value = strlen(subpos);
+			v->s = xstrfmt("%"PRIuMAX, v->value);
+		} else if (atom->u.contents.option == C_BODY)
 			v->s = xmemdupz(bodypos, nonsiglen);
 		else if (atom->u.contents.option == C_SIG)
 			v->s = xmemdupz(sigpos, siglen);
@@ -1509,7 +1991,7 @@
 			struct strbuf s = STRBUF_INIT;
 
 			/* Format the trailer info according to the trailer_opts given */
-			format_trailers_from_commit(&s, subpos, &atom->u.contents.trailer_opts);
+			format_trailers_from_commit(&atom->u.contents.trailer_opts, subpos, &s);
 
 			v->s = strbuf_detach(&s, NULL);
 		} else if (atom->u.contents.option == C_BARE)
@@ -1549,12 +2031,15 @@
 		grab_tag_values(val, deref, obj);
 		grab_sub_body_contents(val, deref, data);
 		grab_person("tagger", val, deref, buf);
+		grab_describe_values(val, deref, obj);
 		break;
 	case OBJ_COMMIT:
 		grab_commit_values(val, deref, obj);
 		grab_sub_body_contents(val, deref, data);
 		grab_person("author", val, deref, buf);
 		grab_person("committer", val, deref, buf);
+		grab_signature(val, deref, obj);
+		grab_describe_values(val, deref, obj);
 		break;
 	case OBJ_TREE:
 		/* grab_tree_values(val, deref, obj, buf, sz); */
@@ -1730,7 +2215,7 @@
 				    state.detached_from);
 	} else if (state.bisect_in_progress)
 		strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
-			    state.branch);
+			    state.bisecting_from);
 	else if (state.detached_from) {
 		if (state.detached_at)
 			strbuf_addf(&desc, _("(HEAD detached at %s)"),
@@ -1822,7 +2307,7 @@
 	populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
 }
 
-static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
+static char *get_worktree_path(const struct ref_array_item *ref)
 {
 	struct hashmap_entry entry, *e;
 	struct ref_to_worktree_entry *lookup_result;
@@ -1848,6 +2333,7 @@
 	struct object *obj;
 	int i;
 	struct object_info empty = OBJECT_INFO_INIT;
+	int ahead_behind_atoms = 0;
 
 	CALLOC_ARRAY(ref->value, used_atom_cnt);
 
@@ -1870,6 +2356,7 @@
 
 		v->s_size = ATOM_SIZE_UNSPECIFIED;
 		v->handler = append_atom;
+		v->value = 0;
 		v->atom = atom;
 
 		if (*name == '*') {
@@ -1881,7 +2368,7 @@
 			refname = get_refname(atom, ref);
 		else if (atom_type == ATOM_WORKTREEPATH) {
 			if (ref->kind == FILTER_REFS_BRANCHES)
-				v->s = get_worktree_path(atom, ref);
+				v->s = get_worktree_path(ref);
 			else
 				v->s = xstrdup("");
 			continue;
@@ -1978,6 +2465,16 @@
 			else
 				v->s = xstrdup("");
 			continue;
+		} else if (atom_type == ATOM_AHEADBEHIND) {
+			if (ref->counts) {
+				const struct ahead_behind_count *count;
+				count = ref->counts[ahead_behind_atoms++];
+				v->s = xstrfmt("%d %d", count->ahead, count->behind);
+			} else {
+				/* Not a commit. */
+				v->s = xstrdup("");
+			}
+			continue;
 		} else
 			continue;
 
@@ -2014,17 +2511,12 @@
 		return 0;
 
 	/*
-	 * If it is a tag object, see if we use a value that derefs
-	 * the object, and if we do grab the object it refers to.
+	 * If it is a tag object, see if we use the peeled value. If we do,
+	 * grab the peeled OID.
 	 */
-	oi_deref.oid = *get_tagged_oid((struct tag *)obj);
+	if (need_tagged && peel_iterated_oid(&obj->oid, &oi_deref.oid))
+		die("bad tag");
 
-	/*
-	 * NEEDSWORK: This derefs tag only once, which
-	 * is good to deal with chains of trust, but
-	 * is not consistent with what deref_tag() does
-	 * which peels the onion to the core.
-	 */
 	return get_object(ref, 1, &obj, &oi_deref, err);
 }
 
@@ -2050,12 +2542,12 @@
  * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
  * matches "refs/heads/mas*", too).
  */
-static int match_pattern(const struct ref_filter *filter, const char *refname)
+static int match_pattern(const char **patterns, const char *refname,
+			 int ignore_case)
 {
-	const char **patterns = filter->name_patterns;
 	unsigned flags = 0;
 
-	if (filter->ignore_case)
+	if (ignore_case)
 		flags |= WM_CASEFOLD;
 
 	/*
@@ -2080,13 +2572,13 @@
  * matches a pattern "refs/heads/" but not "refs/heads/m") or a
  * wildcard (e.g. the same ref matches "refs/heads/m*", too).
  */
-static int match_name_as_path(const struct ref_filter *filter, const char *refname)
+static int match_name_as_path(const char **pattern, const char *refname,
+			      int ignore_case)
 {
-	const char **pattern = filter->name_patterns;
 	int namelen = strlen(refname);
 	unsigned flags = WM_PATHNAME;
 
-	if (filter->ignore_case)
+	if (ignore_case)
 		flags |= WM_CASEFOLD;
 
 	for (; *pattern; pattern++) {
@@ -2111,8 +2603,20 @@
 	if (!*filter->name_patterns)
 		return 1; /* No pattern always matches */
 	if (filter->match_as_path)
-		return match_name_as_path(filter, refname);
-	return match_pattern(filter, refname);
+		return match_name_as_path(filter->name_patterns, refname,
+					  filter->ignore_case);
+	return match_pattern(filter->name_patterns, refname,
+			     filter->ignore_case);
+}
+
+static int filter_exclude_match(struct ref_filter *filter, const char *refname)
+{
+	if (!filter->exclude.nr)
+		return 0;
+	if (filter->match_as_path)
+		return match_name_as_path(filter->exclude.v, refname,
+					  filter->ignore_case);
+	return match_pattern(filter->exclude.v, refname, filter->ignore_case);
 }
 
 /*
@@ -2124,6 +2628,12 @@
 				       each_ref_fn cb,
 				       void *cb_data)
 {
+	if (filter->kind == FILTER_REFS_KIND_MASK) {
+		/* In this case, we want to print all refs including root refs. */
+		return refs_for_each_include_root_refs(get_main_ref_store(the_repository),
+						       cb, cb_data);
+	}
+
 	if (!filter->match_as_path) {
 		/*
 		 * in this case, the patterns are applied after
@@ -2144,43 +2654,53 @@
 
 	if (!filter->name_patterns[0]) {
 		/* no patterns; we have to look at everything */
-		return for_each_fullref_in("", cb, cb_data);
+		return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						 "", filter->exclude.v, cb, cb_data);
 	}
 
 	return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
 						 NULL, filter->name_patterns,
+						 filter->exclude.v,
 						 cb, cb_data);
 }
 
 /*
  * Given a ref (oid, refname), check if the ref belongs to the array
  * of oids. If the given ref is a tag, check if the given tag points
- * at one of the oids in the given oid array.
+ * at one of the oids in the given oid array. Returns non-zero if a
+ * match is found.
+ *
  * NEEDSWORK:
- * 1. Only a single level of indirection is obtained, we might want to
- * change this to account for multiple levels (e.g. annotated tags
- * pointing to annotated tags pointing to a commit.)
- * 2. As the refs are cached we might know what refname peels to without
+ * As the refs are cached we might know what refname peels to without
  * the need to parse the object via parse_object(). peel_ref() might be a
  * more efficient alternative to obtain the pointee.
  */
-static const struct object_id *match_points_at(struct oid_array *points_at,
-					       const struct object_id *oid,
-					       const char *refname)
+static int match_points_at(struct oid_array *points_at,
+			   const struct object_id *oid,
+			   const char *refname)
 {
-	const struct object_id *tagged_oid = NULL;
 	struct object *obj;
 
 	if (oid_array_lookup(points_at, oid) >= 0)
-		return oid;
-	obj = parse_object(the_repository, oid);
+		return 1;
+	obj = parse_object_with_flags(the_repository, oid,
+				      PARSE_OBJECT_SKIP_HASH_CHECK);
+	while (obj && obj->type == OBJ_TAG) {
+		struct tag *tag = (struct tag *)obj;
+
+		if (parse_tag(tag) < 0) {
+			obj = NULL;
+			break;
+		}
+
+		if (oid_array_lookup(points_at, get_tagged_oid(tag)) >= 0)
+			return 1;
+
+		obj = tag->tagged;
+	}
 	if (!obj)
 		die(_("malformed object at '%s'"), refname);
-	if (obj->type == OBJ_TAG)
-		tagged_oid = get_tagged_oid((struct tag *)obj);
-	if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
-		return tagged_oid;
-	return NULL;
+	return 0;
 }
 
 /*
@@ -2200,15 +2720,18 @@
 	return ref;
 }
 
+static void ref_array_append(struct ref_array *array, struct ref_array_item *ref)
+{
+	ALLOC_GROW(array->items, array->nr + 1, array->alloc);
+	array->items[array->nr++] = ref;
+}
+
 struct ref_array_item *ref_array_push(struct ref_array *array,
 				      const char *refname,
 				      const struct object_id *oid)
 {
 	struct ref_array_item *ref = new_ref_array_item(refname, oid);
-
-	ALLOC_GROW(array->items, array->nr + 1, array->alloc);
-	array->items[array->nr++] = ref;
-
+	ref_array_append(array, ref);
 	return ref;
 }
 
@@ -2233,6 +2756,9 @@
 			return ref_kind[i].kind;
 	}
 
+	if (is_pseudoref(get_main_ref_store(the_repository), refname))
+		return FILTER_REFS_PSEUDOREFS;
+
 	return FILTER_REFS_OTHERS;
 }
 
@@ -2245,45 +2771,45 @@
 	return ref_kind_from_refname(refname);
 }
 
-struct ref_filter_cbdata {
-	struct ref_array *array;
-	struct ref_filter *filter;
-	struct contains_cache contains_cache;
-	struct contains_cache no_contains_cache;
-};
-
-/*
- * A call-back given to for_each_ref().  Filter refs and keep them for
- * later object processing.
- */
-static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,
+			    int flag, struct ref_filter *filter)
 {
-	struct ref_filter_cbdata *ref_cbdata = cb_data;
-	struct ref_filter *filter = ref_cbdata->filter;
 	struct ref_array_item *ref;
 	struct commit *commit = NULL;
 	unsigned int kind;
 
 	if (flag & REF_BAD_NAME) {
 		warning(_("ignoring ref with broken name %s"), refname);
-		return 0;
+		return NULL;
 	}
 
 	if (flag & REF_ISBROKEN) {
 		warning(_("ignoring broken ref %s"), refname);
-		return 0;
+		return NULL;
 	}
 
 	/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
 	kind = filter_ref_kind(filter, refname);
-	if (!(kind & filter->kind))
-		return 0;
+
+	/*
+	 * Generally HEAD refs are printed with special description denoting a rebase,
+	 * detached state and so forth. This is useful when only printing the HEAD ref
+	 * But when it is being printed along with other pseudorefs, it makes sense to
+	 * keep the formatting consistent. So we mask the type to act like a pseudoref.
+	 */
+	if (filter->kind == FILTER_REFS_KIND_MASK && kind == FILTER_REFS_DETACHED_HEAD)
+		kind = FILTER_REFS_PSEUDOREFS;
+	else if (!(kind & filter->kind))
+		return NULL;
 
 	if (!filter_pattern_match(filter, refname))
-		return 0;
+		return NULL;
+
+	if (filter_exclude_match(filter, refname))
+		return NULL;
 
 	if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
-		return 0;
+		return NULL;
 
 	/*
 	 * A merge filter is applied on refs pointing to commits. Hence
@@ -2294,15 +2820,15 @@
 	    filter->with_commit || filter->no_commit || filter->verbose) {
 		commit = lookup_commit_reference_gently(the_repository, oid, 1);
 		if (!commit)
-			return 0;
+			return NULL;
 		/* We perform the filtering for the '--contains' option... */
 		if (filter->with_commit &&
-		    !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
-			return 0;
+		    !commit_contains(filter, commit, filter->with_commit, &filter->internal.contains_cache))
+			return NULL;
 		/* ...or for the `--no-contains' option */
 		if (filter->no_commit &&
-		    commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
-			return 0;
+		    commit_contains(filter, commit, filter->no_commit, &filter->internal.no_contains_cache))
+			return NULL;
 	}
 
 	/*
@@ -2310,11 +2836,32 @@
 	 * to do its job and the resulting list may yet to be pruned
 	 * by maxcount logic.
 	 */
-	ref = ref_array_push(ref_cbdata->array, refname, oid);
+	ref = new_ref_array_item(refname, oid);
 	ref->commit = commit;
 	ref->flag = flag;
 	ref->kind = kind;
 
+	return ref;
+}
+
+struct ref_filter_cbdata {
+	struct ref_array *array;
+	struct ref_filter *filter;
+};
+
+/*
+ * A call-back given to for_each_ref().  Filter refs and keep them for
+ * later object processing.
+ */
+static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+	struct ref_filter_cbdata *ref_cbdata = cb_data;
+	struct ref_array_item *ref;
+
+	ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+	if (ref)
+		ref_array_append(ref_cbdata->array, ref);
+
 	return 0;
 }
 
@@ -2328,9 +2875,53 @@
 			free((char *)item->value[i].s);
 		free(item->value);
 	}
+	free(item->counts);
 	free(item);
 }
 
+struct ref_filter_and_format_cbdata {
+	struct ref_filter *filter;
+	struct ref_format *format;
+
+	struct ref_filter_and_format_internal {
+		int count;
+	} internal;
+};
+
+static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+	struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
+	struct ref_array_item *ref;
+	struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+	ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+	if (!ref)
+		return 0;
+
+	if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+		die("%s", err.buf);
+
+	if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
+		fwrite(output.buf, 1, output.len, stdout);
+		putchar('\n');
+	}
+
+	strbuf_release(&output);
+	strbuf_release(&err);
+	free_array_item(ref);
+
+	/*
+	 * Increment the running count of refs that match the filter. If
+	 * max_count is set and we've reached the max, stop the ref
+	 * iteration by returning a nonzero value.
+	 */
+	if (ref_cbdata->format->array_opts.max_count &&
+	    ++ref_cbdata->internal.count >= ref_cbdata->format->array_opts.max_count)
+		return 1;
+
+	return 0;
+}
+
 /* Free all memory allocated for ref_array */
 void ref_array_clear(struct ref_array *array)
 {
@@ -2356,41 +2947,32 @@
 		free_worktrees(ref_to_worktree_map.worktrees);
 		ref_to_worktree_map.worktrees = NULL;
 	}
+
+	FREE_AND_NULL(array->counts);
 }
 
 #define EXCLUDE_REACHED 0
 #define INCLUDE_REACHED 1
 static void reach_filter(struct ref_array *array,
-			 struct commit_list *check_reachable,
+			 struct commit_list **check_reachable,
 			 int include_reached)
 {
-	struct rev_info revs;
 	int i, old_nr;
 	struct commit **to_clear;
-	struct commit_list *cr;
 
-	if (!check_reachable)
+	if (!*check_reachable)
 		return;
 
 	CALLOC_ARRAY(to_clear, array->nr);
-
-	repo_init_revisions(the_repository, &revs, NULL);
-
 	for (i = 0; i < array->nr; i++) {
 		struct ref_array_item *item = array->items[i];
-		add_pending_object(&revs, &item->commit->object, item->refname);
 		to_clear[i] = item->commit;
 	}
 
-	for (cr = check_reachable; cr; cr = cr->next) {
-		struct commit *merge_commit = cr->item;
-		merge_commit->object.flags |= UNINTERESTING;
-		add_pending_object(&revs, &merge_commit->object, "");
-	}
-
-	revs.limited = 1;
-	if (prepare_revision_walk(&revs))
-		die(_("revision walk setup failed"));
+	tips_reachable_from_bases(the_repository,
+				  *check_reachable,
+				  to_clear, array->nr,
+				  UNINTERESTING);
 
 	old_nr = array->nr;
 	array->nr = 0;
@@ -2409,15 +2991,98 @@
 
 	clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
 
-	while (check_reachable) {
-		struct commit *merge_commit = pop_commit(&check_reachable);
+	while (*check_reachable) {
+		struct commit *merge_commit = pop_commit(check_reachable);
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
-	release_revisions(&revs);
 	free(to_clear);
 }
 
+void filter_ahead_behind(struct repository *r,
+			 struct ref_format *format,
+			 struct ref_array *array)
+{
+	struct commit **commits;
+	size_t commits_nr = format->bases.nr + array->nr;
+
+	if (!format->bases.nr || !array->nr)
+		return;
+
+	ALLOC_ARRAY(commits, commits_nr);
+	for (size_t i = 0; i < format->bases.nr; i++)
+		commits[i] = format->bases.items[i].util;
+
+	ALLOC_ARRAY(array->counts, st_mult(format->bases.nr, array->nr));
+
+	commits_nr = format->bases.nr;
+	array->counts_nr = 0;
+	for (size_t i = 0; i < array->nr; i++) {
+		const char *name = array->items[i]->refname;
+		commits[commits_nr] = lookup_commit_reference_by_name(name);
+
+		if (!commits[commits_nr])
+			continue;
+
+		CALLOC_ARRAY(array->items[i]->counts, format->bases.nr);
+		for (size_t j = 0; j < format->bases.nr; j++) {
+			struct ahead_behind_count *count;
+			count = &array->counts[array->counts_nr++];
+			count->tip_index = commits_nr;
+			count->base_index = j;
+
+			array->items[i]->counts[j] = count;
+		}
+		commits_nr++;
+	}
+
+	ahead_behind(r, commits, commits_nr, array->counts, array->counts_nr);
+	free(commits);
+}
+
+static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)
+{
+	int ret = 0;
+
+	filter->kind = type & FILTER_REFS_KIND_MASK;
+
+	init_contains_cache(&filter->internal.contains_cache);
+	init_contains_cache(&filter->internal.no_contains_cache);
+
+	/*  Simple per-ref filtering */
+	if (!filter->kind)
+		die("filter_refs: invalid type");
+	else {
+		/*
+		 * For common cases where we need only branches or remotes or tags,
+		 * we only iterate through those refs. If a mix of refs is needed,
+		 * we iterate over all refs and filter out required refs with the help
+		 * of filter_ref_kind().
+		 */
+		if (filter->kind == FILTER_REFS_BRANCHES)
+			ret = for_each_fullref_in("refs/heads/", fn, cb_data);
+		else if (filter->kind == FILTER_REFS_REMOTES)
+			ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
+		else if (filter->kind == FILTER_REFS_TAGS)
+			ret = for_each_fullref_in("refs/tags/", fn, cb_data);
+		else if (filter->kind & FILTER_REFS_REGULAR)
+			ret = for_each_fullref_in_pattern(filter, fn, cb_data);
+
+		/*
+		 * When printing all ref types, HEAD is already included,
+		 * so we don't want to print HEAD again.
+		 */
+		if (!ret && (filter->kind != FILTER_REFS_KIND_MASK) &&
+		    (filter->kind & FILTER_REFS_DETACHED_HEAD))
+			head_ref(fn, cb_data);
+	}
+
+	clear_contains_cache(&filter->internal.contains_cache);
+	clear_contains_cache(&filter->internal.no_contains_cache);
+
+	return ret;
+}
+
 /*
  * API for filtering a set of refs. Based on the type of refs the user
  * has requested, we iterate through those refs and apply filters
@@ -2433,47 +3098,64 @@
 	ref_cbdata.array = array;
 	ref_cbdata.filter = filter;
 
-	filter->kind = type & FILTER_REFS_KIND_MASK;
-
 	save_commit_buffer_orig = save_commit_buffer;
 	save_commit_buffer = 0;
 
-	init_contains_cache(&ref_cbdata.contains_cache);
-	init_contains_cache(&ref_cbdata.no_contains_cache);
-
-	/*  Simple per-ref filtering */
-	if (!filter->kind)
-		die("filter_refs: invalid type");
-	else {
-		/*
-		 * For common cases where we need only branches or remotes or tags,
-		 * we only iterate through those refs. If a mix of refs is needed,
-		 * we iterate over all refs and filter out required refs with the help
-		 * of filter_ref_kind().
-		 */
-		if (filter->kind == FILTER_REFS_BRANCHES)
-			ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata);
-		else if (filter->kind == FILTER_REFS_REMOTES)
-			ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata);
-		else if (filter->kind == FILTER_REFS_TAGS)
-			ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata);
-		else if (filter->kind & FILTER_REFS_ALL)
-			ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata);
-		if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
-			head_ref(ref_filter_handler, &ref_cbdata);
-	}
-
-	clear_contains_cache(&ref_cbdata.contains_cache);
-	clear_contains_cache(&ref_cbdata.no_contains_cache);
+	ret = do_filter_refs(filter, type, filter_one, &ref_cbdata);
 
 	/*  Filters that need revision walking */
-	reach_filter(array, filter->reachable_from, INCLUDE_REACHED);
-	reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED);
+	reach_filter(array, &filter->reachable_from, INCLUDE_REACHED);
+	reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED);
 
 	save_commit_buffer = save_commit_buffer_orig;
 	return ret;
 }
 
+static inline int can_do_iterative_format(struct ref_filter *filter,
+					  struct ref_sorting *sorting,
+					  struct ref_format *format)
+{
+	/*
+	 * Filtering & formatting results within a single ref iteration
+	 * callback is not compatible with options that require
+	 * post-processing a filtered ref_array. These include:
+	 * - filtering on reachability
+	 * - sorting the filtered results
+	 * - including ahead-behind information in the formatted output
+	 */
+	return !(filter->reachable_from ||
+		 filter->unreachable_from ||
+		 sorting ||
+		 format->bases.nr);
+}
+
+void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
+			    struct ref_sorting *sorting,
+			    struct ref_format *format)
+{
+	if (can_do_iterative_format(filter, sorting, format)) {
+		int save_commit_buffer_orig;
+		struct ref_filter_and_format_cbdata ref_cbdata = {
+			.filter = filter,
+			.format = format,
+		};
+
+		save_commit_buffer_orig = save_commit_buffer;
+		save_commit_buffer = 0;
+
+		do_filter_refs(filter, type, filter_and_format_one, &ref_cbdata);
+
+		save_commit_buffer = save_commit_buffer_orig;
+	} else {
+		struct ref_array array = { 0 };
+		filter_refs(&array, filter, type);
+		filter_ahead_behind(the_repository, format, &array);
+		ref_array_sort(sorting, &array);
+		print_formatted_ref_array(&array, format);
+		ref_array_clear(&array);
+	}
+}
+
 static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
 {
 	if (!(a->kind ^ b->kind))
@@ -2591,7 +3273,8 @@
 
 void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 {
-	QSORT_S(array->items, array->nr, compare_refs, sorting);
+	if (sorting)
+		QSORT_S(array->items, array->nr, compare_refs, sorting);
 }
 
 static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
@@ -2662,6 +3345,29 @@
 	return 0;
 }
 
+void print_formatted_ref_array(struct ref_array *array, struct ref_format *format)
+{
+	int total;
+	struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+	total = format->array_opts.max_count;
+	if (!total || array->nr < total)
+		total = array->nr;
+	for (int i = 0; i < total; i++) {
+		strbuf_reset(&err);
+		strbuf_reset(&output);
+		if (format_ref_array_item(array->items[i], format, &output, &err))
+			die("%s", err.buf);
+		if (output.len || !format->array_opts.omit_empty) {
+			fwrite(output.buf, 1, output.len, stdout);
+			putchar('\n');
+		}
+	}
+
+	strbuf_release(&err);
+	strbuf_release(&output);
+}
+
 void pretty_print_ref(const char *name, const struct object_id *oid,
 		      struct ref_format *format)
 {
@@ -2697,18 +3403,6 @@
 	return res;
 }
 
-/*  If no sorting option is given, use refname to sort as default */
-static struct ref_sorting *ref_default_sorting(void)
-{
-	static const char cstr_name[] = "refname";
-
-	struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
-
-	sorting->next = NULL;
-	sorting->atom = parse_sorting_atom(cstr_name);
-	return sorting;
-}
-
 static void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
 {
 	struct ref_sorting *s;
@@ -2732,9 +3426,7 @@
 	struct string_list_item *item;
 	struct ref_sorting *sorting = NULL, **tail = &sorting;
 
-	if (!options->nr) {
-		sorting = ref_default_sorting();
-	} else {
+	if (options->nr) {
 		for_each_string_list_item(item, options)
 			parse_ref_sorting(tail, item->string);
 	}
@@ -2765,7 +3457,7 @@
 
 	BUG_ON_OPT_NEG(unset);
 
-	if (get_oid(arg, &oid))
+	if (repo_get_oid(the_repository, arg, &oid))
 		die(_("malformed object name %s"), arg);
 
 	merge_commit = lookup_commit_reference_gently(the_repository, &oid, 0);
@@ -2780,3 +3472,20 @@
 
 	return 0;
 }
+
+void ref_filter_init(struct ref_filter *filter)
+{
+	struct ref_filter blank = REF_FILTER_INIT;
+	memcpy(filter, &blank, sizeof(blank));
+}
+
+void ref_filter_clear(struct ref_filter *filter)
+{
+	strvec_clear(&filter->exclude);
+	oid_array_clear(&filter->points_at);
+	free_commit_list(filter->with_commit);
+	free_commit_list(filter->no_commit);
+	free_commit_list(filter->reachable_from);
+	free_commit_list(filter->unreachable_from);
+	ref_filter_init(filter);
+}
diff --git a/ref-filter.h b/ref-filter.h
index aa0eea4..0ca28d2 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -1,10 +1,12 @@
 #ifndef REF_FILTER_H
 #define REF_FILTER_H
 
+#include "gettext.h"
 #include "oid-array.h"
-#include "refs.h"
 #include "commit.h"
-#include "parse-options.h"
+#include "string-list.h"
+#include "strvec.h"
+#include "commit-reach.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -17,13 +19,18 @@
 #define FILTER_REFS_BRANCHES       0x0004
 #define FILTER_REFS_REMOTES        0x0008
 #define FILTER_REFS_OTHERS         0x0010
-#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
+#define FILTER_REFS_REGULAR        (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
 				    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
 #define FILTER_REFS_DETACHED_HEAD  0x0020
-#define FILTER_REFS_KIND_MASK      (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD)
+#define FILTER_REFS_PSEUDOREFS     0x0040
+#define FILTER_REFS_ROOT_REFS      (FILTER_REFS_DETACHED_HEAD | FILTER_REFS_PSEUDOREFS)
+#define FILTER_REFS_KIND_MASK      (FILTER_REFS_REGULAR | FILTER_REFS_DETACHED_HEAD | \
+				    FILTER_REFS_PSEUDOREFS)
 
 struct atom_value;
 struct ref_sorting;
+struct ahead_behind_count;
+struct option;
 
 enum ref_sorting_order {
 	REF_SORTING_REVERSE = 1<<0,
@@ -40,6 +47,8 @@
 	const char *symref;
 	struct commit *commit;
 	struct atom_value *value;
+	struct ahead_behind_count **counts;
+
 	char refname[FLEX_ARRAY];
 };
 
@@ -47,10 +56,14 @@
 	int nr, alloc;
 	struct ref_array_item **items;
 	struct rev_info *revs;
+
+	struct ahead_behind_count *counts;
+	size_t counts_nr;
 };
 
 struct ref_filter {
 	const char **name_patterns;
+	struct strvec exclude;
 	struct oid_array points_at;
 	struct commit_list *with_commit;
 	struct commit_list *no_commit;
@@ -65,6 +78,11 @@
 		lines;
 	int abbrev,
 		verbose;
+
+	struct {
+		struct contains_cache contains_cache;
+		struct contains_cache no_contains_cache;
+	} internal;
 };
 
 struct ref_format {
@@ -75,14 +93,28 @@
 	const char *format;
 	const char *rest;
 	int quote_style;
-	int use_rest;
 	int use_color;
 
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;
+
+	/* List of bases for ahead-behind counts. */
+	struct string_list bases;
+
+	struct {
+		int max_count;
+		int omit_empty;
+	} array_opts;
 };
 
-#define REF_FORMAT_INIT { .use_color = -1 }
+#define REF_FILTER_INIT { \
+	.points_at = OID_ARRAY_INIT, \
+	.exclude = STRVEC_INIT, \
+}
+#define REF_FORMAT_INIT {             \
+	.use_color = -1,              \
+	.bases = STRING_LIST_INIT_DUP, \
+}
 
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
@@ -96,6 +128,9 @@
 #define OPT_REF_SORT(var) \
 	OPT_STRING_LIST(0, "sort", (var), \
 			N_("key"), N_("field name to sort on"))
+#define OPT_REF_FILTER_EXCLUDE(var) \
+	OPT_STRVEC(0, "exclude", &(var)->exclude, \
+		   N_("pattern"), N_("exclude refs which match pattern"))
 
 /*
  * API for filtering a set of refs. Based on the type of refs the user
@@ -104,6 +139,14 @@
  * filtered refs in the ref_array structure.
  */
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
+/*
+ * Filter refs using the given ref_filter and type, sort the contents
+ * according to the given ref_sorting, format the filtered refs with the
+ * given ref_format, and print them to stdout.
+ */
+void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
+			    struct ref_sorting *sorting,
+			    struct ref_format *format);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
@@ -129,6 +172,12 @@
 void setup_ref_filter_porcelain_msg(void);
 
 /*
+ * Print up to maxcount ref_array elements to stdout using the given
+ * ref_format.
+ */
+void print_formatted_ref_array(struct ref_array *array, struct ref_format *format);
+
+/*
  * Print a single ref, outside of any ref-filter. Note that the
  * name must be a fully qualified refname.
  */
@@ -143,4 +192,18 @@
 				      const char *refname,
 				      const struct object_id *oid);
 
+/*
+ * If the provided format includes ahead-behind atoms, then compute the
+ * ahead-behind values for the array of filtered references. Must be
+ * called after filter_refs() but before outputting the formatted refs.
+ *
+ * If this is not called, then any ahead-behind atoms will be blank.
+ */
+void filter_ahead_behind(struct repository *r,
+			 struct ref_format *format,
+			 struct ref_array *array);
+
+void ref_filter_init(struct ref_filter *filter);
+void ref_filter_clear(struct ref_filter *filter);
+
 #endif /*  REF_FILTER_H  */
diff --git a/reflog-walk.c b/reflog-walk.c
index 8a4d8fa..d216f6f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -1,7 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "commit.h"
 #include "refs.h"
 #include "diff.h"
+#include "repository.h"
 #include "revision.h"
 #include "string-list.h"
 #include "reflog-walk.h"
diff --git a/reflog-walk.h b/reflog-walk.h
index 8076f10..4d93a26 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -1,8 +1,6 @@
 #ifndef REFLOG_WALK_H
 #define REFLOG_WALK_H
 
-#include "cache.h"
-
 struct commit;
 struct reflog_walk_info;
 struct date_mode;
diff --git a/reflog.c b/reflog.c
index 04630f5..647f3ca 100644
--- a/reflog.c
+++ b/reflog.c
@@ -1,9 +1,11 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "object-store-ll.h"
 #include "reflog.h"
 #include "refs.h"
 #include "revision.h"
-#include "worktree.h"
+#include "tree.h"
+#include "tree-walk.h"
 
 /* Remember to update object flag allocation in object.h */
 #define INCOMPLETE	(1u<<10)
@@ -28,7 +30,8 @@
 	if (!tree->buffer) {
 		enum object_type type;
 		unsigned long size;
-		void *data = read_object_file(oid, &type, &size);
+		void *data = repo_read_object_file(the_repository, oid, &type,
+						   &size);
 		if (!data) {
 			tree->object.flags |= INCOMPLETE;
 			return 0;
@@ -36,10 +39,10 @@
 		tree->buffer = data;
 		tree->size = size;
 	}
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 	complete = 1;
 	while (tree_entry(&desc, &entry)) {
-		if (!has_object_file(&entry.oid) ||
+		if (!repo_has_object_file(the_repository, &entry.oid) ||
 		    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
 			tree->object.flags |= INCOMPLETE;
 			complete = 0;
@@ -186,7 +189,7 @@
 		struct commit *commit = pop_commit(&pending);
 		if (commit->object.flags & REACHABLE)
 			continue;
-		if (parse_commit(commit))
+		if (repo_parse_commit(the_repository, commit))
 			continue;
 		commit->object.flags |= REACHABLE;
 		if (commit->date < expire_limit) {
diff --git a/refs.c b/refs.c
index aeae31c..55d2e0b 100644
--- a/refs.c
+++ b/refs.c
@@ -2,40 +2,65 @@
  * The backend-independent part of the reference module.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
 #include "hashmap.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "iterator.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #include "run-command.h"
 #include "hook.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "object.h"
+#include "path.h"
 #include "tag.h"
 #include "submodule.h"
 #include "worktree.h"
 #include "strvec.h"
 #include "repository.h"
+#include "setup.h"
 #include "sigchain.h"
 #include "date.h"
 #include "commit.h"
+#include "wildmatch.h"
 
 /*
  * List of all available backends
  */
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static const struct ref_storage_be *refs_backends[] = {
+	[REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+	[REF_STORAGE_FORMAT_REFTABLE] = &refs_be_reftable,
+};
 
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
+static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
 {
-	struct ref_storage_be *be;
-	for (be = refs_backends; be; be = be->next)
-		if (!strcmp(be->name, name))
-			return be;
+	if (ref_storage_format < ARRAY_SIZE(refs_backends))
+		return refs_backends[ref_storage_format];
 	return NULL;
 }
 
+unsigned int ref_storage_format_by_name(const char *name)
+{
+	for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+		if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+			return i;
+	return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
+const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+	const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+	if (!be)
+		return "unknown";
+	return be->name;
+}
+
 /*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
@@ -367,8 +392,8 @@
 				   oid, flags);
 }
 
-/* The argument to filter_refs */
-struct ref_filter {
+/* The argument to for_each_filter_refs */
+struct for_each_ref_filter {
 	const char *pattern;
 	const char *prefix;
 	each_ref_fn *fn;
@@ -401,10 +426,11 @@
 	return refs_ref_exists(get_main_ref_store(the_repository), refname);
 }
 
-static int filter_refs(const char *refname, const struct object_id *oid,
-			   int flags, void *data)
+static int for_each_filter_refs(const char *refname,
+				const struct object_id *oid,
+				int flags, void *data)
 {
-	struct ref_filter *filter = (struct ref_filter *)data;
+	struct for_each_ref_filter *filter = data;
 
 	if (wildmatch(filter->pattern, refname, 0))
 		return 0;
@@ -561,7 +587,7 @@
 	const char *prefix, void *cb_data)
 {
 	struct strbuf real_pattern = STRBUF_INIT;
-	struct ref_filter filter;
+	struct for_each_ref_filter filter;
 	int ret;
 
 	if (!prefix && !starts_with(pattern, "refs/"))
@@ -581,7 +607,7 @@
 	filter.prefix = prefix;
 	filter.fn = fn;
 	filter.cb_data = cb_data;
-	ret = for_each_ref(filter_refs, &filter);
+	ret = for_each_ref(for_each_filter_refs, &filter);
 
 	strbuf_release(&real_pattern);
 	return ret;
@@ -834,6 +860,47 @@
 	return 1;
 }
 
+int is_pseudoref(struct ref_store *refs, const char *refname)
+{
+	static const char *const irregular_pseudorefs[] = {
+		"AUTO_MERGE",
+		"BISECT_EXPECTED_REV",
+		"NOTES_MERGE_PARTIAL",
+		"NOTES_MERGE_REF",
+		"MERGE_AUTOSTASH",
+	};
+	struct object_id oid;
+	size_t i;
+
+	if (!is_pseudoref_syntax(refname))
+		return 0;
+
+	if (ends_with(refname, "_HEAD")) {
+		refs_resolve_ref_unsafe(refs, refname,
+					RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+					&oid, NULL);
+		return !is_null_oid(&oid);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++)
+		if (!strcmp(refname, irregular_pseudorefs[i])) {
+			refs_resolve_ref_unsafe(refs, refname,
+						RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+						&oid, NULL);
+			return !is_null_oid(&oid);
+		}
+
+	return 0;
+}
+
+int is_headref(struct ref_store *refs, const char *refname)
+{
+	if (!strcmp(refname, "HEAD"))
+		return refs_ref_exists(refs, refname);
+
+	return 0;
+}
+
 static int is_current_worktree_ref(const char *ref) {
 	return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
 }
@@ -1013,55 +1080,40 @@
 			   const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
-	int reached_count;
 
 	cb->tz = tz;
 	cb->date = timestamp;
 
-	/*
-	 * It is not possible for cb->cnt == 0 on the first iteration because
-	 * that special case is handled in read_ref_at().
-	 */
-	if (cb->cnt > 0)
-		cb->cnt--;
-	reached_count = cb->cnt == 0 && !is_null_oid(ooid);
-	if (timestamp <= cb->at_time || reached_count) {
+	if (timestamp <= cb->at_time || cb->cnt == 0) {
 		set_read_ref_cutoffs(cb, timestamp, tz, message);
 		/*
 		 * we have not yet updated cb->[n|o]oid so they still
 		 * hold the values for the previous record.
 		 */
-		if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid))
-			warning(_("log for ref %s has gap after %s"),
+		if (!is_null_oid(&cb->ooid)) {
+			oidcpy(cb->oid, noid);
+			if (!oideq(&cb->ooid, noid))
+				warning(_("log for ref %s has gap after %s"),
 					cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
-		if (reached_count)
-			oidcpy(cb->oid, ooid);
-		else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time)
+		}
+		else if (cb->date == cb->at_time)
 			oidcpy(cb->oid, noid);
 		else if (!oideq(noid, cb->oid))
 			warning(_("log for ref %s unexpectedly ended on %s"),
 				cb->refname, show_date(cb->date, cb->tz,
 						       DATE_MODE(RFC2822)));
+		cb->reccnt++;
+		oidcpy(&cb->ooid, ooid);
+		oidcpy(&cb->noid, noid);
 		cb->found_it = 1;
+		return 1;
 	}
 	cb->reccnt++;
 	oidcpy(&cb->ooid, ooid);
 	oidcpy(&cb->noid, noid);
-	return cb->found_it;
-}
-
-static int read_ref_at_ent_newest(struct object_id *ooid UNUSED,
-				  struct object_id *noid,
-				  const char *email UNUSED,
-				  timestamp_t timestamp, int tz,
-				  const char *message, void *cb_data)
-{
-	struct read_ref_at_cb *cb = cb_data;
-
-	set_read_ref_cutoffs(cb, timestamp, tz, message);
-	oidcpy(cb->oid, noid);
-	/* We just want the first entry */
-	return 1;
+	if (cb->cnt > 0)
+		cb->cnt--;
+	return 0;
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
@@ -1073,7 +1125,7 @@
 
 	set_read_ref_cutoffs(cb, timestamp, tz, message);
 	oidcpy(cb->oid, ooid);
-	if (is_null_oid(cb->oid))
+	if (cb->at_time && is_null_oid(cb->oid))
 		oidcpy(cb->oid, noid);
 	/* We just want the first entry */
 	return 1;
@@ -1096,14 +1148,24 @@
 	cb.cutoff_cnt = cutoff_cnt;
 	cb.oid = oid;
 
-	if (cb.cnt == 0) {
-		refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb);
-		return 0;
-	}
-
 	refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
 
 	if (!cb.reccnt) {
+		if (cnt == 0) {
+			/*
+			 * The caller asked for ref@{0}, and we had no entries.
+			 * It's a bit subtle, but in practice all callers have
+			 * prepped the "oid" field with the current value of
+			 * the ref, which is the most reasonable fallback.
+			 *
+			 * We'll put dummy values into the out-parameters (so
+			 * they're not just uninitialized garbage), and the
+			 * caller can take our return value as a hint that
+			 * we did not find any such reflog.
+			 */
+			set_read_ref_cutoffs(&cb, 0, 0, "empty reflog");
+			return 1;
+		}
 		if (flags & GET_OID_QUIETLY)
 			exit(128);
 		else
@@ -1418,7 +1480,7 @@
 }
 
 int parse_hide_refs_config(const char *var, const char *value, const char *section,
-			   struct string_list *hide_refs)
+			   struct strvec *hide_refs)
 {
 	const char *key;
 	if (!strcmp("transfer.hiderefs", var) ||
@@ -1429,22 +1491,23 @@
 
 		if (!value)
 			return config_error_nonbool(var);
-		ref = xstrdup(value);
+
+		/* drop const to remove trailing '/' characters */
+		ref = (char *)strvec_push(hide_refs, value);
 		len = strlen(ref);
 		while (len && ref[len - 1] == '/')
 			ref[--len] = '\0';
-		string_list_append_nodup(hide_refs, ref);
 	}
 	return 0;
 }
 
 int ref_is_hidden(const char *refname, const char *refname_full,
-		  const struct string_list *hide_refs)
+		  const struct strvec *hide_refs)
 {
 	int i;
 
 	for (i = hide_refs->nr - 1; i >= 0; i--) {
-		const char *match = hide_refs->items[i].string;
+		const char *match = hide_refs->v[i];
 		const char *subject;
 		int neg = 0;
 		const char *p;
@@ -1470,6 +1533,30 @@
 	return 0;
 }
 
+const char **hidden_refs_to_excludes(const struct strvec *hide_refs)
+{
+	const char **pattern;
+	for (pattern = hide_refs->v; *pattern; pattern++) {
+		/*
+		 * We can't feed any excludes from hidden refs config
+		 * sections, since later rules may override previous
+		 * ones. For example, with rules "refs/foo" and
+		 * "!refs/foo/bar", we should show "refs/foo/bar" (and
+		 * everything underneath it), but the earlier exclusion
+		 * would cause us to skip all of "refs/foo".  We
+		 * likewise don't implement the namespace stripping
+		 * required for '^' rules.
+		 *
+		 * Both are possible to do, but complicated, so avoid
+		 * populating the jump list at all if we see either of
+		 * these patterns.
+		 */
+		if (**pattern == '!' || **pattern == '^')
+			return NULL;
+	}
+	return hide_refs->v;
+}
+
 const char *find_descendant_ref(const char *dirname,
 				const struct string_list *extras,
 				const struct string_list *skip)
@@ -1517,7 +1604,9 @@
 
 struct ref_iterator *refs_ref_iterator_begin(
 		struct ref_store *refs,
-		const char *prefix, int trim,
+		const char *prefix,
+		const char **exclude_patterns,
+		int trim,
 		enum do_for_each_ref_flags flags)
 {
 	struct ref_iterator *iter;
@@ -1533,8 +1622,7 @@
 		}
 	}
 
-	iter = refs->be->iterator_begin(refs, prefix, flags);
-
+	iter = refs->be->iterator_begin(refs, prefix, exclude_patterns, flags);
 	/*
 	 * `iterator_begin()` already takes care of prefix, but we
 	 * might need to do some trimming:
@@ -1542,10 +1630,6 @@
 	if (trim)
 		iter = prefix_ref_iterator_begin(iter, "", trim);
 
-	/* Sanity check for subclasses: */
-	if (!iter->ordered)
-		BUG("reference iterator is not ordered");
-
 	return iter;
 }
 
@@ -1568,7 +1652,7 @@
 	if (!refs)
 		return 0;
 
-	iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+	iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
 
 	return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
 }
@@ -1578,7 +1662,7 @@
 	void *cb_data;
 };
 
-static int do_for_each_ref_helper(struct repository *r,
+static int do_for_each_ref_helper(struct repository *r UNUSED,
 				  const char *refname,
 				  const struct object_id *oid,
 				  int flags,
@@ -1590,6 +1674,7 @@
 }
 
 static int do_for_each_ref(struct ref_store *refs, const char *prefix,
+			   const char **exclude_patterns,
 			   each_ref_fn fn, int trim,
 			   enum do_for_each_ref_flags flags, void *cb_data)
 {
@@ -1599,7 +1684,8 @@
 	if (!refs)
 		return 0;
 
-	iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+	iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
+				       flags);
 
 	return do_for_each_repo_ref_iterator(the_repository, iter,
 					do_for_each_ref_helper, &hp);
@@ -1607,7 +1693,7 @@
 
 int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+	return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
 }
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
@@ -1618,7 +1704,7 @@
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
 			 each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
+	return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@ -1629,13 +1715,14 @@
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
 	return do_for_each_ref(get_main_ref_store(the_repository),
-			       prefix, fn, 0, 0, cb_data);
+			       prefix, NULL, fn, 0, 0, cb_data);
 }
 
 int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+			     const char **exclude_patterns,
 			     each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data);
+	return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
 }
 
 int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
@@ -1646,20 +1733,21 @@
 				    DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+int for_each_namespaced_ref(const char **exclude_patterns,
+			    each_ref_fn fn, void *cb_data)
 {
 	struct strbuf buf = STRBUF_INIT;
 	int ret;
 	strbuf_addf(&buf, "%srefs/", get_git_namespace());
 	ret = do_for_each_ref(get_main_ref_store(the_repository),
-			      buf.buf, fn, 0, 0, cb_data);
+			      buf.buf, exclude_patterns, fn, 0, 0, cb_data);
 	strbuf_release(&buf);
 	return ret;
 }
 
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(refs, "", fn, 0,
+	return do_for_each_ref(refs, "", NULL, fn, 0,
 			       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -1668,6 +1756,13 @@
 	return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
 }
 
+int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
+				    void *cb_data)
+{
+	return do_for_each_ref(refs, "", NULL, fn, 0,
+			       DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data);
+}
+
 static int qsort_strcmp(const void *va, const void *vb)
 {
 	const char *a = *(const char **)va;
@@ -1729,6 +1824,7 @@
 int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
 				      const char *namespace,
 				      const char **patterns,
+				      const char **exclude_patterns,
 				      each_ref_fn fn, void *cb_data)
 {
 	struct string_list prefixes = STRING_LIST_INIT_DUP;
@@ -1744,7 +1840,8 @@
 
 	for_each_string_list_item(prefix, &prefixes) {
 		strbuf_addstr(&buf, prefix->string);
-		ret = refs_for_each_fullref_in(ref_store, buf.buf, fn, cb_data);
+		ret = refs_for_each_fullref_in(ref_store, buf.buf,
+					       exclude_patterns, fn, cb_data);
 		if (ret)
 			break;
 		strbuf_setlen(&buf, namespace_len);
@@ -1765,8 +1862,10 @@
 	int result = -1;
 	strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
 
-	if (strbuf_read_file(&content, full_path.buf, 0) < 0)
+	if (strbuf_read_file(&content, full_path.buf, 0) < 0) {
+		*failure_errno = errno;
 		goto done;
+	}
 
 	result = parse_loose_ref_contents(content.buf, oid, referent, type,
 					  failure_errno);
@@ -1777,15 +1876,45 @@
 	return result;
 }
 
+static int is_special_ref(const char *refname)
+{
+	/*
+	 * Special references are refs that have different semantics compared
+	 * to "normal" refs. These refs can thus not be stored in the ref
+	 * backend, but must always be accessed via the filesystem. The
+	 * following refs are special:
+	 *
+	 * - FETCH_HEAD may contain multiple object IDs, and each one of them
+	 *   carries additional metadata like where it came from.
+	 *
+	 * - MERGE_HEAD may contain multiple object IDs when merging multiple
+	 *   heads.
+	 *
+	 * Reading, writing or deleting references must consistently go either
+	 * through the filesystem (special refs) or through the reference
+	 * backend (normal ones).
+	 */
+	static const char * const special_refs[] = {
+		"FETCH_HEAD",
+		"MERGE_HEAD",
+	};
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(special_refs); i++)
+		if (!strcmp(refname, special_refs[i]))
+			return 1;
+
+	return 0;
+}
+
 int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
 		      struct object_id *oid, struct strbuf *referent,
 		      unsigned int *type, int *failure_errno)
 {
 	assert(failure_errno);
-	if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+	if (is_special_ref(refname))
 		return refs_read_special_head(ref_store, refname, oid, referent,
 					      type, failure_errno);
-	}
 
 	return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
 					   type, failure_errno);
@@ -1821,7 +1950,7 @@
 			return NULL;
 
 		/*
-		 * dwim_ref() uses REF_ISBROKEN to distinguish between
+		 * repo_dwim_ref() uses REF_ISBROKEN to distinguish between
 		 * missing refs and refs that were present but invalid,
 		 * to complain about the latter to stderr.
 		 *
@@ -1887,11 +2016,9 @@
 }
 
 /* backend functions */
-int refs_init_db(struct strbuf *err)
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
 {
-	struct ref_store *refs = get_main_ref_store(the_repository);
-
-	return refs->be->init_db(refs, err);
+	return refs->be->init_db(refs, flags, err);
 }
 
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
@@ -1988,12 +2115,12 @@
 					const char *gitdir,
 					unsigned int flags)
 {
-	const char *be_name = "files";
-	struct ref_storage_be *be = find_ref_storage_backend(be_name);
+	const struct ref_storage_be *be;
 	struct ref_store *refs;
 
+	be = find_ref_storage_backend(repo->ref_storage_format);
 	if (!be)
-		BUG("reference backend %s is unknown", be_name);
+		BUG("reference backend is unknown");
 
 	refs = be->init(repo, gitdir, flags);
 	return refs;
@@ -2124,9 +2251,9 @@
 }
 
 /* backend functions */
-int refs_pack_refs(struct ref_store *refs, unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
 {
-	return refs->be->pack_refs(refs, flags);
+	return refs->be->pack_refs(refs, opts);
 }
 
 int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
@@ -2399,7 +2526,7 @@
 	strbuf_addstr(&dirname, refname + dirname.len);
 	strbuf_addch(&dirname, '/');
 
-	iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+	iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
 				       DO_FOR_EACH_INCLUDE_BROKEN);
 	while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 		if (skip &&
@@ -2428,18 +2555,33 @@
 	return ret;
 }
 
-int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+struct do_for_each_reflog_help {
+	each_reflog_fn *fn;
+	void *cb_data;
+};
+
+static int do_for_each_reflog_helper(struct repository *r UNUSED,
+				     const char *refname,
+				     const struct object_id *oid UNUSED,
+				     int flags,
+				     void *cb_data)
+{
+	struct do_for_each_reflog_help *hp = cb_data;
+	return hp->fn(refname, hp->cb_data);
+}
+
+int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
 {
 	struct ref_iterator *iter;
-	struct do_for_each_ref_help hp = { fn, cb_data };
+	struct do_for_each_reflog_help hp = { fn, cb_data };
 
 	iter = refs->be->reflog_iterator_begin(refs);
 
 	return do_for_each_repo_ref_iterator(the_repository, iter,
-					     do_for_each_ref_helper, &hp);
+					     do_for_each_reflog_helper, &hp);
 }
 
-int for_each_reflog(each_ref_fn fn, void *cb_data)
+int for_each_reflog(each_reflog_fn fn, void *cb_data)
 {
 	return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
 }
@@ -2558,13 +2700,55 @@
 int refs_delete_refs(struct ref_store *refs, const char *logmsg,
 		     struct string_list *refnames, unsigned int flags)
 {
+	struct ref_transaction *transaction;
+	struct strbuf err = STRBUF_INIT;
+	struct string_list_item *item;
+	int ret = 0, failures = 0;
 	char *msg;
-	int retval;
+
+	if (!refnames->nr)
+		return 0;
 
 	msg = normalize_reflog_message(logmsg);
-	retval = refs->be->delete_refs(refs, msg, refnames, flags);
+
+	/*
+	 * Since we don't check the references' old_oids, the
+	 * individual updates can't fail, so we can pack all of the
+	 * updates into a single transaction.
+	 */
+	transaction = ref_store_transaction_begin(refs, &err);
+	if (!transaction) {
+		ret = error("%s", err.buf);
+		goto out;
+	}
+
+	for_each_string_list_item(item, refnames) {
+		ret = ref_transaction_delete(transaction, item->string,
+					     NULL, flags, msg, &err);
+		if (ret) {
+			warning(_("could not delete reference %s: %s"),
+				item->string, err.buf);
+			strbuf_reset(&err);
+			failures = 1;
+		}
+	}
+
+	ret = ref_transaction_commit(transaction, &err);
+	if (ret) {
+		if (refnames->nr == 1)
+			error(_("could not delete reference %s: %s"),
+			      refnames->items[0].string, err.buf);
+		else
+			error(_("could not delete references: %s"), err.buf);
+	}
+
+out:
+	if (!ret && failures)
+		ret = -1;
+	ref_transaction_free(transaction);
+	strbuf_release(&err);
 	free(msg);
-	return retval;
+	return ret;
 }
 
 int delete_refs(const char *msg, struct string_list *refnames,
diff --git a/refs.h b/refs.h
index 935cdd1..d278775 100644
--- a/refs.h
+++ b/refs.h
@@ -1,7 +1,6 @@
 #ifndef REFS_H
 #define REFS_H
 
-#include "cache.h"
 #include "commit.h"
 
 struct object_id;
@@ -12,6 +11,9 @@
 struct string_list_item;
 struct worktree;
 
+unsigned int ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
@@ -57,7 +59,7 @@
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
  * directory and do not consist of all caps and underscores cannot be
  * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the special refs, such as HEAD,
+ * Caps and underscores refers to the pseudorefs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
  */
 #define RESOLVE_REF_READING 0x01
@@ -118,7 +120,9 @@
 
 int is_branch(const char *refname);
 
-int refs_init_db(struct strbuf *err);
+#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
+
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
 
 /*
  * Return the peeled value of the oid currently being iterated via
@@ -159,12 +163,6 @@
 int repo_dwim_ref(struct repository *r, const char *str, int len,
 		  struct object_id *oid, char **ref, int nonfatal_dangling_mark);
 int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
-static inline int dwim_ref(const char *str, int len, struct object_id *oid,
-			   char **ref, int nonfatal_dangling_mark)
-{
-	return repo_dwim_ref(the_repository, str, len, oid, ref,
-			     nonfatal_dangling_mark);
-}
 int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
 
 /*
@@ -344,7 +342,12 @@
  */
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 
+/*
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ */
 int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+			     const char **exclude_patterns,
 			     each_ref_fn fn, void *cb_data);
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 
@@ -352,10 +355,15 @@
  * iterate all refs in "patterns" by partitioning patterns into disjoint sets
  * and iterating the longest-common prefix of each set.
  *
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ *
  * callers should be prepared to ignore references that they did not ask for.
  */
 int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
-				      const char *namespace, const char **patterns,
+				      const char *namespace,
+				      const char **patterns,
+				      const char **exclude_patterns,
 				      each_ref_fn fn, void *cb_data);
 
 /**
@@ -373,13 +381,24 @@
 			 const char *prefix, void *cb_data);
 
 int head_ref_namespaced(each_ref_fn fn, void *cb_data);
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+/*
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ */
+int for_each_namespaced_ref(const char **exclude_patterns,
+			    each_ref_fn fn, void *cb_data);
 
 /* can be used to learn about broken ref and symref */
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
 int for_each_rawref(each_ref_fn fn, void *cb_data);
 
 /*
+ * Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
+ */
+int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
+				    void *cb_data);
+
+/*
  * Normalizes partial refs to their fully qualified form.
  * Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.
  * <prefix> will default to 'refs/' if NULL.
@@ -403,16 +422,24 @@
 /*
  * Flags for controlling behaviour of pack_refs()
  * PACK_REFS_PRUNE: Prune loose refs after packing
- * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
+ * PACK_REFS_AUTO: Pack refs on a best effort basis. The heuristics and end
+ *                 result are decided by the ref backend. Backends may ignore
+ *                 this flag and fall back to a normal repack.
  */
-#define PACK_REFS_PRUNE 0x0001
-#define PACK_REFS_ALL   0x0002
+#define PACK_REFS_PRUNE (1 << 0)
+#define PACK_REFS_AUTO  (1 << 1)
+
+struct pack_refs_opts {
+	unsigned int flags;
+	struct ref_exclusions *exclusions;
+	struct string_list *includes;
+};
 
 /*
  * Write a packed-refs file for the current repository.
  * flags: Combination of the above PACK_REFS_* flags.
  */
-int refs_pack_refs(struct ref_store *refs, unsigned int flags);
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts);
 
 /*
  * Setup reflog before using. Fill in err and return -1 on failure.
@@ -421,7 +448,20 @@
 		       struct strbuf *err);
 int safe_create_reflog(const char *refname, struct strbuf *err);
 
-/** Reads log for the value of ref during at_time. **/
+/**
+ * Reads log for the value of ref during at_time (in which case "cnt" should be
+ * negative) or the reflog "cnt" entries from the top (in which case "at_time"
+ * should be 0).
+ *
+ * If we found the reflog entry in question, returns 0 (and details of the
+ * entry can be found in the out-parameters).
+ *
+ * If we ran out of reflog entries, the out-parameters are filled with the
+ * details of the oldest entry we did find, and the function returns 1. Note
+ * that there is one important special case here! If the reflog was empty
+ * and the caller asked for the 0-th cnt, we will return "1" but leave the
+ * "oid" field untouched.
+ **/
 int read_ref_at(struct ref_store *refs,
 		const char *refname, unsigned int flags,
 		timestamp_t at_time, int cnt,
@@ -516,11 +556,18 @@
 int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
 
 /*
+ * The signature for the callback function for the {refs_,}for_each_reflog()
+ * functions below. The memory pointed to by the refname argument is only
+ * guaranteed to be valid for the duration of a single callback invocation.
+ */
+typedef int each_reflog_fn(const char *refname, void *cb_data);
+
+/*
  * Calls the specified function for each reflog file until it returns nonzero,
  * and returns the value. Reflog file order is unspecified.
  */
-int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
-int for_each_reflog(each_ref_fn fn, void *cb_data);
+int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
+int for_each_reflog(each_reflog_fn fn, void *cb_data);
 
 #define REFNAME_ALLOW_ONELEVEL 1
 #define REFNAME_REFSPEC_PATTERN 2
@@ -811,7 +858,7 @@
 	       unsigned int flags, enum action_on_err onerr);
 
 int parse_hide_refs_config(const char *var, const char *value, const char *,
-			   struct string_list *);
+			   struct strvec *);
 
 /*
  * Check whether a ref is hidden. If no namespace is set, both the first and
@@ -821,7 +868,13 @@
  * the ref is outside that namespace, the first parameter is NULL. The second
  * parameter always points to the full ref name.
  */
-int ref_is_hidden(const char *, const char *, const struct string_list *);
+int ref_is_hidden(const char *, const char *, const struct strvec *);
+
+/*
+ * Returns an array of patterns to use as excluded_patterns, if none of the
+ * hidden references use the token '!' or '^'.
+ */
+const char **hidden_refs_to_excludes(const struct strvec *hide_refs);
 
 /* Is this a per-worktree ref living in the refs/ namespace? */
 int is_per_worktree_ref(const char *refname);
@@ -998,4 +1051,7 @@
  */
 void update_ref_namespace(enum ref_namespace namespace, char *ref);
 
+int is_pseudoref(struct ref_store *refs, const char *refname);
+int is_headref(struct ref_store *refs, const char *refname);
+
 #endif /* REFS_H */
diff --git a/refs/debug.c b/refs/debug.c
index eed8bc9..c7531b1 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -1,5 +1,7 @@
-
+#include "git-compat-util.h"
+#include "hex.h"
 #include "refs-internal.h"
+#include "string-list.h"
 #include "trace.h"
 
 static struct trace_key trace_refs = TRACE_KEY_INIT(REFS);
@@ -31,10 +33,10 @@
 	return (struct ref_store *)res;
 }
 
-static int debug_init_db(struct ref_store *refs, struct strbuf *err)
+static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
 {
 	struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
-	int res = drefs->refs->be->init_db(drefs->refs, err);
+	int res = drefs->refs->be->init_db(drefs->refs, flags, err);
 	trace_printf_key(&trace_refs, "init_db: %d\n", res);
 	return res;
 }
@@ -121,10 +123,10 @@
 	return res;
 }
 
-static int debug_pack_refs(struct ref_store *ref_store, unsigned int flags)
+static int debug_pack_refs(struct ref_store *ref_store, struct pack_refs_opts *opts)
 {
 	struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-	int res = drefs->refs->be->pack_refs(drefs->refs, flags);
+	int res = drefs->refs->be->pack_refs(drefs->refs, opts);
 	trace_printf_key(&trace_refs, "pack_refs: %d\n", res);
 	return res;
 }
@@ -141,20 +143,6 @@
 	return res;
 }
 
-static int debug_delete_refs(struct ref_store *ref_store, const char *msg,
-			     struct string_list *refnames, unsigned int flags)
-{
-	struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-	int res =
-		drefs->refs->be->delete_refs(drefs->refs, msg, refnames, flags);
-	int i;
-	trace_printf_key(&trace_refs, "delete_refs {\n");
-	for (i = 0; i < refnames->nr; i++)
-		trace_printf_key(&trace_refs, "%s\n", refnames->items[i].string);
-	trace_printf_key(&trace_refs, "}: %d\n", res);
-	return res;
-}
-
 static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
 			    const char *newref, const char *logmsg)
 {
@@ -193,7 +181,6 @@
 		trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
 			diter->iter->refname);
 
-	diter->base.ordered = diter->iter->ordered;
 	diter->base.refname = diter->iter->refname;
 	diter->base.oid = diter->iter->oid;
 	diter->base.flags = diter->iter->flags;
@@ -227,13 +214,14 @@
 
 static struct ref_iterator *
 debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
-			 unsigned int flags)
+			 const char **exclude_patterns, unsigned int flags)
 {
 	struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
 	struct ref_iterator *res =
-		drefs->refs->be->iterator_begin(drefs->refs, prefix, flags);
+		drefs->refs->be->iterator_begin(drefs->refs, prefix,
+						exclude_patterns, flags);
 	struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
-	base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
+	base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable);
 	diter->iter = res;
 	trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n",
 			 prefix, flags);
@@ -437,7 +425,6 @@
 }
 
 struct ref_storage_be refs_be_debug = {
-	.next = NULL,
 	.name = "debug",
 	.init = NULL,
 	.init_db = debug_init_db,
@@ -455,7 +442,6 @@
 
 	.pack_refs = debug_pack_refs,
 	.create_symref = debug_create_symref,
-	.delete_refs = debug_delete_refs,
 	.rename_ref = debug_rename_ref,
 	.copy_ref = debug_copy_ref,
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index b899543..a098d14 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,16 +1,27 @@
-#include "../cache.h"
-#include "../config.h"
+#include "../git-compat-util.h"
+#include "../copy.h"
+#include "../environment.h"
+#include "../gettext.h"
+#include "../hash.h"
+#include "../hex.h"
 #include "../refs.h"
 #include "refs-internal.h"
 #include "ref-cache.h"
 #include "packed-backend.h"
+#include "../ident.h"
 #include "../iterator.h"
 #include "../dir-iterator.h"
 #include "../lockfile.h"
 #include "../object.h"
+#include "../object-file.h"
+#include "../path.h"
 #include "../dir.h"
 #include "../chdir-notify.h"
-#include "worktree.h"
+#include "../setup.h"
+#include "../wrapper.h"
+#include "../write-or-die.h"
+#include "../revision.h"
+#include <wildmatch.h>
 
 /*
  * This backend uses the following flags in `ref_update::flags` for
@@ -218,6 +229,38 @@
 	}
 }
 
+static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
+					    const char *refname,
+					    struct ref_dir *dir)
+{
+	struct object_id oid;
+	int flag;
+
+	if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,
+				     &oid, &flag)) {
+		oidclr(&oid);
+		flag |= REF_ISBROKEN;
+	} else if (is_null_oid(&oid)) {
+		/*
+		 * It is so astronomically unlikely
+		 * that null_oid is the OID of an
+		 * actual object that we consider its
+		 * appearance in a loose reference
+		 * file to be repo corruption
+		 * (probably due to a software bug).
+		 */
+		flag |= REF_ISBROKEN;
+	}
+
+	if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+		if (!refname_is_safe(refname))
+			die("loose refname is dangerous: %s", refname);
+		oidclr(&oid);
+		flag |= REF_BAD_NAME | REF_ISBROKEN;
+	}
+	add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));
+}
+
 /*
  * Read the loose references from the namespace dirname into dir
  * (without recursing).  dirname must end with '/'.  dir must be the
@@ -233,10 +276,8 @@
 	int dirnamelen = strlen(dirname);
 	struct strbuf refname;
 	struct strbuf path = STRBUF_INIT;
-	size_t path_baselen;
 
 	files_ref_path(refs, &path, dirname);
-	path_baselen = path.len;
 
 	d = opendir(path.buf);
 	if (!d) {
@@ -248,54 +289,24 @@
 	strbuf_add(&refname, dirname, dirnamelen);
 
 	while ((de = readdir(d)) != NULL) {
-		struct object_id oid;
-		struct stat st;
-		int flag;
+		unsigned char dtype;
 
 		if (de->d_name[0] == '.')
 			continue;
 		if (ends_with(de->d_name, ".lock"))
 			continue;
 		strbuf_addstr(&refname, de->d_name);
-		strbuf_addstr(&path, de->d_name);
-		if (stat(path.buf, &st) < 0) {
-			; /* silently ignore */
-		} else if (S_ISDIR(st.st_mode)) {
+
+		dtype = get_dtype(de, &path, 1);
+		if (dtype == DT_DIR) {
 			strbuf_addch(&refname, '/');
 			add_entry_to_dir(dir,
 					 create_dir_entry(dir->cache, refname.buf,
 							  refname.len));
-		} else {
-			if (!refs_resolve_ref_unsafe(&refs->base,
-						     refname.buf,
-						     RESOLVE_REF_READING,
-						     &oid, &flag)) {
-				oidclr(&oid);
-				flag |= REF_ISBROKEN;
-			} else if (is_null_oid(&oid)) {
-				/*
-				 * It is so astronomically unlikely
-				 * that null_oid is the OID of an
-				 * actual object that we consider its
-				 * appearance in a loose reference
-				 * file to be repo corruption
-				 * (probably due to a software bug).
-				 */
-				flag |= REF_ISBROKEN;
-			}
-
-			if (check_refname_format(refname.buf,
-						 REFNAME_ALLOW_ONELEVEL)) {
-				if (!refname_is_safe(refname.buf))
-					die("loose refname is dangerous: %s", refname.buf);
-				oidclr(&oid);
-				flag |= REF_BAD_NAME | REF_ISBROKEN;
-			}
-			add_entry_to_dir(dir,
-					 create_ref_entry(refname.buf, &oid, flag));
+		} else if (dtype == DT_REG) {
+			loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
 		}
 		strbuf_setlen(&refname, dirnamelen);
-		strbuf_setlen(&path, path_baselen);
 	}
 	strbuf_release(&refname);
 	strbuf_release(&path);
@@ -304,9 +315,59 @@
 	add_per_worktree_entries_to_dir(dir, dirname);
 }
 
-static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
+/*
+ * Add pseudorefs to the ref dir by parsing the directory for any files
+ * which follow the pseudoref syntax.
+ */
+static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
+					 struct ref_dir *dir,
+					 const char *dirname)
+{
+	struct files_ref_store *refs =
+		files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
+	struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
+	struct dirent *de;
+	size_t dirnamelen;
+	DIR *d;
+
+	files_ref_path(refs, &path, dirname);
+
+	d = opendir(path.buf);
+	if (!d) {
+		strbuf_release(&path);
+		return;
+	}
+
+	strbuf_addstr(&refname, dirname);
+	dirnamelen = refname.len;
+
+	while ((de = readdir(d)) != NULL) {
+		unsigned char dtype;
+
+		if (de->d_name[0] == '.')
+			continue;
+		if (ends_with(de->d_name, ".lock"))
+			continue;
+		strbuf_addstr(&refname, de->d_name);
+
+		dtype = get_dtype(de, &path, 1);
+		if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) ||
+								is_headref(ref_store, de->d_name)))
+			loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
+
+		strbuf_setlen(&refname, dirnamelen);
+	}
+	strbuf_release(&refname);
+	strbuf_release(&path);
+	closedir(d);
+}
+
+static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
+					     unsigned int flags)
 {
 	if (!refs->loose) {
+		struct ref_dir *dir;
+
 		/*
 		 * Mark the top-level directory complete because we
 		 * are about to read the only subdirectory that can
@@ -317,12 +378,17 @@
 		/* We're going to fill the top level ourselves: */
 		refs->loose->root->flag &= ~REF_INCOMPLETE;
 
+		dir = get_ref_dir(refs->loose->root);
+
+		if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
+			add_pseudoref_and_head_entries(dir->cache->ref_store, dir,
+						       refs->loose->root->name);
+
 		/*
 		 * Add an incomplete entry for "refs/" (to be filled
 		 * lazily):
 		 */
-		add_entry_to_dir(get_ref_dir(refs->loose->root),
-				 create_dir_entry(refs->loose, "refs/", 5));
+		add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5));
 	}
 	return refs->loose;
 }
@@ -819,7 +885,8 @@
 
 static struct ref_iterator *files_ref_iterator_begin(
 		struct ref_store *ref_store,
-		const char *prefix, unsigned int flags)
+		const char *prefix, const char **exclude_patterns,
+		unsigned int flags)
 {
 	struct files_ref_store *refs;
 	struct ref_iterator *loose_iter, *packed_iter, *overlay_iter;
@@ -849,7 +916,7 @@
 	 * disk, and re-reads it if not.
 	 */
 
-	loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
+	loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags),
 					      prefix, ref_store->repo, 1);
 
 	/*
@@ -864,15 +931,14 @@
 	 * the packed and loose references.
 	 */
 	packed_iter = refs_ref_iterator_begin(
-			refs->packed_ref_store, prefix, 0,
+			refs->packed_ref_store, prefix, exclude_patterns, 0,
 			DO_FOR_EACH_INCLUDE_BROKEN);
 
 	overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
 
 	CALLOC_ARRAY(iter, 1);
 	ref_iterator = &iter->base;
-	base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
-			       overlay_iter->ordered);
+	base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
 	iter->iter0 = overlay_iter;
 	iter->repo = ref_store->repo;
 	iter->flags = flags;
@@ -1165,17 +1231,15 @@
  */
 static int should_pack_ref(const char *refname,
 			   const struct object_id *oid, unsigned int ref_flags,
-			   unsigned int pack_flags)
+			   struct pack_refs_opts *opts)
 {
+	struct string_list_item *item;
+
 	/* Do not pack per-worktree refs: */
 	if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
 	    REF_WORKTREE_SHARED)
 		return 0;
 
-	/* Do not pack non-tags unless PACK_REFS_ALL is set: */
-	if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/"))
-		return 0;
-
 	/* Do not pack symbolic refs: */
 	if (ref_flags & REF_ISSYMREF)
 		return 0;
@@ -1184,10 +1248,18 @@
 	if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
 		return 0;
 
-	return 1;
+	if (ref_excluded(opts->exclusions, refname))
+		return 0;
+
+	for_each_string_list_item(item, opts->includes)
+		if (!wildmatch(item->string, refname, 0))
+			return 1;
+
+	return 0;
 }
 
-static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
+static int files_pack_refs(struct ref_store *ref_store,
+			   struct pack_refs_opts *opts)
 {
 	struct files_ref_store *refs =
 		files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
@@ -1204,7 +1276,7 @@
 
 	packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
 
-	iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL,
+	iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
 					the_repository, 0);
 	while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 		/*
@@ -1212,8 +1284,7 @@
 		 * in the packed ref cache. If the reference should be
 		 * pruned, also add it to refs_to_prune.
 		 */
-		if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
-				     flags))
+		if (!should_pack_ref(iter->refname, iter->oid, iter->flags, opts))
 			continue;
 
 		/*
@@ -1227,7 +1298,7 @@
 			    iter->refname, err.buf);
 
 		/* Schedule the loose reference for pruning if requested. */
-		if ((flags & PACK_REFS_PRUNE)) {
+		if ((opts->flags & PACK_REFS_PRUNE)) {
 			struct ref_to_prune *n;
 			FLEX_ALLOC_STR(n, name, iter->refname);
 			oidcpy(&n->oid, iter->oid);
@@ -1250,54 +1321,6 @@
 	return 0;
 }
 
-static int files_delete_refs(struct ref_store *ref_store, const char *msg,
-			     struct string_list *refnames, unsigned int flags)
-{
-	struct files_ref_store *refs =
-		files_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
-	struct strbuf err = STRBUF_INIT;
-	int i, result = 0;
-
-	if (!refnames->nr)
-		return 0;
-
-	if (packed_refs_lock(refs->packed_ref_store, 0, &err))
-		goto error;
-
-	if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) {
-		packed_refs_unlock(refs->packed_ref_store);
-		goto error;
-	}
-
-	packed_refs_unlock(refs->packed_ref_store);
-
-	for (i = 0; i < refnames->nr; i++) {
-		const char *refname = refnames->items[i].string;
-
-		if (refs_delete_ref(&refs->base, msg, refname, NULL, flags))
-			result |= error(_("could not remove reference %s"), refname);
-	}
-
-	strbuf_release(&err);
-	return result;
-
-error:
-	/*
-	 * If we failed to rewrite the packed-refs file, then it is
-	 * unsafe to try to remove loose refs, because doing so might
-	 * expose an obsolete packed value for a reference that might
-	 * even point at an object that has been garbage collected.
-	 */
-	if (refnames->nr == 1)
-		error(_("could not delete reference %s: %s"),
-		      refnames->items[0].string, err.buf);
-	else
-		error(_("could not delete references: %s"), err.buf);
-
-	strbuf_release(&err);
-	return -1;
-}
-
 /*
  * People using contrib's git-new-workdir have .git/logs/refs ->
  * /some/other/path/.git/logs/refs, and that may live on another device.
@@ -2151,10 +2174,8 @@
 
 struct files_reflog_iterator {
 	struct ref_iterator base;
-
 	struct ref_store *ref_store;
 	struct dir_iterator *dir_iterator;
-	struct object_id oid;
 };
 
 static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
@@ -2165,25 +2186,13 @@
 	int ok;
 
 	while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
-		int flags;
-
 		if (!S_ISREG(diter->st.st_mode))
 			continue;
-		if (diter->basename[0] == '.')
+		if (check_refname_format(diter->basename,
+					 REFNAME_ALLOW_ONELEVEL))
 			continue;
-		if (ends_with(diter->basename, ".lock"))
-			continue;
-
-		if (!refs_resolve_ref_unsafe(iter->ref_store,
-					     diter->relative_path, 0,
-					     &iter->oid, &flags)) {
-			error("bad ref for %s", diter->path.buf);
-			continue;
-		}
 
 		iter->base.refname = diter->relative_path;
-		iter->base.oid = &iter->oid;
-		iter->base.flags = flags;
 		return ITER_OK;
 	}
 
@@ -2228,7 +2237,7 @@
 
 	strbuf_addf(&sb, "%s/logs", gitdir);
 
-	diter = dir_iterator_begin(sb.buf, 0);
+	diter = dir_iterator_begin(sb.buf, DIR_ITERATOR_SORTED);
 	if (!diter) {
 		strbuf_release(&sb);
 		return empty_ref_iterator_begin();
@@ -2237,7 +2246,7 @@
 	CALLOC_ARRAY(iter, 1);
 	ref_iterator = &iter->base;
 
-	base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
+	base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
 	iter->dir_iterator = diter;
 	iter->ref_store = ref_store;
 	strbuf_release(&sb);
@@ -2245,32 +2254,6 @@
 	return ref_iterator;
 }
 
-static enum iterator_selection reflog_iterator_select(
-	struct ref_iterator *iter_worktree,
-	struct ref_iterator *iter_common,
-	void *cb_data UNUSED)
-{
-	if (iter_worktree) {
-		/*
-		 * We're a bit loose here. We probably should ignore
-		 * common refs if they are accidentally added as
-		 * per-worktree refs.
-		 */
-		return ITER_SELECT_0;
-	} else if (iter_common) {
-		if (parse_worktree_ref(iter_common->refname, NULL, NULL,
-				       NULL) == REF_WORKTREE_SHARED)
-			return ITER_SELECT_1;
-
-		/*
-		 * The main ref store may contain main worktree's
-		 * per-worktree refs, which should be ignored
-		 */
-		return ITER_SKIP_1;
-	} else
-		return ITER_DONE;
-}
-
 static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
 {
 	struct files_ref_store *refs =
@@ -2281,9 +2264,9 @@
 		return reflog_iterator_begin(ref_store, refs->gitcommondir);
 	} else {
 		return merge_ref_iterator_begin(
-			0, reflog_iterator_begin(ref_store, refs->base.gitdir),
+			reflog_iterator_begin(ref_store, refs->base.gitdir),
 			reflog_iterator_begin(ref_store, refs->gitcommondir),
-			reflog_iterator_select, refs);
+			ref_iterator_select, refs);
 	}
 }
 
@@ -3253,28 +3236,52 @@
 	return -1;
 }
 
-static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
+static int files_init_db(struct ref_store *ref_store,
+			 int flags,
+			 struct strbuf *err UNUSED)
 {
 	struct files_ref_store *refs =
 		files_downcast(ref_store, REF_STORE_WRITE, "init_db");
 	struct strbuf sb = STRBUF_INIT;
 
 	/*
-	 * Create .git/refs/{heads,tags}
+	 * We need to create a "refs" dir in any case so that older versions of
+	 * Git can tell that this is a repository. This serves two main purposes:
+	 *
+	 * - Clients will know to stop walking the parent-directory chain when
+	 *   detecting the Git repository. Otherwise they may end up detecting
+	 *   a Git repository in a parent directory instead.
+	 *
+	 * - Instead of failing to detect a repository with unknown reference
+	 *   format altogether, old clients will print an error saying that
+	 *   they do not understand the reference format extension.
 	 */
-	files_ref_path(refs, &sb, "refs/heads");
+	strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
 	safe_create_dir(sb.buf, 1);
+	adjust_shared_perm(sb.buf);
 
-	strbuf_reset(&sb);
-	files_ref_path(refs, &sb, "refs/tags");
-	safe_create_dir(sb.buf, 1);
+	/*
+	 * There is no need to create directories for common refs when creating
+	 * a worktree ref store.
+	 */
+	if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
+		/*
+		 * Create .git/refs/{heads,tags}
+		 */
+		strbuf_reset(&sb);
+		files_ref_path(refs, &sb, "refs/heads");
+		safe_create_dir(sb.buf, 1);
+
+		strbuf_reset(&sb);
+		files_ref_path(refs, &sb, "refs/tags");
+		safe_create_dir(sb.buf, 1);
+	}
 
 	strbuf_release(&sb);
 	return 0;
 }
 
 struct ref_storage_be refs_be_files = {
-	.next = NULL,
 	.name = "files",
 	.init = files_ref_store_create,
 	.init_db = files_init_db,
@@ -3285,7 +3292,6 @@
 
 	.pack_refs = files_pack_refs,
 	.create_symref = files_create_symref,
-	.delete_refs = files_delete_refs,
 	.rename_ref = files_rename_ref,
 	.copy_ref = files_copy_ref,
 
diff --git a/refs/iterator.c b/refs/iterator.c
index c9fd0bc..9db8b05 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -3,7 +3,7 @@
  * documentation about the design and use of reference iterators.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #include "iterator.h"
@@ -25,11 +25,9 @@
 }
 
 void base_ref_iterator_init(struct ref_iterator *iter,
-			    struct ref_iterator_vtable *vtable,
-			    int ordered)
+			    struct ref_iterator_vtable *vtable)
 {
 	iter->vtable = vtable;
-	iter->ordered = !!ordered;
 	iter->refname = NULL;
 	iter->oid = NULL;
 	iter->flags = 0;
@@ -74,7 +72,7 @@
 	struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
 	struct ref_iterator *ref_iterator = &iter->base;
 
-	base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1);
+	base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable);
 	return ref_iterator;
 }
 
@@ -98,6 +96,49 @@
 	struct ref_iterator **current;
 };
 
+enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
+					    struct ref_iterator *iter_common,
+					    void *cb_data UNUSED)
+{
+	if (iter_worktree && !iter_common) {
+		/*
+		 * Return the worktree ref if there are no more common refs.
+		 */
+		return ITER_SELECT_0;
+	} else if (iter_common) {
+		/*
+		 * In case we have pending worktree and common refs we need to
+		 * yield them based on their lexicographical order. Worktree
+		 * refs that have the same name as common refs shadow the
+		 * latter.
+		 */
+		if (iter_worktree) {
+			int cmp = strcmp(iter_worktree->refname,
+					 iter_common->refname);
+			if (cmp < 0)
+				return ITER_SELECT_0;
+			else if (!cmp)
+				return ITER_SELECT_0_SKIP_1;
+		}
+
+		 /*
+		  * We now know that the lexicographically-next ref is a common
+		  * ref. When the common ref is a shared one we return it.
+		  */
+		if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+				       NULL) == REF_WORKTREE_SHARED)
+			return ITER_SELECT_1;
+
+		/*
+		 * Otherwise, if the common ref is a per-worktree ref we skip
+		 * it because it would belong to the main worktree, not ours.
+		 */
+		return ITER_SKIP_1;
+	} else {
+		return ITER_DONE;
+	}
+}
+
 static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
 {
 	struct merge_ref_iterator *iter =
@@ -207,7 +248,6 @@
 };
 
 struct ref_iterator *merge_ref_iterator_begin(
-		int ordered,
 		struct ref_iterator *iter0, struct ref_iterator *iter1,
 		ref_iterator_select_fn *select, void *cb_data)
 {
@@ -222,7 +262,7 @@
 	 * references through only if they exist in both iterators.
 	 */
 
-	base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered);
+	base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
 	iter->iter0 = iter0;
 	iter->iter1 = iter1;
 	iter->select = select;
@@ -271,12 +311,9 @@
 	} else if (is_empty_ref_iterator(back)) {
 		ref_iterator_abort(back);
 		return front;
-	} else if (!front->ordered || !back->ordered) {
-		BUG("overlay_ref_iterator requires ordered inputs");
 	}
 
-	return merge_ref_iterator_begin(1, front, back,
-					overlay_iterator_select, NULL);
+	return merge_ref_iterator_begin(front, back, overlay_iterator_select, NULL);
 }
 
 struct prefix_ref_iterator {
@@ -315,16 +352,12 @@
 
 		if (cmp > 0) {
 			/*
-			 * If the source iterator is ordered, then we
+			 * As the source iterator is ordered, we
 			 * can stop the iteration as soon as we see a
 			 * refname that comes after the prefix:
 			 */
-			if (iter->iter0->ordered) {
-				ok = ref_iterator_abort(iter->iter0);
-				break;
-			} else {
-				continue;
-			}
+			ok = ref_iterator_abort(iter->iter0);
+			break;
 		}
 
 		if (iter->trim) {
@@ -396,7 +429,7 @@
 	CALLOC_ARRAY(iter, 1);
 	ref_iterator = &iter->base;
 
-	base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered);
+	base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable);
 
 	iter->iter0 = iter0;
 	iter->prefix = xstrdup(prefix);
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 6f5a070..4e826c0 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,11 +1,18 @@
-#include "../cache.h"
+#include "../git-compat-util.h"
 #include "../config.h"
+#include "../gettext.h"
+#include "../hash.h"
+#include "../hex.h"
 #include "../refs.h"
 #include "refs-internal.h"
 #include "packed-backend.h"
 #include "../iterator.h"
 #include "../lockfile.h"
 #include "../chdir-notify.h"
+#include "../statinfo.h"
+#include "../wrapper.h"
+#include "../write-or-die.h"
+#include "../trace2.h"
 
 enum mmap_strategy {
 	/*
@@ -297,7 +304,8 @@
  * Compare a snapshot record at `rec` to the specified NUL-terminated
  * refname.
  */
-static int cmp_record_to_refname(const char *rec, const char *refname)
+static int cmp_record_to_refname(const char *rec, const char *refname,
+				 int start)
 {
 	const char *r1 = rec + the_hash_algo->hexsz + 1;
 	const char *r2 = refname;
@@ -306,7 +314,7 @@
 		if (*r1 == '\n')
 			return *r2 ? -1 : 0;
 		if (!*r2)
-			return 1;
+			return start ? 1 : -1;
 		if (*r1 != *r2)
 			return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
 		r1++;
@@ -521,22 +529,9 @@
 	return 1;
 }
 
-/*
- * Find the place in `snapshot->buf` where the start of the record for
- * `refname` starts. If `mustexist` is true and the reference doesn't
- * exist, then return NULL. If `mustexist` is false and the reference
- * doesn't exist, then return the point where that reference would be
- * inserted, or `snapshot->eof` (which might be NULL) if it would be
- * inserted at the end of the file. In the latter mode, `refname`
- * doesn't have to be a proper reference name; for example, one could
- * search for "refs/replace/" to find the start of any replace
- * references.
- *
- * The record is sought using a binary search, so `snapshot->buf` must
- * be sorted.
- */
-static const char *find_reference_location(struct snapshot *snapshot,
-					   const char *refname, int mustexist)
+static const char *find_reference_location_1(struct snapshot *snapshot,
+					     const char *refname, int mustexist,
+					     int start)
 {
 	/*
 	 * This is not *quite* a garden-variety binary search, because
@@ -566,7 +561,7 @@
 
 		mid = lo + (hi - lo) / 2;
 		rec = find_start_of_record(lo, mid);
-		cmp = cmp_record_to_refname(rec, refname);
+		cmp = cmp_record_to_refname(rec, refname, start);
 		if (cmp < 0) {
 			lo = find_end_of_record(mid, hi);
 		} else if (cmp > 0) {
@@ -583,6 +578,41 @@
 }
 
 /*
+ * Find the place in `snapshot->buf` where the start of the record for
+ * `refname` starts. If `mustexist` is true and the reference doesn't
+ * exist, then return NULL. If `mustexist` is false and the reference
+ * doesn't exist, then return the point where that reference would be
+ * inserted, or `snapshot->eof` (which might be NULL) if it would be
+ * inserted at the end of the file. In the latter mode, `refname`
+ * doesn't have to be a proper reference name; for example, one could
+ * search for "refs/replace/" to find the start of any replace
+ * references.
+ *
+ * The record is sought using a binary search, so `snapshot->buf` must
+ * be sorted.
+ */
+static const char *find_reference_location(struct snapshot *snapshot,
+					   const char *refname, int mustexist)
+{
+	return find_reference_location_1(snapshot, refname, mustexist, 1);
+}
+
+/*
+ * Find the place in `snapshot->buf` after the end of the record for
+ * `refname`. In other words, find the location of first thing *after*
+ * `refname`.
+ *
+ * Other semantics are identical to the ones in
+ * `find_reference_location()`.
+ */
+static const char *find_reference_location_end(struct snapshot *snapshot,
+					       const char *refname,
+					       int mustexist)
+{
+	return find_reference_location_1(snapshot, refname, mustexist, 0);
+}
+
+/*
  * Create a newly-allocated `snapshot` of the `packed-refs` file in
  * its current state and return it. The return value will already have
  * its reference count incremented.
@@ -646,7 +676,7 @@
 					 snapshot->buf,
 					 snapshot->eof - snapshot->buf);
 
-		string_list_split_in_place(&traits, p, ' ', -1);
+		string_list_split_in_place(&traits, p, " ", -1);
 
 		if (unsorted_string_list_has_string(&traits, "fully-peeled"))
 			snapshot->peeled = PEELED_FULLY;
@@ -773,6 +803,13 @@
 	/* The end of the part of the buffer that will be iterated over: */
 	const char *eof;
 
+	struct jump_list_entry {
+		const char *start;
+		const char *end;
+	} *jump;
+	size_t jump_nr, jump_alloc;
+	size_t jump_cur;
+
 	/* Scratch space for current values: */
 	struct object_id oid, peeled;
 	struct strbuf refname_buf;
@@ -790,14 +827,36 @@
  */
 static int next_record(struct packed_ref_iterator *iter)
 {
-	const char *p = iter->pos, *eol;
+	const char *p, *eol;
 
 	strbuf_reset(&iter->refname_buf);
 
+	/*
+	 * If iter->pos is contained within a skipped region, jump past
+	 * it.
+	 *
+	 * Note that each skipped region is considered at most once,
+	 * since they are ordered based on their starting position.
+	 */
+	while (iter->jump_cur < iter->jump_nr) {
+		struct jump_list_entry *curr = &iter->jump[iter->jump_cur];
+		if (iter->pos < curr->start)
+			break; /* not to the next jump yet */
+
+		iter->jump_cur++;
+		if (iter->pos < curr->end) {
+			iter->pos = curr->end;
+			trace2_counter_add(TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, 1);
+			/* jumps are coalesced, so only one jump is necessary */
+			break;
+		}
+	}
+
 	if (iter->pos == iter->eof)
 		return ITER_DONE;
 
 	iter->base.flags = REF_ISPACKED;
+	p = iter->pos;
 
 	if (iter->eof - p < the_hash_algo->hexsz + 2 ||
 	    parse_oid_hex(p, &iter->oid, &p) ||
@@ -905,6 +964,7 @@
 	int ok = ITER_DONE;
 
 	strbuf_release(&iter->refname_buf);
+	free(iter->jump);
 	release_snapshot(iter->snapshot);
 	base_ref_iterator_free(ref_iterator);
 	return ok;
@@ -916,9 +976,112 @@
 	.abort = packed_ref_iterator_abort
 };
 
+static int jump_list_entry_cmp(const void *va, const void *vb)
+{
+	const struct jump_list_entry *a = va;
+	const struct jump_list_entry *b = vb;
+
+	if (a->start < b->start)
+		return -1;
+	if (a->start > b->start)
+		return 1;
+	return 0;
+}
+
+static int has_glob_special(const char *str)
+{
+	const char *p;
+	for (p = str; *p; p++) {
+		if (is_glob_special(*p))
+			return 1;
+	}
+	return 0;
+}
+
+static void populate_excluded_jump_list(struct packed_ref_iterator *iter,
+					struct snapshot *snapshot,
+					const char **excluded_patterns)
+{
+	size_t i, j;
+	const char **pattern;
+	struct jump_list_entry *last_disjoint;
+
+	if (!excluded_patterns)
+		return;
+
+	for (pattern = excluded_patterns; *pattern; pattern++) {
+		struct jump_list_entry *e;
+		const char *start, *end;
+
+		/*
+		 * We can't feed any excludes with globs in them to the
+		 * refs machinery.  It only understands prefix matching.
+		 * We likewise can't even feed the string leading up to
+		 * the first meta-character, as something like "foo[a]"
+		 * should not exclude "foobar" (but the prefix "foo"
+		 * would match that and mark it for exclusion).
+		 */
+		if (has_glob_special(*pattern))
+			continue;
+
+		start = find_reference_location(snapshot, *pattern, 0);
+		end = find_reference_location_end(snapshot, *pattern, 0);
+
+		if (start == end)
+			continue; /* nothing to jump over */
+
+		ALLOC_GROW(iter->jump, iter->jump_nr + 1, iter->jump_alloc);
+
+		e = &iter->jump[iter->jump_nr++];
+		e->start = start;
+		e->end = end;
+	}
+
+	if (!iter->jump_nr) {
+		/*
+		 * Every entry in exclude_patterns has a meta-character,
+		 * nothing to do here.
+		 */
+		return;
+	}
+
+	QSORT(iter->jump, iter->jump_nr, jump_list_entry_cmp);
+
+	/*
+	 * As an optimization, merge adjacent entries in the jump list
+	 * to jump forwards as far as possible when entering a skipped
+	 * region.
+	 *
+	 * For example, if we have two skipped regions:
+	 *
+	 *	[[A, B], [B, C]]
+	 *
+	 * we want to combine that into a single entry jumping from A to
+	 * C.
+	 */
+	last_disjoint = iter->jump;
+
+	for (i = 1, j = 1; i < iter->jump_nr; i++) {
+		struct jump_list_entry *ours = &iter->jump[i];
+		if (ours->start <= last_disjoint->end) {
+			/* overlapping regions extend the previous one */
+			last_disjoint->end = last_disjoint->end > ours->end
+				? last_disjoint->end : ours->end;
+		} else {
+			/* otherwise, insert a new region */
+			iter->jump[j++] = *ours;
+			last_disjoint = ours;
+		}
+	}
+
+	iter->jump_nr = j;
+	iter->jump_cur = 0;
+}
+
 static struct ref_iterator *packed_ref_iterator_begin(
 		struct ref_store *ref_store,
-		const char *prefix, unsigned int flags)
+		const char *prefix, const char **exclude_patterns,
+		unsigned int flags)
 {
 	struct packed_ref_store *refs;
 	struct snapshot *snapshot;
@@ -948,7 +1111,10 @@
 
 	CALLOC_ARRAY(iter, 1);
 	ref_iterator = &iter->base;
-	base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
+	base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
+
+	if (exclude_patterns)
+		populate_excluded_jump_list(iter, snapshot, exclude_patterns);
 
 	iter->snapshot = snapshot;
 	acquire_snapshot(snapshot);
@@ -1079,6 +1245,7 @@
 	"# pack-refs with: peeled fully-peeled sorted \n";
 
 static int packed_init_db(struct ref_store *ref_store UNUSED,
+			  int flags UNUSED,
 			  struct strbuf *err UNUSED)
 {
 	/* Nothing to do. */
@@ -1143,7 +1310,7 @@
 	 * list of refs is exhausted, set iter to NULL. When the list
 	 * of updates is exhausted, leave i set to updates->nr.
 	 */
-	iter = packed_ref_iterator_begin(&refs->base, "",
+	iter = packed_ref_iterator_begin(&refs->base, "", NULL,
 					 DO_FOR_EACH_INCLUDE_BROKEN);
 	if ((ok = ref_iterator_advance(iter)) != ITER_OK)
 		iter = NULL;
@@ -1521,57 +1688,8 @@
 	return ref_transaction_commit(transaction, err);
 }
 
-static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
-			     struct string_list *refnames, unsigned int flags)
-{
-	struct packed_ref_store *refs =
-		packed_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
-	struct strbuf err = STRBUF_INIT;
-	struct ref_transaction *transaction;
-	struct string_list_item *item;
-	int ret;
-
-	(void)refs; /* We need the check above, but don't use the variable */
-
-	if (!refnames->nr)
-		return 0;
-
-	/*
-	 * Since we don't check the references' old_oids, the
-	 * individual updates can't fail, so we can pack all of the
-	 * updates into a single transaction.
-	 */
-
-	transaction = ref_store_transaction_begin(ref_store, &err);
-	if (!transaction)
-		return -1;
-
-	for_each_string_list_item(item, refnames) {
-		if (ref_transaction_delete(transaction, item->string, NULL,
-					   flags, msg, &err)) {
-			warning(_("could not delete reference %s: %s"),
-				item->string, err.buf);
-			strbuf_reset(&err);
-		}
-	}
-
-	ret = ref_transaction_commit(transaction, &err);
-
-	if (ret) {
-		if (refnames->nr == 1)
-			error(_("could not delete reference %s: %s"),
-			      refnames->items[0].string, err.buf);
-		else
-			error(_("could not delete references: %s"), err.buf);
-	}
-
-	ref_transaction_free(transaction);
-	strbuf_release(&err);
-	return ret;
-}
-
 static int packed_pack_refs(struct ref_store *ref_store UNUSED,
-			    unsigned int flags UNUSED)
+			    struct pack_refs_opts *pack_opts UNUSED)
 {
 	/*
 	 * Packed refs are already packed. It might be that loose refs
@@ -1587,7 +1705,6 @@
 }
 
 struct ref_storage_be refs_be_packed = {
-	.next = NULL,
 	.name = "packed",
 	.init = packed_ref_store_create,
 	.init_db = packed_init_db,
@@ -1598,7 +1715,6 @@
 
 	.pack_refs = packed_pack_refs,
 	.create_symref = NULL,
-	.delete_refs = packed_delete_refs,
 	.rename_ref = NULL,
 	.copy_ref = NULL,
 
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 32afd8a..9f97972 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -1,5 +1,7 @@
-#include "../cache.h"
+#include "../git-compat-util.h"
+#include "../hash.h"
 #include "../refs.h"
+#include "../repository.h"
 #include "refs-internal.h"
 #include "ref-cache.h"
 #include "../iterator.h"
@@ -409,7 +411,8 @@
 
 		if (level->prefix_state == PREFIX_WITHIN_DIR) {
 			entry_prefix_state = overlaps_prefix(entry->name, iter->prefix);
-			if (entry_prefix_state == PREFIX_EXCLUDES_DIR)
+			if (entry_prefix_state == PREFIX_EXCLUDES_DIR ||
+			    (entry_prefix_state == PREFIX_WITHIN_DIR && !(entry->flag & REF_DIR)))
 				continue;
 		} else {
 			entry_prefix_state = level->prefix_state;
@@ -483,7 +486,7 @@
 
 	CALLOC_ARRAY(iter, 1);
 	ref_iterator = &iter->base;
-	base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1);
+	base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
 	ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
 
 	iter->levels_nr = 1;
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index 850d9d3..95c76e2 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -1,10 +1,11 @@
 #ifndef REFS_REF_CACHE_H
 #define REFS_REF_CACHE_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct ref_dir;
 struct ref_store;
+struct repository;
 
 /*
  * If this ref_cache is filled lazily, this function is used to load
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 69f93b0..56641aa 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -1,7 +1,6 @@
 #ifndef REFS_REFS_INTERNAL_H
 #define REFS_REFS_INTERNAL_H
 
-#include "cache.h"
 #include "refs.h"
 #include "iterator.h"
 
@@ -261,6 +260,12 @@
 	 * INCLUDE_BROKEN, since they are otherwise not included at all.
 	 */
 	DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
+
+	/*
+	 * Include root refs i.e. HEAD and pseudorefs along with the regular
+	 * refs.
+	 */
+	DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
 };
 
 /*
@@ -313,13 +318,6 @@
  */
 struct ref_iterator {
 	struct ref_iterator_vtable *vtable;
-
-	/*
-	 * Does this `ref_iterator` iterate over references in order
-	 * by refname?
-	 */
-	unsigned int ordered : 1;
-
 	const char *refname;
 	const struct object_id *oid;
 	unsigned int flags;
@@ -368,8 +366,8 @@
  */
 struct ref_iterator *refs_ref_iterator_begin(
 		struct ref_store *refs,
-		const char *prefix, int trim,
-		enum do_for_each_ref_flags flags);
+		const char *prefix, const char **exclude_patterns,
+		int trim, enum do_for_each_ref_flags flags);
 
 /*
  * A callback function used to instruct merge_ref_iterator how to
@@ -388,14 +386,21 @@
 		void *cb_data);
 
 /*
+ * An implementation of ref_iterator_select_fn that merges worktree and common
+ * refs. Per-worktree refs from the common iterator are ignored, worktree refs
+ * override common refs. Refs are selected lexicographically.
+ */
+enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
+					    struct ref_iterator *iter_common,
+					    void *cb_data);
+
+/*
  * Iterate over the entries from iter0 and iter1, with the values
  * interleaved as directed by the select function. The iterator takes
  * ownership of iter0 and iter1 and frees them when the iteration is
- * over. A derived class should set `ordered` to 1 or 0 based on
- * whether it generates its output in order by reference name.
+ * over.
  */
 struct ref_iterator *merge_ref_iterator_begin(
-		int ordered,
 		struct ref_iterator *iter0, struct ref_iterator *iter1,
 		ref_iterator_select_fn *select, void *cb_data);
 
@@ -424,8 +429,6 @@
  * As an convenience to callers, if prefix is the empty string and
  * trim is zero, this function returns iter0 directly, without
  * wrapping it.
- *
- * The resulting ref_iterator is ordered if iter0 is.
  */
 struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 					       const char *prefix,
@@ -436,14 +439,11 @@
 /*
  * Base class constructor for ref_iterators. Initialize the
  * ref_iterator part of iter, setting its vtable pointer as specified.
- * `ordered` should be set to 1 if the iterator will iterate over
- * references in order by refname; otherwise it should be set to 0.
  * This is meant to be called only by the initializers of derived
  * classes.
  */
 void base_ref_iterator_init(struct ref_iterator *iter,
-			    struct ref_iterator_vtable *vtable,
-			    int ordered);
+			    struct ref_iterator_vtable *vtable);
 
 /*
  * Base class destructor for ref_iterators. Destroy the ref_iterator
@@ -530,7 +530,9 @@
 					    const char *gitdir,
 					    unsigned int flags);
 
-typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
+typedef int ref_init_db_fn(struct ref_store *refs,
+			   int flags,
+			   struct strbuf *err);
 
 typedef int ref_transaction_prepare_fn(struct ref_store *refs,
 				       struct ref_transaction *transaction,
@@ -548,13 +550,12 @@
 				      struct ref_transaction *transaction,
 				      struct strbuf *err);
 
-typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
+typedef int pack_refs_fn(struct ref_store *ref_store,
+			 struct pack_refs_opts *opts);
 typedef int create_symref_fn(struct ref_store *ref_store,
 			     const char *ref_target,
 			     const char *refs_heads_master,
 			     const char *logmsg);
-typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
-			   struct string_list *refnames, unsigned int flags);
 typedef int rename_ref_fn(struct ref_store *ref_store,
 			  const char *oldref, const char *newref,
 			  const char *logmsg);
@@ -571,7 +572,8 @@
  */
 typedef struct ref_iterator *ref_iterator_begin_fn(
 		struct ref_store *ref_store,
-		const char *prefix, unsigned int flags);
+		const char *prefix, const char **exclude_patterns,
+		unsigned int flags);
 
 /* reflog functions */
 
@@ -664,7 +666,6 @@
 				 struct strbuf *referent);
 
 struct ref_storage_be {
-	struct ref_storage_be *next;
 	const char *name;
 	ref_store_init_fn *init;
 	ref_init_db_fn *init_db;
@@ -676,7 +677,6 @@
 
 	pack_refs_fn *pack_refs;
 	create_symref_fn *create_symref;
-	delete_refs_fn *delete_refs;
 	rename_ref_fn *rename_ref;
 	copy_ref_fn *copy_ref;
 
@@ -694,6 +694,7 @@
 };
 
 extern struct ref_storage_be refs_be_files;
+extern struct ref_storage_be refs_be_reftable;
 extern struct ref_storage_be refs_be_packed;
 
 /*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
new file mode 100644
index 0000000..0bed6d2
--- /dev/null
+++ b/refs/reftable-backend.c
@@ -0,0 +1,2239 @@
+#include "../git-compat-util.h"
+#include "../abspath.h"
+#include "../chdir-notify.h"
+#include "../environment.h"
+#include "../gettext.h"
+#include "../hash.h"
+#include "../hex.h"
+#include "../iterator.h"
+#include "../ident.h"
+#include "../lockfile.h"
+#include "../object.h"
+#include "../path.h"
+#include "../refs.h"
+#include "../reftable/reftable-stack.h"
+#include "../reftable/reftable-record.h"
+#include "../reftable/reftable-error.h"
+#include "../reftable/reftable-iterator.h"
+#include "../reftable/reftable-merged.h"
+#include "../setup.h"
+#include "../strmap.h"
+#include "refs-internal.h"
+
+/*
+ * Used as a flag in ref_update::flags when the ref_update was via an
+ * update to HEAD.
+ */
+#define REF_UPDATE_VIA_HEAD (1 << 8)
+
+struct reftable_ref_store {
+	struct ref_store base;
+
+	/*
+	 * The main stack refers to the common dir and thus contains common
+	 * refs as well as refs of the main repository.
+	 */
+	struct reftable_stack *main_stack;
+	/*
+	 * The worktree stack refers to the gitdir in case the refdb is opened
+	 * via a worktree. It thus contains the per-worktree refs.
+	 */
+	struct reftable_stack *worktree_stack;
+	/*
+	 * Map of worktree stacks by their respective worktree names. The map
+	 * is populated lazily when we try to resolve `worktrees/$worktree` refs.
+	 */
+	struct strmap worktree_stacks;
+	struct reftable_write_options write_options;
+
+	unsigned int store_flags;
+	int err;
+};
+
+/*
+ * Downcast ref_store to reftable_ref_store. Die if ref_store is not a
+ * reftable_ref_store. required_flags is compared with ref_store's store_flags
+ * to ensure the ref_store has all required capabilities. "caller" is used in
+ * any necessary error messages.
+ */
+static struct reftable_ref_store *reftable_be_downcast(struct ref_store *ref_store,
+						       unsigned int required_flags,
+						       const char *caller)
+{
+	struct reftable_ref_store *refs;
+
+	if (ref_store->be != &refs_be_reftable)
+		BUG("ref_store is type \"%s\" not \"reftables\" in %s",
+		    ref_store->be->name, caller);
+
+	refs = (struct reftable_ref_store *)ref_store;
+
+	if ((refs->store_flags & required_flags) != required_flags)
+		BUG("operation %s requires abilities 0x%x, but only have 0x%x",
+		    caller, required_flags, refs->store_flags);
+
+	return refs;
+}
+
+/*
+ * Some refs are global to the repository (refs/heads/{*}), while others are
+ * local to the worktree (eg. HEAD, refs/bisect/{*}). We solve this by having
+ * multiple separate databases (ie. multiple reftable/ directories), one for
+ * the shared refs, one for the current worktree refs, and one for each
+ * additional worktree. For reading, we merge the view of both the shared and
+ * the current worktree's refs, when necessary.
+ *
+ * This function also optionally assigns the rewritten reference name that is
+ * local to the stack. This translation is required when using worktree refs
+ * like `worktrees/$worktree/refs/heads/foo` as worktree stacks will store
+ * those references in their normalized form.
+ */
+static struct reftable_stack *stack_for(struct reftable_ref_store *store,
+					const char *refname,
+					const char **rewritten_ref)
+{
+	const char *wtname;
+	int wtname_len;
+
+	if (!refname)
+		return store->main_stack;
+
+	switch (parse_worktree_ref(refname, &wtname, &wtname_len, rewritten_ref)) {
+	case REF_WORKTREE_OTHER: {
+		static struct strbuf wtname_buf = STRBUF_INIT;
+		struct strbuf wt_dir = STRBUF_INIT;
+		struct reftable_stack *stack;
+
+		/*
+		 * We're using a static buffer here so that we don't need to
+		 * allocate the worktree name whenever we look up a reference.
+		 * This could be avoided if the strmap interface knew how to
+		 * handle keys with a length.
+		 */
+		strbuf_reset(&wtname_buf);
+		strbuf_add(&wtname_buf, wtname, wtname_len);
+
+		/*
+		 * There is an edge case here: when the worktree references the
+		 * current worktree, then we set up the stack once via
+		 * `worktree_stacks` and once via `worktree_stack`. This is
+		 * wasteful, but in the reading case it shouldn't matter. And
+		 * in the writing case we would notice that the stack is locked
+		 * already and error out when trying to write a reference via
+		 * both stacks.
+		 */
+		stack = strmap_get(&store->worktree_stacks, wtname_buf.buf);
+		if (!stack) {
+			strbuf_addf(&wt_dir, "%s/worktrees/%s/reftable",
+				    store->base.repo->commondir, wtname_buf.buf);
+
+			store->err = reftable_new_stack(&stack, wt_dir.buf,
+							store->write_options);
+			assert(store->err != REFTABLE_API_ERROR);
+			strmap_put(&store->worktree_stacks, wtname_buf.buf, stack);
+		}
+
+		strbuf_release(&wt_dir);
+		return stack;
+	}
+	case REF_WORKTREE_CURRENT:
+		/*
+		 * If there is no worktree stack then we're currently in the
+		 * main worktree. We thus return the main stack in that case.
+		 */
+		if (!store->worktree_stack)
+			return store->main_stack;
+		return store->worktree_stack;
+	case REF_WORKTREE_MAIN:
+	case REF_WORKTREE_SHARED:
+		return store->main_stack;
+	default:
+		BUG("unhandled worktree reference type");
+	}
+}
+
+static int should_write_log(struct ref_store *refs, const char *refname)
+{
+	if (log_all_ref_updates == LOG_REFS_UNSET)
+		log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
+
+	switch (log_all_ref_updates) {
+	case LOG_REFS_NONE:
+		return refs_reflog_exists(refs, refname);
+	case LOG_REFS_ALWAYS:
+		return 1;
+	case LOG_REFS_NORMAL:
+		if (should_autocreate_reflog(refname))
+			return 1;
+		return refs_reflog_exists(refs, refname);
+	default:
+		BUG("unhandled core.logAllRefUpdates value %d", log_all_ref_updates);
+	}
+}
+
+static void fill_reftable_log_record(struct reftable_log_record *log)
+{
+	const char *info = git_committer_info(0);
+	struct ident_split split = {0};
+	int sign = 1;
+
+	if (split_ident_line(&split, info, strlen(info)))
+		BUG("failed splitting committer info");
+
+	reftable_log_record_release(log);
+	log->value_type = REFTABLE_LOG_UPDATE;
+	log->value.update.name =
+		xstrndup(split.name_begin, split.name_end - split.name_begin);
+	log->value.update.email =
+		xstrndup(split.mail_begin, split.mail_end - split.mail_begin);
+	log->value.update.time = atol(split.date_begin);
+	if (*split.tz_begin == '-') {
+		sign = -1;
+		split.tz_begin++;
+	}
+	if (*split.tz_begin == '+') {
+		sign = 1;
+		split.tz_begin++;
+	}
+
+	log->value.update.tz_offset = sign * atoi(split.tz_begin);
+}
+
+static int read_ref_without_reload(struct reftable_stack *stack,
+				   const char *refname,
+				   struct object_id *oid,
+				   struct strbuf *referent,
+				   unsigned int *type)
+{
+	struct reftable_ref_record ref = {0};
+	int ret;
+
+	ret = reftable_stack_read_ref(stack, refname, &ref);
+	if (ret)
+		goto done;
+
+	if (ref.value_type == REFTABLE_REF_SYMREF) {
+		strbuf_reset(referent);
+		strbuf_addstr(referent, ref.value.symref);
+		*type |= REF_ISSYMREF;
+	} else if (reftable_ref_record_val1(&ref)) {
+		oidread(oid, reftable_ref_record_val1(&ref));
+	} else {
+		/* We got a tombstone, which should not happen. */
+		BUG("unhandled reference value type %d", ref.value_type);
+	}
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	reftable_ref_record_release(&ref);
+	return ret;
+}
+
+static struct ref_store *reftable_be_init(struct repository *repo,
+					  const char *gitdir,
+					  unsigned int store_flags)
+{
+	struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
+	struct strbuf path = STRBUF_INIT;
+	int is_worktree;
+	mode_t mask;
+
+	mask = umask(0);
+	umask(mask);
+
+	base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable);
+	strmap_init(&refs->worktree_stacks);
+	refs->store_flags = store_flags;
+	refs->write_options.block_size = 4096;
+	refs->write_options.hash_id = repo->hash_algo->format_id;
+	refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
+
+	/*
+	 * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
+	 * This stack contains both the shared and the main worktree refs.
+	 *
+	 * Note that we don't try to resolve the path in case we have a
+	 * worktree because `get_common_dir_noenv()` already does it for us.
+	 */
+	is_worktree = get_common_dir_noenv(&path, gitdir);
+	if (!is_worktree) {
+		strbuf_reset(&path);
+		strbuf_realpath(&path, gitdir, 0);
+	}
+	strbuf_addstr(&path, "/reftable");
+	refs->err = reftable_new_stack(&refs->main_stack, path.buf,
+				       refs->write_options);
+	if (refs->err)
+		goto done;
+
+	/*
+	 * If we're in a worktree we also need to set up the worktree reftable
+	 * stack that is contained in the per-worktree GIT_DIR.
+	 *
+	 * Ideally, we would also add the stack to our worktree stack map. But
+	 * we have no way to figure out the worktree name here and thus can't
+	 * do it efficiently.
+	 */
+	if (is_worktree) {
+		strbuf_reset(&path);
+		strbuf_addf(&path, "%s/reftable", gitdir);
+
+		refs->err = reftable_new_stack(&refs->worktree_stack, path.buf,
+					       refs->write_options);
+		if (refs->err)
+			goto done;
+	}
+
+	chdir_notify_reparent("reftables-backend $GIT_DIR", &refs->base.gitdir);
+
+done:
+	assert(refs->err != REFTABLE_API_ERROR);
+	strbuf_release(&path);
+	return &refs->base;
+}
+
+static int reftable_be_init_db(struct ref_store *ref_store,
+			       int flags UNUSED,
+			       struct strbuf *err UNUSED)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "init_db");
+	struct strbuf sb = STRBUF_INIT;
+
+	strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
+	safe_create_dir(sb.buf, 1);
+	strbuf_reset(&sb);
+
+	strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
+	write_file(sb.buf, "ref: refs/heads/.invalid");
+	adjust_shared_perm(sb.buf);
+	strbuf_reset(&sb);
+
+	strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+	safe_create_dir(sb.buf, 1);
+	strbuf_reset(&sb);
+
+	strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
+	write_file(sb.buf, "this repository uses the reftable format");
+	adjust_shared_perm(sb.buf);
+
+	strbuf_release(&sb);
+	return 0;
+}
+
+struct reftable_ref_iterator {
+	struct ref_iterator base;
+	struct reftable_ref_store *refs;
+	struct reftable_iterator iter;
+	struct reftable_ref_record ref;
+	struct object_id oid;
+
+	const char *prefix;
+	size_t prefix_len;
+	unsigned int flags;
+	int err;
+};
+
+static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
+{
+	struct reftable_ref_iterator *iter =
+		(struct reftable_ref_iterator *)ref_iterator;
+	struct reftable_ref_store *refs = iter->refs;
+
+	while (!iter->err) {
+		int flags = 0;
+
+		iter->err = reftable_iterator_next_ref(&iter->iter, &iter->ref);
+		if (iter->err)
+			break;
+
+		/*
+		 * The files backend only lists references contained in "refs/" unless
+		 * the root refs are to be included. We emulate the same behaviour here.
+		 */
+		if (!starts_with(iter->ref.refname, "refs/") &&
+		    !(iter->flags & DO_FOR_EACH_INCLUDE_ROOT_REFS &&
+		     (is_pseudoref(&iter->refs->base, iter->ref.refname) ||
+		      is_headref(&iter->refs->base, iter->ref.refname)))) {
+			continue;
+		}
+
+		if (iter->prefix_len &&
+		    strncmp(iter->prefix, iter->ref.refname, iter->prefix_len)) {
+			iter->err = 1;
+			break;
+		}
+
+		if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+		    parse_worktree_ref(iter->ref.refname, NULL, NULL, NULL) !=
+			    REF_WORKTREE_CURRENT)
+			continue;
+
+		switch (iter->ref.value_type) {
+		case REFTABLE_REF_VAL1:
+			oidread(&iter->oid, iter->ref.value.val1);
+			break;
+		case REFTABLE_REF_VAL2:
+			oidread(&iter->oid, iter->ref.value.val2.value);
+			break;
+		case REFTABLE_REF_SYMREF:
+			if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname,
+						     RESOLVE_REF_READING, &iter->oid, &flags))
+				oidclr(&iter->oid);
+			break;
+		default:
+			BUG("unhandled reference value type %d", iter->ref.value_type);
+		}
+
+		if (is_null_oid(&iter->oid))
+			flags |= REF_ISBROKEN;
+
+		if (check_refname_format(iter->ref.refname, REFNAME_ALLOW_ONELEVEL)) {
+			if (!refname_is_safe(iter->ref.refname))
+				die(_("refname is dangerous: %s"), iter->ref.refname);
+			oidclr(&iter->oid);
+			flags |= REF_BAD_NAME | REF_ISBROKEN;
+		}
+
+		if (iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS &&
+		    flags & REF_ISSYMREF &&
+		    flags & REF_ISBROKEN)
+			continue;
+
+		if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+		    !ref_resolves_to_object(iter->ref.refname, refs->base.repo,
+					    &iter->oid, flags))
+				continue;
+
+		iter->base.refname = iter->ref.refname;
+		iter->base.oid = &iter->oid;
+		iter->base.flags = flags;
+
+		break;
+	}
+
+	if (iter->err > 0) {
+		if (ref_iterator_abort(ref_iterator) != ITER_DONE)
+			return ITER_ERROR;
+		return ITER_DONE;
+	}
+
+	if (iter->err < 0) {
+		ref_iterator_abort(ref_iterator);
+		return ITER_ERROR;
+	}
+
+	return ITER_OK;
+}
+
+static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
+				      struct object_id *peeled)
+{
+	struct reftable_ref_iterator *iter =
+		(struct reftable_ref_iterator *)ref_iterator;
+
+	if (iter->ref.value_type == REFTABLE_REF_VAL2) {
+		oidread(peeled, iter->ref.value.val2.target_value);
+		return 0;
+	}
+
+	return -1;
+}
+
+static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
+{
+	struct reftable_ref_iterator *iter =
+		(struct reftable_ref_iterator *)ref_iterator;
+	reftable_ref_record_release(&iter->ref);
+	reftable_iterator_destroy(&iter->iter);
+	free(iter);
+	return ITER_DONE;
+}
+
+static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
+	.advance = reftable_ref_iterator_advance,
+	.peel = reftable_ref_iterator_peel,
+	.abort = reftable_ref_iterator_abort
+};
+
+static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_store *refs,
+							    struct reftable_stack *stack,
+							    const char *prefix,
+							    int flags)
+{
+	struct reftable_merged_table *merged_table;
+	struct reftable_ref_iterator *iter;
+	int ret;
+
+	iter = xcalloc(1, sizeof(*iter));
+	base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
+	iter->prefix = prefix;
+	iter->prefix_len = prefix ? strlen(prefix) : 0;
+	iter->base.oid = &iter->oid;
+	iter->flags = flags;
+	iter->refs = refs;
+
+	ret = refs->err;
+	if (ret)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		goto done;
+
+	merged_table = reftable_stack_merged_table(stack);
+
+	ret = reftable_merged_table_seek_ref(merged_table, &iter->iter, prefix);
+	if (ret)
+		goto done;
+
+done:
+	iter->err = ret;
+	return iter;
+}
+
+static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
+						       const char *prefix,
+						       const char **exclude_patterns,
+						       unsigned int flags)
+{
+	struct reftable_ref_iterator *main_iter, *worktree_iter;
+	struct reftable_ref_store *refs;
+	unsigned int required_flags = REF_STORE_READ;
+
+	if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+		required_flags |= REF_STORE_ODB;
+	refs = reftable_be_downcast(ref_store, required_flags, "ref_iterator_begin");
+
+	main_iter = ref_iterator_for_stack(refs, refs->main_stack, prefix, flags);
+
+	/*
+	 * The worktree stack is only set when we're in an actual worktree
+	 * right now. If we aren't, then we return the common reftable
+	 * iterator, only.
+	 */
+	 if (!refs->worktree_stack)
+		return &main_iter->base;
+
+	/*
+	 * Otherwise we merge both the common and the per-worktree refs into a
+	 * single iterator.
+	 */
+	worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags);
+	return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
+					ref_iterator_select, NULL);
+}
+
+static int reftable_be_read_raw_ref(struct ref_store *ref_store,
+				    const char *refname,
+				    struct object_id *oid,
+				    struct strbuf *referent,
+				    unsigned int *type,
+				    int *failure_errno)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	int ret;
+
+	if (refs->err < 0)
+		return refs->err;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		return ret;
+
+	ret = read_ref_without_reload(stack, refname, oid, referent, type);
+	if (ret < 0)
+		return ret;
+	if (ret > 0) {
+		*failure_errno = ENOENT;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int reftable_be_read_symbolic_ref(struct ref_store *ref_store,
+					 const char *refname,
+					 struct strbuf *referent)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_READ, "read_symbolic_ref");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct reftable_ref_record ref = {0};
+	int ret;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		return ret;
+
+	ret = reftable_stack_read_ref(stack, refname, &ref);
+	if (ret == 0 && ref.value_type == REFTABLE_REF_SYMREF)
+		strbuf_addstr(referent, ref.value.symref);
+	else
+		ret = -1;
+
+	reftable_ref_record_release(&ref);
+	return ret;
+}
+
+/*
+ * Return the refname under which update was originally requested.
+ */
+static const char *original_update_refname(struct ref_update *update)
+{
+	while (update->parent_update)
+		update = update->parent_update;
+	return update->refname;
+}
+
+struct reftable_transaction_update {
+	struct ref_update *update;
+	struct object_id current_oid;
+};
+
+struct write_transaction_table_arg {
+	struct reftable_ref_store *refs;
+	struct reftable_stack *stack;
+	struct reftable_addition *addition;
+	struct reftable_transaction_update *updates;
+	size_t updates_nr;
+	size_t updates_alloc;
+	size_t updates_expected;
+};
+
+struct reftable_transaction_data {
+	struct write_transaction_table_arg *args;
+	size_t args_nr, args_alloc;
+};
+
+static void free_transaction_data(struct reftable_transaction_data *tx_data)
+{
+	if (!tx_data)
+		return;
+	for (size_t i = 0; i < tx_data->args_nr; i++) {
+		reftable_addition_destroy(tx_data->args[i].addition);
+		free(tx_data->args[i].updates);
+	}
+	free(tx_data->args);
+	free(tx_data);
+}
+
+/*
+ * Prepare transaction update for the given reference update. This will cause
+ * us to lock the corresponding reftable stack for concurrent modification.
+ */
+static int prepare_transaction_update(struct write_transaction_table_arg **out,
+				      struct reftable_ref_store *refs,
+				      struct reftable_transaction_data *tx_data,
+				      struct ref_update *update,
+				      struct strbuf *err)
+{
+	struct reftable_stack *stack = stack_for(refs, update->refname, NULL);
+	struct write_transaction_table_arg *arg = NULL;
+	size_t i;
+	int ret;
+
+	/*
+	 * Search for a preexisting stack update. If there is one then we add
+	 * the update to it, otherwise we set up a new stack update.
+	 */
+	for (i = 0; !arg && i < tx_data->args_nr; i++)
+		if (tx_data->args[i].stack == stack)
+			arg = &tx_data->args[i];
+
+	if (!arg) {
+		struct reftable_addition *addition;
+
+		ret = reftable_stack_reload(stack);
+		if (ret)
+			return ret;
+
+		ret = reftable_stack_new_addition(&addition, stack);
+		if (ret) {
+			if (ret == REFTABLE_LOCK_ERROR)
+				strbuf_addstr(err, "cannot lock references");
+			return ret;
+		}
+
+		ALLOC_GROW(tx_data->args, tx_data->args_nr + 1,
+			   tx_data->args_alloc);
+		arg = &tx_data->args[tx_data->args_nr++];
+		arg->refs = refs;
+		arg->stack = stack;
+		arg->addition = addition;
+		arg->updates = NULL;
+		arg->updates_nr = 0;
+		arg->updates_alloc = 0;
+		arg->updates_expected = 0;
+	}
+
+	arg->updates_expected++;
+
+	if (out)
+		*out = arg;
+
+	return 0;
+}
+
+/*
+ * Queue a reference update for the correct stack. We potentially need to
+ * handle multiple stack updates in a single transaction when it spans across
+ * multiple worktrees.
+ */
+static int queue_transaction_update(struct reftable_ref_store *refs,
+				    struct reftable_transaction_data *tx_data,
+				    struct ref_update *update,
+				    struct object_id *current_oid,
+				    struct strbuf *err)
+{
+	struct write_transaction_table_arg *arg = NULL;
+	int ret;
+
+	if (update->backend_data)
+		BUG("reference update queued more than once");
+
+	ret = prepare_transaction_update(&arg, refs, tx_data, update, err);
+	if (ret < 0)
+		return ret;
+
+	ALLOC_GROW(arg->updates, arg->updates_nr + 1,
+		   arg->updates_alloc);
+	arg->updates[arg->updates_nr].update = update;
+	oidcpy(&arg->updates[arg->updates_nr].current_oid, current_oid);
+	update->backend_data = &arg->updates[arg->updates_nr++];
+
+	return 0;
+}
+
+static int reftable_be_transaction_prepare(struct ref_store *ref_store,
+					   struct ref_transaction *transaction,
+					   struct strbuf *err)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE|REF_STORE_MAIN, "ref_transaction_prepare");
+	struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT;
+	struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+	struct reftable_transaction_data *tx_data = NULL;
+	struct object_id head_oid;
+	unsigned int head_type = 0;
+	size_t i;
+	int ret;
+
+	ret = refs->err;
+	if (ret < 0)
+		goto done;
+
+	tx_data = xcalloc(1, sizeof(*tx_data));
+
+	/*
+	 * Preprocess all updates. For one we check that there are no duplicate
+	 * reference updates in this transaction. Second, we lock all stacks
+	 * that will be modified during the transaction.
+	 */
+	for (i = 0; i < transaction->nr; i++) {
+		ret = prepare_transaction_update(NULL, refs, tx_data,
+						 transaction->updates[i], err);
+		if (ret)
+			goto done;
+
+		string_list_append(&affected_refnames,
+				   transaction->updates[i]->refname);
+	}
+
+	/*
+	 * Now that we have counted updates per stack we can preallocate their
+	 * arrays. This avoids having to reallocate many times.
+	 */
+	for (i = 0; i < tx_data->args_nr; i++) {
+		CALLOC_ARRAY(tx_data->args[i].updates, tx_data->args[i].updates_expected);
+		tx_data->args[i].updates_alloc = tx_data->args[i].updates_expected;
+	}
+
+	/*
+	 * Fail if a refname appears more than once in the transaction.
+	 * This code is taken from the files backend and is a good candidate to
+	 * be moved into the generic layer.
+	 */
+	string_list_sort(&affected_refnames);
+	if (ref_update_reject_duplicates(&affected_refnames, err)) {
+		ret = TRANSACTION_GENERIC_ERROR;
+		goto done;
+	}
+
+	ret = read_ref_without_reload(stack_for(refs, "HEAD", NULL), "HEAD", &head_oid,
+				      &head_referent, &head_type);
+	if (ret < 0)
+		goto done;
+	ret = 0;
+
+	for (i = 0; i < transaction->nr; i++) {
+		struct ref_update *u = transaction->updates[i];
+		struct object_id current_oid = {0};
+		struct reftable_stack *stack;
+		const char *rewritten_ref;
+
+		stack = stack_for(refs, u->refname, &rewritten_ref);
+
+		/* 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));
+				ret = -1;
+				goto done;
+			}
+
+			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);
+				ret = -1;
+				goto done;
+			}
+		}
+
+		/*
+		 * When we update the reference that HEAD points to we enqueue
+		 * a second log-only update for HEAD so that its reflog is
+		 * updated accordingly.
+		 */
+		if (head_type == REF_ISSYMREF &&
+		    !(u->flags & REF_LOG_ONLY) &&
+		    !(u->flags & REF_UPDATE_VIA_HEAD) &&
+		    !strcmp(rewritten_ref, head_referent.buf)) {
+			struct ref_update *new_update;
+
+			/*
+			 * First make sure that HEAD is not already in the
+			 * transaction. This check is O(lg N) in the transaction
+			 * size, but it happens at most once per transaction.
+			 */
+			if (string_list_has_string(&affected_refnames, "HEAD")) {
+				/* An entry already existed */
+				strbuf_addf(err,
+					    _("multiple updates for 'HEAD' (including one "
+					    "via its referent '%s') are not allowed"),
+					    u->refname);
+				ret = TRANSACTION_NAME_CONFLICT;
+				goto done;
+			}
+
+			new_update = ref_transaction_add_update(
+					transaction, "HEAD",
+					u->flags | REF_LOG_ONLY | REF_NO_DEREF,
+					&u->new_oid, &u->old_oid, u->msg);
+			string_list_insert(&affected_refnames, new_update->refname);
+		}
+
+		ret = read_ref_without_reload(stack, rewritten_ref,
+					      &current_oid, &referent, &u->type);
+		if (ret < 0)
+			goto done;
+		if (ret > 0 && (!(u->flags & REF_HAVE_OLD) || is_null_oid(&u->old_oid))) {
+			/*
+			 * The reference does not exist, and we either have no
+			 * old object ID or expect the reference to not exist.
+			 * We can thus skip below safety checks as well as the
+			 * symref splitting. But we do want to verify that
+			 * there is no conflicting reference here so that we
+			 * can output a proper error message instead of failing
+			 * at a later point.
+			 */
+			ret = refs_verify_refname_available(ref_store, u->refname,
+							    &affected_refnames, NULL, err);
+			if (ret < 0)
+				goto done;
+
+			/*
+			 * There is no need to write the reference deletion
+			 * when the reference in question doesn't exist.
+			 */
+			 if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
+				 ret = queue_transaction_update(refs, tx_data, u,
+								&current_oid, err);
+				 if (ret)
+					 goto done;
+			 }
+
+			continue;
+		}
+		if (ret > 0) {
+			/* The reference does not exist, but we expected it to. */
+			strbuf_addf(err, _("cannot lock ref '%s': "
+				    "unable to resolve reference '%s'"),
+				    original_update_refname(u), u->refname);
+			ret = -1;
+			goto done;
+		}
+
+		if (u->type & REF_ISSYMREF) {
+			/*
+			 * The reftable stack is locked at this point already,
+			 * so it is safe to call `refs_resolve_ref_unsafe()`
+			 * here without causing races.
+			 */
+			const char *resolved = refs_resolve_ref_unsafe(&refs->base, u->refname, 0,
+								       &current_oid, NULL);
+
+			if (u->flags & REF_NO_DEREF) {
+				if (u->flags & REF_HAVE_OLD && !resolved) {
+					strbuf_addf(err, _("cannot lock ref '%s': "
+						    "error reading reference"), u->refname);
+					ret = -1;
+					goto done;
+				}
+			} else {
+				struct ref_update *new_update;
+				int new_flags;
+
+				new_flags = u->flags;
+				if (!strcmp(rewritten_ref, "HEAD"))
+					new_flags |= REF_UPDATE_VIA_HEAD;
+
+				/*
+				 * If we are updating a symref (eg. HEAD), we should also
+				 * update the branch that the symref points to.
+				 *
+				 * This is generic functionality, and would be better
+				 * done in refs.c, but the current implementation is
+				 * intertwined with the locking in files-backend.c.
+				 */
+				new_update = ref_transaction_add_update(
+						transaction, referent.buf, new_flags,
+						&u->new_oid, &u->old_oid, u->msg);
+				new_update->parent_update = u;
+
+				/*
+				 * Change the symbolic ref update to log only. Also, it
+				 * doesn't need to check its old OID value, as that will be
+				 * done when new_update is processed.
+				 */
+				u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
+				u->flags &= ~REF_HAVE_OLD;
+
+				if (string_list_has_string(&affected_refnames, new_update->refname)) {
+					strbuf_addf(err,
+						    _("multiple updates for '%s' (including one "
+						    "via symref '%s') are not allowed"),
+						    referent.buf, u->refname);
+					ret = TRANSACTION_NAME_CONFLICT;
+					goto done;
+				}
+				string_list_insert(&affected_refnames, new_update->refname);
+			}
+		}
+
+		/*
+		 * Verify that the old object matches our expectations. Note
+		 * that the error messages here do not make a lot of sense in
+		 * the context of the reftable backend as we never lock
+		 * individual refs. But the error messages match what the files
+		 * backend returns, which keeps our tests happy.
+		 */
+		if (u->flags & REF_HAVE_OLD && !oideq(&current_oid, &u->old_oid)) {
+			if (is_null_oid(&u->old_oid))
+				strbuf_addf(err, _("cannot lock ref '%s': "
+					    "reference already exists"),
+					    original_update_refname(u));
+			else if (is_null_oid(&current_oid))
+				strbuf_addf(err, _("cannot lock ref '%s': "
+					    "reference is missing but expected %s"),
+					    original_update_refname(u),
+					    oid_to_hex(&u->old_oid));
+			else
+				strbuf_addf(err, _("cannot lock ref '%s': "
+					    "is at %s but expected %s"),
+					    original_update_refname(u),
+					    oid_to_hex(&current_oid),
+					    oid_to_hex(&u->old_oid));
+			ret = -1;
+			goto done;
+		}
+
+		/*
+		 * If all of the following conditions are true:
+		 *
+		 *   - We're not about to write a symref.
+		 *   - We're not about to write a log-only entry.
+		 *   - Old and new object ID are different.
+		 *
+		 * Then we're essentially doing a no-op update that can be
+		 * skipped. This is not only for the sake of efficiency, but
+		 * also skips writing unneeded reflog entries.
+		 */
+		if ((u->type & REF_ISSYMREF) ||
+		    (u->flags & REF_LOG_ONLY) ||
+		    (u->flags & REF_HAVE_NEW && !oideq(&current_oid, &u->new_oid))) {
+			ret = queue_transaction_update(refs, tx_data, u,
+						       &current_oid, err);
+			if (ret)
+				goto done;
+		}
+	}
+
+	transaction->backend_data = tx_data;
+	transaction->state = REF_TRANSACTION_PREPARED;
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	if (ret < 0) {
+		free_transaction_data(tx_data);
+		transaction->state = REF_TRANSACTION_CLOSED;
+		if (!err->len)
+			strbuf_addf(err, _("reftable: transaction prepare: %s"),
+				    reftable_error_str(ret));
+	}
+	string_list_clear(&affected_refnames, 0);
+	strbuf_release(&referent);
+	strbuf_release(&head_referent);
+
+	return ret;
+}
+
+static int reftable_be_transaction_abort(struct ref_store *ref_store,
+					 struct ref_transaction *transaction,
+					 struct strbuf *err)
+{
+	struct reftable_transaction_data *tx_data = transaction->backend_data;
+	free_transaction_data(tx_data);
+	transaction->state = REF_TRANSACTION_CLOSED;
+	return 0;
+}
+
+static int transaction_update_cmp(const void *a, const void *b)
+{
+	return strcmp(((struct reftable_transaction_update *)a)->update->refname,
+		      ((struct reftable_transaction_update *)b)->update->refname);
+}
+
+static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
+{
+	struct write_transaction_table_arg *arg = cb_data;
+	struct reftable_merged_table *mt =
+		reftable_stack_merged_table(arg->stack);
+	uint64_t ts = reftable_stack_next_update_index(arg->stack);
+	struct reftable_log_record *logs = NULL;
+	size_t logs_nr = 0, logs_alloc = 0, i;
+	int ret = 0;
+
+	QSORT(arg->updates, arg->updates_nr, transaction_update_cmp);
+
+	reftable_writer_set_limits(writer, ts, ts);
+
+	for (i = 0; i < arg->updates_nr; i++) {
+		struct reftable_transaction_update *tx_update = &arg->updates[i];
+		struct ref_update *u = tx_update->update;
+
+		/*
+		 * Write a reflog entry when updating a ref to point to
+		 * something new in either of the following cases:
+		 *
+		 * - The reference is about to be deleted. We always want to
+		 *   delete the reflog in that case.
+		 * - REF_FORCE_CREATE_REFLOG is set, asking us to always create
+		 *   the reflog entry.
+		 * - `core.logAllRefUpdates` tells us to create the reflog for
+		 *   the given ref.
+		 */
+		if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
+			struct reftable_log_record log = {0};
+			struct reftable_iterator it = {0};
+
+			/*
+			 * When deleting refs we also delete all reflog entries
+			 * with them. While it is not strictly required to
+			 * delete reflogs together with their refs, this
+			 * matches the behaviour of the files backend.
+			 *
+			 * Unfortunately, we have no better way than to delete
+			 * all reflog entries one by one.
+			 */
+			ret = reftable_merged_table_seek_log(mt, &it, u->refname);
+			while (ret == 0) {
+				struct reftable_log_record *tombstone;
+
+				ret = reftable_iterator_next_log(&it, &log);
+				if (ret < 0)
+					break;
+				if (ret > 0 || strcmp(log.refname, u->refname)) {
+					ret = 0;
+					break;
+				}
+
+				ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+				tombstone = &logs[logs_nr++];
+				tombstone->refname = xstrdup(u->refname);
+				tombstone->value_type = REFTABLE_LOG_DELETION;
+				tombstone->update_index = log.update_index;
+			}
+
+			reftable_log_record_release(&log);
+			reftable_iterator_destroy(&it);
+
+			if (ret)
+				goto done;
+		} else if (u->flags & REF_HAVE_NEW &&
+			   (u->flags & REF_FORCE_CREATE_REFLOG ||
+			    should_write_log(&arg->refs->base, u->refname))) {
+			struct reftable_log_record *log;
+
+			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+			log = &logs[logs_nr++];
+			memset(log, 0, sizeof(*log));
+
+			fill_reftable_log_record(log);
+			log->update_index = ts;
+			log->refname = xstrdup(u->refname);
+			memcpy(log->value.update.new_hash, u->new_oid.hash, GIT_MAX_RAWSZ);
+			memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ);
+			log->value.update.message =
+				xstrndup(u->msg, arg->refs->write_options.block_size / 2);
+		}
+
+		if (u->flags & REF_LOG_ONLY)
+			continue;
+
+		if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+			struct reftable_ref_record ref = {
+				.refname = (char *)u->refname,
+				.update_index = ts,
+				.value_type = REFTABLE_REF_DELETION,
+			};
+
+			ret = reftable_writer_add_ref(writer, &ref);
+			if (ret < 0)
+				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(&u->new_oid, &peeled);
+			if (!peel_error) {
+				ref.value_type = REFTABLE_REF_VAL2;
+				memcpy(ref.value.val2.target_value, 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;
+				memcpy(ref.value.val1, u->new_oid.hash, GIT_MAX_RAWSZ);
+			}
+
+			ret = reftable_writer_add_ref(writer, &ref);
+			if (ret < 0)
+				goto done;
+		}
+	}
+
+	/*
+	 * Logs are written at the end so that we do not have intermixed ref
+	 * and log blocks.
+	 */
+	if (logs) {
+		ret = reftable_writer_add_logs(writer, logs, logs_nr);
+		if (ret < 0)
+			goto done;
+	}
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	for (i = 0; i < logs_nr; i++)
+		reftable_log_record_release(&logs[i]);
+	free(logs);
+	return ret;
+}
+
+static int reftable_be_transaction_finish(struct ref_store *ref_store,
+					  struct ref_transaction *transaction,
+					  struct strbuf *err)
+{
+	struct reftable_transaction_data *tx_data = transaction->backend_data;
+	int ret = 0;
+
+	for (size_t i = 0; i < tx_data->args_nr; i++) {
+		ret = reftable_addition_add(tx_data->args[i].addition,
+					    write_transaction_table, &tx_data->args[i]);
+		if (ret < 0)
+			goto done;
+
+		ret = reftable_addition_commit(tx_data->args[i].addition);
+		if (ret < 0)
+			goto done;
+	}
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	free_transaction_data(tx_data);
+	transaction->state = REF_TRANSACTION_CLOSED;
+
+	if (ret) {
+		strbuf_addf(err, _("reftable: transaction failure: %s"),
+			    reftable_error_str(ret));
+		return -1;
+	}
+	return ret;
+}
+
+static int reftable_be_initial_transaction_commit(struct ref_store *ref_store UNUSED,
+						  struct ref_transaction *transaction,
+						  struct strbuf *err)
+{
+	return ref_transaction_commit(transaction, err);
+}
+
+static int reftable_be_pack_refs(struct ref_store *ref_store,
+				 struct pack_refs_opts *opts)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB, "pack_refs");
+	struct reftable_stack *stack;
+	int ret;
+
+	if (refs->err)
+		return refs->err;
+
+	stack = refs->worktree_stack;
+	if (!stack)
+		stack = refs->main_stack;
+
+	if (opts->flags & PACK_REFS_AUTO)
+		ret = reftable_stack_auto_compact(stack);
+	else
+		ret = reftable_stack_compact_all(stack, NULL);
+	if (ret < 0) {
+		ret = error(_("unable to compact stack: %s"),
+			    reftable_error_str(ret));
+		goto out;
+	}
+
+	ret = reftable_stack_clean(stack);
+	if (ret)
+		goto out;
+
+out:
+	return ret;
+}
+
+struct write_create_symref_arg {
+	struct reftable_ref_store *refs;
+	struct reftable_stack *stack;
+	const char *refname;
+	const char *target;
+	const char *logmsg;
+};
+
+static int write_create_symref_table(struct reftable_writer *writer, void *cb_data)
+{
+	struct write_create_symref_arg *create = cb_data;
+	uint64_t ts = reftable_stack_next_update_index(create->stack);
+	struct reftable_ref_record ref = {
+		.refname = (char *)create->refname,
+		.value_type = REFTABLE_REF_SYMREF,
+		.value.symref = (char *)create->target,
+		.update_index = ts,
+	};
+	struct reftable_log_record log = {0};
+	struct object_id new_oid;
+	struct object_id old_oid;
+	int ret;
+
+	reftable_writer_set_limits(writer, ts, ts);
+
+	ret = reftable_writer_add_ref(writer, &ref);
+	if (ret)
+		return ret;
+
+	/*
+	 * Note that it is important to try and resolve the reference before we
+	 * write the log entry. This is because `should_write_log()` will munge
+	 * `core.logAllRefUpdates`, which is undesirable when we create a new
+	 * repository because it would be written into the config. As HEAD will
+	 * not resolve for new repositories this ordering will ensure that this
+	 * never happens.
+	 */
+	if (!create->logmsg ||
+	    !refs_resolve_ref_unsafe(&create->refs->base, create->target,
+				     RESOLVE_REF_READING, &new_oid, NULL) ||
+	    !should_write_log(&create->refs->base, create->refname))
+		return 0;
+
+	fill_reftable_log_record(&log);
+	log.refname = xstrdup(create->refname);
+	log.update_index = ts;
+	log.value.update.message = xstrndup(create->logmsg,
+					    create->refs->write_options.block_size / 2);
+	memcpy(log.value.update.new_hash, new_oid.hash, GIT_MAX_RAWSZ);
+	if (refs_resolve_ref_unsafe(&create->refs->base, create->refname,
+				    RESOLVE_REF_READING, &old_oid, NULL))
+		memcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);
+
+	ret = reftable_writer_add_log(writer, &log);
+	reftable_log_record_release(&log);
+	return ret;
+}
+
+static int reftable_be_create_symref(struct ref_store *ref_store,
+				     const char *refname,
+				     const char *target,
+				     const char *logmsg)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_symref");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct write_create_symref_arg arg = {
+		.refs = refs,
+		.stack = stack,
+		.refname = refname,
+		.target = target,
+		.logmsg = logmsg,
+	};
+	int ret;
+
+	ret = refs->err;
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		goto done;
+
+	ret = reftable_stack_add(stack, &write_create_symref_table, &arg);
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	if (ret)
+		error("unable to write symref for %s: %s", refname,
+		      reftable_error_str(ret));
+	return ret;
+}
+
+struct write_copy_arg {
+	struct reftable_ref_store *refs;
+	struct reftable_stack *stack;
+	const char *oldname;
+	const char *newname;
+	const char *logmsg;
+	int delete_old;
+};
+
+static int write_copy_table(struct reftable_writer *writer, void *cb_data)
+{
+	struct write_copy_arg *arg = cb_data;
+	uint64_t deletion_ts, creation_ts;
+	struct reftable_merged_table *mt = reftable_stack_merged_table(arg->stack);
+	struct reftable_ref_record old_ref = {0}, refs[2] = {0};
+	struct reftable_log_record old_log = {0}, *logs = NULL;
+	struct reftable_iterator it = {0};
+	struct string_list skip = STRING_LIST_INIT_NODUP;
+	struct strbuf errbuf = STRBUF_INIT;
+	size_t logs_nr = 0, logs_alloc = 0, i;
+	int ret;
+
+	if (reftable_stack_read_ref(arg->stack, arg->oldname, &old_ref)) {
+		ret = error(_("refname %s not found"), arg->oldname);
+		goto done;
+	}
+	if (old_ref.value_type == REFTABLE_REF_SYMREF) {
+		ret = error(_("refname %s is a symbolic ref, copying it is not supported"),
+			    arg->oldname);
+		goto done;
+	}
+
+	/*
+	 * There's nothing to do in case the old and new name are the same, so
+	 * we exit early in that case.
+	 */
+	if (!strcmp(arg->oldname, arg->newname)) {
+		ret = 0;
+		goto done;
+	}
+
+	/*
+	 * Verify that the new refname is available.
+	 */
+	string_list_insert(&skip, arg->oldname);
+	ret = refs_verify_refname_available(&arg->refs->base, arg->newname,
+					    NULL, &skip, &errbuf);
+	if (ret < 0) {
+		error("%s", errbuf.buf);
+		goto done;
+	}
+
+	/*
+	 * When deleting the old reference we have to use two update indices:
+	 * once to delete the old ref and its reflog, and once to create the
+	 * new ref and its reflog. They need to be staged with two separate
+	 * indices because the new reflog needs to encode both the deletion of
+	 * the old branch and the creation of the new branch, and we cannot do
+	 * two changes to a reflog in a single update.
+	 */
+	deletion_ts = creation_ts = reftable_stack_next_update_index(arg->stack);
+	if (arg->delete_old)
+		creation_ts++;
+	reftable_writer_set_limits(writer, deletion_ts, creation_ts);
+
+	/*
+	 * Add the new reference. If this is a rename then we also delete the
+	 * old reference.
+	 */
+	refs[0] = old_ref;
+	refs[0].refname = (char *)arg->newname;
+	refs[0].update_index = creation_ts;
+	if (arg->delete_old) {
+		refs[1].refname = (char *)arg->oldname;
+		refs[1].value_type = REFTABLE_REF_DELETION;
+		refs[1].update_index = deletion_ts;
+	}
+	ret = reftable_writer_add_refs(writer, refs, arg->delete_old ? 2 : 1);
+	if (ret < 0)
+		goto done;
+
+	/*
+	 * When deleting the old branch we need to create a reflog entry on the
+	 * new branch name that indicates that the old branch has been deleted
+	 * and then recreated. This is a tad weird, but matches what the files
+	 * backend does.
+	 */
+	if (arg->delete_old) {
+		struct strbuf head_referent = STRBUF_INIT;
+		struct object_id head_oid;
+		int append_head_reflog;
+		unsigned head_type = 0;
+
+		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+		memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
+		fill_reftable_log_record(&logs[logs_nr]);
+		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].update_index = deletion_ts;
+		logs[logs_nr].value.update.message =
+			xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
+		memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
+		logs_nr++;
+
+		ret = read_ref_without_reload(arg->stack, "HEAD", &head_oid, &head_referent, &head_type);
+		if (ret < 0)
+			goto done;
+		append_head_reflog = (head_type & REF_ISSYMREF) && !strcmp(head_referent.buf, arg->oldname);
+		strbuf_release(&head_referent);
+
+		/*
+		 * The files backend uses `refs_delete_ref()` to delete the old
+		 * branch name, which will append a reflog entry for HEAD in
+		 * case it points to the old branch.
+		 */
+		if (append_head_reflog) {
+			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+			logs[logs_nr] = logs[logs_nr - 1];
+			logs[logs_nr].refname = "HEAD";
+			logs_nr++;
+		}
+	}
+
+	/*
+	 * Create the reflog entry for the newly created branch.
+	 */
+	ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+	memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
+	fill_reftable_log_record(&logs[logs_nr]);
+	logs[logs_nr].refname = (char *)arg->newname;
+	logs[logs_nr].update_index = creation_ts;
+	logs[logs_nr].value.update.message =
+		xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
+	memcpy(logs[logs_nr].value.update.new_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
+	logs_nr++;
+
+	/*
+	 * In addition to writing the reflog entry for the new branch, we also
+	 * copy over all log entries from the old reflog. Last but not least,
+	 * when renaming we also have to delete all the old reflog entries.
+	 */
+	ret = reftable_merged_table_seek_log(mt, &it, arg->oldname);
+	if (ret < 0)
+		goto done;
+
+	while (1) {
+		ret = reftable_iterator_next_log(&it, &old_log);
+		if (ret < 0)
+			goto done;
+		if (ret > 0 || strcmp(old_log.refname, arg->oldname)) {
+			ret = 0;
+			break;
+		}
+
+		free(old_log.refname);
+
+		/*
+		 * Copy over the old reflog entry with the new refname.
+		 */
+		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+		logs[logs_nr] = old_log;
+		logs[logs_nr].refname = (char *)arg->newname;
+		logs_nr++;
+
+		/*
+		 * Delete the old reflog entry in case we are renaming.
+		 */
+		if (arg->delete_old) {
+			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+			memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
+			logs[logs_nr].refname = (char *)arg->oldname;
+			logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
+			logs[logs_nr].update_index = old_log.update_index;
+			logs_nr++;
+		}
+
+		/*
+		 * Transfer ownership of the log record we're iterating over to
+		 * the array of log records. Otherwise, the pointers would get
+		 * free'd or reallocated by the iterator.
+		 */
+		memset(&old_log, 0, sizeof(old_log));
+	}
+
+	ret = reftable_writer_add_logs(writer, logs, logs_nr);
+	if (ret < 0)
+		goto done;
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	reftable_iterator_destroy(&it);
+	string_list_clear(&skip, 0);
+	strbuf_release(&errbuf);
+	for (i = 0; i < logs_nr; i++) {
+		if (!strcmp(logs[i].refname, "HEAD"))
+			continue;
+		logs[i].refname = NULL;
+		reftable_log_record_release(&logs[i]);
+	}
+	free(logs);
+	reftable_ref_record_release(&old_ref);
+	reftable_log_record_release(&old_log);
+	return ret;
+}
+
+static int reftable_be_rename_ref(struct ref_store *ref_store,
+				  const char *oldrefname,
+				  const char *newrefname,
+				  const char *logmsg)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
+	struct reftable_stack *stack = stack_for(refs, newrefname, &newrefname);
+	struct write_copy_arg arg = {
+		.refs = refs,
+		.stack = stack,
+		.oldname = oldrefname,
+		.newname = newrefname,
+		.logmsg = logmsg,
+		.delete_old = 1,
+	};
+	int ret;
+
+	ret = refs->err;
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		goto done;
+	ret = reftable_stack_add(stack, &write_copy_table, &arg);
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	return ret;
+}
+
+static int reftable_be_copy_ref(struct ref_store *ref_store,
+				const char *oldrefname,
+				const char *newrefname,
+				const char *logmsg)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "copy_ref");
+	struct reftable_stack *stack = stack_for(refs, newrefname, &newrefname);
+	struct write_copy_arg arg = {
+		.refs = refs,
+		.stack = stack,
+		.oldname = oldrefname,
+		.newname = newrefname,
+		.logmsg = logmsg,
+	};
+	int ret;
+
+	ret = refs->err;
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		goto done;
+	ret = reftable_stack_add(stack, &write_copy_table, &arg);
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	return ret;
+}
+
+struct reftable_reflog_iterator {
+	struct ref_iterator base;
+	struct reftable_ref_store *refs;
+	struct reftable_iterator iter;
+	struct reftable_log_record log;
+	struct strbuf last_name;
+	int err;
+};
+
+static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
+{
+	struct reftable_reflog_iterator *iter =
+		(struct reftable_reflog_iterator *)ref_iterator;
+
+	while (!iter->err) {
+		iter->err = reftable_iterator_next_log(&iter->iter, &iter->log);
+		if (iter->err)
+			break;
+
+		/*
+		 * We want the refnames that we have reflogs for, so we skip if
+		 * we've already produced this name. This could be faster by
+		 * seeking directly to reflog@update_index==0.
+		 */
+		if (!strcmp(iter->log.refname, iter->last_name.buf))
+			continue;
+
+		if (check_refname_format(iter->log.refname,
+					 REFNAME_ALLOW_ONELEVEL))
+			continue;
+
+		strbuf_reset(&iter->last_name);
+		strbuf_addstr(&iter->last_name, iter->log.refname);
+		iter->base.refname = iter->log.refname;
+
+		break;
+	}
+
+	if (iter->err > 0) {
+		if (ref_iterator_abort(ref_iterator) != ITER_DONE)
+			return ITER_ERROR;
+		return ITER_DONE;
+	}
+
+	if (iter->err < 0) {
+		ref_iterator_abort(ref_iterator);
+		return ITER_ERROR;
+	}
+
+	return ITER_OK;
+}
+
+static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator,
+						 struct object_id *peeled)
+{
+	BUG("reftable reflog iterator cannot be peeled");
+	return -1;
+}
+
+static int reftable_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+{
+	struct reftable_reflog_iterator *iter =
+		(struct reftable_reflog_iterator *)ref_iterator;
+	reftable_log_record_release(&iter->log);
+	reftable_iterator_destroy(&iter->iter);
+	strbuf_release(&iter->last_name);
+	free(iter);
+	return ITER_DONE;
+}
+
+static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
+	.advance = reftable_reflog_iterator_advance,
+	.peel = reftable_reflog_iterator_peel,
+	.abort = reftable_reflog_iterator_abort
+};
+
+static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
+								  struct reftable_stack *stack)
+{
+	struct reftable_merged_table *merged_table;
+	struct reftable_reflog_iterator *iter;
+	int ret;
+
+	iter = xcalloc(1, sizeof(*iter));
+	base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable);
+	strbuf_init(&iter->last_name, 0);
+	iter->refs = refs;
+
+	ret = refs->err;
+	if (ret)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret < 0)
+		goto done;
+
+	merged_table = reftable_stack_merged_table(stack);
+
+	ret = reftable_merged_table_seek_log(merged_table, &iter->iter, "");
+	if (ret < 0)
+		goto done;
+
+done:
+	iter->err = ret;
+	return iter;
+}
+
+static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store *ref_store)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_iterator_begin");
+	struct reftable_reflog_iterator *main_iter, *worktree_iter;
+
+	main_iter = reflog_iterator_for_stack(refs, refs->main_stack);
+	if (!refs->worktree_stack)
+		return &main_iter->base;
+
+	worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack);
+
+	return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
+					ref_iterator_select, NULL);
+}
+
+static int yield_log_record(struct reftable_log_record *log,
+			    each_reflog_ent_fn fn,
+			    void *cb_data)
+{
+	struct object_id old_oid, new_oid;
+	const char *full_committer;
+
+	oidread(&old_oid, log->value.update.old_hash);
+	oidread(&new_oid, log->value.update.new_hash);
+
+	/*
+	 * When both the old object ID and the new object ID are null
+	 * then this is the reflog existence marker. The caller must
+	 * not be aware of it.
+	 */
+	if (is_null_oid(&old_oid) && is_null_oid(&new_oid))
+		return 0;
+
+	full_committer = fmt_ident(log->value.update.name, log->value.update.email,
+				   WANT_COMMITTER_IDENT, NULL, IDENT_NO_DATE);
+	return fn(&old_oid, &new_oid, full_committer,
+		  log->value.update.time, log->value.update.tz_offset,
+		  log->value.update.message, cb_data);
+}
+
+static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
+						   const char *refname,
+						   each_reflog_ent_fn fn,
+						   void *cb_data)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent_reverse");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct reftable_merged_table *mt = NULL;
+	struct reftable_log_record log = {0};
+	struct reftable_iterator it = {0};
+	int ret;
+
+	if (refs->err < 0)
+		return refs->err;
+
+	mt = reftable_stack_merged_table(stack);
+	ret = reftable_merged_table_seek_log(mt, &it, refname);
+	while (!ret) {
+		ret = reftable_iterator_next_log(&it, &log);
+		if (ret < 0)
+			break;
+		if (ret > 0 || strcmp(log.refname, refname)) {
+			ret = 0;
+			break;
+		}
+
+		ret = yield_log_record(&log, fn, cb_data);
+		if (ret)
+			break;
+	}
+
+	reftable_log_record_release(&log);
+	reftable_iterator_destroy(&it);
+	return ret;
+}
+
+static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
+					   const char *refname,
+					   each_reflog_ent_fn fn,
+					   void *cb_data)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct reftable_merged_table *mt = NULL;
+	struct reftable_log_record *logs = NULL;
+	struct reftable_iterator it = {0};
+	size_t logs_alloc = 0, logs_nr = 0, i;
+	int ret;
+
+	if (refs->err < 0)
+		return refs->err;
+
+	mt = reftable_stack_merged_table(stack);
+	ret = reftable_merged_table_seek_log(mt, &it, refname);
+	while (!ret) {
+		struct reftable_log_record log = {0};
+
+		ret = reftable_iterator_next_log(&it, &log);
+		if (ret < 0)
+			goto done;
+		if (ret > 0 || strcmp(log.refname, refname)) {
+			reftable_log_record_release(&log);
+			ret = 0;
+			break;
+		}
+
+		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+		logs[logs_nr++] = log;
+	}
+
+	for (i = logs_nr; i--;) {
+		ret = yield_log_record(&logs[i], fn, cb_data);
+		if (ret)
+			goto done;
+	}
+
+done:
+	reftable_iterator_destroy(&it);
+	for (i = 0; i < logs_nr; i++)
+		reftable_log_record_release(&logs[i]);
+	free(logs);
+	return ret;
+}
+
+static int reftable_be_reflog_exists(struct ref_store *ref_store,
+				     const char *refname)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_exists");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
+	struct reftable_log_record log = {0};
+	struct reftable_iterator it = {0};
+	int ret;
+
+	ret = refs->err;
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_merged_table_seek_log(mt, &it, refname);
+	if (ret < 0)
+		goto done;
+
+	/*
+	 * Check whether we get at least one log record for the given ref name.
+	 * If so, the reflog exists, otherwise it doesn't.
+	 */
+	ret = reftable_iterator_next_log(&it, &log);
+	if (ret < 0)
+		goto done;
+	if (ret > 0) {
+		ret = 0;
+		goto done;
+	}
+
+	ret = strcmp(log.refname, refname) == 0;
+
+done:
+	reftable_iterator_destroy(&it);
+	reftable_log_record_release(&log);
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+struct write_reflog_existence_arg {
+	struct reftable_ref_store *refs;
+	const char *refname;
+	struct reftable_stack *stack;
+};
+
+static int write_reflog_existence_table(struct reftable_writer *writer,
+					void *cb_data)
+{
+	struct write_reflog_existence_arg *arg = cb_data;
+	uint64_t ts = reftable_stack_next_update_index(arg->stack);
+	struct reftable_log_record log = {0};
+	int ret;
+
+	ret = reftable_stack_read_log(arg->stack, arg->refname, &log);
+	if (ret <= 0)
+		goto done;
+
+	reftable_writer_set_limits(writer, ts, ts);
+
+	/*
+	 * The existence entry has both old and new object ID set to the the
+	 * null object ID. Our iterators are aware of this and will not present
+	 * them to their callers.
+	 */
+	log.refname = xstrdup(arg->refname);
+	log.update_index = ts;
+	log.value_type = REFTABLE_LOG_UPDATE;
+	ret = reftable_writer_add_log(writer, &log);
+
+done:
+	assert(ret != REFTABLE_API_ERROR);
+	reftable_log_record_release(&log);
+	return ret;
+}
+
+static int reftable_be_create_reflog(struct ref_store *ref_store,
+				     const char *refname,
+				     struct strbuf *errmsg)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_reflog");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct write_reflog_existence_arg arg = {
+		.refs = refs,
+		.stack = stack,
+		.refname = refname,
+	};
+	int ret;
+
+	ret = refs->err;
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		goto done;
+
+	ret = reftable_stack_add(stack, &write_reflog_existence_table, &arg);
+
+done:
+	return ret;
+}
+
+struct write_reflog_delete_arg {
+	struct reftable_stack *stack;
+	const char *refname;
+};
+
+static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_data)
+{
+	struct write_reflog_delete_arg *arg = cb_data;
+	struct reftable_merged_table *mt =
+		reftable_stack_merged_table(arg->stack);
+	struct reftable_log_record log = {0}, tombstone = {0};
+	struct reftable_iterator it = {0};
+	uint64_t ts = reftable_stack_next_update_index(arg->stack);
+	int ret;
+
+	reftable_writer_set_limits(writer, ts, ts);
+
+	/*
+	 * In order to delete a table we need to delete all reflog entries one
+	 * by one. This is inefficient, but the reftable format does not have a
+	 * better marker right now.
+	 */
+	ret = reftable_merged_table_seek_log(mt, &it, arg->refname);
+	while (ret == 0) {
+		ret = reftable_iterator_next_log(&it, &log);
+		if (ret < 0)
+			break;
+		if (ret > 0 || strcmp(log.refname, arg->refname)) {
+			ret = 0;
+			break;
+		}
+
+		tombstone.refname = (char *)arg->refname;
+		tombstone.value_type = REFTABLE_LOG_DELETION;
+		tombstone.update_index = log.update_index;
+
+		ret = reftable_writer_add_log(writer, &tombstone);
+	}
+
+	reftable_log_record_release(&log);
+	reftable_iterator_destroy(&it);
+	return ret;
+}
+
+static int reftable_be_delete_reflog(struct ref_store *ref_store,
+				     const char *refname)
+{
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "delete_reflog");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct write_reflog_delete_arg arg = {
+		.stack = stack,
+		.refname = refname,
+	};
+	int ret;
+
+	ret = reftable_stack_reload(stack);
+	if (ret)
+		return ret;
+	ret = reftable_stack_add(stack, &write_reflog_delete_table, &arg);
+
+	assert(ret != REFTABLE_API_ERROR);
+	return ret;
+}
+
+struct reflog_expiry_arg {
+	struct reftable_stack *stack;
+	struct reftable_log_record *records;
+	struct object_id update_oid;
+	const char *refname;
+	size_t len;
+};
+
+static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_data)
+{
+	struct reflog_expiry_arg *arg = cb_data;
+	uint64_t ts = reftable_stack_next_update_index(arg->stack);
+	uint64_t live_records = 0;
+	size_t i;
+	int ret;
+
+	for (i = 0; i < arg->len; i++)
+		if (arg->records[i].value_type == REFTABLE_LOG_UPDATE)
+			live_records++;
+
+	reftable_writer_set_limits(writer, ts, ts);
+
+	if (!is_null_oid(&arg->update_oid)) {
+		struct reftable_ref_record ref = {0};
+		struct object_id peeled;
+
+		ref.refname = (char *)arg->refname;
+		ref.update_index = ts;
+
+		if (!peel_object(&arg->update_oid, &peeled)) {
+			ref.value_type = REFTABLE_REF_VAL2;
+			memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
+			memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
+		} else {
+			ref.value_type = REFTABLE_REF_VAL1;
+			memcpy(ref.value.val1, arg->update_oid.hash, GIT_MAX_RAWSZ);
+		}
+
+		ret = reftable_writer_add_ref(writer, &ref);
+		if (ret < 0)
+			return ret;
+	}
+
+	/*
+	 * When there are no more entries left in the reflog we empty it
+	 * completely, but write a placeholder reflog entry that indicates that
+	 * the reflog still exists.
+	 */
+	if (!live_records) {
+		struct reftable_log_record log = {
+			.refname = (char *)arg->refname,
+			.value_type = REFTABLE_LOG_UPDATE,
+			.update_index = ts,
+		};
+
+		ret = reftable_writer_add_log(writer, &log);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < arg->len; i++) {
+		ret = reftable_writer_add_log(writer, &arg->records[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int reftable_be_reflog_expire(struct ref_store *ref_store,
+				     const char *refname,
+				     unsigned int flags,
+				     reflog_expiry_prepare_fn prepare_fn,
+				     reflog_expiry_should_prune_fn should_prune_fn,
+				     reflog_expiry_cleanup_fn cleanup_fn,
+				     void *policy_cb_data)
+{
+	/*
+	 * For log expiry, we write tombstones for every single reflog entry
+	 * that is to be expired. This means that the entries are still
+	 * retrievable by delving into the stack, and expiring entries
+	 * paradoxically takes extra memory. This memory is only reclaimed when
+	 * compacting the reftable stack.
+	 *
+	 * It would be better if the refs backend supported an API that sets a
+	 * criterion for all refs, passing the criterion to pack_refs().
+	 *
+	 * On the plus side, because we do the expiration per ref, we can easily
+	 * insert the reflog existence dummies.
+	 */
+	struct reftable_ref_store *refs =
+		reftable_be_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
+	struct reftable_stack *stack = stack_for(refs, refname, &refname);
+	struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
+	struct reftable_log_record *logs = NULL;
+	struct reftable_log_record *rewritten = NULL;
+	struct reftable_ref_record ref_record = {0};
+	struct reftable_iterator it = {0};
+	struct reftable_addition *add = NULL;
+	struct reflog_expiry_arg arg = {0};
+	struct object_id oid = {0};
+	uint8_t *last_hash = NULL;
+	size_t logs_nr = 0, logs_alloc = 0, i;
+	int ret;
+
+	if (refs->err < 0)
+		return refs->err;
+
+	ret = reftable_stack_reload(stack);
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_merged_table_seek_log(mt, &it, refname);
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_new_addition(&add, stack);
+	if (ret < 0)
+		goto done;
+
+	ret = reftable_stack_read_ref(stack, refname, &ref_record);
+	if (ret < 0)
+		goto done;
+	if (reftable_ref_record_val1(&ref_record))
+		oidread(&oid, reftable_ref_record_val1(&ref_record));
+	prepare_fn(refname, &oid, policy_cb_data);
+
+	while (1) {
+		struct reftable_log_record log = {0};
+		struct object_id old_oid, new_oid;
+
+		ret = reftable_iterator_next_log(&it, &log);
+		if (ret < 0)
+			goto done;
+		if (ret > 0 || strcmp(log.refname, refname)) {
+			reftable_log_record_release(&log);
+			break;
+		}
+
+		oidread(&old_oid, log.value.update.old_hash);
+		oidread(&new_oid, log.value.update.new_hash);
+
+		/*
+		 * Skip over the reflog existence marker. We will add it back
+		 * in when there are no live reflog records.
+		 */
+		if (is_null_oid(&old_oid) && is_null_oid(&new_oid)) {
+			reftable_log_record_release(&log);
+			continue;
+		}
+
+		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+		logs[logs_nr++] = log;
+	}
+
+	/*
+	 * We need to rewrite all reflog entries according to the pruning
+	 * callback function:
+	 *
+	 *   - If a reflog entry shall be pruned we mark the record for
+	 *     deletion.
+	 *
+	 *   - Otherwise we may have to rewrite the chain of reflog entries so
+	 *     that gaps created by just-deleted records get backfilled.
+	 */
+	CALLOC_ARRAY(rewritten, logs_nr);
+	for (i = logs_nr; i--;) {
+		struct reftable_log_record *dest = &rewritten[i];
+		struct object_id old_oid, new_oid;
+
+		*dest = logs[i];
+		oidread(&old_oid, logs[i].value.update.old_hash);
+		oidread(&new_oid, logs[i].value.update.new_hash);
+
+		if (should_prune_fn(&old_oid, &new_oid, logs[i].value.update.email,
+				    (timestamp_t)logs[i].value.update.time,
+				    logs[i].value.update.tz_offset,
+				    logs[i].value.update.message,
+				    policy_cb_data)) {
+			dest->value_type = REFTABLE_LOG_DELETION;
+		} else {
+			if ((flags & EXPIRE_REFLOGS_REWRITE) && last_hash)
+				memcpy(dest->value.update.old_hash, last_hash, GIT_MAX_RAWSZ);
+			last_hash = logs[i].value.update.new_hash;
+		}
+	}
+
+	if (flags & EXPIRE_REFLOGS_UPDATE_REF && last_hash &&
+	    reftable_ref_record_val1(&ref_record))
+		oidread(&arg.update_oid, last_hash);
+
+	arg.records = rewritten;
+	arg.len = logs_nr;
+	arg.stack = stack,
+	arg.refname = refname,
+
+	ret = reftable_addition_add(add, &write_reflog_expiry_table, &arg);
+	if (ret < 0)
+		goto done;
+
+	/*
+	 * Future improvement: we could skip writing records that were
+	 * not changed.
+	 */
+	if (!(flags & EXPIRE_REFLOGS_DRY_RUN))
+		ret = reftable_addition_commit(add);
+
+done:
+	if (add)
+		cleanup_fn(policy_cb_data);
+	assert(ret != REFTABLE_API_ERROR);
+
+	reftable_ref_record_release(&ref_record);
+	reftable_iterator_destroy(&it);
+	reftable_addition_destroy(add);
+	for (i = 0; i < logs_nr; i++)
+		reftable_log_record_release(&logs[i]);
+	free(logs);
+	free(rewritten);
+	return ret;
+}
+
+struct ref_storage_be refs_be_reftable = {
+	.name = "reftable",
+	.init = reftable_be_init,
+	.init_db = reftable_be_init_db,
+	.transaction_prepare = reftable_be_transaction_prepare,
+	.transaction_finish = reftable_be_transaction_finish,
+	.transaction_abort = reftable_be_transaction_abort,
+	.initial_transaction_commit = reftable_be_initial_transaction_commit,
+
+	.pack_refs = reftable_be_pack_refs,
+	.create_symref = reftable_be_create_symref,
+	.rename_ref = reftable_be_rename_ref,
+	.copy_ref = reftable_be_copy_ref,
+
+	.iterator_begin = reftable_be_iterator_begin,
+	.read_raw_ref = reftable_be_read_raw_ref,
+	.read_symbolic_ref = reftable_be_read_symbolic_ref,
+
+	.reflog_iterator_begin = reftable_be_reflog_iterator_begin,
+	.for_each_reflog_ent = reftable_be_for_each_reflog_ent,
+	.for_each_reflog_ent_reverse = reftable_be_for_each_reflog_ent_reverse,
+	.reflog_exists = reftable_be_reflog_exists,
+	.create_reflog = reftable_be_create_reflog,
+	.delete_reflog = reftable_be_delete_reflog,
+	.reflog_expire = reftable_be_reflog_expire,
+};
diff --git a/refspec.c b/refspec.c
index 63e3112..d60932f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -1,7 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
 #include "strvec.h"
 #include "refs.h"
 #include "refspec.h"
+#include "strbuf.h"
 
 static struct refspec_item s_tag_refspec = {
 	.force = 0,
diff --git a/reftable/basics.c b/reftable/basics.c
index f761e48..fea711d 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -27,7 +27,7 @@
 	out[1] = (uint8_t)(i & 0xff);
 }
 
-int binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
+size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
 {
 	size_t lo = 0;
 	size_t hi = sz;
@@ -39,8 +39,11 @@
 	 */
 	while (hi - lo > 1) {
 		size_t mid = lo + (hi - lo) / 2;
+		int ret = f(mid, args);
+		if (ret < 0)
+			return sz;
 
-		if (f(mid, args))
+		if (ret > 0)
 			hi = mid;
 		else
 			lo = mid;
@@ -64,12 +67,11 @@
 	reftable_free(a);
 }
 
-int names_length(char **names)
+size_t names_length(char **names)
 {
 	char **p = names;
-	for (; *p; p++) {
-		/* empty */
-	}
+	while (*p)
+		p++;
 	return p - names;
 }
 
@@ -89,17 +91,13 @@
 			next = end;
 		}
 		if (p < next) {
-			if (names_len == names_cap) {
-				names_cap = 2 * names_cap + 1;
-				names = reftable_realloc(
-					names, names_cap * sizeof(*names));
-			}
+			REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap);
 			names[names_len++] = xstrdup(p);
 		}
 		p = next + 1;
 	}
 
-	names = reftable_realloc(names, (names_len + 1) * sizeof(*names));
+	REFTABLE_REALLOC_ARRAY(names, names_len + 1);
 	names[names_len] = NULL;
 	*namesp = names;
 }
diff --git a/reftable/basics.h b/reftable/basics.h
index 096b368..523ecd5 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -22,13 +22,14 @@
 void put_be16(uint8_t *out, uint16_t i);
 
 /*
- * find smallest index i in [0, sz) at which f(i) is true, assuming
- * that f is ascending. Return sz if f(i) is false for all indices.
+ * find smallest index i in [0, sz) at which `f(i) > 0`, assuming that f is
+ * ascending. Return sz if `f(i) == 0` for all indices. The search is aborted
+ * and `sz` is returned in case `f(i) < 0`.
  *
  * Contrary to bsearch(3), this returns something useful if the argument is not
  * found.
  */
-int binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
+size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
 
 /*
  * Frees a NULL terminated array of malloced strings. The array itself is also
@@ -44,14 +45,27 @@
 int names_equal(char **a, char **b);
 
 /* returns the array size of a NULL-terminated array of strings. */
-int names_length(char **names);
+size_t names_length(char **names);
 
 /* Allocation routines; they invoke the functions set through
  * reftable_set_alloc() */
 void *reftable_malloc(size_t sz);
 void *reftable_realloc(void *p, size_t sz);
 void reftable_free(void *p);
-void *reftable_calloc(size_t sz);
+void *reftable_calloc(size_t nelem, size_t elsize);
+
+#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
+#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
+#define REFTABLE_REALLOC_ARRAY(x, alloc) (x) = reftable_realloc((x), st_mult(sizeof(*(x)), (alloc)))
+#define REFTABLE_ALLOC_GROW(x, nr, alloc) \
+	do { \
+		if ((nr) > alloc) { \
+			alloc = 2 * (alloc) + 1; \
+			if (alloc < (nr)) \
+				alloc = (nr); \
+			REFTABLE_REALLOC_ARRAY(x, alloc); \
+		} \
+	} while (0)
 
 /* Find the longest shared prefix size of `a` and `b` */
 struct strbuf;
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 1fcd229..997c4d9 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -12,40 +12,47 @@
 #include "test_framework.h"
 #include "reftable-tests.h"
 
-struct binsearch_args {
-	int key;
-	int *arr;
+struct integer_needle_lesseq_args {
+	int needle;
+	int *haystack;
 };
 
-static int binsearch_func(size_t i, void *void_args)
+static int integer_needle_lesseq(size_t i, void *_args)
 {
-	struct binsearch_args *args = void_args;
-
-	return args->key < args->arr[i];
+	struct integer_needle_lesseq_args *args = _args;
+	return args->needle <= args->haystack[i];
 }
 
 static void test_binsearch(void)
 {
-	int arr[] = { 2, 4, 6, 8, 10 };
-	size_t sz = ARRAY_SIZE(arr);
-	struct binsearch_args args = {
-		.arr = arr,
+	int haystack[] = { 2, 4, 6, 8, 10 };
+	struct {
+		int needle;
+		size_t expected_idx;
+	} testcases[] = {
+		{-9000, 0},
+		{-1, 0},
+		{0, 0},
+		{2, 0},
+		{3, 1},
+		{4, 1},
+		{7, 3},
+		{9, 4},
+		{10, 4},
+		{11, 5},
+		{9000, 5},
 	};
+	size_t i = 0;
 
-	int i = 0;
-	for (i = 1; i < 11; i++) {
-		int res;
-		args.key = i;
-		res = binsearch(sz, &binsearch_func, &args);
+	for (i = 0; i < ARRAY_SIZE(testcases); i++) {
+		struct integer_needle_lesseq_args args = {
+			.haystack = haystack,
+			.needle = testcases[i].needle,
+		};
+		size_t idx;
 
-		if (res < sz) {
-			EXPECT(args.key < arr[res]);
-			if (res > 0) {
-				EXPECT(args.key >= arr[res - 1]);
-			}
-		} else {
-			EXPECT(args.key == 10 || args.key == 11);
-		}
+		idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
+		EXPECT(idx == testcases[i].expected_idx);
 	}
 }
 
diff --git a/reftable/block.c b/reftable/block.c
index 34d4d07..298e8c5 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -51,12 +51,7 @@
 	if (2 + 3 * rlen + n > w->block_size - w->next)
 		return -1;
 	if (is_restart) {
-		if (w->restart_len == w->restart_cap) {
-			w->restart_cap = w->restart_cap * 2 + 1;
-			w->restarts = reftable_realloc(
-				w->restarts, sizeof(uint32_t) * w->restart_cap);
-		}
-
+		REFTABLE_ALLOC_GROW(w->restarts, w->restart_len + 1, w->restart_cap);
 		w->restarts[w->restart_len++] = w->next;
 	}
 
@@ -148,8 +143,10 @@
 		int block_header_skip = 4 + w->header_off;
 		uLongf src_len = w->next - block_header_skip;
 		uLongf dest_cap = src_len * 1.001 + 12;
+		uint8_t *compressed;
 
-		uint8_t *compressed = reftable_malloc(dest_cap);
+		REFTABLE_ALLOC_ARRAY(compressed, dest_cap);
+
 		while (1) {
 			uLongf out_dest_len = dest_cap;
 			int zresult = compress2(compressed, &out_dest_len,
@@ -206,9 +203,9 @@
 		uLongf dst_len = sz - block_header_skip; /* total size of dest
 							    buffer. */
 		uLongf src_len = block->len - block_header_skip;
-		/* Log blocks specify the *uncompressed* size in their header.
-		 */
-		uncompressed = reftable_malloc(sz);
+
+		/* Log blocks specify the *uncompressed* size in their header. */
+		REFTABLE_ALLOC_ARRAY(uncompressed, sz);
 
 		/* Copy over the block header verbatim. It's not compressed. */
 		memcpy(uncompressed, block->data, block_header_skip);
@@ -276,36 +273,46 @@
 	it->next_off = br->header_off + 4;
 }
 
-struct restart_find_args {
+struct restart_needle_less_args {
 	int error;
-	struct strbuf key;
-	struct block_reader *r;
+	struct strbuf needle;
+	struct block_reader *reader;
 };
 
-static int restart_key_less(size_t idx, void *args)
+static int restart_needle_less(size_t idx, void *_args)
 {
-	struct restart_find_args *a = args;
-	uint32_t off = block_reader_restart_offset(a->r, idx);
+	struct restart_needle_less_args *args = _args;
+	uint32_t off = block_reader_restart_offset(args->reader, idx);
 	struct string_view in = {
-		.buf = a->r->block.data + off,
-		.len = a->r->block_len - off,
+		.buf = args->reader->block.data + off,
+		.len = args->reader->block_len - off,
 	};
+	uint64_t prefix_len, suffix_len;
+	uint8_t extra;
+	int n;
 
-	/* the restart key is verbatim in the block, so this could avoid the
-	   alloc for decoding the key */
-	struct strbuf rkey = STRBUF_INIT;
-	struct strbuf last_key = STRBUF_INIT;
-	uint8_t unused_extra;
-	int n = reftable_decode_key(&rkey, &unused_extra, last_key, in);
-	int result;
-	if (n < 0) {
-		a->error = 1;
+	/*
+	 * Records at restart points are stored without prefix compression, so
+	 * there is no need to fully decode the record key here. This removes
+	 * the need for allocating memory.
+	 */
+	n = reftable_decode_keylen(in, &prefix_len, &suffix_len, &extra);
+	if (n < 0 || prefix_len) {
+		args->error = 1;
 		return -1;
 	}
 
-	result = strbuf_cmp(&a->key, &rkey);
-	strbuf_release(&rkey);
-	return result;
+	string_view_consume(&in, n);
+	if (suffix_len > in.len) {
+		args->error = 1;
+		return -1;
+	}
+
+	n = memcmp(args->needle.buf, in.buf,
+		   args->needle.len < suffix_len ? args->needle.len : suffix_len);
+	if (n)
+		return n < 0;
+	return args->needle.len < suffix_len;
 }
 
 void block_iter_copy_from(struct block_iter *dest, struct block_iter *src)
@@ -323,44 +330,41 @@
 		.len = it->br->block_len - it->next_off,
 	};
 	struct string_view start = in;
-	struct strbuf key = STRBUF_INIT;
 	uint8_t extra = 0;
 	int n = 0;
 
 	if (it->next_off >= it->br->block_len)
 		return 1;
 
-	n = reftable_decode_key(&key, &extra, it->last_key, in);
+	n = reftable_decode_key(&it->last_key, &extra, in);
 	if (n < 0)
 		return -1;
-
-	if (!key.len)
+	if (!it->last_key.len)
 		return REFTABLE_FORMAT_ERROR;
 
 	string_view_consume(&in, n);
-	n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
+	n = reftable_record_decode(rec, it->last_key, extra, in, it->br->hash_size,
+				   &it->scratch);
 	if (n < 0)
 		return -1;
 	string_view_consume(&in, n);
 
-	strbuf_reset(&it->last_key);
-	strbuf_addbuf(&it->last_key, &key);
 	it->next_off += start.len - in.len;
-	strbuf_release(&key);
 	return 0;
 }
 
 int block_reader_first_key(struct block_reader *br, struct strbuf *key)
 {
-	struct strbuf empty = STRBUF_INIT;
-	int off = br->header_off + 4;
+	int off = br->header_off + 4, n;
 	struct string_view in = {
 		.buf = br->block.data + off,
 		.len = br->block_len - off,
 	};
-
 	uint8_t extra = 0;
-	int n = reftable_decode_key(key, &extra, empty, in);
+
+	strbuf_reset(key);
+
+	n = reftable_decode_key(key, &extra, in);
 	if (n < 0)
 		return n;
 	if (!key->len)
@@ -377,57 +381,98 @@
 void block_iter_close(struct block_iter *it)
 {
 	strbuf_release(&it->last_key);
+	strbuf_release(&it->scratch);
 }
 
 int block_reader_seek(struct block_reader *br, struct block_iter *it,
 		      struct strbuf *want)
 {
-	struct restart_find_args args = {
-		.key = *want,
-		.r = br,
+	struct restart_needle_less_args args = {
+		.needle = *want,
+		.reader = br,
 	};
-	struct reftable_record rec = reftable_new_record(block_reader_type(br));
-	struct strbuf key = STRBUF_INIT;
+	struct block_iter next = BLOCK_ITER_INIT;
+	struct reftable_record rec;
 	int err = 0;
-	struct block_iter next = {
-		.last_key = STRBUF_INIT,
-	};
+	size_t i;
 
-	int i = binsearch(br->restart_count, &restart_key_less, &args);
+	/*
+	 * Perform a binary search over the block's restart points, which
+	 * avoids doing a linear scan over the whole block. Like this, we
+	 * identify the section of the block that should contain our key.
+	 *
+	 * Note that we explicitly search for the first restart point _greater_
+	 * than the sought-after record, not _greater or equal_ to it. In case
+	 * the sought-after record is located directly at the restart point we
+	 * would otherwise start doing the linear search at the preceding
+	 * restart point. While that works alright, we would end up scanning
+	 * too many record.
+	 */
+	i = binsearch(br->restart_count, &restart_needle_less, &args);
 	if (args.error) {
 		err = REFTABLE_FORMAT_ERROR;
 		goto done;
 	}
 
-	it->br = br;
-	if (i > 0) {
-		i--;
-		it->next_off = block_reader_restart_offset(br, i);
-	} else {
+	/*
+	 * Now there are multiple cases:
+	 *
+	 *   - `i == 0`: The wanted record is smaller than the record found at
+	 *     the first restart point. As the first restart point is the first
+	 *     record in the block, our wanted record cannot be located in this
+	 *     block at all. We still need to position the iterator so that the
+	 *     next call to `block_iter_next()` will yield an end-of-iterator
+	 *     signal.
+	 *
+	 *   - `i == restart_count`: The wanted record was not found at any of
+	 *     the restart points. As there is no restart point at the end of
+	 *     the section the record may thus be contained in the last block.
+	 *
+	 *   - `i > 0`: The wanted record must be contained in the section
+	 *     before the found restart point. We thus do a linear search
+	 *     starting from the preceding restart point.
+	 */
+	if (i > 0)
+		it->next_off = block_reader_restart_offset(br, i - 1);
+	else
 		it->next_off = br->header_off + 4;
-	}
+	it->br = br;
 
-	/* We're looking for the last entry less/equal than the wanted key, so
-	   we have to go one entry too far and then back up.
-	*/
+	reftable_record_init(&rec, block_reader_type(br));
+
+	/*
+	 * We're looking for the last entry less than the wanted key so that
+	 * the next call to `block_reader_next()` would yield the wanted
+	 * record. We thus don't want to position our reader at the sought
+	 * after record, but one before. To do so, we have to go one entry too
+	 * far and then back up.
+	 */
 	while (1) {
 		block_iter_copy_from(&next, it);
 		err = block_iter_next(&next, &rec);
 		if (err < 0)
 			goto done;
-
-		reftable_record_key(&rec, &key);
-		if (err > 0 || strbuf_cmp(&key, want) >= 0) {
+		if (err > 0) {
 			err = 0;
 			goto done;
 		}
 
+		/*
+		 * Check whether the current key is greater or equal to the
+		 * sought-after key. In case it is greater we know that the
+		 * record does not exist in the block and can thus abort early.
+		 * In case it is equal to the sought-after key we have found
+		 * the desired record.
+		 */
+		reftable_record_key(&rec, &it->last_key);
+		if (strbuf_cmp(&it->last_key, want) >= 0)
+			goto done;
+
 		block_iter_copy_from(it, &next);
 	}
 
 done:
-	strbuf_release(&key);
-	strbuf_release(&next.last_key);
+	block_iter_close(&next);
 	reftable_record_release(&rec);
 
 	return err;
diff --git a/reftable/block.h b/reftable/block.h
index 87c7753..47acc62 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -84,8 +84,14 @@
 
 	/* key for last entry we read. */
 	struct strbuf last_key;
+	struct strbuf scratch;
 };
 
+#define BLOCK_ITER_INIT { \
+	.last_key = STRBUF_INIT, \
+	.scratch = STRBUF_INIT, \
+}
+
 /* initializes a block reader. */
 int block_reader_init(struct block_reader *br, struct reftable_block *bl,
 		      uint32_t header_off, uint32_t table_block_size,
diff --git a/reftable/block_test.c b/reftable/block_test.c
index cb88af4..e162c6e 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -32,11 +32,11 @@
 	int i = 0;
 	int n;
 	struct block_reader br = { 0 };
-	struct block_iter it = { .last_key = STRBUF_INIT };
+	struct block_iter it = BLOCK_ITER_INIT;
 	int j = 0;
 	struct strbuf want = STRBUF_INIT;
 
-	block.data = reftable_calloc(block_size);
+	REFTABLE_CALLOC_ARRAY(block.data, block_size);
 	block.len = block_size;
 	block.source = malloc_block_source();
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
@@ -49,13 +49,11 @@
 
 	for (i = 0; i < N; i++) {
 		char name[100];
-		uint8_t hash[GIT_SHA1_RAWSZ];
 		snprintf(name, sizeof(name), "branch%02d", i);
-		memset(hash, i, sizeof(hash));
 
 		rec.u.ref.refname = name;
 		rec.u.ref.value_type = REFTABLE_REF_VAL1;
-		rec.u.ref.value.val1 = hash;
+		memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
 
 		names[i] = xstrdup(name);
 		n = block_writer_add(&bw, &rec);
@@ -87,7 +85,7 @@
 	block_iter_close(&it);
 
 	for (i = 0; i < N; i++) {
-		struct block_iter it = { .last_key = STRBUF_INIT };
+		struct block_iter it = BLOCK_ITER_INIT;
 		strbuf_reset(&want);
 		strbuf_addstr(&want, names[i]);
 
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index 8331b34..eeed254 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -29,7 +29,7 @@
 {
 	struct strbuf *b = v;
 	assert(off + size <= b->len);
-	dest->data = reftable_calloc(size);
+	REFTABLE_CALLOC_ARRAY(dest->data, size);
 	memcpy(dest->data, b->buf + off, size);
 	dest->len = size;
 	return size;
@@ -76,8 +76,8 @@
 }
 
 struct file_block_source {
-	int fd;
 	uint64_t size;
+	unsigned char *data;
 };
 
 static uint64_t file_size(void *b)
@@ -87,19 +87,12 @@
 
 static void file_return_block(void *b, struct reftable_block *dest)
 {
-	if (dest->len)
-		memset(dest->data, 0xff, dest->len);
-	reftable_free(dest->data);
 }
 
-static void file_close(void *b)
+static void file_close(void *v)
 {
-	int fd = ((struct file_block_source *)b)->fd;
-	if (fd > 0) {
-		close(fd);
-		((struct file_block_source *)b)->fd = 0;
-	}
-
+	struct file_block_source *b = v;
+	munmap(b->data, b->size);
 	reftable_free(b);
 }
 
@@ -108,9 +101,7 @@
 {
 	struct file_block_source *b = v;
 	assert(off + size <= b->size);
-	dest->data = reftable_malloc(size);
-	if (pread(b->fd, dest->data, size, off) != size)
-		return -1;
+	dest->data = b->data + off;
 	dest->len = size;
 	return size;
 }
@@ -125,26 +116,26 @@
 int reftable_block_source_from_file(struct reftable_block_source *bs,
 				    const char *name)
 {
-	struct stat st = { 0 };
-	int err = 0;
-	int fd = open(name, O_RDONLY);
-	struct file_block_source *p = NULL;
+	struct file_block_source *p;
+	struct stat st;
+	int fd;
+
+	fd = open(name, O_RDONLY);
 	if (fd < 0) {
-		if (errno == ENOENT) {
+		if (errno == ENOENT)
 			return REFTABLE_NOT_EXIST_ERROR;
-		}
 		return -1;
 	}
 
-	err = fstat(fd, &st);
-	if (err < 0) {
+	if (fstat(fd, &st) < 0) {
 		close(fd);
 		return REFTABLE_IO_ERROR;
 	}
 
-	p = reftable_calloc(sizeof(struct file_block_source));
+	REFTABLE_CALLOC_ARRAY(p, 1);
 	p->size = st.st_size;
-	p->fd = fd;
+	p->data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	close(fd);
 
 	assert(!bs->ops);
 	bs->ops = &file_vtable;
diff --git a/reftable/dump.c b/reftable/dump.c
index 155953d..26e0393 100644
--- a/reftable/dump.c
+++ b/reftable/dump.c
@@ -7,18 +7,16 @@
 */
 
 #include "git-compat-util.h"
-#include "hash.h"
+#include "hash-ll.h"
 
 #include "reftable-blocksource.h"
 #include "reftable-error.h"
-#include "reftable-merged.h"
 #include "reftable-record.h"
 #include "reftable-tests.h"
 #include "reftable-writer.h"
 #include "reftable-iterator.h"
 #include "reftable-reader.h"
 #include "reftable-stack.h"
-#include "reftable-generic.h"
 
 #include <stddef.h>
 #include <stdio.h>
diff --git a/reftable/error.c b/reftable/error.c
index 93941f2..cfb7a0f 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -6,6 +6,7 @@
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "reftable-error.h"
 
 #include <stdio.h>
@@ -21,7 +22,7 @@
 	case REFTABLE_NOT_EXIST_ERROR:
 		return "file does not exist";
 	case REFTABLE_LOCK_ERROR:
-		return "data is outdated";
+		return "data is locked";
 	case REFTABLE_API_ERROR:
 		return "misuse of the reftable API";
 	case REFTABLE_ZLIB_ERROR:
@@ -34,6 +35,8 @@
 		return "invalid refname";
 	case REFTABLE_ENTRY_TOO_BIG_ERROR:
 		return "entry too large";
+	case REFTABLE_OUTDATED_ERROR:
+		return "data concurrently modified";
 	case -1:
 		return "general error";
 	default:
diff --git a/reftable/generic.c b/reftable/generic.c
index 57f8032..b9f1c7c 100644
--- a/reftable/generic.c
+++ b/reftable/generic.c
@@ -6,7 +6,6 @@
 https://developers.google.com/open-source/licenses/bsd
 */
 
-#include "basics.h"
 #include "constants.h"
 #include "record.h"
 #include "generic.h"
diff --git a/reftable/iter.c b/reftable/iter.c
index a8d174c..7aa30c4 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -16,11 +16,6 @@
 #include "reader.h"
 #include "reftable-error.h"
 
-int iterator_is_null(struct reftable_iterator *it)
-{
-	return !it->ops;
-}
-
 static void filtering_ref_iterator_close(void *iter_arg)
 {
 	struct filtering_ref_iterator *fri = iter_arg;
@@ -160,8 +155,7 @@
 			       int oid_len, uint64_t *offsets, int offset_len)
 {
 	struct indexed_table_ref_iter empty = INDEXED_TABLE_REF_ITER_INIT;
-	struct indexed_table_ref_iter *itr =
-		reftable_calloc(sizeof(struct indexed_table_ref_iter));
+	struct indexed_table_ref_iter *itr = reftable_calloc(1, sizeof(*itr));
 	int err = 0;
 
 	*itr = empty;
diff --git a/reftable/iter.h b/reftable/iter.h
index 09eb0cb..537431b 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -16,10 +16,6 @@
 #include "reftable-iterator.h"
 #include "reftable-generic.h"
 
-/* Returns true for a zeroed out iterator, such as the one returned from
- * iterator_destroy. */
-int iterator_is_null(struct reftable_iterator *it);
-
 /* iterator that produces only ref records that point to `oid` */
 struct filtering_ref_iterator {
 	int double_check;
@@ -53,10 +49,10 @@
 	int is_finished;
 };
 
-#define INDEXED_TABLE_REF_ITER_INIT                                     \
-	{                                                               \
-		.cur = { .last_key = STRBUF_INIT }, .oid = STRBUF_INIT, \
-	}
+#define INDEXED_TABLE_REF_ITER_INIT { \
+	.cur = BLOCK_ITER_INIT, \
+	.oid = STRBUF_INIT, \
+}
 
 void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
 					  struct indexed_table_ref_iter *itr);
diff --git a/reftable/merged.c b/reftable/merged.c
index 5ded470..f85a24c 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -11,33 +11,45 @@
 #include "constants.h"
 #include "iter.h"
 #include "pq.h"
-#include "reader.h"
 #include "record.h"
 #include "generic.h"
 #include "reftable-merged.h"
 #include "reftable-error.h"
 #include "system.h"
 
+struct merged_subiter {
+	struct reftable_iterator iter;
+	struct reftable_record rec;
+};
+
+struct merged_iter {
+	struct merged_subiter *subiters;
+	struct merged_iter_pqueue pq;
+	uint32_t hash_id;
+	size_t stack_len;
+	uint8_t typ;
+	int suppress_deletions;
+	ssize_t advance_index;
+};
+
 static int merged_iter_init(struct merged_iter *mi)
 {
-	int i = 0;
-	for (i = 0; i < mi->stack_len; i++) {
-		struct reftable_record rec = reftable_new_record(mi->typ);
-		int err = iterator_next(&mi->stack[i], &rec);
-		if (err < 0) {
-			return err;
-		}
+	for (size_t i = 0; i < mi->stack_len; i++) {
+		struct pq_entry e = {
+			.index = i,
+			.rec = &mi->subiters[i].rec,
+		};
+		int err;
 
-		if (err > 0) {
-			reftable_iterator_destroy(&mi->stack[i]);
-			reftable_record_release(&rec);
-		} else {
-			struct pq_entry e = {
-				.rec = rec,
-				.index = i,
-			};
-			merged_iter_pqueue_add(&mi->pq, &e);
-		}
+		reftable_record_init(&mi->subiters[i].rec, mi->typ);
+		err = iterator_next(&mi->subiters[i].iter,
+				    &mi->subiters[i].rec);
+		if (err < 0)
+			return err;
+		if (err > 0)
+			continue;
+
+		merged_iter_pqueue_add(&mi->pq, &e);
 	}
 
 	return 0;
@@ -46,56 +58,68 @@
 static void merged_iter_close(void *p)
 {
 	struct merged_iter *mi = p;
-	int i = 0;
+
 	merged_iter_pqueue_release(&mi->pq);
-	for (i = 0; i < mi->stack_len; i++) {
-		reftable_iterator_destroy(&mi->stack[i]);
+	for (size_t i = 0; i < mi->stack_len; i++) {
+		reftable_iterator_destroy(&mi->subiters[i].iter);
+		reftable_record_release(&mi->subiters[i].rec);
 	}
-	reftable_free(mi->stack);
+	reftable_free(mi->subiters);
 }
 
-static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
-					       size_t idx)
+static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
 {
 	struct pq_entry e = {
-		.rec = reftable_new_record(mi->typ),
 		.index = idx,
+		.rec = &mi->subiters[idx].rec,
 	};
-	int err = iterator_next(&mi->stack[idx], &e.rec);
-	if (err < 0)
-		return err;
+	int err;
 
-	if (err > 0) {
-		reftable_iterator_destroy(&mi->stack[idx]);
-		reftable_record_release(&e.rec);
-		return 0;
-	}
+	err = iterator_next(&mi->subiters[idx].iter, &mi->subiters[idx].rec);
+	if (err)
+		return err;
 
 	merged_iter_pqueue_add(&mi->pq, &e);
 	return 0;
 }
 
-static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
-{
-	if (iterator_is_null(&mi->stack[idx]))
-		return 0;
-	return merged_iter_advance_nonnull_subiter(mi, idx);
-}
-
 static int merged_iter_next_entry(struct merged_iter *mi,
 				  struct reftable_record *rec)
 {
-	struct strbuf entry_key = STRBUF_INIT;
 	struct pq_entry entry = { 0 };
-	int err = 0;
+	int err = 0, empty;
 
-	if (merged_iter_pqueue_is_empty(mi->pq))
+	empty = merged_iter_pqueue_is_empty(mi->pq);
+
+	if (mi->advance_index >= 0) {
+		/*
+		 * When there are no pqueue entries then we only have a single
+		 * subiter left. There is no need to use the pqueue in that
+		 * case anymore as we know that the subiter will return entries
+		 * in the correct order already.
+		 *
+		 * While this may sound like a very specific edge case, it may
+		 * happen more frequently than you think. Most repositories
+		 * will end up having a single large base table that contains
+		 * most of the refs. It's thus likely that we exhaust all
+		 * subiters but the one from that base ref.
+		 */
+		if (empty)
+			return iterator_next(&mi->subiters[mi->advance_index].iter,
+					     rec);
+
+		err = merged_iter_advance_subiter(mi, mi->advance_index);
+		if (err < 0)
+			return err;
+		if (!err)
+			empty = 0;
+		mi->advance_index = -1;
+	}
+
+	if (empty)
 		return 1;
 
 	entry = merged_iter_pqueue_remove(&mi->pq);
-	err = merged_iter_advance_subiter(mi, entry.index);
-	if (err < 0)
-		return err;
 
 	/*
 	  One can also use reftable as datacenter-local storage, where the ref
@@ -105,55 +129,36 @@
 	  such a deployment, the loop below must be changed to collect all
 	  entries for the same key, and return new the newest one.
 	*/
-	reftable_record_key(&entry.rec, &entry_key);
 	while (!merged_iter_pqueue_is_empty(mi->pq)) {
 		struct pq_entry top = merged_iter_pqueue_top(mi->pq);
-		struct strbuf k = STRBUF_INIT;
-		int err = 0, cmp = 0;
+		int cmp;
 
-		reftable_record_key(&top.rec, &k);
-
-		cmp = strbuf_cmp(&k, &entry_key);
-		strbuf_release(&k);
-
-		if (cmp > 0) {
+		cmp = reftable_record_cmp(top.rec, entry.rec);
+		if (cmp > 0)
 			break;
-		}
 
 		merged_iter_pqueue_remove(&mi->pq);
 		err = merged_iter_advance_subiter(mi, top.index);
-		if (err < 0) {
+		if (err < 0)
 			return err;
-		}
-		reftable_record_release(&top.rec);
 	}
 
-	reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
-	reftable_record_release(&entry.rec);
-	strbuf_release(&entry_key);
+	mi->advance_index = entry.index;
+	SWAP(*rec, *entry.rec);
 	return 0;
 }
 
-static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec)
-{
-	while (1) {
-		int err = merged_iter_next_entry(mi, rec);
-		if (err == 0 && mi->suppress_deletions &&
-		    reftable_record_is_deletion(rec)) {
-			continue;
-		}
-
-		return err;
-	}
-}
-
 static int merged_iter_next_void(void *p, struct reftable_record *rec)
 {
 	struct merged_iter *mi = p;
-	if (merged_iter_pqueue_is_empty(mi->pq))
-		return 1;
-
-	return merged_iter_next(mi, rec);
+	while (1) {
+		int err = merged_iter_next_entry(mi, rec);
+		if (err)
+			return err;
+		if (mi->suppress_deletions && reftable_record_is_deletion(rec))
+			continue;
+		return 0;
+	}
 }
 
 static struct reftable_iterator_vtable merged_iter_vtable = {
@@ -170,14 +175,14 @@
 }
 
 int reftable_new_merged_table(struct reftable_merged_table **dest,
-			      struct reftable_table *stack, int n,
+			      struct reftable_table *stack, size_t n,
 			      uint32_t hash_id)
 {
 	struct reftable_merged_table *m = NULL;
 	uint64_t last_max = 0;
 	uint64_t first_min = 0;
-	int i = 0;
-	for (i = 0; i < n; i++) {
+
+	for (size_t i = 0; i < n; i++) {
 		uint64_t min = reftable_table_min_update_index(&stack[i]);
 		uint64_t max = reftable_table_max_update_index(&stack[i]);
 
@@ -192,7 +197,7 @@
 		}
 	}
 
-	m = reftable_calloc(sizeof(struct reftable_merged_table));
+	REFTABLE_CALLOC_ARRAY(m, 1);
 	m->stack = stack;
 	m->stack_len = n;
 	m->min = first_min;
@@ -241,48 +246,37 @@
 				    struct reftable_iterator *it,
 				    struct reftable_record *rec)
 {
-	struct reftable_iterator *iters = reftable_calloc(
-		sizeof(struct reftable_iterator) * mt->stack_len);
 	struct merged_iter merged = {
-		.stack = iters,
 		.typ = reftable_record_type(rec),
 		.hash_id = mt->hash_id,
 		.suppress_deletions = mt->suppress_deletions,
+		.advance_index = -1,
 	};
-	int n = 0;
-	int err = 0;
-	int i = 0;
-	for (i = 0; i < mt->stack_len && err == 0; i++) {
-		int e = reftable_table_seek_record(&mt->stack[i], &iters[n],
-						   rec);
-		if (e < 0) {
-			err = e;
-		}
-		if (e == 0) {
-			n++;
-		}
-	}
-	if (err < 0) {
-		int i = 0;
-		for (i = 0; i < n; i++) {
-			reftable_iterator_destroy(&iters[i]);
-		}
-		reftable_free(iters);
-		return err;
+	struct merged_iter *p;
+	int err;
+
+	REFTABLE_CALLOC_ARRAY(merged.subiters, mt->stack_len);
+	for (size_t i = 0; i < mt->stack_len; i++) {
+		err = reftable_table_seek_record(&mt->stack[i],
+						 &merged.subiters[merged.stack_len].iter, rec);
+		if (err < 0)
+			goto out;
+		if (!err)
+			merged.stack_len++;
 	}
 
-	merged.stack_len = n;
 	err = merged_iter_init(&merged);
-	if (err < 0) {
+	if (err < 0)
+		goto out;
+
+	p = reftable_malloc(sizeof(struct merged_iter));
+	*p = merged;
+	iterator_from_merged_iter(it, p);
+
+out:
+	if (err < 0)
 		merged_iter_close(&merged);
-		return err;
-	} else {
-		struct merged_iter *p =
-			reftable_malloc(sizeof(struct merged_iter));
-		*p = merged;
-		iterator_from_merged_iter(it, p);
-	}
-	return 0;
+	return err;
 }
 
 int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
diff --git a/reftable/merged.h b/reftable/merged.h
index 7d9f95d..a2571db 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -9,7 +9,7 @@
 #ifndef MERGED_H
 #define MERGED_H
 
-#include "pq.h"
+#include "system.h"
 
 struct reftable_merged_table {
 	struct reftable_table *stack;
@@ -24,15 +24,6 @@
 	uint64_t max;
 };
 
-struct merged_iter {
-	struct reftable_iterator *stack;
-	uint32_t hash_id;
-	size_t stack_len;
-	uint8_t typ;
-	int suppress_deletions;
-	struct merged_iter_pqueue pq;
-};
-
 void merged_table_release(struct reftable_merged_table *mt);
 
 #endif
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index d08c16a..530fc82 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -12,7 +12,6 @@
 
 #include "basics.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "test_framework.h"
@@ -43,7 +42,7 @@
 		}
 	}
 
-	w = reftable_new_writer(&strbuf_add_void, buf, &opts);
+	w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
 	reftable_writer_set_limits(w, min, max);
 
 	for (i = 0; i < n; i++) {
@@ -71,7 +70,7 @@
 		.exact_log_message = 1,
 	};
 	struct reftable_writer *w = NULL;
-	w = reftable_new_writer(&strbuf_add_void, buf, &opts);
+	w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
 	reftable_writer_set_limits(w, update_index, update_index);
 
 	for (i = 0; i < n; i++) {
@@ -89,16 +88,17 @@
 merged_table_from_records(struct reftable_ref_record **refs,
 			  struct reftable_block_source **source,
 			  struct reftable_reader ***readers, int *sizes,
-			  struct strbuf *buf, int n)
+			  struct strbuf *buf, size_t n)
 {
-	int i = 0;
 	struct reftable_merged_table *mt = NULL;
+	struct reftable_table *tabs;
 	int err;
-	struct reftable_table *tabs =
-		reftable_calloc(n * sizeof(struct reftable_table));
-	*readers = reftable_calloc(n * sizeof(struct reftable_reader *));
-	*source = reftable_calloc(n * sizeof(**source));
-	for (i = 0; i < n; i++) {
+
+	REFTABLE_CALLOC_ARRAY(tabs, n);
+	REFTABLE_CALLOC_ARRAY(*readers, n);
+	REFTABLE_CALLOC_ARRAY(*source, n);
+
+	for (size_t i = 0; i < n; i++) {
 		write_test_table(&buf[i], refs[i], sizes[i]);
 		block_source_from_strbuf(&(*source)[i], &buf[i]);
 
@@ -123,13 +123,11 @@
 
 static void test_merged_between(void)
 {
-	uint8_t hash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 0 };
-
 	struct reftable_ref_record r1[] = { {
 		.refname = "b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
-		.value.val1 = hash1,
+		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
 		.refname = "a",
@@ -165,26 +163,24 @@
 
 static void test_merged(void)
 {
-	uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-	uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
 	struct reftable_ref_record r1[] = {
 		{
 			.refname = "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
-			.value.val1 = hash1,
+			.value.val1 = { 1 },
 		},
 		{
 			.refname = "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
-			.value.val1 = hash1,
+			.value.val1 = { 1 },
 		},
 		{
 			.refname = "c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
-			.value.val1 = hash1,
+			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
@@ -197,13 +193,13 @@
 			.refname = "c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
-			.value.val1 = hash2,
+			.value.val1 = { 2 },
 		},
 		{
 			.refname = "d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
-			.value.val1 = hash1,
+			.value.val1 = { 1 },
 		},
 	};
 
@@ -236,14 +232,10 @@
 	while (len < 100) { /* cap loops/recursion. */
 		struct reftable_ref_record ref = { NULL };
 		int err = reftable_iterator_next_ref(&it, &ref);
-		if (err > 0) {
+		if (err > 0)
 			break;
-		}
-		if (len == cap) {
-			cap = 2 * cap + 1;
-			out = reftable_realloc(
-				out, sizeof(struct reftable_ref_record) * cap);
-		}
+
+		REFTABLE_ALLOC_GROW(out, len + 1, cap);
 		out[len++] = ref;
 	}
 	reftable_iterator_destroy(&it);
@@ -270,16 +262,17 @@
 merged_table_from_log_records(struct reftable_log_record **logs,
 			      struct reftable_block_source **source,
 			      struct reftable_reader ***readers, int *sizes,
-			      struct strbuf *buf, int n)
+			      struct strbuf *buf, size_t n)
 {
-	int i = 0;
 	struct reftable_merged_table *mt = NULL;
+	struct reftable_table *tabs;
 	int err;
-	struct reftable_table *tabs =
-		reftable_calloc(n * sizeof(struct reftable_table));
-	*readers = reftable_calloc(n * sizeof(struct reftable_reader *));
-	*source = reftable_calloc(n * sizeof(**source));
-	for (i = 0; i < n; i++) {
+
+	REFTABLE_CALLOC_ARRAY(tabs, n);
+	REFTABLE_CALLOC_ARRAY(*readers, n);
+	REFTABLE_CALLOC_ARRAY(*source, n);
+
+	for (size_t i = 0; i < n; i++) {
 		write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
 		block_source_from_strbuf(&(*source)[i], &buf[i]);
 
@@ -296,16 +289,13 @@
 
 static void test_merged_logs(void)
 {
-	uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-	uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
-	uint8_t hash3[GIT_SHA1_RAWSZ] = { 3 };
 	struct reftable_log_record r1[] = {
 		{
 			.refname = "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
-				.old_hash = hash2,
+				.old_hash = { 2 },
 				/* deletion */
 				.name = "jane doe",
 				.email = "jane@invalid",
@@ -317,8 +307,8 @@
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
-				.old_hash = hash1,
-				.new_hash = hash2,
+				.old_hash = { 1 },
+				.new_hash = { 2 },
 				.name = "jane doe",
 				.email = "jane@invalid",
 				.message = "message1",
@@ -331,7 +321,7 @@
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
-				.new_hash = hash3,
+				.new_hash = { 3 },
 				.name = "jane doe",
 				.email = "jane@invalid",
 				.message = "message3",
@@ -373,14 +363,10 @@
 	while (len < 100) { /* cap loops/recursion. */
 		struct reftable_log_record log = { NULL };
 		int err = reftable_iterator_next_log(&it, &log);
-		if (err > 0) {
+		if (err > 0)
 			break;
-		}
-		if (len == cap) {
-			cap = 2 * cap + 1;
-			out = reftable_realloc(
-				out, sizeof(struct reftable_log_record) * cap);
-		}
+
+		REFTABLE_ALLOC_GROW(out, len + 1, cap);
 		out[len++] = log;
 	}
 	reftable_iterator_destroy(&it);
@@ -417,7 +403,7 @@
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	struct reftable_ref_record rec = {
 		.refname = "master",
@@ -425,7 +411,7 @@
 	};
 	int err;
 	struct reftable_block_source source = { NULL };
-	struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1);
+	struct reftable_table *tab = reftable_calloc(1, sizeof(*tab));
 	uint32_t hash_id;
 	struct reftable_reader *rd = NULL;
 	struct reftable_merged_table *merged = NULL;
diff --git a/reftable/pq.c b/reftable/pq.c
index dcefeb7..7fb45d8 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -14,33 +14,12 @@
 
 int pq_less(struct pq_entry *a, struct pq_entry *b)
 {
-	struct strbuf ak = STRBUF_INIT;
-	struct strbuf bk = STRBUF_INIT;
-	int cmp = 0;
-	reftable_record_key(&a->rec, &ak);
-	reftable_record_key(&b->rec, &bk);
-
-	cmp = strbuf_cmp(&ak, &bk);
-
-	strbuf_release(&ak);
-	strbuf_release(&bk);
-
+	int cmp = reftable_record_cmp(a->rec, b->rec);
 	if (cmp == 0)
 		return a->index > b->index;
-
 	return cmp < 0;
 }
 
-struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
-{
-	return pq.heap[0];
-}
-
-int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
-{
-	return pq.len == 0;
-}
-
 struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
 {
 	int i = 0;
@@ -75,13 +54,9 @@
 {
 	int i = 0;
 
-	if (pq->len == pq->cap) {
-		pq->cap = 2 * pq->cap + 1;
-		pq->heap = reftable_realloc(pq->heap,
-					    pq->cap * sizeof(struct pq_entry));
-	}
-
+	REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap);
 	pq->heap[pq->len++] = *e;
+
 	i = pq->len - 1;
 	while (i > 0) {
 		int j = (i - 1) / 2;
@@ -97,10 +72,6 @@
 
 void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
 {
-	int i = 0;
-	for (i = 0; i < pq->len; i++) {
-		reftable_record_release(&pq->heap[i].rec);
-	}
 	FREE_AND_NULL(pq->heap);
-	pq->len = pq->cap = 0;
+	memset(pq, 0, sizeof(*pq));
 }
diff --git a/reftable/pq.h b/reftable/pq.h
index e85bac9..f796c23 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -12,8 +12,8 @@
 #include "record.h"
 
 struct pq_entry {
-	int index;
-	struct reftable_record rec;
+	size_t index;
+	struct reftable_record *rec;
 };
 
 struct merged_iter_pqueue {
@@ -22,12 +22,20 @@
 	size_t cap;
 };
 
-struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq);
-int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq);
 void merged_iter_pqueue_check(struct merged_iter_pqueue pq);
 struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
 void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e);
 void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
 int pq_less(struct pq_entry *a, struct pq_entry *b);
 
+static inline struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
+{
+	return pq.heap[0];
+}
+
+static inline int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
+{
+	return pq.len == 0;
+}
+
 #endif
diff --git a/reftable/pq_test.c b/reftable/pq_test.c
index 011b5c7..b7d3c80 100644
--- a/reftable/pq_test.c
+++ b/reftable/pq_test.c
@@ -27,48 +27,43 @@
 
 static void test_pq(void)
 {
-	char *names[54] = { NULL };
-	int N = ARRAY_SIZE(names) - 1;
-
 	struct merged_iter_pqueue pq = { NULL };
+	struct reftable_record recs[54];
+	int N = ARRAY_SIZE(recs) - 1, i;
 	char *last = NULL;
 
-	int i = 0;
 	for (i = 0; i < N; i++) {
-		char name[100];
-		snprintf(name, sizeof(name), "%02d", i);
-		names[i] = xstrdup(name);
+		struct strbuf refname = STRBUF_INIT;
+		strbuf_addf(&refname, "%02d", i);
+
+		reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+		recs[i].u.ref.refname = strbuf_detach(&refname, NULL);
 	}
 
 	i = 1;
 	do {
-		struct pq_entry e = { .rec = { .type = BLOCK_TYPE_REF,
-					       .u.ref = {
-						       .refname = names[i],
-					       } } };
+		struct pq_entry e = {
+			.rec = &recs[i],
+		};
+
 		merged_iter_pqueue_add(&pq, &e);
 		merged_iter_pqueue_check(pq);
+
 		i = (i * 7) % N;
 	} while (i != 1);
 
 	while (!merged_iter_pqueue_is_empty(pq)) {
 		struct pq_entry e = merged_iter_pqueue_remove(&pq);
-		struct reftable_record *rec = &e.rec;
 		merged_iter_pqueue_check(pq);
 
-		EXPECT(reftable_record_type(rec) == BLOCK_TYPE_REF);
-		if (last) {
-			EXPECT(strcmp(last, rec->u.ref.refname) < 0);
-		}
-		// this is names[i], so don't dealloc.
-		last = rec->u.ref.refname;
-		rec->u.ref.refname = NULL;
-		reftable_record_release(rec);
-	}
-	for (i = 0; i < N; i++) {
-		reftable_free(names[i]);
+		EXPECT(reftable_record_type(e.rec) == BLOCK_TYPE_REF);
+		if (last)
+			EXPECT(strcmp(last, e.rec->u.ref.refname) < 0);
+		last = e.rec->u.ref.refname;
 	}
 
+	for (i = 0; i < N; i++)
+		reftable_record_release(&recs[i]);
 	merged_iter_pqueue_release(&pq);
 }
 
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
index 0ad7d5c..44b84a1 100644
--- a/reftable/publicbasics.c
+++ b/reftable/publicbasics.c
@@ -6,10 +6,10 @@
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "reftable-malloc.h"
 
 #include "basics.h"
-#include "system.h"
 
 static void *(*reftable_malloc_ptr)(size_t sz);
 static void *(*reftable_realloc_ptr)(void *, size_t);
@@ -37,8 +37,9 @@
 		free(p);
 }
 
-void *reftable_calloc(size_t sz)
+void *reftable_calloc(size_t nelem, size_t elsize)
 {
+	size_t sz = st_mult(nelem, elsize);
 	void *p = reftable_malloc(sz);
 	memset(p, 0, sz);
 	return p;
diff --git a/reftable/reader.c b/reftable/reader.c
index b4db23c..b113daa 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -16,7 +16,6 @@
 #include "record.h"
 #include "reftable-error.h"
 #include "reftable-generic.h"
-#include "tree.h"
 
 uint64_t block_source_size(struct reftable_block_source *source)
 {
@@ -224,10 +223,9 @@
 	struct block_iter bi;
 	int is_finished;
 };
-#define TABLE_ITER_INIT                          \
-	{                                        \
-		.bi = {.last_key = STRBUF_INIT } \
-	}
+#define TABLE_ITER_INIT { \
+	.bi = BLOCK_ITER_INIT \
+}
 
 static void table_iter_copy_from(struct table_iter *dest,
 				 struct table_iter *src)
@@ -359,24 +357,32 @@
 
 	while (1) {
 		struct table_iter next = TABLE_ITER_INIT;
-		int err = 0;
-		if (ti->is_finished) {
+		int err;
+
+		if (ti->is_finished)
 			return 1;
-		}
 
+		/*
+		 * Check whether the current block still has more records. If
+		 * so, return it. If the iterator returns positive then the
+		 * current block has been exhausted.
+		 */
 		err = table_iter_next_in_block(ti, rec);
-		if (err <= 0) {
+		if (err <= 0)
+			return err;
+
+		/*
+		 * Otherwise, we need to continue to the next block in the
+		 * table and retry. If there are no more blocks then the
+		 * iterator is drained.
+		 */
+		err = table_iter_next_block(&next, ti);
+		table_iter_block_done(ti);
+		if (err) {
+			ti->is_finished = 1;
 			return err;
 		}
 
-		err = table_iter_next_block(&next, ti);
-		if (err != 0) {
-			ti->is_finished = 1;
-		}
-		table_iter_block_done(ti);
-		if (err != 0) {
-			return err;
-		}
 		table_iter_copy_from(ti, &next);
 		block_iter_close(&next.bi);
 	}
@@ -446,13 +452,13 @@
 static int reader_seek_linear(struct table_iter *ti,
 			      struct reftable_record *want)
 {
-	struct reftable_record rec =
-		reftable_new_record(reftable_record_type(want));
 	struct strbuf want_key = STRBUF_INIT;
 	struct strbuf got_key = STRBUF_INIT;
 	struct table_iter next = TABLE_ITER_INIT;
+	struct reftable_record rec;
 	int err = -1;
 
+	reftable_record_init(&rec, reftable_record_type(want));
 	reftable_record_key(want, &want_key);
 
 	while (1) {
@@ -510,8 +516,38 @@
 	if (err < 0)
 		goto done;
 
+	/*
+	 * The index may consist of multiple levels, where each level may have
+	 * multiple index blocks. We start by doing a linear search in the
+	 * highest layer that identifies the relevant index block as well as
+	 * the record inside that block that corresponds to our wanted key.
+	 */
 	err = reader_seek_linear(&index_iter, &want_index);
+	if (err < 0)
+		goto done;
+
+	/*
+	 * Traverse down the levels until we find a non-index entry.
+	 */
 	while (1) {
+		/*
+		 * In case we seek a record that does not exist the index iter
+		 * will tell us that the iterator is over. This works because
+		 * the last index entry of the current level will contain the
+		 * last key it knows about. So in case our seeked key is larger
+		 * than the last indexed key we know that it won't exist.
+		 *
+		 * There is one subtlety in the layout of the index section
+		 * that makes this work as expected: the highest-level index is
+		 * at end of the section and will point backwards and thus we
+		 * start reading from the end of the index section, not the
+		 * beginning.
+		 *
+		 * If that wasn't the case and the order was reversed then the
+		 * linear seek would seek into the lower levels and traverse
+		 * all levels of the index only to find out that the key does
+		 * not exist.
+		 */
 		err = table_iter_next(&index_iter, &index_result);
 		table_iter_block_done(&index_iter);
 		if (err != 0)
@@ -541,8 +577,7 @@
 
 	if (err == 0) {
 		struct table_iter empty = TABLE_ITER_INIT;
-		struct table_iter *malloced =
-			reftable_calloc(sizeof(struct table_iter));
+		struct table_iter *malloced = reftable_calloc(1, sizeof(*malloced));
 		*malloced = empty;
 		table_iter_copy_from(malloced, &next);
 		iterator_from_table_iter(it, malloced);
@@ -637,8 +672,7 @@
 int reftable_new_reader(struct reftable_reader **p,
 			struct reftable_block_source *src, char const *name)
 {
-	struct reftable_reader *rd =
-		reftable_calloc(sizeof(struct reftable_reader));
+	struct reftable_reader *rd = reftable_calloc(1, sizeof(*rd));
 	int err = init_reader(rd, src, name);
 	if (err == 0) {
 		*p = rd;
@@ -713,7 +747,7 @@
 					      uint8_t *oid)
 {
 	struct table_iter ti_empty = TABLE_ITER_INIT;
-	struct table_iter *ti = reftable_calloc(sizeof(struct table_iter));
+	struct table_iter *ti = reftable_calloc(1, sizeof(*ti));
 	struct filtering_ref_iterator *filter = NULL;
 	struct filtering_ref_iterator empty = FILTERING_REF_ITERATOR_INIT;
 	int oid_len = hash_size(r->hash_id);
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index 469ab79..a6dbd21 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -11,7 +11,6 @@
 #include "basics.h"
 #include "block.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "test_framework.h"
@@ -52,26 +51,25 @@
 		.hash_id = hash_id,
 	};
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
 	struct reftable_ref_record ref = { NULL };
 	int i = 0, n;
 	struct reftable_log_record log = { NULL };
 	const struct reftable_stats *stats = NULL;
-	*names = reftable_calloc(sizeof(char *) * (N + 1));
+
+	REFTABLE_CALLOC_ARRAY(*names, N + 1);
+
 	reftable_writer_set_limits(w, update_index, update_index);
 	for (i = 0; i < N; i++) {
-		uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
 		char name[100];
 		int n;
 
-		set_test_hash(hash, i);
-
 		snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
 
 		ref.refname = name;
 		ref.update_index = update_index;
 		ref.value_type = REFTABLE_REF_VAL1;
-		ref.value.val1 = hash;
+		set_test_hash(ref.value.val1, i);
 		(*names)[i] = xstrdup(name);
 
 		n = reftable_writer_add_ref(w, &ref);
@@ -79,18 +77,15 @@
 	}
 
 	for (i = 0; i < N; i++) {
-		uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
 		char name[100];
 		int n;
 
-		set_test_hash(hash, i);
-
 		snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
 
 		log.refname = name;
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
-		log.value.update.new_hash = hash;
+		set_test_hash(log.value.update.new_hash, i);
 		log.value.update.message = "message";
 
 		n = reftable_writer_add_log(w, &log);
@@ -134,18 +129,15 @@
 					   .message = "commit: 9\n",
 				   } } };
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	/* This tests buffer extension for log compression. Must use a random
 	   hash, to ensure that the compressed part is larger than the original.
 	*/
-	uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
 	for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
-		hash1[i] = (uint8_t)(rand() % 256);
-		hash2[i] = (uint8_t)(rand() % 256);
+		log.value.update.old_hash[i] = (uint8_t)(git_rand() % 256);
+		log.value.update.new_hash[i] = (uint8_t)(git_rand() % 256);
 	}
-	log.value.update.old_hash = hash1;
-	log.value.update.new_hash = hash2;
 	reftable_writer_set_limits(w, update_index, update_index);
 	err = reftable_writer_add_log(w, &log);
 	EXPECT_ERR(err);
@@ -163,25 +155,26 @@
 		.block_size = ARRAY_SIZE(msg),
 	};
 	int err;
-	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
-			.update_index = 0xa,
-			.value_type = REFTABLE_LOG_UPDATE,
-			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
-					   .tz_offset = 100,
-					   .time = 0x5e430672,
-					   .message = msg,
-				   } } };
+	struct reftable_log_record log = {
+		.refname = "refs/heads/master",
+		.update_index = 0xa,
+		.value_type = REFTABLE_LOG_UPDATE,
+		.value = {
+			.update = {
+				.old_hash = { 1 },
+				.new_hash = { 2 },
+				.name = "Han-Wen Nienhuys",
+				.email = "hanwen@google.com",
+				.tz_offset = 100,
+				.time = 0x5e430672,
+				.message = msg,
+			},
+		},
+	};
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
-
-	uint8_t hash1[GIT_SHA1_RAWSZ]  = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 };
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	memset(msg, 'x', sizeof(msg) - 1);
-	log.value.update.old_hash = hash1;
-	log.value.update.new_hash = hash2;
 	reftable_writer_set_limits(w, update_index, update_index);
 	err = reftable_writer_add_log(w, &log);
 	EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR);
@@ -192,7 +185,7 @@
 static void test_log_write_read(void)
 {
 	int N = 2;
-	char **names = reftable_calloc(sizeof(char *) * (N + 1));
+	char **names = reftable_calloc(N + 1, sizeof(*names));
 	int err;
 	struct reftable_write_options opts = {
 		.block_size = 256,
@@ -206,7 +199,7 @@
 	struct reftable_block_source source = { NULL };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	const struct reftable_stats *stats = NULL;
 	reftable_writer_set_limits(w, 0, N);
 	for (i = 0; i < N; i++) {
@@ -221,16 +214,13 @@
 		EXPECT_ERR(err);
 	}
 	for (i = 0; i < N; i++) {
-		uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
 		struct reftable_log_record log = { NULL };
-		set_test_hash(hash1, i);
-		set_test_hash(hash2, i + 1);
 
 		log.refname = names[i];
 		log.update_index = i;
 		log.value_type = REFTABLE_LOG_UPDATE;
-		log.value.update.old_hash = hash1;
-		log.value.update.new_hash = hash2;
+		set_test_hash(log.value.update.old_hash, i);
+		set_test_hash(log.value.update.new_hash, i + 1);
 
 		err = reftable_writer_add_log(w, &log);
 		EXPECT_ERR(err);
@@ -298,20 +288,17 @@
 	struct reftable_block_source source = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	const struct reftable_stats *stats = NULL;
-	uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-	uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
 	char message[100] = { 0 };
 	int err, i, n;
-
 	struct reftable_log_record log = {
 		.refname = "refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
-				.new_hash = hash1,
-				.old_hash = hash2,
+				.new_hash = { 1 },
+				.old_hash = { 2 },
 				.name = "My Name",
 				.email = "myname@invalid",
 				.message = message,
@@ -320,7 +307,7 @@
 	};
 
 	for (i = 0; i < sizeof(message) - 1; i++)
-		message[i] = (uint8_t)(rand() % 64 + ' ');
+		message[i] = (uint8_t)(git_rand() % 64 + ' ');
 
 	reftable_writer_set_limits(w, 1, 1);
 
@@ -523,7 +510,7 @@
 static void test_table_refs_for(int indexed)
 {
 	int N = 50;
-	char **want_names = reftable_calloc(sizeof(char *) * (N + 1));
+	char **want_names = reftable_calloc(N + 1, sizeof(*want_names));
 	int want_names_len = 0;
 	uint8_t want_hash[GIT_SHA1_RAWSZ];
 
@@ -539,7 +526,7 @@
 
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	struct reftable_iterator it = { NULL };
 	int j;
@@ -550,8 +537,6 @@
 		uint8_t hash[GIT_SHA1_RAWSZ];
 		char fill[51] = { 0 };
 		char name[100];
-		uint8_t hash1[GIT_SHA1_RAWSZ];
-		uint8_t hash2[GIT_SHA1_RAWSZ];
 		struct reftable_ref_record ref = { NULL };
 
 		memset(hash, i, sizeof(hash));
@@ -561,11 +546,9 @@
 		name[40] = 0;
 		ref.refname = name;
 
-		set_test_hash(hash1, i / 4);
-		set_test_hash(hash2, 3 + i / 4);
 		ref.value_type = REFTABLE_REF_VAL2;
-		ref.value.val2.value = hash1;
-		ref.value.val2.target_value = hash2;
+		set_test_hash(ref.value.val2.value, i / 4);
+		set_test_hash(ref.value.val2.target_value, 3 + i / 4);
 
 		/* 80 bytes / entry, so 3 entries per block. Yields 17
 		 */
@@ -573,8 +556,8 @@
 		n = reftable_writer_add_ref(w, &ref);
 		EXPECT(n == 0);
 
-		if (!memcmp(hash1, want_hash, GIT_SHA1_RAWSZ) ||
-		    !memcmp(hash2, want_hash, GIT_SHA1_RAWSZ)) {
+		if (!memcmp(ref.value.val2.value, want_hash, GIT_SHA1_RAWSZ) ||
+		    !memcmp(ref.value.val2.target_value, want_hash, GIT_SHA1_RAWSZ)) {
 			want_names[want_names_len++] = xstrdup(name);
 		}
 	}
@@ -636,7 +619,7 @@
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_block_source source = { NULL };
 	struct reftable_reader *rd = NULL;
 	struct reftable_ref_record rec = { NULL };
@@ -674,12 +657,11 @@
 	};
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
-	uint8_t hash[GIT_SHA1_RAWSZ] = {42};
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
-		.value.val1 = hash,
+		.value.val1 = {42},
 	};
 	int err;
 	int i;
@@ -710,12 +692,11 @@
 	};
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
-	uint8_t hash[GIT_SHA1_RAWSZ] = {42};
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
-		.value.val1 = hash,
+		.value.val1 = {42},
 	};
 	int err;
 	int i;
@@ -745,7 +726,7 @@
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
 		.refname = "",
 		.update_index = 1,
@@ -768,7 +749,7 @@
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record refs[2] = {
 		{
 			.refname = "b",
@@ -798,6 +779,138 @@
 	strbuf_release(&buf);
 }
 
+static void test_write_multiple_indices(void)
+{
+	struct reftable_write_options opts = {
+		.block_size = 100,
+	};
+	struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
+	struct reftable_block_source source = { 0 };
+	struct reftable_iterator it = { 0 };
+	const struct reftable_stats *stats;
+	struct reftable_writer *writer;
+	struct reftable_reader *reader;
+	int err, i;
+
+	writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts);
+	reftable_writer_set_limits(writer, 1, 1);
+	for (i = 0; i < 100; i++) {
+		struct reftable_ref_record ref = {
+			.update_index = 1,
+			.value_type = REFTABLE_REF_VAL1,
+			.value.val1 = {i},
+		};
+
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/heads/%04d", i);
+		ref.refname = buf.buf,
+
+		err = reftable_writer_add_ref(writer, &ref);
+		EXPECT_ERR(err);
+	}
+
+	for (i = 0; i < 100; i++) {
+		struct reftable_log_record log = {
+			.update_index = 1,
+			.value_type = REFTABLE_LOG_UPDATE,
+			.value.update = {
+				.old_hash = { i },
+				.new_hash = { i },
+			},
+		};
+
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/heads/%04d", i);
+		log.refname = buf.buf,
+
+		err = reftable_writer_add_log(writer, &log);
+		EXPECT_ERR(err);
+	}
+
+	reftable_writer_close(writer);
+
+	/*
+	 * The written data should be sufficiently large to result in indices
+	 * for each of the block types.
+	 */
+	stats = reftable_writer_stats(writer);
+	EXPECT(stats->ref_stats.index_offset > 0);
+	EXPECT(stats->obj_stats.index_offset > 0);
+	EXPECT(stats->log_stats.index_offset > 0);
+
+	block_source_from_strbuf(&source, &writer_buf);
+	err = reftable_new_reader(&reader, &source, "filename");
+	EXPECT_ERR(err);
+
+	/*
+	 * Seeking the log uses the log index now. In case there is any
+	 * confusion regarding indices we would notice here.
+	 */
+	err = reftable_reader_seek_log(reader, &it, "");
+	EXPECT_ERR(err);
+
+	reftable_iterator_destroy(&it);
+	reftable_writer_free(writer);
+	reftable_reader_free(reader);
+	strbuf_release(&writer_buf);
+	strbuf_release(&buf);
+}
+
+static void test_write_multi_level_index(void)
+{
+	struct reftable_write_options opts = {
+		.block_size = 100,
+	};
+	struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
+	struct reftable_block_source source = { 0 };
+	struct reftable_iterator it = { 0 };
+	const struct reftable_stats *stats;
+	struct reftable_writer *writer;
+	struct reftable_reader *reader;
+	int err;
+
+	writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts);
+	reftable_writer_set_limits(writer, 1, 1);
+	for (size_t i = 0; i < 200; i++) {
+		struct reftable_ref_record ref = {
+			.update_index = 1,
+			.value_type = REFTABLE_REF_VAL1,
+			.value.val1 = {i},
+		};
+
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "refs/heads/%03" PRIuMAX, (uintmax_t)i);
+		ref.refname = buf.buf,
+
+		err = reftable_writer_add_ref(writer, &ref);
+		EXPECT_ERR(err);
+	}
+	reftable_writer_close(writer);
+
+	/*
+	 * The written refs should be sufficiently large to result in a
+	 * multi-level index.
+	 */
+	stats = reftable_writer_stats(writer);
+	EXPECT(stats->ref_stats.max_index_level == 2);
+
+	block_source_from_strbuf(&source, &writer_buf);
+	err = reftable_new_reader(&reader, &source, "filename");
+	EXPECT_ERR(err);
+
+	/*
+	 * Seeking the last ref should work as expected.
+	 */
+	err = reftable_reader_seek_ref(reader, &it, "refs/heads/199");
+	EXPECT_ERR(err);
+
+	reftable_iterator_destroy(&it);
+	reftable_writer_free(writer);
+	reftable_reader_free(reader);
+	strbuf_release(&writer_buf);
+	strbuf_release(&buf);
+}
+
 static void test_corrupt_table_empty(void)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -847,5 +960,7 @@
 	RUN_TEST(test_log_overflow);
 	RUN_TEST(test_write_object_id_length);
 	RUN_TEST(test_write_object_id_min_length);
+	RUN_TEST(test_write_multiple_indices);
+	RUN_TEST(test_write_multi_level_index);
 	return 0;
 }
diff --git a/reftable/record.c b/reftable/record.c
index fbaa1fb..5506f3e 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -76,7 +76,7 @@
 	return 0;
 }
 
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec)
 {
 	switch (rec->value_type) {
 	case REFTABLE_REF_VAL1:
@@ -88,7 +88,7 @@
 	}
 }
 
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec)
 {
 	switch (rec->value_type) {
 	case REFTABLE_REF_VAL2:
@@ -159,34 +159,49 @@
 	return start.len - dest.len;
 }
 
-int reftable_decode_key(struct strbuf *key, uint8_t *extra,
-			struct strbuf last_key, struct string_view in)
+int reftable_decode_keylen(struct string_view in,
+			   uint64_t *prefix_len,
+			   uint64_t *suffix_len,
+			   uint8_t *extra)
 {
-	int start_len = in.len;
-	uint64_t prefix_len = 0;
-	uint64_t suffix_len = 0;
-	int n = get_var_int(&prefix_len, &in);
+	size_t start_len = in.len;
+	int n;
+
+	n = get_var_int(prefix_len, &in);
 	if (n < 0)
 		return -1;
 	string_view_consume(&in, n);
 
-	if (prefix_len > last_key.len)
-		return -1;
-
-	n = get_var_int(&suffix_len, &in);
+	n = get_var_int(suffix_len, &in);
 	if (n <= 0)
 		return -1;
 	string_view_consume(&in, n);
 
-	*extra = (uint8_t)(suffix_len & 0x7);
-	suffix_len >>= 3;
+	*extra = (uint8_t)(*suffix_len & 0x7);
+	*suffix_len >>= 3;
 
-	if (in.len < suffix_len)
+	return start_len - in.len;
+}
+
+int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+			struct string_view in)
+{
+	int start_len = in.len;
+	uint64_t prefix_len = 0;
+	uint64_t suffix_len = 0;
+	int n;
+
+	n = reftable_decode_keylen(in, &prefix_len, &suffix_len, extra);
+	if (n < 0)
+		return -1;
+	string_view_consume(&in, n);
+
+	if (in.len < suffix_len ||
+	    prefix_len > last_key->len)
 		return -1;
 
-	strbuf_reset(key);
-	strbuf_add(key, last_key.buf, prefix_len);
-	strbuf_add(key, in.buf, suffix_len);
+	strbuf_setlen(last_key, prefix_len);
+	strbuf_add(last_key, in.buf, suffix_len);
 	string_view_consume(&in, suffix_len);
 
 	return start_len - in.len;
@@ -205,27 +220,36 @@
 {
 	struct reftable_ref_record *ref = rec;
 	const struct reftable_ref_record *src = src_rec;
+	char *refname = NULL;
+	size_t refname_cap = 0;
+
 	assert(hash_size > 0);
 
-	/* This is simple and correct, but we could probably reuse the hash
-	 * fields. */
+	SWAP(refname, ref->refname);
+	SWAP(refname_cap, ref->refname_cap);
 	reftable_ref_record_release(ref);
+	SWAP(ref->refname, refname);
+	SWAP(ref->refname_cap, refname_cap);
+
 	if (src->refname) {
-		ref->refname = xstrdup(src->refname);
+		size_t refname_len = strlen(src->refname);
+
+		REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
+				    ref->refname_cap);
+		memcpy(ref->refname, src->refname, refname_len);
+		ref->refname[refname_len] = 0;
 	}
+
 	ref->update_index = src->update_index;
 	ref->value_type = src->value_type;
 	switch (src->value_type) {
 	case REFTABLE_REF_DELETION:
 		break;
 	case REFTABLE_REF_VAL1:
-		ref->value.val1 = reftable_malloc(hash_size);
 		memcpy(ref->value.val1, src->value.val1, hash_size);
 		break;
 	case REFTABLE_REF_VAL2:
-		ref->value.val2.value = reftable_malloc(hash_size);
 		memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
-		ref->value.val2.target_value = reftable_malloc(hash_size);
 		memcpy(ref->value.val2.target_value,
 		       src->value.val2.target_value, hash_size);
 		break;
@@ -242,7 +266,7 @@
 	return 'a' + (c - 10);
 }
 
-static void hex_format(char *dest, uint8_t *src, int hash_size)
+static void hex_format(char *dest, const unsigned char *src, int hash_size)
 {
 	assert(hash_size > 0);
 	if (src) {
@@ -299,11 +323,8 @@
 		reftable_free(ref->value.symref);
 		break;
 	case REFTABLE_REF_VAL2:
-		reftable_free(ref->value.val2.target_value);
-		reftable_free(ref->value.val2.value);
 		break;
 	case REFTABLE_REF_VAL1:
-		reftable_free(ref->value.val1);
 		break;
 	case REFTABLE_REF_DELETION:
 		break;
@@ -369,24 +390,33 @@
 
 static int reftable_ref_record_decode(void *rec, struct strbuf key,
 				      uint8_t val_type, struct string_view in,
-				      int hash_size)
+				      int hash_size, struct strbuf *scratch)
 {
 	struct reftable_ref_record *r = rec;
 	struct string_view start = in;
 	uint64_t update_index = 0;
-	int n = get_var_int(&update_index, &in);
+	const char *refname = NULL;
+	size_t refname_cap = 0;
+	int n;
+
+	assert(hash_size > 0);
+
+	n = get_var_int(&update_index, &in);
 	if (n < 0)
 		return n;
 	string_view_consume(&in, n);
 
+	SWAP(refname, r->refname);
+	SWAP(refname_cap, r->refname_cap);
 	reftable_ref_record_release(r);
+	SWAP(r->refname, refname);
+	SWAP(r->refname_cap, refname_cap);
 
-	assert(hash_size > 0);
-
-	r->refname = reftable_realloc(r->refname, key.len + 1);
+	REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
 	memcpy(r->refname, key.buf, key.len);
-	r->update_index = update_index;
 	r->refname[key.len] = 0;
+
+	r->update_index = update_index;
 	r->value_type = val_type;
 	switch (val_type) {
 	case REFTABLE_REF_VAL1:
@@ -394,7 +424,6 @@
 			return -1;
 		}
 
-		r->value.val1 = reftable_malloc(hash_size);
 		memcpy(r->value.val1, in.buf, hash_size);
 		string_view_consume(&in, hash_size);
 		break;
@@ -404,23 +433,20 @@
 			return -1;
 		}
 
-		r->value.val2.value = reftable_malloc(hash_size);
 		memcpy(r->value.val2.value, in.buf, hash_size);
 		string_view_consume(&in, hash_size);
 
-		r->value.val2.target_value = reftable_malloc(hash_size);
 		memcpy(r->value.val2.target_value, in.buf, hash_size);
 		string_view_consume(&in, hash_size);
 		break;
 
 	case REFTABLE_REF_SYMREF: {
-		struct strbuf dest = STRBUF_INIT;
-		int n = decode_string(&dest, in);
+		int n = decode_string(scratch, in);
 		if (n < 0) {
 			return -1;
 		}
 		string_view_consume(&in, n);
-		r->value.symref = dest.buf;
+		r->value.symref = strbuf_detach(scratch, NULL);
 	} break;
 
 	case REFTABLE_REF_DELETION:
@@ -439,7 +465,6 @@
 		(const struct reftable_ref_record *)p);
 }
 
-
 static int reftable_ref_record_equal_void(const void *a,
 					  const void *b, int hash_size)
 {
@@ -448,6 +473,13 @@
 	return reftable_ref_record_equal(ra, rb, hash_size);
 }
 
+static int reftable_ref_record_cmp_void(const void *_a, const void *_b)
+{
+	const struct reftable_ref_record *a = _a;
+	const struct reftable_ref_record *b = _b;
+	return strcmp(a->refname, b->refname);
+}
+
 static void reftable_ref_record_print_void(const void *rec,
 					   int hash_size)
 {
@@ -464,6 +496,7 @@
 	.release = &reftable_ref_record_release_void,
 	.is_deletion = &reftable_ref_record_is_deletion_void,
 	.equal = &reftable_ref_record_equal_void,
+	.cmp = &reftable_ref_record_cmp_void,
 	.print = &reftable_ref_record_print_void,
 };
 
@@ -506,12 +539,13 @@
 		(const struct reftable_obj_record *)src_rec;
 
 	reftable_obj_record_release(obj);
-	obj->hash_prefix = reftable_malloc(src->hash_prefix_len);
+
+	REFTABLE_ALLOC_ARRAY(obj->hash_prefix, src->hash_prefix_len);
 	obj->hash_prefix_len = src->hash_prefix_len;
 	if (src->hash_prefix_len)
 		memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
 
-	obj->offsets = reftable_malloc(src->offset_len * sizeof(uint64_t));
+	REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
 	obj->offset_len = src->offset_len;
 	COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
 }
@@ -560,7 +594,7 @@
 
 static int reftable_obj_record_decode(void *rec, struct strbuf key,
 				      uint8_t val_type, struct string_view in,
-				      int hash_size)
+				      int hash_size, struct strbuf *scratch UNUSED)
 {
 	struct string_view start = in;
 	struct reftable_obj_record *r = rec;
@@ -568,7 +602,10 @@
 	int n = 0;
 	uint64_t last;
 	int j;
-	r->hash_prefix = reftable_malloc(key.len);
+
+	reftable_obj_record_release(r);
+
+	REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
 	memcpy(r->hash_prefix, key.buf, key.len);
 	r->hash_prefix_len = key.len;
 
@@ -586,7 +623,7 @@
 	if (count == 0)
 		return start.len - in.len;
 
-	r->offsets = reftable_malloc(count * sizeof(uint64_t));
+	REFTABLE_ALLOC_ARRAY(r->offsets, count);
 	r->offset_len = count;
 
 	n = get_var_int(&r->offsets[0], &in);
@@ -634,6 +671,25 @@
 	return 1;
 }
 
+static int reftable_obj_record_cmp_void(const void *_a, const void *_b)
+{
+	const struct reftable_obj_record *a = _a;
+	const struct reftable_obj_record *b = _b;
+	int cmp;
+
+	cmp = memcmp(a->hash_prefix, b->hash_prefix,
+		     a->hash_prefix_len > b->hash_prefix_len ?
+		     a->hash_prefix_len : b->hash_prefix_len);
+	if (cmp)
+		return cmp;
+
+	/*
+	 * When the prefix is the same then the object record that is longer is
+	 * considered to be bigger.
+	 */
+	return a->hash_prefix_len - b->hash_prefix_len;
+}
+
 static struct reftable_record_vtable reftable_obj_record_vtable = {
 	.key = &reftable_obj_record_key,
 	.type = BLOCK_TYPE_OBJ,
@@ -644,6 +700,7 @@
 	.release = &reftable_obj_record_release,
 	.is_deletion = &not_a_deletion,
 	.equal = &reftable_obj_record_equal_void,
+	.cmp = &reftable_obj_record_cmp_void,
 	.print = &reftable_obj_record_print,
 };
 
@@ -723,16 +780,10 @@
 				xstrdup(dst->value.update.message);
 		}
 
-		if (dst->value.update.new_hash) {
-			dst->value.update.new_hash = reftable_malloc(hash_size);
-			memcpy(dst->value.update.new_hash,
-			       src->value.update.new_hash, hash_size);
-		}
-		if (dst->value.update.old_hash) {
-			dst->value.update.old_hash = reftable_malloc(hash_size);
-			memcpy(dst->value.update.old_hash,
-			       src->value.update.old_hash, hash_size);
-		}
+		memcpy(dst->value.update.new_hash,
+		       src->value.update.new_hash, hash_size);
+		memcpy(dst->value.update.old_hash,
+		       src->value.update.old_hash, hash_size);
 		break;
 	}
 }
@@ -750,8 +801,6 @@
 	case REFTABLE_LOG_DELETION:
 		break;
 	case REFTABLE_LOG_UPDATE:
-		reftable_free(r->value.update.new_hash);
-		reftable_free(r->value.update.old_hash);
 		reftable_free(r->value.update.name);
 		reftable_free(r->value.update.email);
 		reftable_free(r->value.update.message);
@@ -768,33 +817,20 @@
 	return reftable_log_record_is_deletion(log) ? 0 : 1;
 }
 
-static uint8_t zero[GIT_SHA256_RAWSZ] = { 0 };
-
 static int reftable_log_record_encode(const void *rec, struct string_view s,
 				      int hash_size)
 {
 	const struct reftable_log_record *r = rec;
 	struct string_view start = s;
 	int n = 0;
-	uint8_t *oldh = NULL;
-	uint8_t *newh = NULL;
 	if (reftable_log_record_is_deletion(r))
 		return 0;
 
-	oldh = r->value.update.old_hash;
-	newh = r->value.update.new_hash;
-	if (!oldh) {
-		oldh = zero;
-	}
-	if (!newh) {
-		newh = zero;
-	}
-
 	if (s.len < 2 * hash_size)
 		return -1;
 
-	memcpy(s.buf, oldh, hash_size);
-	memcpy(s.buf + hash_size, newh, hash_size);
+	memcpy(s.buf, r->value.update.old_hash, hash_size);
+	memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
 	string_view_consume(&s, 2 * hash_size);
 
 	n = encode_string(r->value.update.name ? r->value.update.name : "", s);
@@ -830,19 +866,18 @@
 
 static int reftable_log_record_decode(void *rec, struct strbuf key,
 				      uint8_t val_type, struct string_view in,
-				      int hash_size)
+				      int hash_size, struct strbuf *scratch)
 {
 	struct string_view start = in;
 	struct reftable_log_record *r = rec;
 	uint64_t max = 0;
 	uint64_t ts = 0;
-	struct strbuf dest = STRBUF_INIT;
 	int n;
 
 	if (key.len <= 9 || key.buf[key.len - 9] != 0)
 		return REFTABLE_FORMAT_ERROR;
 
-	r->refname = reftable_realloc(r->refname, key.len - 8);
+	REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
 	memcpy(r->refname, key.buf, key.len - 8);
 	ts = get_be64(key.buf + key.len - 8);
 
@@ -851,9 +886,8 @@
 	if (val_type != r->value_type) {
 		switch (r->value_type) {
 		case REFTABLE_LOG_UPDATE:
-			FREE_AND_NULL(r->value.update.old_hash);
-			FREE_AND_NULL(r->value.update.new_hash);
 			FREE_AND_NULL(r->value.update.message);
+			r->value.update.message_cap = 0;
 			FREE_AND_NULL(r->value.update.email);
 			FREE_AND_NULL(r->value.update.name);
 			break;
@@ -869,36 +903,43 @@
 	if (in.len < 2 * hash_size)
 		return REFTABLE_FORMAT_ERROR;
 
-	r->value.update.old_hash =
-		reftable_realloc(r->value.update.old_hash, hash_size);
-	r->value.update.new_hash =
-		reftable_realloc(r->value.update.new_hash, hash_size);
-
 	memcpy(r->value.update.old_hash, in.buf, hash_size);
 	memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
 
 	string_view_consume(&in, 2 * hash_size);
 
-	n = decode_string(&dest, in);
+	n = decode_string(scratch, in);
 	if (n < 0)
 		goto done;
 	string_view_consume(&in, n);
 
-	r->value.update.name =
-		reftable_realloc(r->value.update.name, dest.len + 1);
-	memcpy(r->value.update.name, dest.buf, dest.len);
-	r->value.update.name[dest.len] = 0;
+	/*
+	 * In almost all cases we can expect the reflog name to not change for
+	 * reflog entries as they are tied to the local identity, not to the
+	 * target commits. As an optimization for this common case we can thus
+	 * skip copying over the name in case it's accurate already.
+	 */
+	if (!r->value.update.name ||
+	    strcmp(r->value.update.name, scratch->buf)) {
+		r->value.update.name =
+			reftable_realloc(r->value.update.name, scratch->len + 1);
+		memcpy(r->value.update.name, scratch->buf, scratch->len);
+		r->value.update.name[scratch->len] = 0;
+	}
 
-	strbuf_reset(&dest);
-	n = decode_string(&dest, in);
+	n = decode_string(scratch, in);
 	if (n < 0)
 		goto done;
 	string_view_consume(&in, n);
 
-	r->value.update.email =
-		reftable_realloc(r->value.update.email, dest.len + 1);
-	memcpy(r->value.update.email, dest.buf, dest.len);
-	r->value.update.email[dest.len] = 0;
+	/* Same as above, but for the reflog email. */
+	if (!r->value.update.email ||
+	    strcmp(r->value.update.email, scratch->buf)) {
+		r->value.update.email =
+			reftable_realloc(r->value.update.email, scratch->len + 1);
+		memcpy(r->value.update.email, scratch->buf, scratch->len);
+		r->value.update.email[scratch->len] = 0;
+	}
 
 	ts = 0;
 	n = get_var_int(&ts, &in);
@@ -912,22 +953,19 @@
 	r->value.update.tz_offset = get_be16(in.buf);
 	string_view_consume(&in, 2);
 
-	strbuf_reset(&dest);
-	n = decode_string(&dest, in);
+	n = decode_string(scratch, in);
 	if (n < 0)
 		goto done;
 	string_view_consume(&in, n);
 
-	r->value.update.message =
-		reftable_realloc(r->value.update.message, dest.len + 1);
-	memcpy(r->value.update.message, dest.buf, dest.len);
-	r->value.update.message[dest.len] = 0;
+	REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
+			    r->value.update.message_cap);
+	memcpy(r->value.update.message, scratch->buf, scratch->len);
+	r->value.update.message[scratch->len] = 0;
 
-	strbuf_release(&dest);
 	return start.len - in.len;
 
 done:
-	strbuf_release(&dest);
 	return REFTABLE_FORMAT_ERROR;
 }
 
@@ -943,17 +981,6 @@
 	return 0 == strcmp(a, b);
 }
 
-static int zero_hash_eq(uint8_t *a, uint8_t *b, int sz)
-{
-	if (!a)
-		a = zero;
-
-	if (!b)
-		b = zero;
-
-	return !memcmp(a, b, sz);
-}
-
 static int reftable_log_record_equal_void(const void *a,
 					  const void *b, int hash_size)
 {
@@ -962,6 +989,22 @@
 					 hash_size);
 }
 
+static int reftable_log_record_cmp_void(const void *_a, const void *_b)
+{
+	const struct reftable_log_record *a = _a;
+	const struct reftable_log_record *b = _b;
+	int cmp = strcmp(a->refname, b->refname);
+	if (cmp)
+		return cmp;
+
+	/*
+	 * Note that the comparison here is reversed. This is because the
+	 * update index is reversed when comparing keys. For reference, see how
+	 * we handle this in reftable_log_record_key()`.
+	 */
+	return b->update_index - a->update_index;
+}
+
 int reftable_log_record_equal(const struct reftable_log_record *a,
 			      const struct reftable_log_record *b, int hash_size)
 {
@@ -981,10 +1024,10 @@
 				  b->value.update.email) &&
 		       null_streq(a->value.update.message,
 				  b->value.update.message) &&
-		       zero_hash_eq(a->value.update.old_hash,
-				    b->value.update.old_hash, hash_size) &&
-		       zero_hash_eq(a->value.update.new_hash,
-				    b->value.update.new_hash, hash_size);
+		       !memcmp(a->value.update.old_hash,
+			       b->value.update.old_hash, hash_size) &&
+		       !memcmp(a->value.update.new_hash,
+			       b->value.update.new_hash, hash_size);
 	}
 
 	abort();
@@ -1011,6 +1054,7 @@
 	.release = &reftable_log_record_release_void,
 	.is_deletion = &reftable_log_record_is_deletion_void,
 	.equal = &reftable_log_record_equal_void,
+	.cmp = &reftable_log_record_cmp_void,
 	.print = &reftable_log_record_print_void,
 };
 
@@ -1061,7 +1105,7 @@
 
 static int reftable_index_record_decode(void *rec, struct strbuf key,
 					uint8_t val_type, struct string_view in,
-					int hash_size)
+					int hash_size, struct strbuf *scratch UNUSED)
 {
 	struct string_view start = in;
 	struct reftable_index_record *r = rec;
@@ -1086,6 +1130,13 @@
 	return ia->offset == ib->offset && !strbuf_cmp(&ia->last_key, &ib->last_key);
 }
 
+static int reftable_index_record_cmp(const void *_a, const void *_b)
+{
+	const struct reftable_index_record *a = _a;
+	const struct reftable_index_record *b = _b;
+	return strbuf_cmp(&a->last_key, &b->last_key);
+}
+
 static void reftable_index_record_print(const void *rec, int hash_size)
 {
 	const struct reftable_index_record *idx = rec;
@@ -1103,6 +1154,7 @@
 	.release = &reftable_index_record_release,
 	.is_deletion = &not_a_deletion,
 	.equal = &reftable_index_record_equal,
+	.cmp = &reftable_index_record_cmp,
 	.print = &reftable_index_record_print,
 };
 
@@ -1111,11 +1163,6 @@
 	reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
 }
 
-uint8_t reftable_record_type(struct reftable_record *rec)
-{
-	return rec->type;
-}
-
 int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
 			   int hash_size)
 {
@@ -1139,10 +1186,12 @@
 }
 
 int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
-			   uint8_t extra, struct string_view src, int hash_size)
+			   uint8_t extra, struct string_view src, int hash_size,
+			   struct strbuf *scratch)
 {
 	return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
-						   key, extra, src, hash_size);
+						   key, extra, src, hash_size,
+						   scratch);
 }
 
 void reftable_record_release(struct reftable_record *rec)
@@ -1156,6 +1205,14 @@
 		reftable_record_data(rec));
 }
 
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b)
+{
+	if (a->type != b->type)
+		BUG("cannot compare reftable records of different type");
+	return reftable_record_vtable(a)->cmp(
+		reftable_record_data(a), reftable_record_data(b));
+}
+
 int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size)
 {
 	if (a->type != b->type)
@@ -1164,7 +1221,7 @@
 		reftable_record_data(a), reftable_record_data(b), hash_size);
 }
 
-static int hash_equal(uint8_t *a, uint8_t *b, int hash_size)
+static int hash_equal(const unsigned char *a, const unsigned char *b, int hash_size)
 {
 	if (a && b)
 		return !memcmp(a, b, hash_size);
@@ -1229,12 +1286,6 @@
 	return (log->value_type == REFTABLE_LOG_DELETION);
 }
 
-void string_view_consume(struct string_view *s, int n)
-{
-	s->buf += n;
-	s->len -= n;
-}
-
 static void *reftable_record_data(struct reftable_record *rec)
 {
 	switch (rec->type) {
@@ -1266,45 +1317,22 @@
 	abort();
 }
 
-struct reftable_record reftable_new_record(uint8_t typ)
+void reftable_record_init(struct reftable_record *rec, uint8_t typ)
 {
-	struct reftable_record clean = {
-		.type = typ,
-	};
+	memset(rec, 0, sizeof(*rec));
+	rec->type = typ;
 
-	/* the following is involved, but the naive solution (just return
-	 * `clean` as is, except for BLOCK_TYPE_INDEX), returns a garbage
-	 * clean.u.obj.offsets pointer on Windows VS CI.  Go figure.
-	 */
 	switch (typ) {
-	case BLOCK_TYPE_OBJ:
-	{
-		struct reftable_obj_record obj = { 0 };
-		clean.u.obj = obj;
-		break;
-	}
-	case BLOCK_TYPE_INDEX:
-	{
-		struct reftable_index_record idx = {
-			.last_key = STRBUF_INIT,
-		};
-		clean.u.idx = idx;
-		break;
-	}
 	case BLOCK_TYPE_REF:
-	{
-		struct reftable_ref_record ref = { 0 };
-		clean.u.ref = ref;
-		break;
-	}
 	case BLOCK_TYPE_LOG:
-	{
-		struct reftable_log_record log = { 0 };
-		clean.u.log = log;
-		break;
+	case BLOCK_TYPE_OBJ:
+		return;
+	case BLOCK_TYPE_INDEX:
+		strbuf_init(&rec->u.idx.last_key, 0);
+		return;
+	default:
+		BUG("unhandled record type");
 	}
-	}
-	return clean;
 }
 
 void reftable_record_print(struct reftable_record *rec, int hash_size)
diff --git a/reftable/record.h b/reftable/record.h
index fd80cd4..d778133 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -25,7 +25,11 @@
 };
 
 /* Advance `s.buf` by `n`, and decrease length. */
-void string_view_consume(struct string_view *s, int n);
+static inline void string_view_consume(struct string_view *s, int n)
+{
+	s->buf += n;
+	s->len -= n;
+}
 
 /* utilities for de/encoding varints */
 
@@ -51,7 +55,8 @@
 
 	/* decode data from `src` into the record. */
 	int (*decode)(void *rec, struct strbuf key, uint8_t extra,
-		      struct string_view src, int hash_size);
+		      struct string_view src, int hash_size,
+		      struct strbuf *scratch);
 
 	/* deallocate and null the record. */
 	void (*release)(void *rec);
@@ -62,6 +67,12 @@
 	/* Are two records equal? This assumes they have the same type. Returns 0 for non-equal. */
 	int (*equal)(const void *a, const void *b, int hash_size);
 
+	/*
+	 * Compare keys of two records with each other. The records must have
+	 * the same type.
+	 */
+	int (*cmp)(const void *a, const void *b);
+
 	/* Print on stdout, for debugging. */
 	void (*print)(const void *rec, int hash_size);
 };
@@ -69,18 +80,24 @@
 /* returns true for recognized block types. Block start with the block type. */
 int reftable_is_block_type(uint8_t typ);
 
-/* return an initialized record for the given type */
-struct reftable_record reftable_new_record(uint8_t typ);
-
 /* Encode `key` into `dest`. Sets `is_restart` to indicate a restart. Returns
  * number of bytes written. */
 int reftable_encode_key(int *is_restart, struct string_view dest,
 			struct strbuf prev_key, struct strbuf key,
 			uint8_t extra);
 
-/* Decode into `key` and `extra` from `in` */
-int reftable_decode_key(struct strbuf *key, uint8_t *extra,
-			struct strbuf last_key, struct string_view in);
+/* Decode a record's key lengths. */
+int reftable_decode_keylen(struct string_view in,
+			   uint64_t *prefix_len,
+			   uint64_t *suffix_len,
+			   uint8_t *extra);
+
+/*
+ * Decode into `last_key` and `extra` from `in`. `last_key` is expected to
+ * contain the decoded key of the preceding record, if any.
+ */
+int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+			struct string_view in);
 
 /* reftable_index_record are used internally to speed up lookups. */
 struct reftable_index_record {
@@ -100,8 +117,8 @@
 /* record is a generic wrapper for different types of records. It is normally
  * created on the stack, or embedded within another struct. If the type is
  * known, a fresh instance can be initialized explicitly. Otherwise, use
- * reftable_new_record() to initialize generically (as the index_record is not
- * valid as 0-initialized structure)
+ * `reftable_record_init()` to initialize generically (as the index_record is
+ * not valid as 0-initialized structure)
  */
 struct reftable_record {
 	uint8_t type;
@@ -113,11 +130,14 @@
 	} u;
 };
 
+/* Initialize the reftable record for the given type */
+void reftable_record_init(struct reftable_record *rec, uint8_t typ);
+
 /* see struct record_vtable */
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b);
 int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size);
 void reftable_record_print(struct reftable_record *rec, int hash_size);
 void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
-uint8_t reftable_record_type(struct reftable_record *rec);
 void reftable_record_copy_from(struct reftable_record *rec,
 			       struct reftable_record *src, int hash_size);
 uint8_t reftable_record_val_type(struct reftable_record *rec);
@@ -125,9 +145,14 @@
 			   int hash_size);
 int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
 			   uint8_t extra, struct string_view src,
-			   int hash_size);
+			   int hash_size, struct strbuf *scratch);
 int reftable_record_is_deletion(struct reftable_record *rec);
 
+static inline uint8_t reftable_record_type(struct reftable_record *rec)
+{
+	return rec->type;
+}
+
 /* frees and zeroes out the embedded record */
 void reftable_record_release(struct reftable_record *rec);
 
diff --git a/reftable/record_test.c b/reftable/record_test.c
index 70ae78f..c158ee7 100644
--- a/reftable/record_test.c
+++ b/reftable/record_test.c
@@ -16,11 +16,11 @@
 
 static void test_copy(struct reftable_record *rec)
 {
-	struct reftable_record copy = { 0 };
+	struct reftable_record copy;
 	uint8_t typ;
 
 	typ = reftable_record_type(rec);
-	copy = reftable_new_record(typ);
+	reftable_record_init(&copy, typ);
 	reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
 	/* do it twice to catch memory leaks */
 	reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
@@ -99,6 +99,7 @@
 
 static void test_reftable_ref_record_roundtrip(void)
 {
+	struct strbuf scratch = STRBUF_INIT;
 	int i = 0;
 
 	for (i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
@@ -119,15 +120,10 @@
 		case REFTABLE_REF_DELETION:
 			break;
 		case REFTABLE_REF_VAL1:
-			in.u.ref.value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
 			set_hash(in.u.ref.value.val1, 1);
 			break;
 		case REFTABLE_REF_VAL2:
-			in.u.ref.value.val2.value =
-				reftable_malloc(GIT_SHA1_RAWSZ);
 			set_hash(in.u.ref.value.val2.value, 1);
-			in.u.ref.value.val2.target_value =
-				reftable_malloc(GIT_SHA1_RAWSZ);
 			set_hash(in.u.ref.value.val2.target_value, 2);
 			break;
 		case REFTABLE_REF_SYMREF:
@@ -145,7 +141,7 @@
 		EXPECT(n > 0);
 
 		/* decode into a non-zero reftable_record to test for leaks. */
-		m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ);
+		m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ, &scratch);
 		EXPECT(n == m);
 
 		EXPECT(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
@@ -155,6 +151,8 @@
 		strbuf_release(&key);
 		reftable_record_release(&out);
 	}
+
+	strbuf_release(&scratch);
 }
 
 static void test_reftable_log_record_equal(void)
@@ -180,7 +178,6 @@
 static void test_reftable_log_record_roundtrip(void)
 {
 	int i;
-
 	struct reftable_log_record in[] = {
 		{
 			.refname = xstrdup("refs/heads/master"),
@@ -188,8 +185,6 @@
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value = {
 				.update = {
-					.old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
-					.new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
 					.name = xstrdup("han-wen"),
 					.email = xstrdup("hanwen@google.com"),
 					.message = xstrdup("test"),
@@ -207,15 +202,10 @@
 			.refname = xstrdup("branch"),
 			.update_index = 33,
 			.value_type = REFTABLE_LOG_UPDATE,
-			.value = {
-				.update = {
-					.old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
-					.new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
-					/* rest of fields left empty. */
-				},
-			},
 		}
 	};
+	struct strbuf scratch = STRBUF_INIT;
+
 	set_test_hash(in[0].value.update.new_hash, 1);
 	set_test_hash(in[0].value.update.old_hash, 2);
 	set_test_hash(in[2].value.update.new_hash, 3);
@@ -236,8 +226,6 @@
 				.value_type = REFTABLE_LOG_UPDATE,
 				.value = {
 					.update = {
-						.new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
-						.old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
 						.name = xstrdup("old name"),
 						.email = xstrdup("old@email"),
 						.message = xstrdup("old message"),
@@ -257,7 +245,7 @@
 		EXPECT(n >= 0);
 		valtype = reftable_record_val_type(&rec);
 		m = reftable_record_decode(&out, key, valtype, dest,
-					   GIT_SHA1_RAWSZ);
+					   GIT_SHA1_RAWSZ, &scratch);
 		EXPECT(n == m);
 
 		EXPECT(reftable_log_record_equal(&in[i], &out.u.log,
@@ -266,6 +254,8 @@
 		strbuf_release(&key);
 		reftable_record_release(&out);
 	}
+
+	strbuf_release(&scratch);
 }
 
 static void test_u24_roundtrip(void)
@@ -300,7 +290,8 @@
 	EXPECT(!restart);
 	EXPECT(n > 0);
 
-	m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
+	strbuf_addstr(&roundtrip, "refs/heads/master");
+	m = reftable_decode_key(&roundtrip, &rt_extra, dest);
 	EXPECT(n == m);
 	EXPECT(0 == strbuf_cmp(&key, &roundtrip));
 	EXPECT(rt_extra == extra);
@@ -314,23 +305,27 @@
 {
 	uint8_t testHash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 4, 0 };
 	uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
-	struct reftable_obj_record recs[3] = { {
-						       .hash_prefix = testHash1,
-						       .hash_prefix_len = 5,
-						       .offsets = till9,
-						       .offset_len = 3,
-					       },
-					       {
-						       .hash_prefix = testHash1,
-						       .hash_prefix_len = 5,
-						       .offsets = till9,
-						       .offset_len = 9,
-					       },
-					       {
-						       .hash_prefix = testHash1,
-						       .hash_prefix_len = 5,
-					       } };
+	struct reftable_obj_record recs[3] = {
+		{
+			.hash_prefix = testHash1,
+			.hash_prefix_len = 5,
+			.offsets = till9,
+			.offset_len = 3,
+		},
+		{
+			.hash_prefix = testHash1,
+			.hash_prefix_len = 5,
+			.offsets = till9,
+			.offset_len = 9,
+		},
+		{
+			.hash_prefix = testHash1,
+			.hash_prefix_len = 5,
+		},
+	};
+	struct strbuf scratch = STRBUF_INIT;
 	int i = 0;
+
 	for (i = 0; i < ARRAY_SIZE(recs); i++) {
 		uint8_t buffer[1024] = { 0 };
 		struct string_view dest = {
@@ -354,13 +349,15 @@
 		EXPECT(n > 0);
 		extra = reftable_record_val_type(&in);
 		m = reftable_record_decode(&out, key, extra, dest,
-					   GIT_SHA1_RAWSZ);
+					   GIT_SHA1_RAWSZ, &scratch);
 		EXPECT(n == m);
 
 		EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
 		strbuf_release(&key);
 		reftable_record_release(&out);
 	}
+
+	strbuf_release(&scratch);
 }
 
 static void test_reftable_index_record_roundtrip(void)
@@ -377,6 +374,7 @@
 		.buf = buffer,
 		.len = sizeof(buffer),
 	};
+	struct strbuf scratch = STRBUF_INIT;
 	struct strbuf key = STRBUF_INIT;
 	struct reftable_record out = {
 		.type = BLOCK_TYPE_INDEX,
@@ -394,13 +392,15 @@
 	EXPECT(n > 0);
 
 	extra = reftable_record_val_type(&in);
-	m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ);
+	m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ,
+				   &scratch);
 	EXPECT(m == n);
 
 	EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
 
 	reftable_record_release(&out);
 	strbuf_release(&key);
+	strbuf_release(&scratch);
 	strbuf_release(&in.u.idx.last_key);
 }
 
diff --git a/reftable/refname.c b/reftable/refname.c
index 9573496..bbfde15 100644
--- a/reftable/refname.c
+++ b/reftable/refname.c
@@ -12,15 +12,15 @@
 #include "refname.h"
 #include "reftable-iterator.h"
 
-struct find_arg {
-	char **names;
-	const char *want;
+struct refname_needle_lesseq_args {
+	char **haystack;
+	const char *needle;
 };
 
-static int find_name(size_t k, void *arg)
+static int refname_needle_lesseq(size_t k, void *_args)
 {
-	struct find_arg *f_arg = arg;
-	return strcmp(f_arg->names[k], f_arg->want) >= 0;
+	struct refname_needle_lesseq_args *args = _args;
+	return strcmp(args->needle, args->haystack[k]) <= 0;
 }
 
 static int modification_has_ref(struct modification *mod, const char *name)
@@ -29,25 +29,23 @@
 	int err = 0;
 
 	if (mod->add_len > 0) {
-		struct find_arg arg = {
-			.names = mod->add,
-			.want = name,
+		struct refname_needle_lesseq_args args = {
+			.haystack = mod->add,
+			.needle = name,
 		};
-		int idx = binsearch(mod->add_len, find_name, &arg);
-		if (idx < mod->add_len && !strcmp(mod->add[idx], name)) {
+		size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
+		if (idx < mod->add_len && !strcmp(mod->add[idx], name))
 			return 0;
-		}
 	}
 
 	if (mod->del_len > 0) {
-		struct find_arg arg = {
-			.names = mod->del,
-			.want = name,
+		struct refname_needle_lesseq_args args = {
+			.haystack = mod->del,
+			.needle = name,
 		};
-		int idx = binsearch(mod->del_len, find_name, &arg);
-		if (idx < mod->del_len && !strcmp(mod->del[idx], name)) {
+		size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
+		if (idx < mod->del_len && !strcmp(mod->del[idx], name))
 			return 1;
-		}
 	}
 
 	err = reftable_table_read_ref(&mod->tab, name, &ref);
@@ -73,11 +71,11 @@
 	int err = 0;
 
 	if (mod->add_len > 0) {
-		struct find_arg arg = {
-			.names = mod->add,
-			.want = prefix,
+		struct refname_needle_lesseq_args args = {
+			.haystack = mod->add,
+			.needle = prefix,
 		};
-		int idx = binsearch(mod->add_len, find_name, &arg);
+		size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
 		if (idx < mod->add_len &&
 		    !strncmp(prefix, mod->add[idx], strlen(prefix)))
 			goto done;
@@ -92,15 +90,14 @@
 			goto done;
 
 		if (mod->del_len > 0) {
-			struct find_arg arg = {
-				.names = mod->del,
-				.want = ref.refname,
+			struct refname_needle_lesseq_args args = {
+				.haystack = mod->del,
+				.needle = ref.refname,
 			};
-			int idx = binsearch(mod->del_len, find_name, &arg);
+			size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
 			if (idx < mod->del_len &&
-			    !strcmp(ref.refname, mod->del[idx])) {
+			    !strcmp(ref.refname, mod->del[idx]))
 				continue;
-			}
 		}
 
 		if (strncmp(ref.refname, prefix, strlen(prefix))) {
@@ -140,8 +137,8 @@
 {
 	struct modification mod = {
 		.tab = tab,
-		.add = reftable_calloc(sizeof(char *) * sz),
-		.del = reftable_calloc(sizeof(char *) * sz),
+		.add = reftable_calloc(sz, sizeof(*mod.add)),
+		.del = reftable_calloc(sz, sizeof(*mod.del)),
 	};
 	int i = 0;
 	int err = 0;
diff --git a/reftable/refname_test.c b/reftable/refname_test.c
index 8645cd9..b9cc625 100644
--- a/reftable/refname_test.c
+++ b/reftable/refname_test.c
@@ -9,7 +9,6 @@
 #include "basics.h"
 #include "block.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "refname.h"
@@ -31,7 +30,7 @@
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
-		reftable_new_writer(&strbuf_add_void, &buf, &opts);
+		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record rec = {
 		.refname = "a/b",
 		.value_type = REFTABLE_REF_SYMREF,
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 4c457aa..e9b07c9 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -25,7 +25,7 @@
 	 */
 	REFTABLE_NOT_EXIST_ERROR = -4,
 
-	/* Trying to write out-of-date data. */
+	/* Trying to access locked data. */
 	REFTABLE_LOCK_ERROR = -5,
 
 	/* Misuse of the API:
@@ -57,6 +57,9 @@
 	/* Entry does not fit. This can happen when writing outsize reflog
 	   messages. */
 	REFTABLE_ENTRY_TOO_BIG_ERROR = -11,
+
+	/* Trying to write out-of-date data. */
+	REFTABLE_OUTDATED_ERROR = -12,
 };
 
 /* convert the numeric error code to a string. The string should not be
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 1a6d169..c91a2d8 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -33,7 +33,7 @@
    the stack array.
 */
 int reftable_new_merged_table(struct reftable_merged_table **dest,
-			      struct reftable_table *stack, int n,
+			      struct reftable_table *stack, size_t n,
 			      uint32_t hash_id);
 
 /* returns an iterator positioned just before 'name' */
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index 67104f8..2a2943c 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_RECORD_H
 #define REFTABLE_RECORD_H
 
+#include "hash-ll.h"
 #include <stdint.h>
 
 /*
@@ -21,6 +22,7 @@
 /* reftable_ref_record holds a ref database entry target_value */
 struct reftable_ref_record {
 	char *refname; /* Name of the ref, malloced. */
+	size_t refname_cap;
 	uint64_t update_index; /* Logical timestamp at which this value is
 				* written */
 
@@ -38,10 +40,10 @@
 #define REFTABLE_NR_REF_VALUETYPES 4
 	} value_type;
 	union {
-		uint8_t *val1; /* malloced hash. */
+		unsigned char val1[GIT_MAX_RAWSZ];
 		struct {
-			uint8_t *value; /* first value, malloced hash  */
-			uint8_t *target_value; /* second value, malloced hash */
+			unsigned char value[GIT_MAX_RAWSZ]; /* first hash  */
+			unsigned char target_value[GIT_MAX_RAWSZ]; /* second hash */
 		} val2;
 		char *symref; /* referent, malloced 0-terminated string */
 	} value;
@@ -49,11 +51,11 @@
 
 /* Returns the first hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL1 or REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec);
 
 /* Returns the second hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec);
 
 /* returns whether 'ref' represents a deletion */
 int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
@@ -72,6 +74,7 @@
 /* reftable_log_record holds a reflog entry */
 struct reftable_log_record {
 	char *refname;
+	size_t refname_cap;
 	uint64_t update_index; /* logical timestamp of a transactional update.
 				*/
 
@@ -86,13 +89,14 @@
 
 	union {
 		struct {
-			uint8_t *new_hash;
-			uint8_t *old_hash;
+			unsigned char new_hash[GIT_MAX_RAWSZ];
+			unsigned char old_hash[GIT_MAX_RAWSZ];
 			char *name;
 			char *email;
 			uint64_t time;
 			int16_t tz_offset;
 			char *message;
+			size_t message_cap;
 		} update;
 	} value;
 };
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index db8de19..7c7cae5 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -88,6 +88,7 @@
 /* reftable_new_writer creates a new writer */
 struct reftable_writer *
 reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
+		    int (*flush_func)(void *),
 		    void *writer_arg, struct reftable_write_options *opts);
 
 /* Set the range of update indices for the records we will add. When writing a
diff --git a/reftable/stack.c b/reftable/stack.c
index ddbdf1b..dde50b6 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -8,6 +8,7 @@
 
 #include "stack.h"
 
+#include "../write-or-die.h"
 #include "system.h"
 #include "merged.h"
 #include "reader.h"
@@ -16,13 +17,15 @@
 #include "reftable-record.h"
 #include "reftable-merged.h"
 #include "writer.h"
+#include "tempfile.h"
 
 static int stack_try_add(struct reftable_stack *st,
 			 int (*write_table)(struct reftable_writer *wr,
 					    void *arg),
 			 void *arg);
 static int stack_write_compact(struct reftable_stack *st,
-			       struct reftable_writer *wr, int first, int last,
+			       struct reftable_writer *wr,
+			       size_t first, size_t last,
 			       struct reftable_log_expiry_config *config);
 static int stack_check_addition(struct reftable_stack *st,
 				const char *new_tab_name);
@@ -42,14 +45,20 @@
 static ssize_t reftable_fd_write(void *arg, const void *data, size_t sz)
 {
 	int *fdp = (int *)arg;
-	return write(*fdp, data, sz);
+	return write_in_full(*fdp, data, sz);
+}
+
+static int reftable_fd_flush(void *arg)
+{
+	int *fdp = (int *)arg;
+
+	return fsync_component(FSYNC_COMPONENT_REFERENCE, *fdp);
 }
 
 int reftable_new_stack(struct reftable_stack **dest, const char *dir,
 		       struct reftable_write_options config)
 {
-	struct reftable_stack *p =
-		reftable_calloc(sizeof(struct reftable_stack));
+	struct reftable_stack *p = reftable_calloc(1, sizeof(*p));
 	struct strbuf list_file_name = STRBUF_INIT;
 	int err = 0;
 
@@ -64,6 +73,7 @@
 	strbuf_addstr(&list_file_name, "/tables.list");
 
 	p->list_file = strbuf_detach(&list_file_name, NULL);
+	p->list_fd = -1;
 	p->reftable_dir = xstrdup(dir);
 	p->config = config;
 
@@ -91,8 +101,8 @@
 		goto done;
 	}
 
-	buf = reftable_malloc(size + 1);
-	if (read(fd, buf, size) != size) {
+	REFTABLE_ALLOC_ARRAY(buf, size + 1);
+	if (read_in_full(fd, buf, size) != size) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
 	}
@@ -111,7 +121,7 @@
 	int err = 0;
 	if (fd < 0) {
 		if (errno == ENOENT) {
-			*namesp = reftable_calloc(sizeof(char *));
+			REFTABLE_CALLOC_ARRAY(*namesp, 1);
 			return 0;
 		}
 
@@ -173,6 +183,12 @@
 		st->readers_len = 0;
 		FREE_AND_NULL(st->readers);
 	}
+
+	if (st->list_fd >= 0) {
+		close(st->list_fd);
+		st->list_fd = -1;
+	}
+
 	FREE_AND_NULL(st->list_file);
 	FREE_AND_NULL(st->reftable_dir);
 	reftable_free(st);
@@ -182,8 +198,7 @@
 static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 						   int cur_len)
 {
-	struct reftable_reader **cur =
-		reftable_calloc(sizeof(struct reftable_reader *) * cur_len);
+	struct reftable_reader **cur = reftable_calloc(cur_len, sizeof(*cur));
 	int i = 0;
 	for (i = 0; i < cur_len; i++) {
 		cur[i] = st->readers[i];
@@ -194,17 +209,18 @@
 static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 				      int reuse_open)
 {
-	int cur_len = !st->merged ? 0 : st->merged->stack_len;
+	size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
 	struct reftable_reader **cur = stack_copy_readers(st, cur_len);
-	int err = 0;
-	int names_len = names_length(names);
+	size_t names_len = names_length(names);
 	struct reftable_reader **new_readers =
-		reftable_calloc(sizeof(struct reftable_reader *) * names_len);
+		reftable_calloc(names_len, sizeof(*new_readers));
 	struct reftable_table *new_tables =
-		reftable_calloc(sizeof(struct reftable_table) * names_len);
-	int new_readers_len = 0;
+		reftable_calloc(names_len, sizeof(*new_tables));
+	size_t new_readers_len = 0;
 	struct reftable_merged_table *new_merged = NULL;
-	int i;
+	struct strbuf table_path = STRBUF_INIT;
+	int err = 0;
+	size_t i;
 
 	while (*names) {
 		struct reftable_reader *rd = NULL;
@@ -212,24 +228,20 @@
 
 		/* this is linear; we assume compaction keeps the number of
 		   tables under control so this is not quadratic. */
-		int j = 0;
-		for (j = 0; reuse_open && j < cur_len; j++) {
-			if (cur[j] && 0 == strcmp(cur[j]->name, name)) {
-				rd = cur[j];
-				cur[j] = NULL;
+		for (i = 0; reuse_open && i < cur_len; i++) {
+			if (cur[i] && 0 == strcmp(cur[i]->name, name)) {
+				rd = cur[i];
+				cur[i] = NULL;
 				break;
 			}
 		}
 
 		if (!rd) {
 			struct reftable_block_source src = { NULL };
-			struct strbuf table_path = STRBUF_INIT;
 			stack_filename(&table_path, st, name);
 
 			err = reftable_block_source_from_file(&src,
 							      table_path.buf);
-			strbuf_release(&table_path);
-
 			if (err < 0)
 				goto done;
 
@@ -267,16 +279,13 @@
 	for (i = 0; i < cur_len; i++) {
 		if (cur[i]) {
 			const char *name = reader_name(cur[i]);
-			struct strbuf filename = STRBUF_INIT;
-			stack_filename(&filename, st, name);
+			stack_filename(&table_path, st, name);
 
 			reader_close(cur[i]);
 			reftable_reader_free(cur[i]);
 
 			/* On Windows, can only unlink after closing. */
-			unlink(filename.buf);
-
-			strbuf_release(&filename);
+			unlink(table_path.buf);
 		}
 	}
 
@@ -288,6 +297,7 @@
 	reftable_free(new_readers);
 	reftable_free(new_tables);
 	reftable_free(cur);
+	strbuf_release(&table_path);
 	return err;
 }
 
@@ -306,69 +316,134 @@
 static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 					     int reuse_open)
 {
-	struct timeval deadline = { 0 };
-	int err = gettimeofday(&deadline, NULL);
+	char **names = NULL, **names_after = NULL;
+	struct timeval deadline;
 	int64_t delay = 0;
-	int tries = 0;
+	int tries = 0, err;
+	int fd = -1;
+
+	err = gettimeofday(&deadline, NULL);
 	if (err < 0)
-		return err;
-
+		goto out;
 	deadline.tv_sec += 3;
+
 	while (1) {
-		char **names = NULL;
-		char **names_after = NULL;
-		struct timeval now = { 0 };
-		int err = gettimeofday(&now, NULL);
-		int err2 = 0;
-		if (err < 0) {
-			return err;
-		}
+		struct timeval now;
 
-		/* Only look at deadlines after the first few times. This
-		   simplifies debugging in GDB */
+		err = gettimeofday(&now, NULL);
+		if (err < 0)
+			goto out;
+
+		/*
+		 * Only look at deadlines after the first few times. This
+		 * simplifies debugging in GDB.
+		 */
 		tries++;
-		if (tries > 3 && tv_cmp(&now, &deadline) >= 0) {
-			break;
+		if (tries > 3 && tv_cmp(&now, &deadline) >= 0)
+			goto out;
+
+		fd = open(st->list_file, O_RDONLY);
+		if (fd < 0) {
+			if (errno != ENOENT) {
+				err = REFTABLE_IO_ERROR;
+				goto out;
+			}
+
+			REFTABLE_CALLOC_ARRAY(names, 1);
+		} else {
+			err = fd_read_lines(fd, &names);
+			if (err < 0)
+				goto out;
 		}
 
-		err = read_lines(st->list_file, &names);
-		if (err < 0) {
-			free_names(names);
-			return err;
-		}
 		err = reftable_stack_reload_once(st, names, reuse_open);
-		if (err == 0) {
-			free_names(names);
+		if (!err)
 			break;
-		}
-		if (err != REFTABLE_NOT_EXIST_ERROR) {
-			free_names(names);
-			return err;
-		}
+		if (err != REFTABLE_NOT_EXIST_ERROR)
+			goto out;
 
-		/* err == REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
-		   writer. Check if there was one by checking if the name list
-		   changed.
-		*/
-		err2 = read_lines(st->list_file, &names_after);
-		if (err2 < 0) {
-			free_names(names);
-			return err2;
-		}
-
+		/*
+		 * REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
+		 * writer. Check if there was one by checking if the name list
+		 * changed.
+		 */
+		err = read_lines(st->list_file, &names_after);
+		if (err < 0)
+			goto out;
 		if (names_equal(names_after, names)) {
-			free_names(names);
-			free_names(names_after);
-			return err;
+			err = REFTABLE_NOT_EXIST_ERROR;
+			goto out;
 		}
+
 		free_names(names);
+		names = NULL;
 		free_names(names_after);
+		names_after = NULL;
+		close(fd);
+		fd = -1;
 
 		delay = delay + (delay * rand()) / RAND_MAX + 1;
 		sleep_millisec(delay);
 	}
 
-	return 0;
+out:
+	/*
+	 * Invalidate the stat cache. It is sufficient to only close the file
+	 * descriptor and keep the cached stat info because we never use the
+	 * latter when the former is negative.
+	 */
+	if (st->list_fd >= 0) {
+		close(st->list_fd);
+		st->list_fd = -1;
+	}
+
+	/*
+	 * Cache stat information in case it provides a useful signal to us.
+	 * According to POSIX, "The st_ino and st_dev fields taken together
+	 * uniquely identify the file within the system." That being said,
+	 * Windows is not POSIX compliant and we do not have these fields
+	 * available. So the information we have there is insufficient to
+	 * determine whether two file descriptors point to the same file.
+	 *
+	 * While we could fall back to using other signals like the file's
+	 * mtime, those are not sufficient to avoid races. We thus refrain from
+	 * using the stat cache on such systems and fall back to the secondary
+	 * caching mechanism, which is to check whether contents of the file
+	 * have changed.
+	 *
+	 * On other systems which are POSIX compliant we must keep the file
+	 * descriptor open. This is to avoid a race condition where two
+	 * processes access the reftable stack at the same point in time:
+	 *
+	 *   1. A reads the reftable stack and caches its stat info.
+	 *
+	 *   2. B updates the stack, appending a new table to "tables.list".
+	 *      This will both use a new inode and result in a different file
+	 *      size, thus invalidating A's cache in theory.
+	 *
+	 *   3. B decides to auto-compact the stack and merges two tables. The
+	 *      file size now matches what A has cached again. Furthermore, the
+	 *      filesystem may decide to recycle the inode number of the file
+	 *      we have replaced in (2) because it is not in use anymore.
+	 *
+	 *   4. A reloads the reftable stack. Neither the inode number nor the
+	 *      file size changed. If the timestamps did not change either then
+	 *      we think the cached copy of our stack is up-to-date.
+	 *
+	 * By keeping the file descriptor open the inode number cannot be
+	 * recycled, mitigating the race.
+	 */
+	if (!err && fd >= 0 && !fstat(fd, &st->list_st) &&
+	    st->list_st.st_dev && st->list_st.st_ino) {
+		st->list_fd = fd;
+		fd = -1;
+	}
+
+	if (fd >= 0)
+		close(fd);
+	free_names(names);
+	free_names(names_after);
+	return err;
 }
 
 /* -1 = error
@@ -377,8 +452,44 @@
 static int stack_uptodate(struct reftable_stack *st)
 {
 	char **names = NULL;
-	int err = read_lines(st->list_file, &names);
+	int err;
 	int i = 0;
+
+	/*
+	 * When we have cached stat information available then we use it to
+	 * verify whether the file has been rewritten.
+	 *
+	 * Note that we explicitly do not want to use `stat_validity_check()`
+	 * and friends here because they may end up not comparing the `st_dev`
+	 * and `st_ino` fields. These functions thus cannot guarantee that we
+	 * indeed still have the same file.
+	 */
+	if (st->list_fd >= 0) {
+		struct stat list_st;
+
+		if (stat(st->list_file, &list_st) < 0) {
+			/*
+			 * It's fine for "tables.list" to not exist. In that
+			 * case, we have to refresh when the loaded stack has
+			 * any readers.
+			 */
+			if (errno == ENOENT)
+				return !!st->readers_len;
+			return REFTABLE_IO_ERROR;
+		}
+
+		/*
+		 * When "tables.list" refers to the same file we can assume
+		 * that it didn't change. This is because we always use
+		 * rename(3P) to update the file and never write to it
+		 * directly.
+		 */
+		if (st->list_st.st_dev == list_st.st_dev &&
+		    st->list_st.st_ino == list_st.st_ino)
+			return 0;
+	}
+
+	err = read_lines(st->list_file, &names);
 	if (err < 0)
 		return err;
 
@@ -418,25 +529,22 @@
 {
 	int err = stack_try_add(st, write, arg);
 	if (err < 0) {
-		if (err == REFTABLE_LOCK_ERROR) {
+		if (err == REFTABLE_OUTDATED_ERROR) {
 			/* Ignore error return, we want to propagate
-			   REFTABLE_LOCK_ERROR.
+			   REFTABLE_OUTDATED_ERROR.
 			*/
 			reftable_stack_reload(st);
 		}
 		return err;
 	}
 
-	if (!st->disable_auto_compact)
-		return reftable_stack_auto_compact(st);
-
 	return 0;
 }
 
 static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
 {
 	char buf[100];
-	uint32_t rnd = (uint32_t)rand();
+	uint32_t rnd = (uint32_t)git_rand();
 	snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
 		 min, max, rnd);
 	strbuf_reset(dest);
@@ -444,33 +552,27 @@
 }
 
 struct reftable_addition {
-	int lock_file_fd;
-	struct strbuf lock_file_name;
+	struct tempfile *lock_file;
 	struct reftable_stack *stack;
 
 	char **new_tables;
-	int new_tables_len;
+	size_t new_tables_len, new_tables_cap;
 	uint64_t next_update_index;
 };
 
-#define REFTABLE_ADDITION_INIT                \
-	{                                     \
-		.lock_file_name = STRBUF_INIT \
-	}
+#define REFTABLE_ADDITION_INIT {0}
 
 static int reftable_stack_init_addition(struct reftable_addition *add,
 					struct reftable_stack *st)
 {
+	struct strbuf lock_file_name = STRBUF_INIT;
 	int err = 0;
 	add->stack = st;
 
-	strbuf_reset(&add->lock_file_name);
-	strbuf_addstr(&add->lock_file_name, st->list_file);
-	strbuf_addstr(&add->lock_file_name, ".lock");
+	strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
 
-	add->lock_file_fd = open(add->lock_file_name.buf,
-				 O_EXCL | O_CREAT | O_WRONLY, 0666);
-	if (add->lock_file_fd < 0) {
+	add->lock_file = create_tempfile(lock_file_name.buf);
+	if (!add->lock_file) {
 		if (errno == EEXIST) {
 			err = REFTABLE_LOCK_ERROR;
 		} else {
@@ -479,7 +581,7 @@
 		goto done;
 	}
 	if (st->config.default_permissions) {
-		if (chmod(add->lock_file_name.buf, st->config.default_permissions) < 0) {
+		if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
 			err = REFTABLE_IO_ERROR;
 			goto done;
 		}
@@ -488,9 +590,8 @@
 	err = stack_uptodate(st);
 	if (err < 0)
 		goto done;
-
-	if (err > 1) {
-		err = REFTABLE_LOCK_ERROR;
+	if (err > 0) {
+		err = REFTABLE_OUTDATED_ERROR;
 		goto done;
 	}
 
@@ -499,13 +600,15 @@
 	if (err) {
 		reftable_addition_close(add);
 	}
+	strbuf_release(&lock_file_name);
 	return err;
 }
 
 static void reftable_addition_close(struct reftable_addition *add)
 {
-	int i = 0;
 	struct strbuf nm = STRBUF_INIT;
+	size_t i;
+
 	for (i = 0; i < add->new_tables_len; i++) {
 		stack_filename(&nm, add->stack, add->new_tables[i]);
 		unlink(nm.buf);
@@ -515,16 +618,9 @@
 	reftable_free(add->new_tables);
 	add->new_tables = NULL;
 	add->new_tables_len = 0;
+	add->new_tables_cap = 0;
 
-	if (add->lock_file_fd > 0) {
-		close(add->lock_file_fd);
-		add->lock_file_fd = 0;
-	}
-	if (add->lock_file_name.len > 0) {
-		unlink(add->lock_file_name.buf);
-		strbuf_release(&add->lock_file_name);
-	}
-
+	delete_tempfile(&add->lock_file);
 	strbuf_release(&nm);
 }
 
@@ -540,8 +636,10 @@
 int reftable_addition_commit(struct reftable_addition *add)
 {
 	struct strbuf table_list = STRBUF_INIT;
-	int i = 0;
+	int lock_file_fd = get_tempfile_fd(add->lock_file);
 	int err = 0;
+	size_t i;
+
 	if (add->new_tables_len == 0)
 		goto done;
 
@@ -554,36 +652,48 @@
 		strbuf_addstr(&table_list, "\n");
 	}
 
-	err = write(add->lock_file_fd, table_list.buf, table_list.len);
+	err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
 	strbuf_release(&table_list);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
 	}
 
-	err = close(add->lock_file_fd);
-	add->lock_file_fd = 0;
-	if (err < 0) {
-		err = REFTABLE_IO_ERROR;
-		goto done;
-	}
+	fsync_component_or_die(FSYNC_COMPONENT_REFERENCE, lock_file_fd,
+			       get_tempfile_path(add->lock_file));
 
-	err = rename(add->lock_file_name.buf, add->stack->list_file);
+	err = rename_tempfile(&add->lock_file, add->stack->list_file);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
 	}
 
 	/* success, no more state to clean up. */
-	strbuf_release(&add->lock_file_name);
-	for (i = 0; i < add->new_tables_len; i++) {
+	for (i = 0; i < add->new_tables_len; i++)
 		reftable_free(add->new_tables[i]);
-	}
 	reftable_free(add->new_tables);
 	add->new_tables = NULL;
 	add->new_tables_len = 0;
+	add->new_tables_cap = 0;
 
-	err = reftable_stack_reload(add->stack);
+	err = reftable_stack_reload_maybe_reuse(add->stack, 1);
+	if (err)
+		goto done;
+
+	if (!add->stack->disable_auto_compact) {
+		/*
+		 * Auto-compact the stack to keep the number of tables in
+		 * control. It is possible that a concurrent writer is already
+		 * trying to compact parts of the stack, which would lead to a
+		 * `REFTABLE_LOCK_ERROR` because parts of the stack are locked
+		 * already. This is a benign error though, so we ignore it.
+		 */
+		err = reftable_stack_auto_compact(add->stack);
+		if (err < 0 && err != REFTABLE_LOCK_ERROR)
+			goto done;
+		err = 0;
+	}
+
 done:
 	reftable_addition_close(add);
 	return err;
@@ -594,7 +704,7 @@
 {
 	int err = 0;
 	struct reftable_addition empty = REFTABLE_ADDITION_INIT;
-	*dest = reftable_calloc(sizeof(**dest));
+	REFTABLE_CALLOC_ARRAY(*dest, 1);
 	**dest = empty;
 	err = reftable_stack_init_addition(*dest, st);
 	if (err) {
@@ -613,10 +723,6 @@
 	int err = reftable_stack_init_addition(&add, st);
 	if (err < 0)
 		goto done;
-	if (err > 0) {
-		err = REFTABLE_LOCK_ERROR;
-		goto done;
-	}
 
 	err = reftable_addition_add(&add, write_table, arg);
 	if (err < 0)
@@ -637,8 +743,9 @@
 	struct strbuf tab_file_name = STRBUF_INIT;
 	struct strbuf next_name = STRBUF_INIT;
 	struct reftable_writer *wr = NULL;
+	struct tempfile *tab_file = NULL;
 	int err = 0;
-	int tab_fd = 0;
+	int tab_fd;
 
 	strbuf_reset(&next_name);
 	format_name(&next_name, add->next_update_index, add->next_update_index);
@@ -646,18 +753,21 @@
 	stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
 	strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
 
-	tab_fd = mkstemp(temp_tab_file_name.buf);
-	if (tab_fd < 0) {
+	tab_file = mks_tempfile(temp_tab_file_name.buf);
+	if (!tab_file) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
 	}
 	if (add->stack->config.default_permissions) {
-		if (chmod(temp_tab_file_name.buf, add->stack->config.default_permissions)) {
+		if (chmod(get_tempfile_path(tab_file),
+			  add->stack->config.default_permissions)) {
 			err = REFTABLE_IO_ERROR;
 			goto done;
 		}
 	}
-	wr = reftable_new_writer(reftable_fd_write, &tab_fd,
+	tab_fd = get_tempfile_fd(tab_file);
+
+	wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
 				 &add->stack->config);
 	err = write_table(wr, arg);
 	if (err < 0)
@@ -671,14 +781,13 @@
 	if (err < 0)
 		goto done;
 
-	err = close(tab_fd);
-	tab_fd = 0;
+	err = close_tempfile_gently(tab_file);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
 	}
 
-	err = stack_check_addition(add->stack, temp_tab_file_name.buf);
+	err = stack_check_addition(add->stack, get_tempfile_path(tab_file));
 	if (err < 0)
 		goto done;
 
@@ -689,33 +798,23 @@
 
 	format_name(&next_name, wr->min_update_index, wr->max_update_index);
 	strbuf_addstr(&next_name, ".ref");
-
 	stack_filename(&tab_file_name, add->stack, next_name.buf);
 
 	/*
 	  On windows, this relies on rand() picking a unique destination name.
 	  Maybe we should do retry loop as well?
 	 */
-	err = rename(temp_tab_file_name.buf, tab_file_name.buf);
+	err = rename_tempfile(&tab_file, tab_file_name.buf);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
 	}
 
-	add->new_tables = reftable_realloc(add->new_tables,
-					   sizeof(*add->new_tables) *
-						   (add->new_tables_len + 1));
-	add->new_tables[add->new_tables_len] = strbuf_detach(&next_name, NULL);
-	add->new_tables_len++;
+	REFTABLE_ALLOC_GROW(add->new_tables, add->new_tables_len + 1,
+			    add->new_tables_cap);
+	add->new_tables[add->new_tables_len++] = strbuf_detach(&next_name, NULL);
 done:
-	if (tab_fd > 0) {
-		close(tab_fd);
-		tab_fd = 0;
-	}
-	if (temp_tab_file_name.len > 0) {
-		unlink(temp_tab_file_name.buf);
-	}
-
+	delete_tempfile(&tab_file);
 	strbuf_release(&temp_tab_file_name);
 	strbuf_release(&tab_file_name);
 	strbuf_release(&next_name);
@@ -732,66 +831,77 @@
 	return 1;
 }
 
-static int stack_compact_locked(struct reftable_stack *st, int first, int last,
-				struct strbuf *temp_tab,
-				struct reftable_log_expiry_config *config)
+static int stack_compact_locked(struct reftable_stack *st,
+				size_t first, size_t last,
+				struct reftable_log_expiry_config *config,
+				struct tempfile **tab_file_out)
 {
 	struct strbuf next_name = STRBUF_INIT;
-	int tab_fd = -1;
+	struct strbuf tab_file_path = STRBUF_INIT;
 	struct reftable_writer *wr = NULL;
-	int err = 0;
+	struct tempfile *tab_file;
+	int tab_fd, err = 0;
 
 	format_name(&next_name,
 		    reftable_reader_min_update_index(st->readers[first]),
 		    reftable_reader_max_update_index(st->readers[last]));
+	stack_filename(&tab_file_path, st, next_name.buf);
+	strbuf_addstr(&tab_file_path, ".temp.XXXXXX");
 
-	stack_filename(temp_tab, st, next_name.buf);
-	strbuf_addstr(temp_tab, ".temp.XXXXXX");
+	tab_file = mks_tempfile(tab_file_path.buf);
+	if (!tab_file) {
+		err = REFTABLE_IO_ERROR;
+		goto done;
+	}
+	tab_fd = get_tempfile_fd(tab_file);
 
-	tab_fd = mkstemp(temp_tab->buf);
-	wr = reftable_new_writer(reftable_fd_write, &tab_fd, &st->config);
+	if (st->config.default_permissions &&
+	    chmod(get_tempfile_path(tab_file), st->config.default_permissions) < 0) {
+		err = REFTABLE_IO_ERROR;
+		goto done;
+	}
 
+	wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush,
+				 &tab_fd, &st->config);
 	err = stack_write_compact(st, wr, first, last, config);
 	if (err < 0)
 		goto done;
+
 	err = reftable_writer_close(wr);
 	if (err < 0)
 		goto done;
 
-	err = close(tab_fd);
-	tab_fd = 0;
+	err = close_tempfile_gently(tab_file);
+	if (err < 0)
+		goto done;
+
+	*tab_file_out = tab_file;
+	tab_file = NULL;
 
 done:
+	delete_tempfile(&tab_file);
 	reftable_writer_free(wr);
-	if (tab_fd > 0) {
-		close(tab_fd);
-		tab_fd = 0;
-	}
-	if (err != 0 && temp_tab->len > 0) {
-		unlink(temp_tab->buf);
-		strbuf_release(temp_tab);
-	}
 	strbuf_release(&next_name);
+	strbuf_release(&tab_file_path);
 	return err;
 }
 
 static int stack_write_compact(struct reftable_stack *st,
-			       struct reftable_writer *wr, int first, int last,
+			       struct reftable_writer *wr,
+			       size_t first, size_t last,
 			       struct reftable_log_expiry_config *config)
 {
-	int subtabs_len = last - first + 1;
+	size_t subtabs_len = last - first + 1;
 	struct reftable_table *subtabs = reftable_calloc(
-		sizeof(struct reftable_table) * (last - first + 1));
+		last - first + 1, sizeof(*subtabs));
 	struct reftable_merged_table *mt = NULL;
-	int err = 0;
 	struct reftable_iterator it = { NULL };
 	struct reftable_ref_record ref = { NULL };
 	struct reftable_log_record log = { NULL };
-
 	uint64_t entries = 0;
+	int err = 0;
 
-	int i = 0, j = 0;
-	for (i = first, j = 0; i <= last; i++) {
+	for (size_t i = first, j = 0; i <= last; i++) {
 		struct reftable_reader *t = st->readers[i];
 		reftable_table_from_reader(&subtabs[j++], t);
 		st->stats.bytes += t->size;
@@ -816,18 +926,16 @@
 			err = 0;
 			break;
 		}
-		if (err < 0) {
-			break;
-		}
+		if (err < 0)
+			goto done;
 
 		if (first == 0 && reftable_ref_record_is_deletion(&ref)) {
 			continue;
 		}
 
 		err = reftable_writer_add_ref(wr, &ref);
-		if (err < 0) {
-			break;
-		}
+		if (err < 0)
+			goto done;
 		entries++;
 	}
 	reftable_iterator_destroy(&it);
@@ -842,9 +950,8 @@
 			err = 0;
 			break;
 		}
-		if (err < 0) {
-			break;
-		}
+		if (err < 0)
+			goto done;
 		if (first == 0 && reftable_log_record_is_deletion(&log)) {
 			continue;
 		}
@@ -860,9 +967,8 @@
 		}
 
 		err = reftable_writer_add_log(wr, &log);
-		if (err < 0) {
-			break;
-		}
+		if (err < 0)
+			goto done;
 		entries++;
 	}
 
@@ -878,27 +984,28 @@
 	return err;
 }
 
-/* <  0: error. 0 == OK, > 0 attempt failed; could retry. */
-static int stack_compact_range(struct reftable_stack *st, int first, int last,
+/*
+ * Compact all tables in the range `[first, last)` into a single new table.
+ *
+ * This function returns `0` on success or a code `< 0` on failure. When the
+ * stack or any of the tables in the specified range are already locked then
+ * this function returns `REFTABLE_LOCK_ERROR`. This is a benign error that
+ * callers can either ignore, or they may choose to retry compaction after some
+ * amount of time.
+ */
+static int stack_compact_range(struct reftable_stack *st,
+			       size_t first, size_t last,
 			       struct reftable_log_expiry_config *expiry)
 {
-	struct strbuf temp_tab_file_name = STRBUF_INIT;
+	struct strbuf tables_list_buf = STRBUF_INIT;
 	struct strbuf new_table_name = STRBUF_INIT;
-	struct strbuf lock_file_name = STRBUF_INIT;
-	struct strbuf ref_list_contents = STRBUF_INIT;
 	struct strbuf new_table_path = STRBUF_INIT;
-	int err = 0;
-	int have_lock = 0;
-	int lock_file_fd = -1;
-	int compact_count = last - first + 1;
-	char **listp = NULL;
-	char **delete_on_success =
-		reftable_calloc(sizeof(char *) * (compact_count + 1));
-	char **subtable_locks =
-		reftable_calloc(sizeof(char *) * (compact_count + 1));
-	int i = 0;
-	int j = 0;
-	int is_empty_table = 0;
+	struct strbuf table_name = STRBUF_INIT;
+	struct lock_file tables_list_lock = LOCK_INIT;
+	struct lock_file *table_locks = NULL;
+	struct tempfile *new_table = NULL;
+	int is_empty_table = 0, err = 0;
+	size_t i;
 
 	if (first > last || (!expiry && first == last)) {
 		err = 0;
@@ -907,196 +1014,200 @@
 
 	st->stats.attempts++;
 
-	strbuf_reset(&lock_file_name);
-	strbuf_addstr(&lock_file_name, st->list_file);
-	strbuf_addstr(&lock_file_name, ".lock");
-
-	lock_file_fd =
-		open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
-	if (lock_file_fd < 0) {
-		if (errno == EEXIST) {
-			err = 1;
-		} else {
+	/*
+	 * Hold the lock so that we can read "tables.list" and lock all tables
+	 * which are part of the user-specified range.
+	 */
+	err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
+					LOCK_NO_DEREF);
+	if (err < 0) {
+		if (errno == EEXIST)
+			err = REFTABLE_LOCK_ERROR;
+		else
 			err = REFTABLE_IO_ERROR;
-		}
 		goto done;
 	}
-	/* Don't want to write to the lock for now.  */
-	close(lock_file_fd);
-	lock_file_fd = -1;
 
-	have_lock = 1;
 	err = stack_uptodate(st);
-	if (err != 0)
+	if (err)
 		goto done;
 
-	for (i = first, j = 0; i <= last; i++) {
-		struct strbuf subtab_file_name = STRBUF_INIT;
-		struct strbuf subtab_lock = STRBUF_INIT;
-		int sublock_file_fd = -1;
+	/*
+	 * Lock all tables in the user-provided range. This is the slice of our
+	 * stack which we'll compact.
+	 */
+	REFTABLE_CALLOC_ARRAY(table_locks, last - first + 1);
+	for (i = first; i <= last; i++) {
+		stack_filename(&table_name, st, reader_name(st->readers[i]));
 
-		stack_filename(&subtab_file_name, st,
-			       reader_name(st->readers[i]));
-
-		strbuf_reset(&subtab_lock);
-		strbuf_addbuf(&subtab_lock, &subtab_file_name);
-		strbuf_addstr(&subtab_lock, ".lock");
-
-		sublock_file_fd = open(subtab_lock.buf,
-				       O_EXCL | O_CREAT | O_WRONLY, 0666);
-		if (sublock_file_fd >= 0) {
-			close(sublock_file_fd);
-		} else if (sublock_file_fd < 0) {
-			if (errno == EEXIST) {
-				err = 1;
-			} else {
+		err = hold_lock_file_for_update(&table_locks[i - first],
+						table_name.buf, LOCK_NO_DEREF);
+		if (err < 0) {
+			if (errno == EEXIST)
+				err = REFTABLE_LOCK_ERROR;
+			else
 				err = REFTABLE_IO_ERROR;
-			}
-		}
-
-		subtable_locks[j] = subtab_lock.buf;
-		delete_on_success[j] = subtab_file_name.buf;
-		j++;
-
-		if (err != 0)
-			goto done;
-	}
-
-	err = unlink(lock_file_name.buf);
-	if (err < 0)
-		goto done;
-	have_lock = 0;
-
-	err = stack_compact_locked(st, first, last, &temp_tab_file_name,
-				   expiry);
-	/* Compaction + tombstones can create an empty table out of non-empty
-	 * tables. */
-	is_empty_table = (err == REFTABLE_EMPTY_TABLE_ERROR);
-	if (is_empty_table) {
-		err = 0;
-	}
-	if (err < 0)
-		goto done;
-
-	lock_file_fd =
-		open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
-	if (lock_file_fd < 0) {
-		if (errno == EEXIST) {
-			err = 1;
-		} else {
-			err = REFTABLE_IO_ERROR;
-		}
-		goto done;
-	}
-	have_lock = 1;
-	if (st->config.default_permissions) {
-		if (chmod(lock_file_name.buf, st->config.default_permissions) < 0) {
-			err = REFTABLE_IO_ERROR;
 			goto done;
 		}
-	}
 
-	format_name(&new_table_name, st->readers[first]->min_update_index,
-		    st->readers[last]->max_update_index);
-	strbuf_addstr(&new_table_name, ".ref");
-
-	stack_filename(&new_table_path, st, new_table_name.buf);
-
-	if (!is_empty_table) {
-		/* retry? */
-		err = rename(temp_tab_file_name.buf, new_table_path.buf);
+		/*
+		 * We need to close the lockfiles as we might otherwise easily
+		 * run into file descriptor exhaustion when we compress a lot
+		 * of tables.
+		 */
+		err = close_lock_file_gently(&table_locks[i - first]);
 		if (err < 0) {
 			err = REFTABLE_IO_ERROR;
 			goto done;
 		}
 	}
 
-	for (i = 0; i < first; i++) {
-		strbuf_addstr(&ref_list_contents, st->readers[i]->name);
-		strbuf_addstr(&ref_list_contents, "\n");
-	}
-	if (!is_empty_table) {
-		strbuf_addbuf(&ref_list_contents, &new_table_name);
-		strbuf_addstr(&ref_list_contents, "\n");
-	}
-	for (i = last + 1; i < st->merged->stack_len; i++) {
-		strbuf_addstr(&ref_list_contents, st->readers[i]->name);
-		strbuf_addstr(&ref_list_contents, "\n");
-	}
-
-	err = write(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
+	/*
+	 * We have locked all tables in our range and can thus release the
+	 * "tables.list" lock while compacting the locked tables. This allows
+	 * concurrent updates to the stack to proceed.
+	 */
+	err = rollback_lock_file(&tables_list_lock);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
-		unlink(new_table_path.buf);
-		goto done;
-	}
-	err = close(lock_file_fd);
-	lock_file_fd = -1;
-	if (err < 0) {
-		err = REFTABLE_IO_ERROR;
-		unlink(new_table_path.buf);
 		goto done;
 	}
 
-	err = rename(lock_file_name.buf, st->list_file);
+	/*
+	 * Compact the now-locked tables into a new table. Note that compacting
+	 * these tables may end up with an empty new table in case tombstones
+	 * end up cancelling out all refs in that range.
+	 */
+	err = stack_compact_locked(st, first, last, expiry, &new_table);
 	if (err < 0) {
-		err = REFTABLE_IO_ERROR;
-		unlink(new_table_path.buf);
+		if (err != REFTABLE_EMPTY_TABLE_ERROR)
+			goto done;
+		is_empty_table = 1;
+	}
+
+	/*
+	 * Now that we have written the new, compacted table we need to re-lock
+	 * "tables.list". We'll then replace the compacted range of tables with
+	 * the new table.
+	 */
+	err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
+					LOCK_NO_DEREF);
+	if (err < 0) {
+		if (errno == EEXIST)
+			err = REFTABLE_LOCK_ERROR;
+		else
+			err = REFTABLE_IO_ERROR;
 		goto done;
 	}
-	have_lock = 0;
 
-	/* Reload the stack before deleting. On windows, we can only delete the
-	   files after we closed them.
-	*/
-	err = reftable_stack_reload_maybe_reuse(st, first < last);
-
-	listp = delete_on_success;
-	while (*listp) {
-		if (strcmp(*listp, new_table_path.buf)) {
-			unlink(*listp);
+	if (st->config.default_permissions) {
+		if (chmod(get_lock_file_path(&tables_list_lock),
+			  st->config.default_permissions) < 0) {
+			err = REFTABLE_IO_ERROR;
+			goto done;
 		}
-		listp++;
+	}
+
+	/*
+	 * If the resulting compacted table is not empty, then we need to move
+	 * it into place now.
+	 */
+	if (!is_empty_table) {
+		format_name(&new_table_name, st->readers[first]->min_update_index,
+			    st->readers[last]->max_update_index);
+		strbuf_addstr(&new_table_name, ".ref");
+		stack_filename(&new_table_path, st, new_table_name.buf);
+
+		err = rename_tempfile(&new_table, new_table_path.buf);
+		if (err < 0) {
+			err = REFTABLE_IO_ERROR;
+			goto done;
+		}
+	}
+
+	/*
+	 * Write the new "tables.list" contents with the compacted table we
+	 * have just written. In case the compacted table became empty we
+	 * simply skip writing it.
+	 */
+	for (i = 0; i < first; i++)
+		strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
+	if (!is_empty_table)
+		strbuf_addf(&tables_list_buf, "%s\n", new_table_name.buf);
+	for (i = last + 1; i < st->merged->stack_len; i++)
+		strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
+
+	err = write_in_full(get_lock_file_fd(&tables_list_lock),
+			    tables_list_buf.buf, tables_list_buf.len);
+	if (err < 0) {
+		err = REFTABLE_IO_ERROR;
+		unlink(new_table_path.buf);
+		goto done;
+	}
+
+	err = fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&tables_list_lock));
+	if (err < 0) {
+		err = REFTABLE_IO_ERROR;
+		unlink(new_table_path.buf);
+		goto done;
+	}
+
+	err = commit_lock_file(&tables_list_lock);
+	if (err < 0) {
+		err = REFTABLE_IO_ERROR;
+		unlink(new_table_path.buf);
+		goto done;
+	}
+
+	/*
+	 * Reload the stack before deleting the compacted tables. We can only
+	 * delete the files after we closed them on Windows, so this needs to
+	 * happen first.
+	 */
+	err = reftable_stack_reload_maybe_reuse(st, first < last);
+	if (err < 0)
+		goto done;
+
+	/*
+	 * Delete the old tables. They may still be in use by concurrent
+	 * readers, so it is expected that unlinking tables may fail.
+	 */
+	for (i = first; i <= last; i++) {
+		struct lock_file *table_lock = &table_locks[i - first];
+		char *table_path = get_locked_file_path(table_lock);
+		unlink(table_path);
+		free(table_path);
 	}
 
 done:
-	free_names(delete_on_success);
+	rollback_lock_file(&tables_list_lock);
+	for (i = first; table_locks && i <= last; i++)
+		rollback_lock_file(&table_locks[i - first]);
+	reftable_free(table_locks);
 
-	listp = subtable_locks;
-	while (*listp) {
-		unlink(*listp);
-		listp++;
-	}
-	free_names(subtable_locks);
-	if (lock_file_fd >= 0) {
-		close(lock_file_fd);
-		lock_file_fd = -1;
-	}
-	if (have_lock) {
-		unlink(lock_file_name.buf);
-	}
+	delete_tempfile(&new_table);
 	strbuf_release(&new_table_name);
 	strbuf_release(&new_table_path);
-	strbuf_release(&ref_list_contents);
-	strbuf_release(&temp_tab_file_name);
-	strbuf_release(&lock_file_name);
+
+	strbuf_release(&tables_list_buf);
+	strbuf_release(&table_name);
 	return err;
 }
 
 int reftable_stack_compact_all(struct reftable_stack *st,
 			       struct reftable_log_expiry_config *config)
 {
-	return stack_compact_range(st, 0, st->merged->stack_len - 1, config);
+	return stack_compact_range(st, 0, st->merged->stack_len ?
+			st->merged->stack_len - 1 : 0, config);
 }
 
-static int stack_compact_range_stats(struct reftable_stack *st, int first,
-				     int last,
+static int stack_compact_range_stats(struct reftable_stack *st,
+				     size_t first, size_t last,
 				     struct reftable_log_expiry_config *config)
 {
 	int err = stack_compact_range(st, first, last, config);
-	if (err > 0) {
+	if (err == REFTABLE_LOCK_ERROR)
 		st->stats.failures++;
-	}
 	return err;
 }
 
@@ -1116,12 +1227,11 @@
 	return l - 1;
 }
 
-struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n)
+struct segment *sizes_to_segments(size_t *seglen, uint64_t *sizes, size_t n)
 {
-	struct segment *segs = reftable_calloc(sizeof(struct segment) * n);
-	int next = 0;
+	struct segment *segs = reftable_calloc(n, sizeof(*segs));
 	struct segment cur = { 0 };
-	int i = 0;
+	size_t next = 0, i;
 
 	if (n == 0) {
 		*seglen = 0;
@@ -1147,29 +1257,27 @@
 	return segs;
 }
 
-struct segment suggest_compaction_segment(uint64_t *sizes, int n)
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
 {
-	int seglen = 0;
-	struct segment *segs = sizes_to_segments(&seglen, sizes, n);
 	struct segment min_seg = {
 		.log = 64,
 	};
-	int i = 0;
-	for (i = 0; i < seglen; i++) {
-		if (segment_size(&segs[i]) == 1) {
-			continue;
-		}
+	struct segment *segs;
+	size_t seglen = 0, i;
 
-		if (segs[i].log < min_seg.log) {
+	segs = sizes_to_segments(&seglen, sizes, n);
+	for (i = 0; i < seglen; i++) {
+		if (segment_size(&segs[i]) == 1)
+			continue;
+
+		if (segs[i].log < min_seg.log)
 			min_seg = segs[i];
-		}
 	}
 
 	while (min_seg.start > 0) {
-		int prev = min_seg.start - 1;
-		if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev])) {
+		size_t prev = min_seg.start - 1;
+		if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev]))
 			break;
-		}
 
 		min_seg.start = prev;
 		min_seg.bytes += sizes[prev];
@@ -1182,7 +1290,7 @@
 static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
 {
 	uint64_t *sizes =
-		reftable_calloc(sizeof(uint64_t) * st->merged->stack_len);
+		reftable_calloc(st->merged->stack_len, sizeof(*sizes));
 	int version = (st->config.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
 	int overhead = header_size(version) - 1;
 	int i = 0;
@@ -1281,17 +1389,12 @@
 	while (1) {
 		struct reftable_ref_record ref = { NULL };
 		err = reftable_iterator_next_ref(&it, &ref);
-		if (err > 0) {
+		if (err > 0)
 			break;
-		}
 		if (err < 0)
 			goto done;
 
-		if (len >= cap) {
-			cap = 2 * cap + 1;
-			refs = reftable_realloc(refs, cap * sizeof(refs[0]));
-		}
-
+		REFTABLE_ALLOC_GROW(refs, len + 1, cap);
 		refs[len++] = ref;
 	}
 
diff --git a/reftable/stack.h b/reftable/stack.h
index f570058..d919455 100644
--- a/reftable/stack.h
+++ b/reftable/stack.h
@@ -14,7 +14,10 @@
 #include "reftable-stack.h"
 
 struct reftable_stack {
+	struct stat list_st;
 	char *list_file;
+	int list_fd;
+
 	char *reftable_dir;
 	int disable_auto_compact;
 
@@ -29,13 +32,13 @@
 int read_lines(const char *filename, char ***lines);
 
 struct segment {
-	int start, end;
+	size_t start, end;
 	int log;
 	uint64_t bytes;
 };
 
 int fastlog2(uint64_t sz);
-struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n);
-struct segment suggest_compaction_segment(uint64_t *sizes, int n);
+struct segment *sizes_to_segments(size_t *seglen, uint64_t *sizes, size_t n);
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n);
 
 #endif
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index d0b7175..351e35b 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -13,7 +13,6 @@
 #include "reftable-reader.h"
 #include "merged.h"
 #include "basics.h"
-#include "constants.h"
 #include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
@@ -39,7 +38,17 @@
 		return 0;
 
 	while ((d = readdir(dir))) {
-		if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
+		/*
+		 * Besides skipping over "." and "..", we also need to
+		 * skip over other files that have a leading ".". This
+		 * is due to behaviour of NFS, which will rename files
+		 * to ".nfs*" to emulate delete-on-last-close.
+		 *
+		 * In any case this should be fine as the reftable
+		 * library will never write files with leading dots
+		 * anyway.
+		 */
+		if (starts_with(d->d_name, "."))
 			continue;
 		len++;
 	}
@@ -78,7 +87,7 @@
 	int i = 0;
 
 	EXPECT(fd > 0);
-	n = write(fd, out, strlen(out));
+	n = write_in_full(fd, out, strlen(out));
 	EXPECT(n == strlen(out));
 	err = close(fd);
 	EXPECT(err >= 0);
@@ -233,7 +242,7 @@
 	EXPECT_ERR(err);
 
 	err = reftable_stack_add(st2, &write_test_ref, &ref2);
-	EXPECT(err == REFTABLE_LOCK_ERROR);
+	EXPECT(err == REFTABLE_OUTDATED_ERROR);
 
 	err = reftable_stack_reload(st2);
 	EXPECT_ERR(err);
@@ -289,6 +298,104 @@
 	clear_dir(dir);
 }
 
+static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
+{
+	char *dir = get_tmp_dir(__LINE__);
+	struct reftable_write_options cfg = {0};
+	struct reftable_addition *add = NULL;
+	struct reftable_stack *st = NULL;
+	int i, n = 20, err;
+
+	err = reftable_new_stack(&st, dir, cfg);
+	EXPECT_ERR(err);
+
+	for (i = 0; i <= n; i++) {
+		struct reftable_ref_record ref = {
+			.update_index = reftable_stack_next_update_index(st),
+			.value_type = REFTABLE_REF_SYMREF,
+			.value.symref = "master",
+		};
+		char name[100];
+
+		snprintf(name, sizeof(name), "branch%04d", i);
+		ref.refname = name;
+
+		/*
+		 * Disable auto-compaction for all but the last runs. Like this
+		 * we can ensure that we indeed honor this setting and have
+		 * better control over when exactly auto compaction runs.
+		 */
+		st->disable_auto_compact = i != n;
+
+		err = reftable_stack_new_addition(&add, st);
+		EXPECT_ERR(err);
+
+		err = reftable_addition_add(add, &write_test_ref, &ref);
+		EXPECT_ERR(err);
+
+		err = reftable_addition_commit(add);
+		EXPECT_ERR(err);
+
+		reftable_addition_destroy(add);
+
+		/*
+		 * The stack length should grow continuously for all runs where
+		 * auto compaction is disabled. When enabled, we should merge
+		 * all tables in the stack.
+		 */
+		if (i != n)
+			EXPECT(st->merged->stack_len == i + 1);
+		else
+			EXPECT(st->merged->stack_len == 1);
+	}
+
+	reftable_stack_destroy(st);
+	clear_dir(dir);
+}
+
+static void test_reftable_stack_auto_compaction_fails_gracefully(void)
+{
+	struct reftable_ref_record ref = {
+		.refname = "refs/heads/master",
+		.update_index = 1,
+		.value_type = REFTABLE_REF_VAL1,
+		.value.val1 = {0x01},
+	};
+	struct reftable_write_options cfg = {0};
+	struct reftable_stack *st;
+	struct strbuf table_path = STRBUF_INIT;
+	char *dir = get_tmp_dir(__LINE__);
+	int err;
+
+	err = reftable_new_stack(&st, dir, cfg);
+	EXPECT_ERR(err);
+
+	err = reftable_stack_add(st, write_test_ref, &ref);
+	EXPECT_ERR(err);
+	EXPECT(st->merged->stack_len == 1);
+	EXPECT(st->stats.attempts == 0);
+	EXPECT(st->stats.failures == 0);
+
+	/*
+	 * Lock the newly written table such that it cannot be compacted.
+	 * Adding a new table to the stack should not be impacted by this, even
+	 * though auto-compaction will now fail.
+	 */
+	strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
+	write_file_buf(table_path.buf, "", 0);
+
+	ref.update_index = 2;
+	err = reftable_stack_add(st, write_test_ref, &ref);
+	EXPECT_ERR(err);
+	EXPECT(st->merged->stack_len == 2);
+	EXPECT(st->stats.attempts == 1);
+	EXPECT(st->stats.failures == 1);
+
+	reftable_stack_destroy(st);
+	strbuf_release(&table_path);
+	clear_dir(dir);
+}
+
 static void test_reftable_stack_validate_refname(void)
 {
 	struct reftable_write_options cfg = { 0 };
@@ -389,15 +496,16 @@
 	int err = 0;
 	struct reftable_write_options cfg = {
 		.exact_log_message = 1,
+		.default_permissions = 0660,
 	};
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
-
 	struct reftable_ref_record refs[2] = { { NULL } };
 	struct reftable_log_record logs[2] = { { NULL } };
+	struct strbuf path = STRBUF_INIT;
+	struct stat stat_result;
 	int N = ARRAY_SIZE(refs);
 
-
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 	st->disable_auto_compact = 1;
@@ -408,14 +516,11 @@
 		refs[i].refname = xstrdup(buf);
 		refs[i].update_index = i + 1;
 		refs[i].value_type = REFTABLE_REF_VAL1;
-		refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
 		set_test_hash(refs[i].value.val1, i);
 
 		logs[i].refname = xstrdup(buf);
 		logs[i].update_index = N + i + 1;
 		logs[i].value_type = REFTABLE_LOG_UPDATE;
-
-		logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
 		logs[i].value.update.email = xstrdup("identity@invalid");
 		set_test_hash(logs[i].value.update.new_hash, i);
 	}
@@ -456,12 +561,32 @@
 		reftable_log_record_release(&dest);
 	}
 
+#ifndef GIT_WINDOWS_NATIVE
+	strbuf_addstr(&path, dir);
+	strbuf_addstr(&path, "/tables.list");
+	err = stat(path.buf, &stat_result);
+	EXPECT(!err);
+	EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+
+	strbuf_reset(&path);
+	strbuf_addstr(&path, dir);
+	strbuf_addstr(&path, "/");
+	/* do not try at home; not an external API for reftable. */
+	strbuf_addstr(&path, st->readers[0]->name);
+	err = stat(path.buf, &stat_result);
+	EXPECT(!err);
+	EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+#else
+	(void) stat_result;
+#endif
+
 	/* cleanup */
 	reftable_stack_destroy(st);
 	for (i = 0; i < N; i++) {
 		reftable_ref_record_release(&refs[i]);
 		reftable_log_record_release(&logs[i]);
 	}
+	strbuf_release(&path);
 	clear_dir(dir);
 }
 
@@ -473,16 +598,17 @@
 	};
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
-
-	uint8_t h1[GIT_SHA1_RAWSZ] = { 0x01 }, h2[GIT_SHA1_RAWSZ] = { 0x02 };
-
-	struct reftable_log_record input = { .refname = "branch",
-					     .update_index = 1,
-					     .value_type = REFTABLE_LOG_UPDATE,
-					     .value = { .update = {
-								.new_hash = h1,
-								.old_hash = h2,
-							} } };
+	struct reftable_log_record input = {
+		.refname = "branch",
+		.update_index = 1,
+		.value_type = REFTABLE_LOG_UPDATE,
+		.value = {
+			.update = {
+				.new_hash = { 1 },
+				.old_hash = { 2 },
+			},
+		},
+	};
 	struct reftable_log_record dest = {
 		.update_index = 0,
 	};
@@ -545,7 +671,6 @@
 		refs[i].update_index = i + 1;
 		if (i % 2 == 0) {
 			refs[i].value_type = REFTABLE_REF_VAL1;
-			refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
 			set_test_hash(refs[i].value.val1, i);
 		}
 
@@ -554,8 +679,6 @@
 		logs[i].update_index = 42;
 		if (i % 2 == 0) {
 			logs[i].value_type = REFTABLE_LOG_UPDATE;
-			logs[i].value.update.new_hash =
-				reftable_malloc(GIT_SHA1_RAWSZ);
 			set_test_hash(logs[i].value.update.new_hash, i);
 			logs[i].value.update.email =
 				xstrdup("identity@invalid");
@@ -659,7 +782,7 @@
 	uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
 	/* .................0  1  2  3  4  5 */
 
-	int seglen = 0;
+	size_t seglen = 0;
 	struct segment *segs =
 		sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
 	EXPECT(segs[2].log == 3);
@@ -674,7 +797,7 @@
 
 static void test_sizes_to_segments_empty(void)
 {
-	int seglen = 0;
+	size_t seglen = 0;
 	struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
 	EXPECT(seglen == 0);
 	reftable_free(segs);
@@ -683,8 +806,7 @@
 static void test_sizes_to_segments_all_equal(void)
 {
 	uint64_t sizes[] = { 5, 5 };
-
-	int seglen = 0;
+	size_t seglen = 0;
 	struct segment *segs =
 		sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
 	EXPECT(seglen == 1);
@@ -738,7 +860,6 @@
 		logs[i].update_index = i;
 		logs[i].value_type = REFTABLE_LOG_UPDATE;
 		logs[i].value.update.time = i;
-		logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
 		logs[i].value.update.email = xstrdup("identity@invalid");
 		set_test_hash(logs[i].value.update.new_hash, i);
 	}
@@ -850,6 +971,54 @@
 	clear_dir(dir);
 }
 
+static void test_reftable_stack_add_performs_auto_compaction(void)
+{
+	struct reftable_write_options cfg = { 0 };
+	struct reftable_stack *st = NULL;
+	struct strbuf refname = STRBUF_INIT;
+	char *dir = get_tmp_dir(__LINE__);
+	int err, i, n = 20;
+
+	err = reftable_new_stack(&st, dir, cfg);
+	EXPECT_ERR(err);
+
+	for (i = 0; i <= n; i++) {
+		struct reftable_ref_record ref = {
+			.update_index = reftable_stack_next_update_index(st),
+			.value_type = REFTABLE_REF_SYMREF,
+			.value.symref = "master",
+		};
+
+		/*
+		 * Disable auto-compaction for all but the last runs. Like this
+		 * we can ensure that we indeed honor this setting and have
+		 * better control over when exactly auto compaction runs.
+		 */
+		st->disable_auto_compact = i != n;
+
+		strbuf_reset(&refname);
+		strbuf_addf(&refname, "branch-%04d", i);
+		ref.refname = refname.buf;
+
+		err = reftable_stack_add(st, &write_test_ref, &ref);
+		EXPECT_ERR(err);
+
+		/*
+		 * The stack length should grow continuously for all runs where
+		 * auto compaction is disabled. When enabled, we should merge
+		 * all tables in the stack.
+		 */
+		if (i != n)
+			EXPECT(st->merged->stack_len == i + 1);
+		else
+			EXPECT(st->merged->stack_len == 1);
+	}
+
+	reftable_stack_destroy(st);
+	strbuf_release(&refname);
+	clear_dir(dir);
+}
+
 static void test_reftable_stack_compaction_concurrent(void)
 {
 	struct reftable_write_options cfg = { 0 };
@@ -960,6 +1129,7 @@
 	RUN_TEST(test_reftable_stack_add);
 	RUN_TEST(test_reftable_stack_add_one);
 	RUN_TEST(test_reftable_stack_auto_compaction);
+	RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
 	RUN_TEST(test_reftable_stack_compaction_concurrent);
 	RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
 	RUN_TEST(test_reftable_stack_hash_id);
@@ -967,6 +1137,8 @@
 	RUN_TEST(test_reftable_stack_log_normalize);
 	RUN_TEST(test_reftable_stack_tombstone);
 	RUN_TEST(test_reftable_stack_transaction_api);
+	RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
+	RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
 	RUN_TEST(test_reftable_stack_update_index_check);
 	RUN_TEST(test_reftable_stack_uptodate);
 	RUN_TEST(test_reftable_stack_validate_refname);
diff --git a/reftable/system.h b/reftable/system.h
index 18f9207..5d8b6de 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -12,8 +12,10 @@
 /* This header glues the reftable library to the rest of Git */
 
 #include "git-compat-util.h"
+#include "lockfile.h"
 #include "strbuf.h"
-#include "hash.h" /* hash ID, sizes.*/
+#include "tempfile.h"
+#include "hash-ll.h" /* hash ID, sizes.*/
 #include "dir.h" /* remove_dir_recursively, for tests.*/
 
 int hash_size(uint32_t id);
diff --git a/reftable/test_framework.c b/reftable/test_framework.c
index 84ac972..4066924 100644
--- a/reftable/test_framework.c
+++ b/reftable/test_framework.c
@@ -9,7 +9,6 @@
 #include "system.h"
 #include "test_framework.h"
 
-#include "basics.h"
 
 void set_test_hash(uint8_t *p, int i)
 {
@@ -21,3 +20,8 @@
 	strbuf_add(b, data, sz);
 	return sz;
 }
+
+int noop_flush(void *arg)
+{
+	return 0;
+}
diff --git a/reftable/test_framework.h b/reftable/test_framework.h
index 774cb27..687390f 100644
--- a/reftable/test_framework.h
+++ b/reftable/test_framework.h
@@ -12,32 +12,38 @@
 #include "system.h"
 #include "reftable-error.h"
 
-#define EXPECT_ERR(c)                                                  \
-	if (c != 0) {                                                  \
-		fflush(stderr);                                        \
-		fflush(stdout);                                        \
-		fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
-			__FILE__, __LINE__, c, reftable_error_str(c)); \
-		abort();                                               \
-	}
+#define EXPECT_ERR(c)                                                          \
+	do {                                                                   \
+		if (c != 0) {                                                  \
+			fflush(stderr);                                        \
+			fflush(stdout);                                        \
+			fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
+				__FILE__, __LINE__, c, reftable_error_str(c)); \
+			abort();                                               \
+		}                                                              \
+	} while (0)
 
-#define EXPECT_STREQ(a, b)                                               \
-	if (strcmp(a, b)) {                                              \
-		fflush(stderr);                                          \
-		fflush(stdout);                                          \
-		fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
-			__LINE__, #a, a, #b, b);                         \
-		abort();                                                 \
-	}
+#define EXPECT_STREQ(a, b)                                                       \
+	do {                                                                     \
+		if (strcmp(a, b)) {                                              \
+			fflush(stderr);                                          \
+			fflush(stdout);                                          \
+			fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
+				__LINE__, #a, a, #b, b);                         \
+			abort();                                                 \
+		}                                                                \
+	} while (0)
 
-#define EXPECT(c)                                                          \
-	if (!(c)) {                                                        \
-		fflush(stderr);                                            \
-		fflush(stdout);                                            \
-		fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
-			__LINE__, #c);                                     \
-		abort();                                                   \
-	}
+#define EXPECT(c)                                                                  \
+	do {                                                                       \
+		if (!(c)) {                                                        \
+			fflush(stderr);                                            \
+			fflush(stdout);                                            \
+			fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
+				__LINE__, #c);                                     \
+			abort();                                                   \
+		}                                                                  \
+	} while (0)
 
 #define RUN_TEST(f)                          \
 	fprintf(stderr, "running %s\n", #f); \
@@ -50,4 +56,6 @@
  */
 ssize_t strbuf_add_void(void *b, const void *data, size_t sz);
 
+int noop_flush(void *);
+
 #endif
diff --git a/reftable/tree.c b/reftable/tree.c
index b8899e0..528f33a 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -6,10 +6,10 @@
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "tree.h"
 
 #include "basics.h"
-#include "system.h"
 
 struct tree_node *tree_search(void *key, struct tree_node **rootp,
 			      int (*compare)(const void *, const void *),
@@ -20,8 +20,8 @@
 		if (!insert) {
 			return NULL;
 		} else {
-			struct tree_node *n =
-				reftable_calloc(sizeof(struct tree_node));
+			struct tree_node *n;
+			REFTABLE_CALLOC_ARRAY(n, 1);
 			n->key = key;
 			*rootp = n;
 			return *rootp;
diff --git a/reftable/tree_test.c b/reftable/tree_test.c
index cbff125..6961a65 100644
--- a/reftable/tree_test.c
+++ b/reftable/tree_test.c
@@ -6,10 +6,9 @@
 https://developers.google.com/open-source/licenses/bsd
 */
 
+#include "system.h"
 #include "tree.h"
 
-#include "basics.h"
-#include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
 
diff --git a/reftable/writer.c b/reftable/writer.c
index 2e322a5..1d9ff0f 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -49,7 +49,7 @@
 {
 	int n = 0;
 	if (w->pending_padding > 0) {
-		uint8_t *zeroed = reftable_calloc(w->pending_padding);
+		uint8_t *zeroed = reftable_calloc(w->pending_padding, sizeof(*zeroed));
 		int n = w->write(w->write_arg, zeroed, w->pending_padding);
 		if (n < 0)
 			return n;
@@ -121,10 +121,10 @@
 
 struct reftable_writer *
 reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
+		    int (*flush_func)(void *),
 		    void *writer_arg, struct reftable_write_options *opts)
 {
-	struct reftable_writer *wp =
-		reftable_calloc(sizeof(struct reftable_writer));
+	struct reftable_writer *wp = reftable_calloc(1, sizeof(*wp));
 	strbuf_init(&wp->block_writer_data.last_key, 0);
 	options_set_defaults(opts);
 	if (opts->block_size >= (1 << 24)) {
@@ -132,10 +132,11 @@
 		abort();
 	}
 	wp->last_key = reftable_empty_strbuf;
-	wp->block = reftable_calloc(opts->block_size);
+	REFTABLE_CALLOC_ARRAY(wp->block, opts->block_size);
 	wp->write = writer_func;
 	wp->write_arg = writer_arg;
 	wp->opts = *opts;
+	wp->flush = flush_func;
 	writer_reinit_block_writer(wp, BLOCK_TYPE_REF);
 
 	return wp;
@@ -200,12 +201,7 @@
 		return;
 	}
 
-	if (key->offset_len == key->offset_cap) {
-		key->offset_cap = 2 * key->offset_cap + 1;
-		key->offsets = reftable_realloc(
-			key->offsets, sizeof(uint64_t) * key->offset_cap);
-	}
-
+	REFTABLE_ALLOC_GROW(key->offsets, key->offset_len + 1, key->offset_cap);
 	key->offsets[key->offset_len++] = off;
 }
 
@@ -377,20 +373,39 @@
 
 static int writer_finish_section(struct reftable_writer *w)
 {
+	struct reftable_block_stats *bstats = NULL;
 	uint8_t typ = block_writer_type(w->block_writer);
 	uint64_t index_start = 0;
 	int max_level = 0;
-	int threshold = w->opts.unpadded ? 1 : 3;
+	size_t threshold = w->opts.unpadded ? 1 : 3;
 	int before_blocks = w->stats.idx_stats.blocks;
-	int err = writer_flush_block(w);
-	int i = 0;
-	struct reftable_block_stats *bstats = NULL;
+	int err;
+
+	err = writer_flush_block(w);
 	if (err < 0)
 		return err;
 
+	/*
+	 * When the section we are about to index has a lot of blocks then the
+	 * index itself may span across multiple blocks, as well. This would
+	 * require a linear scan over index blocks only to find the desired
+	 * indexed block, which is inefficient. Instead, we write a multi-level
+	 * index where index records of level N+1 will refer to index blocks of
+	 * level N. This isn't constant time, either, but at least logarithmic.
+	 *
+	 * This loop handles writing this multi-level index. Note that we write
+	 * the lowest-level index pointing to the indexed blocks first. We then
+	 * continue writing additional index levels until the current level has
+	 * less blocks than the threshold so that the highest level will be at
+	 * the end of the index section.
+	 *
+	 * Readers are thus required to start reading the index section from
+	 * its end, which is why we set `index_start` to the beginning of the
+	 * last index section.
+	 */
 	while (w->index_len > threshold) {
 		struct reftable_index_record *idx = NULL;
-		int idx_len = 0;
+		size_t i, idx_len;
 
 		max_level++;
 		index_start = w->next;
@@ -409,35 +424,28 @@
 					.idx = idx[i],
 				},
 			};
-			if (block_writer_add(w->block_writer, &rec) == 0) {
-				continue;
-			}
 
-			err = writer_flush_block(w);
+			err = writer_add_record(w, &rec);
 			if (err < 0)
 				return err;
-
-			writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
-
-			err = block_writer_add(w->block_writer, &rec);
-			if (err != 0) {
-				/* write into fresh block should always succeed
-				 */
-				abort();
-			}
 		}
-		for (i = 0; i < idx_len; i++) {
+
+		err = writer_flush_block(w);
+		if (err < 0)
+			return err;
+
+		for (i = 0; i < idx_len; i++)
 			strbuf_release(&idx[i].last_key);
-		}
 		reftable_free(idx);
 	}
 
+	/*
+	 * The index may still contain a number of index blocks lower than the
+	 * threshold. Clear it so that these entries don't leak into the next
+	 * index section.
+	 */
 	writer_clear_index(w);
 
-	err = writer_flush_block(w);
-	if (err < 0)
-		return err;
-
 	bstats = writer_reftable_block_stats(w, typ);
 	bstats->index_blocks = w->stats.idx_stats.blocks - before_blocks;
 	bstats->index_offset = index_start;
@@ -603,6 +611,12 @@
 	put_be32(p, crc32(0, footer, p - footer));
 	p += 4;
 
+	err = w->flush(w->write_arg);
+	if (err < 0) {
+		err = REFTABLE_IO_ERROR;
+		goto done;
+	}
+
 	err = padded_write(w, footer, footer_size(writer_version(w)), 0);
 	if (err < 0)
 		goto done;
@@ -622,11 +636,8 @@
 
 static void writer_clear_index(struct reftable_writer *w)
 {
-	int i = 0;
-	for (i = 0; i < w->index_len; i++) {
+	for (size_t i = 0; i < w->index_len; i++)
 		strbuf_release(&w->index[i].last_key);
-	}
-
 	FREE_AND_NULL(w->index);
 	w->index_len = 0;
 	w->index_cap = 0;
@@ -674,12 +685,7 @@
 	if (err < 0)
 		return err;
 
-	if (w->index_cap == w->index_len) {
-		w->index_cap = 2 * w->index_cap + 1;
-		w->index = reftable_realloc(
-			w->index,
-			sizeof(struct reftable_index_record) * w->index_cap);
-	}
+	REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
 
 	ir.offset = w->next;
 	strbuf_reset(&ir.last_key);
diff --git a/reftable/writer.h b/reftable/writer.h
index 09b8867..8d0df9c 100644
--- a/reftable/writer.h
+++ b/reftable/writer.h
@@ -16,6 +16,7 @@
 
 struct reftable_writer {
 	ssize_t (*write)(void *, const void *, size_t);
+	int (*flush)(void *);
 	void *write_arg;
 	int pending_padding;
 	struct strbuf last_key;
diff --git a/remote-curl.c b/remote-curl.c
index a76b640..0b6d781 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -1,22 +1,28 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "git-curl-compat.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "remote.h"
 #include "connect.h"
 #include "strbuf.h"
 #include "walker.h"
 #include "http.h"
-#include "exec-cmd.h"
 #include "run-command.h"
 #include "pkt-line.h"
 #include "string-list.h"
-#include "sideband.h"
 #include "strvec.h"
 #include "credential.h"
 #include "oid-array.h"
 #include "send-pack.h"
+#include "setup.h"
 #include "protocol.h"
 #include "quote.h"
+#include "trace2.h"
 #include "transport.h"
+#include "url.h"
+#include "write-or-die.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -206,14 +212,9 @@
 		options.filter = xstrdup(value);
 		return 0;
 	} else if (!strcmp(name, "object-format")) {
-		int algo;
 		options.object_format = 1;
-		if (strcmp(value, "true")) {
-			algo = hash_algo_by_name(value);
-			if (algo == GIT_HASH_UNKNOWN)
-				die("unknown object format '%s'", value);
-			options.hash_algo = &hash_algos[algo];
-		}
+		if (strcmp(value, "true"))
+			die(_("unknown value for object-format: %s"), value);
 		return 0;
 	} else {
 		return 1 /* unsupported */;
@@ -472,10 +473,11 @@
 
 	/*
 	 * NEEDSWORK: If we are trying to use protocol v2 and we are planning
-	 * to perform a push, then fallback to v0 since the client doesn't know
-	 * how to push yet using v2.
+	 * to perform any operation that doesn't involve upload-pack (i.e., a
+	 * 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-receive-pack", service))
+	if (version == protocol_v2 && strcmp("git-upload-pack", service))
 		version = protocol_v0;
 
 	/* Add the extra Git-Protocol header */
@@ -755,7 +757,8 @@
 			size -= digits_remaining;
 
 			if (state->len_filled == 4) {
-				state->remaining = packet_length(state->len_buf);
+				state->remaining = packet_length(state->len_buf,
+								 sizeof(state->len_buf));
 				if (state->remaining < 0) {
 					die(_("remote-curl: bad line length character: %.4s"), state->len_buf);
 				} else if (state->remaining == 2) {
@@ -953,7 +956,9 @@
 		/* The request body is large and the size cannot be predicted.
 		 * We must use chunked encoding to send it.
 		 */
+#ifdef GIT_CURL_NEED_TRANSFER_ENCODING_HEADER
 		headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+#endif
 		rpc->initial_buffer = 1;
 		curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
 		curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
@@ -1439,8 +1444,14 @@
 	 * establish a stateless connection, otherwise we need to tell the
 	 * client to fallback to using other transport helper functions to
 	 * complete their request.
+	 *
+	 * The "git-upload-archive" service is a read-only operation. Fallback
+	 * to use "git-upload-pack" service to discover protocol version.
 	 */
-	discover = discover_refs(service_name, 0);
+	if (!strcmp(service_name, "git-upload-archive"))
+		discover = discover_refs("git-upload-pack", 0);
+	else
+		discover = discover_refs(service_name, 0);
 	if (discover->version != protocol_v2) {
 		printf("fallback\n");
 		fflush(stdout);
@@ -1478,9 +1489,11 @@
 
 	/*
 	 * Dump the capability listing that we got from the server earlier
-	 * during the info/refs request.
+	 * during the info/refs request. This does not work with the
+	 * "git-upload-archive" service.
 	 */
-	write_or_die(rpc.in, discover->buf, discover->len);
+	if (strcmp(service_name, "git-upload-archive"))
+		write_or_die(rpc.in, discover->buf, discover->len);
 
 	/* Until we see EOF keep sending POSTs */
 	while (1) {
@@ -1556,8 +1569,11 @@
 		if (buf.len == 0)
 			break;
 		if (starts_with(buf.buf, "fetch ")) {
-			if (nongit)
-				die(_("remote-curl: fetch attempted without a local repo"));
+			if (nongit) {
+				setup_git_directory_gently(&nongit);
+				if (nongit)
+					die(_("remote-curl: fetch attempted without a local repo"));
+			}
 			parse_fetch(&buf);
 
 		} else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
diff --git a/remote.c b/remote.c
index 60869be..2b650b8 100644
--- a/remote.c
+++ b/remote.c
@@ -1,20 +1,27 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "remote.h"
 #include "urlmatch.h"
 #include "refs.h"
 #include "refspec.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
 #include "dir.h"
-#include "tag.h"
+#include "setup.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "commit-reach.h"
 #include "advice.h"
 #include "connect.h"
+#include "parse-options.h"
 
 enum map_direction { FROM_SRC, FROM_DST };
 
@@ -98,7 +105,7 @@
 	b = container_of(entry_or_key, const struct remote, ent);
 
 	if (key)
-		return strncmp(a->name, key->str, key->len) || a->name[key->len];
+		return !!xstrncmpz(a->name, key->str, key->len);
 	else
 		return strcmp(a->name, b->name);
 }
@@ -182,8 +189,7 @@
 	b = container_of(entry_or_key, const struct branch, ent);
 
 	if (key)
-		return strncmp(a->name, key->str, key->len) ||
-		       a->name[key->len];
+		return !!xstrncmpz(a->name, key->str, key->len);
 	else
 		return strcmp(a->name, b->name);
 }
@@ -341,7 +347,8 @@
 	remote->fetch_tags = 1; /* always auto-follow */
 }
 
-static int handle_config(const char *key, const char *value, void *cb)
+static int handle_config(const char *key, const char *value,
+			 const struct config_context *ctx, void *cb)
 {
 	const char *name;
 	size_t namelen;
@@ -349,6 +356,7 @@
 	struct remote *remote;
 	struct branch *branch;
 	struct remote_state *remote_state = cb;
+	const struct key_value_info *kvi = ctx->kvi;
 
 	if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
 		/* There is no subsection. */
@@ -406,8 +414,8 @@
 	}
 	remote = make_remote(remote_state, name, namelen);
 	remote->origin = REMOTE_CONFIG;
-	if (current_config_scope() == CONFIG_SCOPE_LOCAL ||
-	    current_config_scope() == CONFIG_SCOPE_WORKTREE)
+	if (kvi->scope == CONFIG_SCOPE_LOCAL ||
+	    kvi->scope == CONFIG_SCOPE_WORKTREE)
 		remote->configured_in_repo = 1;
 	if (!strcmp(subkey, "mirror"))
 		remote->mirror = git_config_bool(key, value);
@@ -499,7 +507,7 @@
 	}
 }
 
-static void read_config(struct repository *repo)
+static void read_config(struct repository *repo, int early)
 {
 	int flag;
 
@@ -508,7 +516,7 @@
 	repo->remote_state->initialized = 1;
 
 	repo->remote_state->current_branch = NULL;
-	if (startup_info->have_repository) {
+	if (startup_info->have_repository && !early) {
 		const char *head_ref = refs_resolve_ref_unsafe(
 			get_main_ref_store(repo), "HEAD", 0, NULL, &flag);
 		if (head_ref && (flag & REF_ISSYMREF) &&
@@ -551,7 +559,7 @@
 
 const char *remote_for_branch(struct branch *branch, int *explicit)
 {
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	die_on_missing_branch(the_repository, branch);
 
 	return remotes_remote_for_branch(the_repository->remote_state, branch,
@@ -577,7 +585,7 @@
 
 const char *pushremote_for_branch(struct branch *branch, int *explicit)
 {
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	die_on_missing_branch(the_repository, branch);
 
 	return remotes_pushremote_for_branch(the_repository->remote_state,
@@ -589,7 +597,7 @@
 
 const char *remote_ref_for_branch(struct branch *branch, int for_push)
 {
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	die_on_missing_branch(the_repository, branch);
 
 	if (branch) {
@@ -699,7 +707,13 @@
 
 struct remote *remote_get(const char *name)
 {
-	read_config(the_repository);
+	read_config(the_repository, 0);
+	return remotes_remote_get(the_repository->remote_state, name);
+}
+
+struct remote *remote_get_early(const char *name)
+{
+	read_config(the_repository, 1);
 	return remotes_remote_get(the_repository->remote_state, name);
 }
 
@@ -712,7 +726,7 @@
 
 struct remote *pushremote_get(const char *name)
 {
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	return remotes_pushremote_get(the_repository->remote_state, name);
 }
 
@@ -728,7 +742,7 @@
 int for_each_remote(each_remote_fn fn, void *priv)
 {
 	int i, result = 0;
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	for (i = 0; i < the_repository->remote_state->remotes_nr && !result;
 	     i++) {
 		struct remote *remote =
@@ -882,7 +896,7 @@
 {
 	int i, matched_negative = 0;
 	int find_src = !query->src;
-	struct string_list reversed = STRING_LIST_INIT_NODUP;
+	struct string_list reversed = STRING_LIST_INIT_DUP;
 	const char *needle = find_src ? query->dst : query->src;
 
 	/*
@@ -1163,7 +1177,7 @@
 		return 0;
 	}
 
-	if (get_oid(name, &oid))
+	if (repo_get_oid(the_repository, name, &oid))
 		return -1;
 
 	if (match) {
@@ -1251,7 +1265,7 @@
 	if (!advice_enabled(ADVICE_PUSH_UNQUALIFIED_REF_NAME))
 		return;
 
-	if (get_oid(matched_src_name, &oid))
+	if (repo_get_oid(the_repository, matched_src_name, &oid))
 		BUG("'%s' is not a valid object, "
 		    "match_explicit_lhs() should catch this!",
 		    matched_src_name);
@@ -1759,7 +1773,7 @@
 		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 (!has_object_file(&ref->old_oid))
+			else if (!repo_has_object_file(the_repository, &ref->old_oid))
 				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))
@@ -1808,8 +1822,9 @@
 		if (!remote_find_tracking(remote, ret->merge[i]) ||
 		    strcmp(ret->remote_name, "."))
 			continue;
-		if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
-			     &oid, &ref, 0) == 1)
+		if (repo_dwim_ref(the_repository, ret->merge_name[i],
+				  strlen(ret->merge_name[i]), &oid, &ref,
+				  0) == 1)
 			ret->merge[i]->dst = ref;
 		else
 			ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
@@ -1820,7 +1835,7 @@
 {
 	struct branch *ret;
 
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	if (!name || !*name || !strcmp(name, "HEAD"))
 		ret = the_repository->remote_state->current_branch;
 	else
@@ -1962,7 +1977,7 @@
 
 const char *branch_get_push(struct branch *branch, struct strbuf *err)
 {
-	read_config(the_repository);
+	read_config(the_repository, 0);
 	die_on_missing_branch(the_repository, branch);
 
 	if (!branch)
@@ -2248,7 +2263,8 @@
  * Return true when there is anything to report, otherwise false.
  */
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
-			 enum ahead_behind_flags abf)
+			 enum ahead_behind_flags abf,
+			 int show_divergence_advice)
 {
 	int ours, theirs, sti;
 	const char *full_base;
@@ -2311,9 +2327,10 @@
 			       "respectively.\n",
 			   ours + theirs),
 			base, ours, theirs);
-		if (advice_enabled(ADVICE_STATUS_HINTS))
+		if (show_divergence_advice &&
+		    advice_enabled(ADVICE_STATUS_HINTS))
 			strbuf_addstr(sb,
-				_("  (use \"git pull\" to merge the remote branch into yours)\n"));
+				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
 	}
 	free(base);
 	return 1;
@@ -2505,7 +2522,7 @@
 		entry->use_tracking = 1;
 	else if (!colon[1])
 		oidclr(&entry->expect);
-	else if (get_oid(colon + 1, &entry->expect))
+	else if (repo_get_oid(the_repository, colon + 1, &entry->expect))
 		return error(_("cannot parse expected object name '%s'"),
 			     colon + 1);
 	return 0;
@@ -2662,7 +2679,7 @@
 		if (MERGE_BASES_BATCH_SIZE < size)
 			size = MERGE_BASES_BATCH_SIZE;
 
-		if ((ret = in_merge_bases_many(commit, size, chunk)))
+		if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk, 0)))
 			break;
 	}
 
diff --git a/remote.h b/remote.h
index 1ebbe42..dfd4837 100644
--- a/remote.h
+++ b/remote.h
@@ -1,11 +1,11 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
-#include "cache.h"
-#include "parse-options.h"
+#include "hash-ll.h"
 #include "hashmap.h"
 #include "refspec.h"
 
+struct option;
 struct transport_ls_refs_options;
 
 /**
@@ -118,6 +118,7 @@
  * and configuration.
  */
 struct remote *remote_get(const char *name);
+struct remote *remote_get_early(const char *name);
 
 struct remote *pushremote_get(const char *name);
 int remote_is_configured(struct remote *remote, int in_repo);
@@ -380,7 +381,8 @@
 		       const char **upstream_name, int for_push,
 		       enum ahead_behind_flags abf);
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
-			 enum ahead_behind_flags abf);
+			 enum ahead_behind_flags abf,
+			 int show_divergence_advice);
 
 struct ref *get_local_heads(void);
 /*
@@ -399,8 +401,6 @@
 /*
  * Compare-and-swap
  */
-#define CAS_OPT_NAME "force-with-lease"
-
 struct push_cas_option {
 	unsigned use_tracking_for_rest:1;
 	unsigned use_force_if_includes:1;
diff --git a/replace-object.c b/replace-object.c
index 320be25..5232155 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "oidmap.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "replace-object.h"
 #include "refs.h"
 #include "repository.h"
@@ -62,7 +64,7 @@
  * replacement object's name (replaced recursively, if necessary).
  * The return value is either oid or a pointer to a
  * permanently-allocated value.  This function always respects replace
- * references, regardless of the value of read_replace_refs.
+ * references, regardless of the value of r->settings.read_replace_refs.
  */
 const struct object_id *do_lookup_replace_object(struct repository *r,
 						 const struct object_id *oid)
@@ -82,3 +84,29 @@
 	}
 	die(_("replace depth too high for object %s"), oid_to_hex(oid));
 }
+
+/*
+ * This indicator determines whether replace references should be
+ * respected process-wide, regardless of which repository is being
+ * using at the time.
+ */
+static int read_replace_refs = 1;
+
+void disable_replace_refs(void)
+{
+	read_replace_refs = 0;
+}
+
+int replace_refs_enabled(struct repository *r)
+{
+	if (!read_replace_refs)
+		return 0;
+
+	if (r->gitdir) {
+		prepare_repo_settings(r);
+		return r->settings.read_replace_refs;
+	}
+
+	/* repository has no objects or refs. */
+	return 0;
+}
diff --git a/replace-object.h b/replace-object.h
index 3fbc32e..66c41b9 100644
--- a/replace-object.h
+++ b/replace-object.h
@@ -3,7 +3,7 @@
 
 #include "oidmap.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 struct replace_object {
 	struct oidmap_entry original;
@@ -20,6 +20,18 @@
 						 const struct object_id *oid);
 
 /*
+ * Some commands disable replace-refs unconditionally, and otherwise each
+ * repository could alter the core.useReplaceRefs config value.
+ *
+ * Return 1 if and only if all of the following are true:
+ *
+ *  a. disable_replace_refs() has not been called.
+ *  b. GIT_NO_REPLACE_OBJECTS is unset or zero.
+ *  c. the given repository does not have core.useReplaceRefs=false.
+ */
+int replace_refs_enabled(struct repository *r);
+
+/*
  * If object sha1 should be replaced, return the replacement object's
  * name (replaced recursively, if necessary).  The return value is
  * either sha1 or a pointer to a permanently-allocated value.  When
@@ -33,11 +45,19 @@
 static inline const struct object_id *lookup_replace_object(struct repository *r,
 							    const struct object_id *oid)
 {
-	if (!read_replace_refs ||
+	if (!replace_refs_enabled(r) ||
 	    (r->objects->replace_map_initialized &&
 	     r->objects->replace_map->map.tablesize == 0))
 		return oid;
 	return do_lookup_replace_object(r, oid);
 }
 
+/*
+ * Some commands override config and environment settings for using
+ * replace references. Use this method to disable the setting and ensure
+ * those other settings will not override this choice. This applies
+ * globally to all in-process repositories.
+ */
+void disable_replace_refs(void);
+
 #endif /* REPLACE_OBJECT_H */
diff --git a/repo-settings.c b/repo-settings.c
index 3dbd3f0..a0b590b 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -1,8 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "repository.h"
 #include "midx.h"
-#include "compat/fsmonitor/fsm-listen.h"
 
 static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
 			  int def)
@@ -43,7 +42,8 @@
 	/* Defaults modified by feature.* */
 	if (experimental) {
 		r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
-		r->settings.gc_cruft_packs = 1;
+		r->settings.pack_use_bitmap_boundary_traversal = 1;
+		r->settings.pack_use_multi_pack_reuse = 1;
 	}
 	if (manyfiles) {
 		r->settings.index_version = 4;
@@ -63,6 +63,11 @@
 	repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1);
 	repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0);
 	repo_cfg_bool(r, "index.skiphash", &r->settings.index_skip_hash, r->settings.index_skip_hash);
+	repo_cfg_bool(r, "pack.readreverseindex", &r->settings.pack_read_reverse_index, 1);
+	repo_cfg_bool(r, "pack.usebitmapboundarytraversal",
+		      &r->settings.pack_use_bitmap_boundary_traversal,
+		      r->settings.pack_use_bitmap_boundary_traversal);
+	repo_cfg_bool(r, "core.usereplacerefs", &r->settings.read_replace_refs, 1);
 
 	/*
 	 * The GIT_TEST_MULTI_PACK_INDEX variable is special in that
diff --git a/repository.c b/repository.c
index 937fa97..e15b416 100644
--- a/repository.c
+++ b/repository.c
@@ -3,15 +3,21 @@
  * declaration matches the definition in this file.
  */
 #define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "config.h"
 #include "object.h"
 #include "lockfile.h"
+#include "path.h"
+#include "read-cache-ll.h"
 #include "remote.h"
+#include "setup.h"
+#include "loose.h"
 #include "submodule-config.h"
 #include "sparse-index.h"
+#include "trace2.h"
 #include "promisor-remote.h"
 
 /* The main repository */
@@ -99,6 +105,20 @@
 	repo->hash_algo = &hash_algos[hash_algo];
 }
 
+void repo_set_compat_hash_algo(struct repository *repo, int algo)
+{
+	if (hash_algo_by_ptr(repo->hash_algo) == algo)
+		BUG("hash_algo and compat_hash_algo match");
+	repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
+	if (repo->compat_hash_algo)
+		repo_read_loose_object_map(repo);
+}
+
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+{
+	repo->ref_storage_format = format;
+}
+
 /*
  * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
  * Return 0 upon success and a non-zero value upon failure.
@@ -179,6 +199,9 @@
 		goto error;
 
 	repo_set_hash_algo(repo, format.hash_algo);
+	repo_set_compat_hash_algo(repo, format.compat_hash_algo);
+	repo_set_ref_storage_format(repo, format.ref_storage_format);
+	repo->repository_format_worktree_config = format.worktree_config;
 
 	/* take ownership of format.partial_clone */
 	repo->repository_format_partial_clone = format.partial_clone;
@@ -187,6 +210,9 @@
 	if (worktree)
 		repo_set_worktree(repo, worktree);
 
+	if (repo->compat_hash_algo)
+		repo_read_loose_object_map(repo);
+
 	clear_repository_format(&format);
 	return 0;
 
@@ -250,8 +276,6 @@
 	FREE_AND_NULL(cache->merge_rr);
 	FREE_AND_NULL(cache->merge_mode);
 	FREE_AND_NULL(cache->merge_head);
-	FREE_AND_NULL(cache->merge_autostash);
-	FREE_AND_NULL(cache->auto_merge);
 	FREE_AND_NULL(cache->fetch_head);
 	FREE_AND_NULL(cache->shallow);
 }
diff --git a/repository.h b/repository.h
index e8c67ff..2684367 100644
--- a/repository.h
+++ b/repository.h
@@ -1,9 +1,6 @@
 #ifndef REPOSITORY_H
 #define REPOSITORY_H
 
-#include "git-compat-util.h"
-#include "path.h"
-
 struct config_set;
 struct fsmonitor_settings;
 struct git_hash_algo;
@@ -27,6 +24,10 @@
 	FETCH_NEGOTIATION_NOOP,
 };
 
+#define REF_STORAGE_FORMAT_UNKNOWN  0
+#define REF_STORAGE_FORMAT_FILES    1
+#define REF_STORAGE_FORMAT_REFTABLE 2
+
 struct repo_settings {
 	int initialized;
 
@@ -34,10 +35,21 @@
 	int commit_graph_generation_version;
 	int commit_graph_read_changed_paths;
 	int gc_write_commit_graph;
-	int gc_cruft_packs;
 	int fetch_write_commit_graph;
 	int command_requires_full_index;
 	int sparse_index;
+	int pack_read_reverse_index;
+	int pack_use_bitmap_boundary_traversal;
+	int pack_use_multi_pack_reuse;
+
+	/*
+	 * Does this repository have core.useReplaceRefs=true (on by
+	 * default)? This provides a repository-scoped version of this
+	 * config, though it could be disabled process-wide via some Git
+	 * builtins or the --no-replace-objects option. See
+	 * replace_refs_enabled() for more details.
+	 */
+	int read_replace_refs;
 
 	struct fsmonitor_settings *fsmonitor; /* lazily loaded */
 
@@ -57,8 +69,6 @@
 	char *merge_rr;
 	char *merge_mode;
 	char *merge_head;
-	char *merge_autostash;
-	char *auto_merge;
 	char *fetch_head;
 	char *shallow;
 };
@@ -153,6 +163,12 @@
 	/* Repository's current hash algorithm, as serialized on disk. */
 	const struct git_hash_algo *hash_algo;
 
+	/* Repository's compatibility hash algorithm. */
+	const struct git_hash_algo *compat_hash_algo;
+
+	/* Repository's reference storage format, as serialized on disk. */
+	unsigned int ref_storage_format;
+
 	/* A unique-id for tracing purposes. */
 	int trace2_repo_id;
 
@@ -164,12 +180,16 @@
 	struct promisor_remote_config *promisor_remote_config;
 
 	/* Configurations */
+	int repository_format_worktree_config;
 
 	/* Indicate if a repository has a different 'commondir' from 'gitdir' */
 	unsigned different_commondir:1;
 };
 
 extern struct repository *the_repository;
+#ifdef USE_THE_INDEX_VARIABLE
+extern struct index_state the_index;
+#endif
 
 /*
  * Define a custom repository layout. Any field can be NULL, which
@@ -188,6 +208,8 @@
 		     const struct set_gitdir_args *extra_args);
 void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
 void initialize_the_repository(void);
 RESULT_MUST_BE_USED
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);
@@ -221,9 +243,6 @@
 			   struct lock_file *lf,
 			   int flags);
 
-int repo_read_index_preload(struct repository *,
-			    const struct pathspec *pathspec,
-			    unsigned refresh_flags);
 int repo_read_index_unmerged(struct repository *);
 /*
  * Opportunistically update the index but do not complain if we can't.
diff --git a/rerere.c b/rerere.c
index 876ab43..13c94de 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,16 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "config.h"
+#include "copy.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "string-list.h"
+#include "read-cache-ll.h"
 #include "rerere.h"
 #include "xdiff-interface.h"
 #include "dir.h"
 #include "resolve-undo.h"
-#include "ll-merge.h"
-#include "attr.h"
+#include "merge-ll.h"
+#include "path.h"
 #include "pathspec.h"
-#include "object-store.h"
-#include "hash-lookup.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "strmap.h"
 
 #define RESOLVED 0
@@ -197,7 +202,7 @@
 		const unsigned hexsz = the_hash_algo->hexsz;
 
 		/* There has to be the hash, tab, path and then NUL */
-		if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash))
+		if (buf.len < hexsz + 2 || get_hash_hex(buf.buf, hash))
 			die(_("corrupt MERGE_RR"));
 
 		if (buf.buf[hexsz] != '.') {
@@ -965,8 +970,12 @@
 			break;
 		i = ce_stage(ce) - 1;
 		if (!mmfile[i].ptr) {
-			mmfile[i].ptr = read_object_file(&ce->oid, &type,
-							 &size);
+			mmfile[i].ptr = repo_read_object_file(the_repository,
+							      &ce->oid, &type,
+							      &size);
+			if (!mmfile[i].ptr)
+				die(_("unable to read %s"),
+				    oid_to_hex(&ce->oid));
 			mmfile[i].size = size;
 		}
 	}
@@ -1104,7 +1113,7 @@
 	 * recover the original conflicted state and then
 	 * find the conflicted paths.
 	 */
-	unmerge_index(r->index, pathspec);
+	unmerge_index(r->index, pathspec, 0);
 	find_conflict(r, &conflict);
 	for (i = 0; i < conflict.nr; i++) {
 		struct string_list_item *it = &conflict.items[i];
diff --git a/rerere.h b/rerere.h
index c32d79c..5d6cb63 100644
--- a/rerere.h
+++ b/rerere.h
@@ -1,6 +1,7 @@
 #ifndef RERERE_H
 #define RERERE_H
 
+#include "gettext.h"
 #include "string-list.h"
 
 struct pathspec;
@@ -24,9 +25,6 @@
 };
 
 int setup_rerere(struct repository *,struct string_list *, int);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define rerere(flags) repo_rerere(the_repository, flags)
-#endif
 int repo_rerere(struct repository *, int);
 /*
  * Given the conflict ID and the name of a "file" used for replaying
diff --git a/reset.c b/reset.c
index 5ded236..d619cb7 100644
--- a/reset.c
+++ b/reset.c
@@ -1,9 +1,11 @@
 #include "git-compat-util.h"
 #include "cache-tree.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
+#include "object-name.h"
 #include "refs.h"
 #include "reset.h"
-#include "run-command.h"
 #include "tree-walk.h"
 #include "tree.h"
 #include "unpack-trees.h"
@@ -38,7 +40,7 @@
 	prefix_len = msg.len;
 
 	if (update_orig_head) {
-		if (!get_oid("ORIG_HEAD", &oid_old_orig))
+		if (!repo_get_oid(the_repository, "ORIG_HEAD", &oid_old_orig))
 			old_orig = &oid_old_orig;
 		if (head) {
 			if (!reflog_orig_head) {
@@ -106,7 +108,7 @@
 		goto leave_reset_head;
 	}
 
-	if (!get_oid("HEAD", &head_oid)) {
+	if (!repo_get_oid(r, "HEAD", &head_oid)) {
 		head = &head_oid;
 	} else if (!oid || !reset_hard) {
 		ret = error(_("could not determine HEAD revision"));
@@ -155,6 +157,11 @@
 	}
 
 	tree = parse_tree_indirect(oid);
+	if (!tree) {
+		ret = error(_("unable to read tree (%s)"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
 	prime_cache_tree(r, r->index, tree);
 
 	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
diff --git a/reset.h b/reset.h
index a28f818..10708d8 100644
--- a/reset.h
+++ b/reset.h
@@ -1,7 +1,7 @@
 #ifndef RESET_H
 #define RESET_H
 
-#include "hash.h"
+#include "hash-ll.h"
 #include "repository.h"
 
 #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
diff --git a/resolve-undo.c b/resolve-undo.c
index e81096e..cd02dc9 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
+#include "hash.h"
+#include "read-cache.h"
 #include "resolve-undo.h"
+#include "sparse-index.h"
 #include "string-list.h"
 
 /* The only error case is to run out of memory in string-list */
@@ -114,86 +117,59 @@
 	istate->cache_changed |= RESOLVE_UNDO_CHANGED;
 }
 
-int unmerge_index_entry_at(struct index_state *istate, int pos)
+int unmerge_index_entry(struct index_state *istate, const char *path,
+			struct resolve_undo_info *ru, unsigned ce_flags)
 {
-	const struct cache_entry *ce;
-	struct string_list_item *item;
-	struct resolve_undo_info *ru;
-	int i, err = 0, matched;
-	char *name;
+	int i = index_name_pos(istate, path, strlen(path));
 
-	if (!istate->resolve_undo)
-		return pos;
-
-	ce = istate->cache[pos];
-	if (ce_stage(ce)) {
-		/* already unmerged */
-		while ((pos < istate->cache_nr) &&
-		       ! strcmp(istate->cache[pos]->name, ce->name))
-			pos++;
-		return pos - 1; /* return the last entry processed */
+	if (i < 0) {
+		/* unmerged? */
+		i = -i - 1;
+		if (i < istate->cache_nr &&
+		    !strcmp(istate->cache[i]->name, path))
+			/* yes, it is already unmerged */
+			return 0;
+		/* fallthru: resolved to removal */
+	} else {
+		/* merged - remove it to replace it with unmerged entries */
+		remove_index_entry_at(istate, i);
 	}
-	item = string_list_lookup(istate->resolve_undo, ce->name);
-	if (!item)
-		return pos;
-	ru = item->util;
-	if (!ru)
-		return pos;
-	matched = ce->ce_flags & CE_MATCHED;
-	name = xstrdup(ce->name);
-	remove_index_entry_at(istate, pos);
+
 	for (i = 0; i < 3; i++) {
-		struct cache_entry *nce;
+		struct cache_entry *ce;
 		if (!ru->mode[i])
 			continue;
-		nce = make_cache_entry(istate,
-				       ru->mode[i],
-				       &ru->oid[i],
-				       name, i + 1, 0);
-		if (matched)
-			nce->ce_flags |= CE_MATCHED;
-		if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
-			err = 1;
-			error("cannot unmerge '%s'", name);
-		}
+		ce = make_cache_entry(istate, ru->mode[i], &ru->oid[i],
+				      path, i + 1, 0);
+		ce->ce_flags |= ce_flags;
+		if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD))
+			return error("cannot unmerge '%s'", path);
 	}
-	free(name);
-	if (err)
-		return pos;
-	free(ru);
-	item->util = NULL;
-	return unmerge_index_entry_at(istate, pos);
+	return 0;
 }
 
-void unmerge_marked_index(struct index_state *istate)
+void unmerge_index(struct index_state *istate, const struct pathspec *pathspec,
+		   unsigned ce_flags)
 {
-	int i;
+	struct string_list_item *item;
 
 	if (!istate->resolve_undo)
 		return;
 
 	/* TODO: audit for interaction with sparse-index. */
 	ensure_full_index(istate);
-	for (i = 0; i < istate->cache_nr; i++) {
-		const struct cache_entry *ce = istate->cache[i];
-		if (ce->ce_flags & CE_MATCHED)
-			i = unmerge_index_entry_at(istate, i);
-	}
-}
 
-void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
-{
-	int i;
-
-	if (!istate->resolve_undo)
-		return;
-
-	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(istate);
-	for (i = 0; i < istate->cache_nr; i++) {
-		const struct cache_entry *ce = istate->cache[i];
-		if (!ce_path_match(istate, ce, pathspec, NULL))
+	for_each_string_list_item(item, istate->resolve_undo) {
+		const char *path = item->string;
+		struct resolve_undo_info *ru = item->util;
+		if (!item->util)
 			continue;
-		i = unmerge_index_entry_at(istate, i);
+		if (!match_pathspec(istate, pathspec,
+				    item->string, strlen(item->string),
+				    0, NULL, 0))
+			continue;
+		unmerge_index_entry(istate, path, ru, ce_flags);
+		free(ru);
+		item->util = NULL;
 	}
 }
diff --git a/resolve-undo.h b/resolve-undo.h
index 2b3f0f9..f3f8462 100644
--- a/resolve-undo.h
+++ b/resolve-undo.h
@@ -1,7 +1,12 @@
 #ifndef RESOLVE_UNDO_H
 #define RESOLVE_UNDO_H
 
-#include "cache.h"
+struct cache_entry;
+struct index_state;
+struct pathspec;
+struct string_list;
+
+#include "hash-ll.h"
 
 struct resolve_undo_info {
 	unsigned int mode[3];
@@ -12,8 +17,7 @@
 void resolve_undo_write(struct strbuf *, struct string_list *);
 struct string_list *resolve_undo_read(const char *, unsigned long);
 void resolve_undo_clear_index(struct index_state *);
-int unmerge_index_entry_at(struct index_state *, int);
-void unmerge_index(struct index_state *, const struct pathspec *);
-void unmerge_marked_index(struct index_state *);
+int unmerge_index_entry(struct index_state *, const char *, struct resolve_undo_info *, unsigned);
+void unmerge_index(struct index_state *, const struct pathspec *, unsigned);
 
 #endif
diff --git a/revision.c b/revision.c
index 21f5f57..7e45f76 100644
--- a/revision.c
+++ b/revision.c
@@ -1,6 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oidset.h"
 #include "tag.h"
 #include "blob.h"
 #include "tree.h"
@@ -15,17 +21,19 @@
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "decorate.h"
-#include "log-tree.h"
 #include "string-list.h"
 #include "line-log.h"
 #include "mailmap.h"
 #include "commit-slab.h"
-#include "dir.h"
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
 #include "worktree.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
 #include "strvec.h"
+#include "trace2.h"
 #include "commit-reach.h"
 #include "commit-graph.h"
 #include "prio-queue.h"
@@ -35,6 +43,8 @@
 #include "json-writer.h"
 #include "list-objects-filter-options.h"
 #include "resolve-undo.h"
+#include "parse-options.h"
+#include "wildmatch.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -71,7 +81,7 @@
 	if (parse_tree_gently(tree, 1) < 0)
 		return;
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 	while (tree_entry(&desc, &entry)) {
 		switch (object_type(entry.mode)) {
 		case OBJ_TREE:
@@ -178,7 +188,7 @@
 	if (parse_tree_gently(tree, 1) < 0)
 		return;
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 	while (tree_entry(&desc, &entry)) {
 		switch (object_type(entry.mode)) {
 		case OBJ_TREE:
@@ -324,7 +334,8 @@
 	if (revs->reflog_info && obj->type == OBJ_COMMIT) {
 		struct strbuf buf = STRBUF_INIT;
 		size_t namelen = strlen(name);
-		int len = interpret_branch_name(name, namelen, &buf, &options);
+		int len = repo_interpret_branch_name(the_repository, name,
+						     namelen, &buf, &options);
 
 		if (0 < len && len < namelen && buf.len)
 			strbuf_addstr(&buf, name + len);
@@ -354,7 +365,7 @@
 {
 	struct object_id oid;
 	struct object *obj;
-	if (get_oid("HEAD", &oid))
+	if (repo_get_oid(the_repository, "HEAD", &oid))
 		return;
 	obj = parse_object(revs->repo, &oid);
 	if (!obj)
@@ -370,13 +381,18 @@
 
 	object = parse_object_with_flags(revs->repo, oid,
 					 revs->verify_objects ? 0 :
-					 PARSE_OBJECT_SKIP_HASH_CHECK);
+					 PARSE_OBJECT_SKIP_HASH_CHECK |
+					 PARSE_OBJECT_DISCARD_TREE);
 
 	if (!object) {
 		if (revs->ignore_missing)
-			return object;
+			return NULL;
 		if (revs->exclude_promisor_objects && is_promisor_object(oid))
 			return NULL;
+		if (revs->do_not_die_on_missing_objects) {
+			oidset_insert(&revs->missing_commits, oid);
+			return NULL;
+		}
 		die("bad object %s", name);
 	}
 	object->flags |= flags;
@@ -404,15 +420,21 @@
 	 */
 	while (object->type == OBJ_TAG) {
 		struct tag *tag = (struct tag *) object;
+		struct object_id *oid;
 		if (revs->tag_objects && !(flags & UNINTERESTING))
 			add_pending_object(revs, object, tag->tag);
-		object = parse_object(revs->repo, get_tagged_oid(tag));
+		oid = get_tagged_oid(tag);
+		object = parse_object(revs->repo, oid);
 		if (!object) {
 			if (revs->ignore_missing_links || (flags & UNINTERESTING))
 				return NULL;
 			if (revs->exclude_promisor_objects &&
 			    is_promisor_object(&tag->tagged->oid))
 				return NULL;
+			if (revs->do_not_die_on_missing_objects && oid) {
+				oidset_insert(&revs->missing_commits, oid);
+				return NULL;
+			}
 			die("bad object %s", oid_to_hex(&tag->tagged->oid));
 		}
 		object->flags |= flags;
@@ -777,8 +799,8 @@
 static int rev_compare_tree(struct rev_info *revs,
 			    struct commit *parent, struct commit *commit, int nth_parent)
 {
-	struct tree *t1 = get_commit_tree(parent);
-	struct tree *t2 = get_commit_tree(commit);
+	struct tree *t1 = repo_get_commit_tree(the_repository, parent);
+	struct tree *t2 = repo_get_commit_tree(the_repository, commit);
 	int bloom_ret = 1;
 
 	if (!t1)
@@ -824,7 +846,7 @@
 
 static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 {
-	struct tree *t1 = get_commit_tree(commit);
+	struct tree *t1 = repo_get_commit_tree(the_repository, commit);
 
 	if (!t1)
 		return 0;
@@ -962,7 +984,7 @@
 	if (!revs->prune)
 		return;
 
-	if (!get_commit_tree(commit))
+	if (!repo_get_commit_tree(the_repository, commit))
 		return;
 
 	if (!commit->parents) {
@@ -1100,6 +1122,9 @@
 
 	if (commit->object.flags & ADDED)
 		return 0;
+	if (revs->do_not_die_on_missing_objects &&
+	    oidset_contains(&revs->missing_commits, &commit->object.oid))
+		return 0;
 	commit->object.flags |= ADDED;
 
 	if (revs->include_check &&
@@ -1156,7 +1181,8 @@
 	for (parent = commit->parents; parent; parent = parent->next) {
 		struct commit *p = parent->item;
 		int gently = revs->ignore_missing_links ||
-			     revs->exclude_promisor_objects;
+			     revs->exclude_promisor_objects ||
+			     revs->do_not_die_on_missing_objects;
 		if (repo_parse_commit_gently(revs->repo, p, gently) < 0) {
 			if (revs->exclude_promisor_objects &&
 			    is_promisor_object(&p->object.oid)) {
@@ -1164,7 +1190,11 @@
 					break;
 				continue;
 			}
-			return -1;
+
+			if (revs->do_not_die_on_missing_objects)
+				oidset_insert(&revs->missing_commits, &p->object.oid);
+			else
+				return -1; /* corrupt repository */
 		}
 		if (revs->sources) {
 			char **slot = revision_sources_at(revs->sources, p);
@@ -1548,7 +1578,7 @@
 void clear_ref_exclusions(struct ref_exclusions *exclusions)
 {
 	string_list_clear(&exclusions->excluded_refs, 0);
-	string_list_clear(&exclusions->hidden_refs, 0);
+	strvec_clear(&exclusions->hidden_refs);
 	exclusions->hidden_refs_configured = 0;
 }
 
@@ -1562,7 +1592,9 @@
 	const char *section;
 };
 
-static int hide_refs_config(const char *var, const char *value, void *cb_data)
+static int hide_refs_config(const char *var, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *cb_data)
 {
 	struct exclude_hidden_refs_cb *cb = cb_data;
 	cb->exclusions->hidden_refs_configured = 1;
@@ -1574,7 +1606,8 @@
 {
 	struct exclude_hidden_refs_cb cb;
 
-	if (strcmp(section, "receive") && strcmp(section, "uploadpack"))
+	if (strcmp(section, "fetch") && strcmp(section, "receive") &&
+			strcmp(section, "uploadpack"))
 		die(_("unsupported section for hidden refs: %s"), section);
 
 	if (exclusions->hidden_refs_configured)
@@ -1664,9 +1697,7 @@
 	return 0;
 }
 
-static int handle_one_reflog(const char *refname_in_wt,
-			     const struct object_id *oid UNUSED,
-			     int flag UNUSED, void *cb_data)
+static int handle_one_reflog(const char *refname_in_wt, void *cb_data)
 {
 	struct all_refs_cb *cb = cb_data;
 	struct strbuf refname = STRBUF_INIT;
@@ -1867,7 +1898,7 @@
 		flags ^= UNINTERESTING | BOTTOM;
 		arg++;
 	}
-	if (get_oid_committish(arg, &oid))
+	if (repo_get_oid_committish(the_repository, arg, &oid))
 		return 0;
 	while (1) {
 		it = get_reference(revs, arg, &oid, 0);
@@ -1925,6 +1956,7 @@
 	init_display_notes(&revs->notes_opt);
 	list_objects_filter_init(&revs->filter);
 	init_ref_exclusions(&revs->ref_excludes);
+	oidset_init(&revs->missing_commits, 0);
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
@@ -1939,24 +1971,44 @@
 	}
 }
 
+static const char *lookup_other_head(struct object_id *oid)
+{
+	int i;
+	static const char *const other_head[] = {
+		"MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"
+	};
+
+	for (i = 0; i < ARRAY_SIZE(other_head); i++)
+		if (!read_ref_full(other_head[i],
+				RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+				oid, NULL)) {
+			if (is_null_oid(oid))
+				die(_("%s exists but is a symbolic ref"), other_head[i]);
+			return other_head[i];
+		}
+
+	die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+}
+
 static void prepare_show_merge(struct rev_info *revs)
 {
-	struct commit_list *bases;
+	struct commit_list *bases = NULL;
 	struct commit *head, *other;
 	struct object_id oid;
+	const char *other_name;
 	const char **prune = NULL;
 	int i, prune_num = 1; /* counting terminating NULL */
 	struct index_state *istate = revs->repo->index;
 
-	if (get_oid("HEAD", &oid))
+	if (repo_get_oid(the_repository, "HEAD", &oid))
 		die("--merge without HEAD?");
 	head = lookup_commit_or_die(&oid, "HEAD");
-	if (get_oid("MERGE_HEAD", &oid))
-		die("--merge without MERGE_HEAD?");
-	other = lookup_commit_or_die(&oid, "MERGE_HEAD");
+	other_name = lookup_other_head(&oid);
+	other = lookup_commit_or_die(&oid, other_name);
 	add_pending_object(revs, &head->object, "HEAD");
-	add_pending_object(revs, &other->object, "MERGE_HEAD");
-	bases = get_merge_bases(head, other);
+	add_pending_object(revs, &other->object, other_name);
+	if (repo_get_merge_bases(the_repository, head, other, &bases) < 0)
+		exit(128);
 	add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
 	add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
 	free_commit_list(bases);
@@ -2044,14 +2096,17 @@
 	} else {
 		/* A...B -- find merge bases between the two */
 		struct commit *a, *b;
-		struct commit_list *exclude;
+		struct commit_list *exclude = NULL;
 
 		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);
 
-		exclude = get_merge_bases(a, b);
+		if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
+			free_commit_list(exclude);
+			return -1;
+		}
 		add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
 				     flags_exclude);
 		add_pending_commit_list(revs, exclude, flags_exclude);
@@ -2156,13 +2211,18 @@
 	if (revarg_opt & REVARG_COMMITTISH)
 		get_sha1_flags |= GET_OID_COMMITTISH;
 
+	/*
+	 * Even if revs->do_not_die_on_missing_objects is set, we
+	 * should error out if we can't even get an oid, as
+	 * `--missing=print` should be able to report missing oids.
+	 */
 	if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc))
 		return revs->ignore_missing ? 0 : -1;
 	if (!cant_be_filename)
 		verify_non_filename(revs->prefix, arg);
 	object = get_reference(revs, arg, &oid, flags ^ local_flags);
 	if (!object)
-		return revs->ignore_missing ? 0 : -1;
+		return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
 	add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
 	add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
 	free(oc.path);
@@ -2184,39 +2244,6 @@
 		strvec_push(prune, sb->buf);
 }
 
-static void read_revisions_from_stdin(struct rev_info *revs,
-				      struct strvec *prune)
-{
-	struct strbuf sb;
-	int seen_dashdash = 0;
-	int save_warning;
-
-	save_warning = warn_on_object_refname_ambiguity;
-	warn_on_object_refname_ambiguity = 0;
-
-	strbuf_init(&sb, 1000);
-	while (strbuf_getline(&sb, stdin) != EOF) {
-		int len = sb.len;
-		if (!len)
-			break;
-		if (sb.buf[0] == '-') {
-			if (len == 2 && sb.buf[1] == '-') {
-				seen_dashdash = 1;
-				break;
-			}
-			die("options not supported in --stdin mode");
-		}
-		if (handle_revision_arg(sb.buf, revs, 0,
-					REVARG_CANNOT_BE_FILENAME))
-			die("bad revision '%s'", sb.buf);
-	}
-	if (seen_dashdash)
-		read_pathspec_from_stdin(&sb, prune);
-
-	strbuf_release(&sb);
-	warn_on_object_refname_ambiguity = save_warning;
-}
-
 static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
 {
 	append_grep_pattern(&revs->grep_filter, ptn, "command line", 0, what);
@@ -2232,6 +2259,27 @@
 	add_grep(revs, pattern, GREP_PATTERN_BODY);
 }
 
+static int parse_count(const char *arg)
+{
+	int count;
+
+	if (strtol_i(arg, 10, &count) < 0)
+		die("'%s': not an integer", arg);
+	return count;
+}
+
+static timestamp_t parse_age(const char *arg)
+{
+	timestamp_t num;
+	char *p;
+
+	errno = 0;
+	num = parse_timestamp(arg, &p, 10);
+	if (errno || *p || p == arg)
+		die("'%s': not a number of seconds since epoch", arg);
+	return num;
+}
+
 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
 			       int *unkc, const char **unkv,
 			       const struct setup_revision_opt* opt)
@@ -2258,29 +2306,27 @@
 	}
 
 	if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
-		revs->max_count = atoi(optarg);
+		revs->max_count = parse_count(optarg);
 		revs->no_walk = 0;
 		return argcount;
 	} else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
-		revs->skip_count = atoi(optarg);
+		revs->skip_count = parse_count(optarg);
 		return argcount;
 	} else if ((*arg == '-') && isdigit(arg[1])) {
 		/* accept -<digit>, like traditional "head" */
-		if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
-		    revs->max_count < 0)
-			die("'%s': not a non-negative integer", arg + 1);
+		revs->max_count = parse_count(arg + 1);
 		revs->no_walk = 0;
 	} else if (!strcmp(arg, "-n")) {
 		if (argc <= 1)
 			return error("-n requires an argument");
-		revs->max_count = atoi(argv[1]);
+		revs->max_count = parse_count(argv[1]);
 		revs->no_walk = 0;
 		return 2;
 	} else if (skip_prefix(arg, "-n", &optarg)) {
-		revs->max_count = atoi(optarg);
+		revs->max_count = parse_count(optarg);
 		revs->no_walk = 0;
 	} else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
-		revs->max_age = atoi(optarg);
+		revs->max_age = parse_age(optarg);
 		return argcount;
 	} else if ((argcount = parse_long_opt("since", argv, &optarg))) {
 		revs->max_age = approxidate(optarg);
@@ -2292,7 +2338,7 @@
 		revs->max_age = approxidate(optarg);
 		return argcount;
 	} else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
-		revs->min_age = atoi(optarg);
+		revs->min_age = parse_age(optarg);
 		return argcount;
 	} else if ((argcount = parse_long_opt("before", argv, &optarg))) {
 		revs->min_age = approxidate(optarg);
@@ -2312,7 +2358,7 @@
 	} else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
 		struct commit *c;
 		struct object_id oid;
-		const char *msg = _("could not get commit for ancestry-path argument %s");
+		const char *msg = _("could not get commit for --ancestry-path argument %s");
 
 		revs->ancestry_path = 1;
 		revs->simplify_history = 0;
@@ -2380,11 +2426,11 @@
 	} else if (!strcmp(arg, "--no-merges")) {
 		revs->max_parents = 1;
 	} else if (skip_prefix(arg, "--min-parents=", &optarg)) {
-		revs->min_parents = atoi(optarg);
+		revs->min_parents = parse_count(optarg);
 	} else if (!strcmp(arg, "--no-min-parents")) {
 		revs->min_parents = 0;
 	} else if (skip_prefix(arg, "--max-parents=", &optarg)) {
-		revs->max_parents = atoi(optarg);
+		revs->max_parents = parse_count(optarg);
 	} else if (!strcmp(arg, "--no-max-parents")) {
 		revs->max_parents = -1;
 	} else if (!strcmp(arg, "--boundary")) {
@@ -2393,8 +2439,8 @@
 		revs->left_right = 1;
 	} else if (!strcmp(arg, "--left-only")) {
 		if (revs->right_only)
-			die("--left-only is incompatible with --right-only"
-			    " or --cherry");
+			die(_("options '%s' and '%s' cannot be used together"),
+			    "--left-only", "--right-only/--cherry");
 		revs->left_only = 1;
 	} else if (!strcmp(arg, "--right-only")) {
 		if (revs->left_only)
@@ -2502,6 +2548,8 @@
 		revs->break_bar = xstrdup(optarg);
 		revs->track_linear = 1;
 		revs->track_first_time = 1;
+	} else if (!strcmp(arg, "--show-notes-by-default")) {
+		revs->show_notes_by_default = 1;
 	} else if (skip_prefix(arg, "--show-notes=", &optarg) ||
 		   skip_prefix(arg, "--notes=", &optarg)) {
 		if (starts_with(arg, "--show-notes=") &&
@@ -2659,7 +2707,7 @@
 	struct strbuf bisect_refs = STRBUF_INIT;
 	int status;
 	strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
-	status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data);
+	status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data);
 	strbuf_release(&bisect_refs);
 	return status;
 }
@@ -2716,7 +2764,8 @@
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (!strcmp(arg, "--branches")) {
 		if (revs->ref_excludes.hidden_refs_configured)
-			return error(_("--exclude-hidden cannot be used together with --branches"));
+			return error(_("options '%s' and '%s' cannot be used together"),
+				     "--exclude-hidden", "--branches");
 		handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (!strcmp(arg, "--bisect")) {
@@ -2727,12 +2776,14 @@
 		revs->bisect = 1;
 	} else if (!strcmp(arg, "--tags")) {
 		if (revs->ref_excludes.hidden_refs_configured)
-			return error(_("--exclude-hidden cannot be used together with --tags"));
+			return error(_("options '%s' and '%s' cannot be used together"),
+				     "--exclude-hidden", "--tags");
 		handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (!strcmp(arg, "--remotes")) {
 		if (revs->ref_excludes.hidden_refs_configured)
-			return error(_("--exclude-hidden cannot be used together with --remotes"));
+			return error(_("options '%s' and '%s' cannot be used together"),
+				     "--exclude-hidden", "--remotes");
 		handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
@@ -2750,21 +2801,24 @@
 	} else if (skip_prefix(arg, "--branches=", &optarg)) {
 		struct all_refs_cb cb;
 		if (revs->ref_excludes.hidden_refs_configured)
-			return error(_("--exclude-hidden cannot be used together with --branches"));
+			return error(_("options '%s' and '%s' cannot be used together"),
+				     "--exclude-hidden", "--branches");
 		init_all_refs_cb(&cb, revs, *flags);
 		for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (skip_prefix(arg, "--tags=", &optarg)) {
 		struct all_refs_cb cb;
 		if (revs->ref_excludes.hidden_refs_configured)
-			return error(_("--exclude-hidden cannot be used together with --tags"));
+			return error(_("options '%s' and '%s' cannot be used together"),
+				     "--exclude-hidden", "--tags");
 		init_all_refs_cb(&cb, revs, *flags);
 		for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
 	} else if (skip_prefix(arg, "--remotes=", &optarg)) {
 		struct all_refs_cb cb;
 		if (revs->ref_excludes.hidden_refs_configured)
-			return error(_("--exclude-hidden cannot be used together with --remotes"));
+			return error(_("options '%s' and '%s' cannot be used together"),
+				     "--exclude-hidden", "--remotes");
 		init_all_refs_cb(&cb, revs, *flags);
 		for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
 		clear_ref_exclusions(&revs->ref_excludes);
@@ -2805,6 +2859,53 @@
 	return 1;
 }
 
+static void read_revisions_from_stdin(struct rev_info *revs,
+				      struct strvec *prune)
+{
+	struct strbuf sb;
+	int seen_dashdash = 0;
+	int seen_end_of_options = 0;
+	int save_warning;
+	int flags = 0;
+
+	save_warning = warn_on_object_refname_ambiguity;
+	warn_on_object_refname_ambiguity = 0;
+
+	strbuf_init(&sb, 1000);
+	while (strbuf_getline(&sb, stdin) != EOF) {
+		if (!sb.len)
+			break;
+
+		if (!strcmp(sb.buf, "--")) {
+			seen_dashdash = 1;
+			break;
+		}
+
+		if (!seen_end_of_options && sb.buf[0] == '-') {
+			const char *argv[] = { sb.buf, NULL };
+
+			if (!strcmp(sb.buf, "--end-of-options")) {
+				seen_end_of_options = 1;
+				continue;
+			}
+
+			if (handle_revision_pseudo_opt(revs, argv, &flags) > 0)
+				continue;
+
+			die(_("invalid option '%s' in --stdin mode"), sb.buf);
+		}
+
+		if (handle_revision_arg(sb.buf, revs, flags,
+					REVARG_CANNOT_BE_FILENAME))
+			die("bad revision '%s'", sb.buf);
+	}
+	if (seen_dashdash)
+		read_pathspec_from_stdin(&sb, prune);
+
+	strbuf_release(&sb);
+	warn_on_object_refname_ambiguity = save_warning;
+}
+
 static void NORETURN diagnose_missing_default(const char *def)
 {
 	int flags;
@@ -2941,7 +3042,7 @@
 	if (!revs->def)
 		revs->def = opt ? opt->def : NULL;
 	if (opt && opt->tweak)
-		opt->tweak(revs, opt);
+		opt->tweak(revs);
 	if (revs->show_merge)
 		prepare_show_merge(revs);
 	if (revs->def && !revs->pending.nr && !revs->rev_input_given) {
@@ -2996,8 +3097,6 @@
 		revs->grep_filter.ignore_locale = 1;
 	compile_grep_patterns(&revs->grep_filter);
 
-	if (revs->reverse && revs->reflog_info)
-		die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--walk-reflogs");
 	if (revs->reflog_info && revs->limited)
 		die("cannot combine --walk-reflogs with history-limiting options");
 	if (revs->rewrite_parents && revs->children.name)
@@ -3008,11 +3107,10 @@
 	/*
 	 * Limitations on the graph functionality
 	 */
-	if (revs->reverse && revs->graph)
-		die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--graph");
+	die_for_incompatible_opt3(!!revs->graph, "--graph",
+				  !!revs->reverse, "--reverse",
+				  !!revs->reflog_info, "--walk-reflogs");
 
-	if (revs->reflog_info && revs->graph)
-		die(_("options '%s' and '%s' cannot be used together"), "--walk-reflogs", "--graph");
 	if (revs->no_walk && revs->graph)
 		die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
 	if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
@@ -3025,6 +3123,11 @@
 	if (revs->expand_tabs_in_log < 0)
 		revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
 
+	if (!revs->show_notes_given && revs->show_notes_by_default) {
+		enable_default_display_notes(&revs->notes_opt, &revs->show_notes);
+		revs->show_notes_given = 1;
+	}
+
 	return left;
 }
 
@@ -3047,6 +3150,11 @@
 
 static void release_revisions_topo_walk_info(struct topo_walk_info *info);
 
+static void free_void_commit_list(void *list)
+{
+	free_commit_list(list);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
@@ -3064,6 +3172,11 @@
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 	release_revisions_topo_walk_info(revs->topo_walk_info);
+	clear_decoration(&revs->children, free_void_commit_list);
+	clear_decoration(&revs->merge_simplification, free);
+	clear_decoration(&revs->treesame, free);
+	line_log_free(revs);
+	oidset_clear(&revs->missing_commits);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3440,8 +3553,8 @@
 }
 
 static int mark_uninteresting(const struct object_id *oid,
-			      struct packed_git *pack,
-			      uint32_t pos,
+			      struct packed_git *pack UNUSED,
+			      uint32_t pos UNUSED,
 			      void *cb)
 {
 	struct rev_info *revs = cb;
@@ -3877,7 +3990,7 @@
 	 * in it.
 	 */
 	encoding = get_log_output_encoding();
-	message = logmsg_reencode(commit, NULL, encoding);
+	message = repo_logmsg_reencode(the_repository, commit, NULL, encoding);
 
 	/* Copy the commit to temporary if we are using "fake" headers */
 	if (buf.len)
@@ -3913,7 +4026,7 @@
 		retval = grep_buffer(&opt->grep_filter,
 				     (char *)message, strlen(message));
 	strbuf_release(&buf);
-	unuse_commit_buffer(commit, message);
+	repo_unuse_commit_buffer(the_repository, commit, message);
 	return retval;
 }
 
@@ -4159,7 +4272,7 @@
  * Return true for entries that have not yet been shown.  (This is an
  * object_array_each_func_t.)
  */
-static int entry_unshown(struct object_array_entry *entry, void *cb_data_unused)
+static int entry_unshown(struct object_array_entry *entry, void *cb_data UNUSED)
 {
 	return !(entry->item->flags & SHOWN);
 }
diff --git a/revision.h b/revision.h
index 30febad..0e470d1 100644
--- a/revision.h
+++ b/revision.h
@@ -2,13 +2,16 @@
 #define REVISION_H
 
 #include "commit.h"
-#include "parse-options.h"
 #include "grep.h"
 #include "notes.h"
+#include "oidset.h"
 #include "pretty.h"
 #include "diff.h"
 #include "commit-slab-decl.h"
+#include "decorate.h"
+#include "ident.h"
 #include "list-objects-filter-options.h"
+#include "strvec.h"
 
 /**
  * The revision walking API offers functions to build a list of revisions
@@ -61,6 +64,8 @@
 struct saved_parents;
 struct bloom_key;
 struct bloom_filter_settings;
+struct option;
+struct parse_opt_ctx_t;
 define_shared_commit_slab(revision_sources, char *);
 
 struct rev_cmdline_info {
@@ -84,7 +89,7 @@
 struct ref_exclusions {
 	/*
 	 * Excluded refs is a list of wildmatch patterns. If any of the
-	 * patterns matches, the reference will be excluded.
+	 * patterns match, the reference will be excluded.
 	 */
 	struct string_list excluded_refs;
 
@@ -92,7 +97,7 @@
 	 * Hidden refs is a list of patterns that is to be hidden via
 	 * `ref_is_hidden()`.
 	 */
-	struct string_list hidden_refs;
+	struct strvec hidden_refs;
 
 	/*
 	 * Indicates whether hidden refs have been configured. This is to
@@ -107,7 +112,7 @@
  */
 #define REF_EXCLUSIONS_INIT { \
 	.excluded_refs = STRING_LIST_INIT_DUP, \
-	.hidden_refs = STRING_LIST_INIT_DUP, \
+	.hidden_refs = STRVEC_INIT, \
 }
 
 struct oidset;
@@ -137,6 +142,7 @@
 	/* Basic information */
 	const char *prefix;
 	const char *def;
+	char *ps_matched; /* optionally record matches of prune_data */
 	struct pathspec prune_data;
 
 	/*
@@ -208,18 +214,19 @@
 
 			/*
 			 * Blobs are shown without regard for their existence.
-			 * But not so for trees: unless exclude_promisor_objects
+			 * But not so for trees/commits: unless exclude_promisor_objects
 			 * is set and the tree in question is a promisor object;
 			 * OR ignore_missing_links is set, the revision walker
-			 * dies with a "bad tree object HASH" message when
-			 * encountering a missing tree. For callers that can
-			 * handle missing trees and want them to be filterable
+			 * dies with a "bad <type> object HASH" message when
+			 * encountering a missing object. For callers that can
+			 * handle missing trees/commits and want them to be filterable
 			 * and showable, set this to true. The revision walker
-			 * will filter and show such a missing tree as usual,
-			 * but will not attempt to recurse into this tree
-			 * object.
+			 * will filter and show such a missing object as usual,
+			 * but will not attempt to recurse into this tree/commit
+			 * object. The revision walker will also set the MISSING
+			 * flag for such objects.
 			 */
-			do_not_die_on_missing_tree:1,
+			do_not_die_on_missing_objects:1,
 
 			/* for internal use only */
 			exclude_promisor_objects:1;
@@ -249,6 +256,7 @@
 			shown_dashes:1,
 			show_merge:1,
 			show_notes_given:1,
+			show_notes_by_default:1,
 			show_signature:1,
 			pretty_given:1,
 			abbrev_commit:1,
@@ -367,6 +375,9 @@
 
 	/* Location where temporary objects for remerge-diff are written. */
 	struct tmp_objdir *remerge_objdir;
+
+	/* Missing commits to be tracked without failing traversal. */
+	struct oidset missing_commits;
 };
 
 /**
@@ -415,9 +426,6 @@
 void repo_init_revisions(struct repository *r,
 			 struct rev_info *revs,
 			 const char *prefix);
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
-#endif
 
 /**
  * Parse revision information, filling in the `rev_info` structure, and
@@ -428,7 +436,7 @@
  */
 struct setup_revision_opt {
 	const char *def;
-	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+	void (*tweak)(struct rev_info *);
 	unsigned int	assume_dashdash:1,
 			allow_exclude_promisor_objects:1,
 			free_removed_argv_elements:1;
diff --git a/run-command.c b/run-command.c
index 6bd16ac..0e74357 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1,15 +1,19 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "run-command.h"
+#include "environment.h"
 #include "exec-cmd.h"
+#include "gettext.h"
 #include "sigchain.h"
 #include "strvec.h"
+#include "symlinks.h"
 #include "thread-utils.h"
 #include "strbuf.h"
 #include "string-list.h"
+#include "trace.h"
+#include "trace2.h"
 #include "quote.h"
 #include "config.h"
 #include "packfile.h"
-#include "hook.h"
 #include "compat/nonblock.h"
 
 void child_process_init(struct child_process *child)
@@ -164,6 +168,7 @@
 	return st.st_mode & S_IXUSR;
 }
 
+#ifndef locate_in_PATH
 /*
  * Search $PATH for a command.  This emulates the path search that
  * execvp would perform, without actually executing the command so it
@@ -212,6 +217,7 @@
 	strbuf_release(&buf);
 	return NULL;
 }
+#endif
 
 int exists_in_PATH(const char *command)
 {
@@ -301,7 +307,6 @@
 	CHILD_ERR_DUP2,
 	CHILD_ERR_CLOSE,
 	CHILD_ERR_SIGPROCMASK,
-	CHILD_ERR_ENOENT,
 	CHILD_ERR_SILENT,
 	CHILD_ERR_ERRNO
 };
@@ -341,19 +346,19 @@
 	child_close(fd[1]);
 }
 
-static void child_error_fn(const char *err, va_list params)
+static void child_error_fn(const char *err UNUSED, va_list params UNUSED)
 {
 	const char msg[] = "error() should not be called in child\n";
 	xwrite(2, msg, sizeof(msg) - 1);
 }
 
-static void child_warn_fn(const char *err, va_list params)
+static void child_warn_fn(const char *err UNUSED, va_list params UNUSED)
 {
 	const char msg[] = "warn() should not be called in child\n";
 	xwrite(2, msg, sizeof(msg) - 1);
 }
 
-static void NORETURN child_die_fn(const char *err, va_list params)
+static void NORETURN child_die_fn(const char *err UNUSED, va_list params UNUSED)
 {
 	const char msg[] = "die() should not be called in child\n";
 	xwrite(2, msg, sizeof(msg) - 1);
@@ -384,9 +389,6 @@
 	case CHILD_ERR_SIGPROCMASK:
 		error_errno("sigprocmask failed restoring signals");
 		break;
-	case CHILD_ERR_ENOENT:
-		error_errno("cannot run %s", cmd->args.v[0]);
-		break;
 	case CHILD_ERR_SILENT:
 		break;
 	case CHILD_ERR_ERRNO:
@@ -840,13 +842,9 @@
 			execve(argv.v[0], (char *const *) argv.v,
 			       (char *const *) childenv);
 
-		if (errno == ENOENT) {
-			if (cmd->silent_exec_failure)
-				child_die(CHILD_ERR_SILENT);
-			child_die(CHILD_ERR_ENOENT);
-		} else {
-			child_die(CHILD_ERR_ERRNO);
-		}
+		if (cmd->silent_exec_failure && errno == ENOENT)
+			child_die(CHILD_ERR_SILENT);
+		child_die(CHILD_ERR_ERRNO);
 	}
 	atfork_parent(&as);
 	if (cmd->pid < 0)
diff --git a/run-command.h b/run-command.h
index 072db56..1f22cc3 100644
--- a/run-command.h
+++ b/run-command.h
@@ -503,7 +503,7 @@
  * exception of GIT_CONFIG_PARAMETERS and GIT_CONFIG_COUNT (which cause the
  * corresponding environment variables to be unset in the subprocess) and adds
  * an environment variable pointing to new_git_dir. See local_repo_env in
- * cache.h for more information.
+ * environment.h for more information.
  */
 void prepare_other_repo_env(struct strvec *env, const char *new_git_dir);
 
@@ -564,4 +564,6 @@
 				      void *cb_data,
 				      unsigned int timeout_sec);
 
+int sane_execvp(const char *file, char *const argv[]);
+
 #endif
diff --git a/sane-ctype.h b/sane-ctype.h
new file mode 100644
index 0000000..cbea1b2
--- /dev/null
+++ b/sane-ctype.h
@@ -0,0 +1,66 @@
+#ifndef SANE_CTYPE_H
+#define SANE_CTYPE_H
+
+/* Sane ctype - no locale, and works with signed chars */
+#undef isascii
+#undef isspace
+#undef isdigit
+#undef isalpha
+#undef isalnum
+#undef isprint
+#undef islower
+#undef isupper
+#undef tolower
+#undef toupper
+#undef iscntrl
+#undef ispunct
+#undef isxdigit
+
+extern const unsigned char sane_ctype[256];
+extern const signed char hexval_table[256];
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
+#define GIT_CNTRL 0x40
+#define GIT_PUNCT 0x80
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define isascii(x) (((x) & ~0x7f) == 0)
+#define isspace(x) sane_istest(x,GIT_SPACE)
+#define isdigit(x) sane_istest(x,GIT_DIGIT)
+#define isalpha(x) sane_istest(x,GIT_ALPHA)
+#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define isprint(x) ((x) >= 0x20 && (x) <= 0x7e)
+#define islower(x) sane_iscase(x, 1)
+#define isupper(x) sane_iscase(x, 0)
+#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
+#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
+#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
+#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
+		GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
+#define isxdigit(x) (hexval_table[(unsigned char)(x)] != -1)
+#define tolower(x) sane_case((unsigned char)(x), 0x20)
+#define toupper(x) sane_case((unsigned char)(x), 0)
+#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
+
+static inline int sane_case(int x, int high)
+{
+	if (sane_istest(x, GIT_ALPHA))
+		x = (x & ~0x20) | high;
+	return x;
+}
+
+static inline int sane_iscase(int x, int is_lower)
+{
+	if (!sane_istest(x, GIT_ALPHA))
+		return 0;
+
+	if (is_lower)
+		return (x & 0x20) != 0;
+	else
+		return (x & 0x20) == 0;
+}
+
+#endif
diff --git a/scalar.c b/scalar.c
index ca19b95..fb2940c 100644
--- a/scalar.c
+++ b/scalar.c
@@ -2,7 +2,8 @@
  * The Scalar command-line interface.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "gettext.h"
 #include "parse-options.h"
 #include "config.h"
@@ -14,6 +15,8 @@
 #include "dir.h"
 #include "packfile.h"
 #include "help.h"
+#include "setup.h"
+#include "trace2.h"
 
 static void setup_enlistment_directory(int argc, const char **argv,
 				       const char * const *usagestr,
@@ -406,6 +409,7 @@
 {
 	const char *branch = NULL;
 	int full_clone = 0, single_branch = 0, show_progress = isatty(2);
+	int src = 1;
 	struct option clone_options[] = {
 		OPT_STRING('b', "branch", &branch, N_("<branch>"),
 			   N_("branch to checkout after clone")),
@@ -414,10 +418,13 @@
 		OPT_BOOL(0, "single-branch", &single_branch,
 			 N_("only download metadata for the branch that will "
 			    "be checked out")),
+		OPT_BOOL(0, "src", &src,
+			 N_("create repository within 'src' directory")),
 		OPT_END(),
 	};
 	const char * const clone_usage[] = {
-		N_("scalar clone [<options>] [--] <repo> [<dir>]"),
+		N_("scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
+		   "\t[--[no-]src] <url> [<enlistment>]"),
 		NULL
 	};
 	const char *url;
@@ -453,7 +460,10 @@
 	if (is_directory(enlistment))
 		die(_("directory '%s' exists already"), enlistment);
 
-	dir = xstrfmt("%s/src", enlistment);
+	if (src)
+		dir = xstrfmt("%s/src", enlistment);
+	else
+		dir = xstrdup(enlistment);
 
 	strbuf_reset(&buf);
 	if (branch)
@@ -563,7 +573,7 @@
 	return res;
 }
 
-static int cmd_list(int argc, const char **argv)
+static int cmd_list(int argc, const char **argv UNUSED)
 {
 	if (argc != 1)
 		die(_("`scalar list` does not take arguments"));
@@ -591,7 +601,9 @@
 	return register_dir();
 }
 
-static int get_scalar_repos(const char *key, const char *value, void *data)
+static int get_scalar_repos(const char *key, const char *value,
+			    const struct config_context *ctx UNUSED,
+			    void *data)
 {
 	struct string_list *list = data;
 
@@ -652,6 +664,7 @@
 	git_config(get_scalar_repos, &scalar_repos);
 
 	for (i = 0; i < scalar_repos.nr; i++) {
+		int succeeded = 0;
 		const char *dir = scalar_repos.items[i].string;
 
 		strbuf_reset(&commondir);
@@ -662,30 +675,56 @@
 
 			if (errno != ENOENT) {
 				warning_errno(_("could not switch to '%s'"), dir);
-				res = -1;
-				continue;
+				goto loop_end;
 			}
 
 			strbuf_addstr(&buf, dir);
 			if (remove_deleted_enlistment(&buf))
-				res = error(_("could not remove stale "
-					      "scalar.repo '%s'"), dir);
-			else
-				warning(_("removing stale scalar.repo '%s'"),
+				error(_("could not remove stale "
+					"scalar.repo '%s'"), dir);
+			else {
+				warning(_("removed stale scalar.repo '%s'"),
 					dir);
+				succeeded = 1;
+			}
 			strbuf_release(&buf);
-		} else if (discover_git_directory(&commondir, &gitdir) < 0) {
-			warning_errno(_("git repository gone in '%s'"), dir);
+			goto loop_end;
+		}
+
+		switch (discover_git_directory_reason(&commondir, &gitdir)) {
+		case GIT_DIR_INVALID_OWNERSHIP:
+			warning(_("repository at '%s' has different owner"), dir);
+			goto loop_end;
+
+		case GIT_DIR_INVALID_GITFILE:
+		case GIT_DIR_INVALID_FORMAT:
+			warning(_("repository at '%s' has a format issue"), dir);
+			goto loop_end;
+
+		case GIT_DIR_DISCOVERED:
+			succeeded = 1;
+			break;
+
+		default:
+			warning(_("repository not found in '%s'"), dir);
+			break;
+		}
+
+		git_config_clear();
+
+		the_repository = &r;
+		r.commondir = commondir.buf;
+		r.gitdir = gitdir.buf;
+
+		if (set_recommended_config(1) >= 0)
+			succeeded = 1;
+
+loop_end:
+		if (!succeeded) {
 			res = -1;
-		} else {
-			git_config_clear();
-
-			the_repository = &r;
-			r.commondir = commondir.buf;
-			r.gitdir = gitdir.buf;
-
-			if (set_recommended_config(1) < 0)
-				res = -1;
+			warning(_("to unregister this repository from Scalar, run\n"
+				  "\tgit config --global --unset --fixed-value scalar.repo \"%s\""),
+				dir);
 		}
 	}
 
diff --git a/send-pack.c b/send-pack.c
index f2e1983..37f59d4 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -1,21 +1,24 @@
-#include "builtin.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "commit.h"
-#include "refs.h"
-#include "object-store.h"
+#include "date.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-store-ll.h"
 #include "pkt-line.h"
 #include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
-#include "quote.h"
 #include "transport.h"
 #include "version.h"
 #include "oid-array.h"
 #include "gpg-interface.h"
-#include "cache.h"
 #include "shallow.h"
+#include "parse-options.h"
+#include "trace2.h"
+#include "write-or-die.h"
 
 int option_parse_push_signed(const struct option *opt,
 			     const char *arg, int unset)
@@ -42,9 +45,9 @@
 static void feed_object(const struct object_id *oid, FILE *fh, int negative)
 {
 	if (negative &&
-	    !has_object_file_with_flags(oid,
-					OBJECT_INFO_SKIP_FETCH_OBJECT |
-					OBJECT_INFO_QUICK))
+	    !repo_has_object_file_with_flags(the_repository, oid,
+					     OBJECT_INFO_SKIP_FETCH_OBJECT |
+					     OBJECT_INFO_QUICK))
 		return;
 
 	if (negative)
@@ -534,7 +537,7 @@
 		die(_("the receiving end does not support this repository's hash algorithm"));
 
 	if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
-		int len;
+		size_t len;
 		push_cert_nonce = server_feature_value("push-cert", &len);
 		if (push_cert_nonce) {
 			reject_invalid_nonce(push_cert_nonce, len);
diff --git a/sequencer.c b/sequencer.c
index 1c96a75..2c19846 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,23 +1,33 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
 #include "config.h"
+#include "copy.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "object.h"
+#include "pager.h"
 #include "commit.h"
 #include "sequencer.h"
-#include "tag.h"
 #include "run-command.h"
 #include "hook.h"
-#include "exec-cmd.h"
 #include "utf8.h"
 #include "cache-tree.h"
 #include "diff.h"
+#include "path.h"
 #include "revision.h"
 #include "rerere.h"
+#include "merge.h"
 #include "merge-ort.h"
 #include "merge-ort-wrappers.h"
 #include "refs.h"
+#include "sparse-index.h"
 #include "strvec.h"
 #include "quote.h"
 #include "trailer.h"
@@ -27,7 +37,6 @@
 #include "notes-utils.h"
 #include "sigchain.h"
 #include "unpack-trees.h"
-#include "worktree.h"
 #include "oidmap.h"
 #include "oidset.h"
 #include "commit-slab.h"
@@ -39,6 +48,15 @@
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
+/*
+ * To accommodate common filesystem limitations, where the loose refs' file
+ * names must not exceed `NAME_MAX`, the labels generated by `git rebase
+ * --rebase-merges` need to be truncated if the corresponding commit subjects
+ * are too long.
+ * Add some margin to stay clear from reaching `NAME_MAX`.
+ */
+#define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16)
+
 static const char sign_off_header[] = "Signed-off-by: ";
 static const char cherry_picked_prefix[] = "(cherry picked from commit ";
 
@@ -127,6 +145,11 @@
  */
 static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
 /*
+ * When we stop for the user to resolve conflicts this file contains
+ * the patch of the commit that is being picked.
+ */
+static GIT_PATH_FUNC(rebase_path_patch, "rebase-merge/patch")
+/*
  * For the post-rewrite hook, we make a list of rewritten commits and
  * their new sha1s.  The rewritten-pending list keeps the sha1s of
  * commits that have been processed, but not committed yet,
@@ -208,37 +231,33 @@
 	return rec;
 }
 
-static int git_sequencer_config(const char *k, const char *v, void *cb)
+static int git_sequencer_config(const char *k, const char *v,
+				const struct config_context *ctx, void *cb)
 {
 	struct replay_opts *opts = cb;
-	int status;
 
 	if (!strcmp(k, "commit.cleanup")) {
-		const char *s;
+		if (!v)
+			return config_error_nonbool(k);
 
-		status = git_config_string(&s, k, v);
-		if (status)
-			return status;
-
-		if (!strcmp(s, "verbatim")) {
+		if (!strcmp(v, "verbatim")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
 			opts->explicit_cleanup = 1;
-		} else if (!strcmp(s, "whitespace")) {
+		} else if (!strcmp(v, "whitespace")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
 			opts->explicit_cleanup = 1;
-		} else if (!strcmp(s, "strip")) {
+		} else if (!strcmp(v, "strip")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
 			opts->explicit_cleanup = 1;
-		} else if (!strcmp(s, "scissors")) {
+		} else if (!strcmp(v, "scissors")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
 			opts->explicit_cleanup = 1;
 		} else {
 			warning(_("invalid commit message cleanup mode '%s'"),
-				  s);
+				  v);
 		}
 
-		free((char *)s);
-		return status;
+		return 0;
 	}
 
 	if (!strcmp(k, "commit.gpgsign")) {
@@ -263,11 +282,7 @@
 	if (opts->action == REPLAY_REVERT && !strcmp(k, "revert.reference"))
 		opts->commit_use_reference = git_config_bool(k, v);
 
-	status = git_gpg_config(k, v, NULL);
-	if (status)
-		return status;
-
-	return git_diff_basic_config(k, v, NULL);
+	return git_diff_basic_config(k, v, ctx, NULL);
 }
 
 void sequencer_init_config(struct replay_opts *opts)
@@ -317,12 +332,12 @@
 		sb->buf[sb->len - ignore_footer] = '\0';
 	}
 
-	trailer_info_get(&info, sb->buf, &opts);
+	trailer_info_get(&opts, sb->buf, &info);
 
 	if (ignore_footer)
 		sb->buf[sb->len - ignore_footer] = saved_char;
 
-	if (info.trailer_start == info.trailer_end)
+	if (info.trailer_block_start == info.trailer_block_end)
 		return 0;
 
 	for (i = 0; i < info.trailer_nr; i++)
@@ -357,9 +372,7 @@
 	free(opts->reflog_action);
 	free(opts->default_strategy);
 	free(opts->strategy);
-	for (size_t i = 0; i < opts->xopts_nr; i++)
-		free(opts->xopts[i]);
-	free(opts->xopts);
+	strvec_clear (&opts->xopts);
 	strbuf_release(&opts->current_fixups);
 	if (opts->revs)
 		release_revisions(opts->revs);
@@ -417,9 +430,9 @@
 	const char *message;
 };
 
-static const char *short_commit_name(struct commit *commit)
+static const char *short_commit_name(struct repository *r, struct commit *commit)
 {
-	return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV);
+	return repo_find_unique_abbrev(r, &commit->object.oid, DEFAULT_ABBREV);
 }
 
 static int get_message(struct commit *commit, struct commit_message *out)
@@ -427,8 +440,9 @@
 	const char *abbrev, *subject;
 	int subject_len;
 
-	out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding());
-	abbrev = short_commit_name(commit);
+	out->message = repo_logmsg_reencode(the_repository, commit, NULL,
+					    get_commit_output_encoding());
+	abbrev = short_commit_name(the_repository, commit);
 
 	subject_len = find_commit_subject(out->message, &subject);
 
@@ -444,44 +458,59 @@
 	free(msg->parent_label);
 	free(msg->label);
 	free(msg->subject);
-	unuse_commit_buffer(commit, msg->message);
+	repo_unuse_commit_buffer(the_repository, commit, msg->message);
 }
 
+const char *rebase_resolvemsg =
+N_("Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run "
+"\"git rebase --abort\".");
+
 static void print_advice(struct repository *r, int show_hint,
 			 struct replay_opts *opts)
 {
-	char *msg = getenv("GIT_CHERRY_PICK_HELP");
+	const char *msg;
+
+	if (is_rebase_i(opts))
+		msg = rebase_resolvemsg;
+	else
+		msg = getenv("GIT_CHERRY_PICK_HELP");
 
 	if (msg) {
-		advise("%s\n", msg);
+		advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", msg);
 		/*
 		 * A conflict has occurred but the porcelain
 		 * (typically rebase --interactive) wants to take care
 		 * of the commit itself so remove CHERRY_PICK_HEAD
 		 */
 		refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
-				NULL, 0);
+				NULL, REF_NO_DEREF);
 		return;
 	}
 
 	if (show_hint) {
 		if (opts->no_commit)
-			advise(_("after resolving the conflicts, mark the corrected paths\n"
-				 "with 'git add <paths>' or 'git rm <paths>'"));
+			advise_if_enabled(ADVICE_MERGE_CONFLICT,
+					  _("after resolving the conflicts, mark the corrected paths\n"
+					    "with 'git add <paths>' or 'git rm <paths>'"));
 		else if (opts->action == REPLAY_PICK)
-			advise(_("After resolving the conflicts, mark them with\n"
-				 "\"git add/rm <pathspec>\", then run\n"
-				 "\"git cherry-pick --continue\".\n"
-				 "You can instead skip this commit with \"git cherry-pick --skip\".\n"
-				 "To abort and get back to the state before \"git cherry-pick\",\n"
-				 "run \"git cherry-pick --abort\"."));
+			advise_if_enabled(ADVICE_MERGE_CONFLICT,
+					  _("After resolving the conflicts, mark them with\n"
+					    "\"git add/rm <pathspec>\", then run\n"
+					    "\"git cherry-pick --continue\".\n"
+					    "You can instead skip this commit with \"git cherry-pick --skip\".\n"
+					    "To abort and get back to the state before \"git cherry-pick\",\n"
+					    "run \"git cherry-pick --abort\"."));
 		else if (opts->action == REPLAY_REVERT)
-			advise(_("After resolving the conflicts, mark them with\n"
-				 "\"git add/rm <pathspec>\", then run\n"
-				 "\"git revert --continue\".\n"
-				 "You can instead skip this commit with \"git revert --skip\".\n"
-				 "To abort and get back to the state before \"git revert\",\n"
-				 "run \"git revert --abort\"."));
+			advise_if_enabled(ADVICE_MERGE_CONFLICT,
+					  _("After resolving the conflicts, mark them with\n"
+					    "\"git add/rm <pathspec>\", then run\n"
+					    "\"git revert --continue\".\n"
+					    "You can instead skip this commit with \"git revert --skip\".\n"
+					    "To abort and get back to the state before \"git revert\",\n"
+					    "run \"git revert --abort\"."));
 		else
 			BUG("unexpected pick action in print_advice()");
 	}
@@ -561,7 +590,7 @@
 	if (!file_exists(git_path_seq_dir()))
 		return;
 
-	if (!get_oid("HEAD", &head))
+	if (!repo_get_oid(the_repository, "HEAD", &head))
 		write_file(git_path_abort_safety_file(), "%s", oid_to_hex(&head));
 	else
 		write_file(git_path_abort_safety_file(), "%s", "");
@@ -649,15 +678,16 @@
 	if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
 		strbuf_addch(msgbuf, '\n');
 		wt_status_append_cut_line(msgbuf);
-		strbuf_addch(msgbuf, comment_line_char);
+		strbuf_addstr(msgbuf, comment_line_str);
 	}
 
 	strbuf_addch(msgbuf, '\n');
-	strbuf_commented_addf(msgbuf, "Conflicts:\n");
+	strbuf_commented_addf(msgbuf, comment_line_str, "Conflicts:\n");
 	for (i = 0; i < istate->cache_nr;) {
 		const struct cache_entry *ce = istate->cache[i++];
 		if (ce_stage(ce)) {
-			strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
+			strbuf_commented_addf(msgbuf, comment_line_str,
+					      "\t%s\n", ce->name);
 			while (i < istate->cache_nr &&
 			       !strcmp(ce->name, istate->cache[i]->name))
 				i++;
@@ -692,11 +722,13 @@
 	o.show_rename_progress = 1;
 
 	head_tree = parse_tree_indirect(head);
-	next_tree = next ? get_commit_tree(next) : empty_tree(r);
-	base_tree = base ? get_commit_tree(base) : empty_tree(r);
+	if (!head_tree)
+		return error(_("unable to read tree (%s)"), oid_to_hex(head));
+	next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
+	base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r);
 
-	for (i = 0; i < opts->xopts_nr; i++)
-		parse_merge_opt(&o, opts->xopts[i]);
+	for (i = 0; i < opts->xopts.nr; i++)
+		parse_merge_opt(&o, opts->xopts.v[i]);
 
 	if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
 		memset(&result, 0, sizeof(result));
@@ -755,29 +787,42 @@
 static int is_index_unchanged(struct repository *r)
 {
 	struct object_id head_oid, *cache_tree_oid;
+	const struct object_id *head_tree_oid;
 	struct commit *head_commit;
 	struct index_state *istate = r->index;
+	const char *head_name;
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-		return error(_("could not resolve HEAD commit"));
+	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+		/* Check to see if this is an unborn branch */
+		head_name = resolve_ref_unsafe("HEAD",
+			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			&head_oid, NULL);
+		if (!head_name ||
+			!starts_with(head_name, "refs/heads/") ||
+			!is_null_oid(&head_oid))
+			return error(_("could not resolve HEAD commit"));
+		head_tree_oid = the_hash_algo->empty_tree;
+	} else {
+		head_commit = lookup_commit(r, &head_oid);
 
-	head_commit = lookup_commit(r, &head_oid);
+		/*
+		 * If head_commit is NULL, check_commit, called from
+		 * lookup_commit, would have indicated that head_commit is not
+		 * a commit object already.  repo_parse_commit() will return failure
+		 * without further complaints in such a case.  Otherwise, if
+		 * the commit is invalid, repo_parse_commit() will complain.  So
+		 * there is nothing for us to say here.  Just return failure.
+		 */
+		if (repo_parse_commit(r, head_commit))
+			return -1;
 
-	/*
-	 * If head_commit is NULL, check_commit, called from
-	 * lookup_commit, would have indicated that head_commit is not
-	 * a commit object already.  parse_commit() will return failure
-	 * without further complaints in such a case.  Otherwise, if
-	 * the commit is invalid, parse_commit() will complain.  So
-	 * there is nothing for us to say here.  Just return failure.
-	 */
-	if (parse_commit(head_commit))
-		return -1;
+		head_tree_oid = get_commit_tree_oid(head_commit);
+	}
 
 	if (!(cache_tree_oid = get_cache_tree_oid(istate)))
 		return -1;
 
-	return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+	return oideq(cache_tree_oid, head_tree_oid);
 }
 
 static int write_author_script(const char *message)
@@ -1136,7 +1181,8 @@
 	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
 		strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
 	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
-		strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+		strbuf_stripspace(msgbuf,
+		  cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
 }
 
 /*
@@ -1167,7 +1213,8 @@
 	if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
 		return 0;
 
-	strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+	strbuf_stripspace(&tmpl,
+	  cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
 	if (!skip_prefix(sb->buf, tmpl.buf, &start))
 		start = sb->buf;
 	strbuf_release(&tmpl);
@@ -1336,13 +1383,15 @@
 	commit = lookup_commit(r, oid);
 	if (!commit)
 		die(_("couldn't look up newly created commit"));
-	if (parse_commit(commit))
+	if (repo_parse_commit(r, commit))
 		die(_("could not parse newly created commit"));
 
 	strbuf_addstr(&format, "format:%h] %s");
 
-	format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-	format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
+	repo_format_commit_message(r, commit, "%an <%ae>", &author_ident,
+				   &pctx);
+	repo_format_commit_message(r, commit, "%cn <%ce>", &committer_ident,
+				   &pctx);
 	if (strbuf_cmp(&author_ident, &committer_ident)) {
 		strbuf_addstr(&format, "\n Author: ");
 		strbuf_addbuf_percentquote(&format, &author_ident);
@@ -1350,7 +1399,7 @@
 	if (flags & SUMMARY_SHOW_AUTHOR_DATE) {
 		struct strbuf date = STRBUF_INIT;
 
-		format_commit_message(commit, "%ad", &date, &pctx);
+		repo_format_commit_message(r, commit, "%ad", &date, &pctx);
 		strbuf_addstr(&format, "\n Date: ");
 		strbuf_addbuf_percentquote(&format, &date);
 		strbuf_release(&date);
@@ -1380,7 +1429,7 @@
 	rev.diffopt.detect_rename = DIFF_DETECT_RENAME;
 	diff_setup_done(&rev.diffopt);
 
-	refs = get_main_ref_store(the_repository);
+	refs = get_main_ref_store(r);
 	head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL);
 	if (!head)
 		die(_("unable to resolve HEAD after creating commit"));
@@ -1406,7 +1455,7 @@
 	struct commit *current_head;
 	struct object_id oid;
 
-	if (get_oid("HEAD", &oid)) {
+	if (repo_get_oid(r, "HEAD", &oid)) {
 		current_head = NULL;
 	} else {
 		current_head = lookup_commit_reference(r, &oid);
@@ -1416,7 +1465,7 @@
 			warning(_("HEAD %s is not a commit!"),
 				oid_to_hex(&oid));
 		}
-		if (parse_commit(current_head))
+		if (repo_parse_commit(r, current_head))
 			return error(_("could not parse HEAD commit"));
 	}
 	*head = current_head;
@@ -1459,8 +1508,8 @@
 	if (flags & AMEND_MSG) {
 		const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
 		const char *out_enc = get_commit_output_encoding();
-		const char *message = logmsg_reencode(current_head, NULL,
-						      out_enc);
+		const char *message = repo_logmsg_reencode(r, current_head,
+							   NULL, out_enc);
 
 		if (!msg) {
 			const char *orig_message = NULL;
@@ -1471,7 +1520,8 @@
 			hook_commit = "HEAD";
 		}
 		author = amend_author = get_author(message);
-		unuse_commit_buffer(current_head, message);
+		repo_unuse_commit_buffer(r, current_head,
+					 message);
 		if (!author) {
 			res = error(_("unable to parse commit author"));
 			goto out;
@@ -1536,7 +1586,8 @@
 		cleanup = opts->default_msg_cleanup;
 
 	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
-		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+		strbuf_stripspace(msg,
+		  cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
 	if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
 		res = 1; /* run 'git commit' to display error message */
 		goto out;
@@ -1646,7 +1697,7 @@
 		strbuf_release(&sb);
 		if (!res) {
 			refs_delete_ref(get_main_ref_store(r), "",
-					"CHERRY_PICK_HEAD", NULL, 0);
+					"CHERRY_PICK_HEAD", NULL, REF_NO_DEREF);
 			unlink(git_path_merge_msg(r));
 			if (!is_rebase_i(opts))
 				print_commit_summary(r, NULL, &oid,
@@ -1668,12 +1719,12 @@
 {
 	const struct object_id *ptree_oid;
 
-	if (parse_commit(commit))
+	if (repo_parse_commit(the_repository, commit))
 		return error(_("could not parse commit %s"),
 			     oid_to_hex(&commit->object.oid));
 	if (commit->parents) {
 		struct commit *parent = commit->parents->item;
-		if (parse_commit(parent))
+		if (repo_parse_commit(the_repository, parent))
 			return error(_("could not parse parent commit %s"),
 				oid_to_hex(&parent->object.oid));
 		ptree_oid = get_commit_tree_oid(parent);
@@ -1698,34 +1749,25 @@
 	int index_unchanged, originally_empty;
 
 	/*
-	 * Four cases:
+	 * For a commit that is initially empty, allow_empty determines if it
+	 * should be kept or not
 	 *
-	 * (1) we do not allow empty at all and error out.
-	 *
-	 * (2) we allow ones that were initially empty, and
-	 *     just drop the ones that become empty
-	 *
-	 * (3) we allow ones that were initially empty, but
-	 *     halt for the ones that become empty;
-	 *
-	 * (4) we allow both.
+	 * For a commit that becomes empty, keep_redundant_commits and
+	 * drop_redundant_commits determine whether the commit should be kept or
+	 * dropped. If neither is specified, halt.
 	 */
-	if (!opts->allow_empty)
-		return 0; /* let "git commit" barf as necessary */
-
 	index_unchanged = is_index_unchanged(r);
 	if (index_unchanged < 0)
 		return index_unchanged;
 	if (!index_unchanged)
 		return 0; /* we do not have to say --allow-empty */
 
-	if (opts->keep_redundant_commits)
-		return 1;
-
 	originally_empty = is_original_commit_empty(commit);
 	if (originally_empty < 0)
 		return originally_empty;
 	if (originally_empty)
+		return opts->allow_empty;
+	else if (opts->keep_redundant_commits)
 		return 1;
 	else if (opts->drop_redundant_commits)
 		return 2;
@@ -1758,6 +1800,8 @@
 {
 	if (command < TODO_COMMENT)
 		return todo_command_info[command].str;
+	if (command == TODO_COMMENT)
+		return comment_line_str;
 	die(_("unknown command: %d"), command);
 }
 
@@ -1765,7 +1809,7 @@
 {
 	if (command < TODO_COMMENT)
 		return todo_command_info[command].c;
-	return comment_line_char;
+	return 0;
 }
 
 static int is_noop(const enum todo_command command)
@@ -1819,7 +1863,7 @@
 static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
 {
 	const char *s = str;
-	while (len > 0 && s[0] == comment_line_char) {
+	while (starts_with_mem(s, len, comment_line_str)) {
 		size_t count;
 		const char *n = memchr(s, '\n', len);
 		if (!n)
@@ -1830,7 +1874,7 @@
 		s += count;
 		len -= count;
 	}
-	strbuf_add_commented_lines(buf, s, len);
+	strbuf_add_commented_lines(buf, s, len, comment_line_str);
 }
 
 /* Does the current fixup chain contain a squash command? */
@@ -1925,11 +1969,11 @@
 	     (starts_with(body, "squash!") || starts_with(body, "fixup!"))))
 		commented_len = commit_subject_length(body);
 
-	strbuf_addf(buf, "\n%c ", comment_line_char);
+	strbuf_addf(buf, "\n%s ", comment_line_str);
 	strbuf_addf(buf, _(nth_commit_msg_fmt),
 		    ++opts->current_fixup_count + 1);
 	strbuf_addstr(buf, "\n\n");
-	strbuf_add_commented_lines(buf, body, commented_len);
+	strbuf_add_commented_lines(buf, body, commented_len, comment_line_str);
 	/* buf->buf may be reallocated so store an offset into the buffer */
 	fixup_off = buf->len;
 	strbuf_addstr(buf, body + commented_len);
@@ -1982,10 +2026,10 @@
 			return error(_("could not read '%s'"),
 				rebase_path_squash_msg());
 
-		eol = buf.buf[0] != comment_line_char ?
+		eol = !starts_with(buf.buf, comment_line_str) ?
 			buf.buf : strchrnul(buf.buf, '\n');
 
-		strbuf_addf(&header, "%c ", comment_line_char);
+		strbuf_addf(&header, "%s ", comment_line_str);
 		strbuf_addf(&header, _(combined_commit_msg_fmt),
 			    opts->current_fixup_count + 2);
 		strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
@@ -1997,35 +2041,37 @@
 		struct commit *head_commit;
 		const char *head_message, *body;
 
-		if (get_oid("HEAD", &head))
+		if (repo_get_oid(r, "HEAD", &head))
 			return error(_("need a HEAD to fixup"));
 		if (!(head_commit = lookup_commit_reference(r, &head)))
 			return error(_("could not read HEAD"));
-		if (!(head_message = logmsg_reencode(head_commit, NULL, encoding)))
+		if (!(head_message = repo_logmsg_reencode(r, head_commit, NULL,
+							  encoding)))
 			return error(_("could not read HEAD's commit message"));
 
 		find_commit_subject(head_message, &body);
 		if (command == TODO_FIXUP && !flag && write_message(body, strlen(body),
 							rebase_path_fixup_msg(), 0) < 0) {
-			unuse_commit_buffer(head_commit, head_message);
+			repo_unuse_commit_buffer(r, head_commit, head_message);
 			return error(_("cannot write '%s'"), rebase_path_fixup_msg());
 		}
-		strbuf_addf(&buf, "%c ", comment_line_char);
+		strbuf_addf(&buf, "%s ", comment_line_str);
 		strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
-		strbuf_addf(&buf, "\n%c ", comment_line_char);
+		strbuf_addf(&buf, "\n%s ", comment_line_str);
 		strbuf_addstr(&buf, is_fixup_flag(command, flag) ?
 			      _(skip_first_commit_msg_str) :
 			      _(first_commit_msg_str));
 		strbuf_addstr(&buf, "\n\n");
 		if (is_fixup_flag(command, flag))
-			strbuf_add_commented_lines(&buf, body, strlen(body));
+			strbuf_add_commented_lines(&buf, body, strlen(body),
+						   comment_line_str);
 		else
 			strbuf_addstr(&buf, body);
 
-		unuse_commit_buffer(head_commit, head_message);
+		repo_unuse_commit_buffer(r, head_commit, head_message);
 	}
 
-	if (!(message = logmsg_reencode(commit, NULL, encoding)))
+	if (!(message = repo_logmsg_reencode(r, commit, NULL, encoding)))
 		return error(_("could not read commit message of %s"),
 			     oid_to_hex(&commit->object.oid));
 	find_commit_subject(message, &body);
@@ -2033,14 +2079,15 @@
 	if (command == TODO_SQUASH || is_fixup_flag(command, flag)) {
 		res = append_squash_message(&buf, body, command, opts, flag);
 	} else if (command == TODO_FIXUP) {
-		strbuf_addf(&buf, "\n%c ", comment_line_char);
+		strbuf_addf(&buf, "\n%s ", comment_line_str);
 		strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
 			    ++opts->current_fixup_count + 1);
 		strbuf_addstr(&buf, "\n\n");
-		strbuf_add_commented_lines(&buf, body, strlen(body));
+		strbuf_add_commented_lines(&buf, body, strlen(body),
+					   comment_line_str);
 	} else
 		return error(_("unknown command: %d"), command);
-	unuse_commit_buffer(commit, message);
+	repo_unuse_commit_buffer(r, commit, message);
 
 	if (!res)
 		res = write_message(buf.buf, buf.len, rebase_path_squash_msg(),
@@ -2067,7 +2114,7 @@
 	FILE *out;
 
 	if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), (GIT_MAX_HEXSZ + 1) * 2) > 0 &&
-	    !get_oid("HEAD", &newoid) &&
+	    !repo_get_oid(the_repository, "HEAD", &newoid) &&
 	    (out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) {
 		char *bol = buf.buf, *eol;
 
@@ -2119,7 +2166,8 @@
 			.abbrev = DEFAULT_ABBREV,
 			.date_mode.type = DATE_SHORT,
 		};
-		format_commit_message(commit, "%h (%s, %ad)", msgbuf, &ctx);
+		repo_format_commit_message(the_repository, commit,
+					   "%h (%s, %ad)", msgbuf, &ctx);
 	} else {
 		strbuf_addstr(msgbuf, oid_to_hex(&commit->object.oid));
 	}
@@ -2152,7 +2200,7 @@
 		if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL))
 			return error(_("your index file is unmerged."));
 	} else {
-		unborn = get_oid("HEAD", &head);
+		unborn = repo_get_oid(r, "HEAD", &head);
 		/* Do we want to generate a root commit? */
 		if (is_pick_or_similar(command) && opts->have_squash_onto &&
 		    oideq(&head, &opts->squash_onto)) {
@@ -2214,7 +2262,7 @@
 		msg_file = NULL;
 		goto fast_forward_edit;
 	}
-	if (parent && parse_commit(parent) < 0)
+	if (parent && repo_parse_commit(r, parent) < 0)
 		/* TRANSLATORS: The first %s will be a "todo" command like
 		   "revert" or "pick", the second %s a SHA1. */
 		return error(_("%s: cannot parse parent commit %s"),
@@ -2229,6 +2277,8 @@
 	 */
 
 	if (command == TODO_REVERT) {
+		const char *orig_subject;
+
 		base = commit;
 		base_label = msg.label;
 		next = parent;
@@ -2236,6 +2286,15 @@
 		if (opts->commit_use_reference) {
 			strbuf_addstr(&msgbuf,
 				"# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
+		} else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) &&
+			   /*
+			    * We don't touch pre-existing repeated reverts, because
+			    * theoretically these can be nested arbitrarily deeply,
+			    * thus requiring excessive complexity to deal with.
+			    */
+			   !starts_with(orig_subject, "Revert \"")) {
+			strbuf_addstr(&msgbuf, "Reapply \"");
+			strbuf_addstr(&msgbuf, orig_subject);
 		} else {
 			strbuf_addstr(&msgbuf, "Revert \"");
 			strbuf_addstr(&msgbuf, msg.subject);
@@ -2291,7 +2350,7 @@
 			const char *dest = git_path_squash_msg(r);
 			unlink(dest);
 			if (copy_file(dest, rebase_path_squash_msg(), 0666)) {
-				res = error(_("could not rename '%s' to '%s'"),
+				res = error(_("could not copy '%s' to '%s'"),
 					    rebase_path_squash_msg(), dest);
 				goto leave;
 			}
@@ -2327,7 +2386,7 @@
 		commit_list_insert(base, &common);
 		commit_list_insert(next, &remotes);
 		res |= try_merge_command(r, opts->strategy,
-					 opts->xopts_nr, (const char **)opts->xopts,
+					 opts->xopts.nr, opts->xopts.v,
 					common, oid_to_hex(&head), remotes);
 		free_commit_list(common);
 		free_commit_list(remotes);
@@ -2354,7 +2413,7 @@
 		error(command == TODO_REVERT
 		      ? _("could not revert %s... %s")
 		      : _("could not apply %s... %s"),
-		      short_commit_name(commit), msg.subject);
+		      short_commit_name(r, commit), msg.subject);
 		print_advice(r, res == 1, opts);
 		repo_rerere(r, opts->allow_rerere_auto);
 		goto leave;
@@ -2370,9 +2429,10 @@
 	} else if (allow == 2) {
 		drop_commit = 1;
 		refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
-				NULL, 0);
+				NULL, REF_NO_DEREF);
 		unlink(git_path_merge_msg(r));
-		unlink(git_path_auto_merge(r));
+		refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+				NULL, REF_NO_DEREF);
 		fprintf(stderr,
 			_("dropping %s %s -- patch contents already upstream\n"),
 			oid_to_hex(&commit->object.oid), msg.subject);
@@ -2465,7 +2525,6 @@
 static struct todo_item *append_new_todo(struct todo_list *todo_list)
 {
 	ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
-	todo_list->total_nr++;
 	return todo_list->items + todo_list->nr++;
 }
 
@@ -2526,7 +2585,7 @@
 	/* left-trim */
 	bol += strspn(bol, " \t");
 
-	if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
+	if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) {
 		item->command = TODO_COMMENT;
 		item->commit = NULL;
 		item->arg_offset = bol - buf;
@@ -2606,7 +2665,7 @@
 	end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
 	saved = *end_of_object_name;
 	*end_of_object_name = '\0';
-	status = get_oid(bol, &commit_oid);
+	status = repo_get_oid(r, bol, &commit_oid);
 	if (status < 0)
 		error(_("could not parse '%s'"), bol); /* return later */
 	*end_of_object_name = saved;
@@ -2622,7 +2681,7 @@
 	return item->commit ? 0 : -1;
 }
 
-int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
 {
 	const char *todo_file, *bol;
 	struct strbuf buf = STRBUF_INIT;
@@ -2656,7 +2715,7 @@
 	char *p = buf, *next_p;
 	int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
-	todo_list->current = todo_list->nr = 0;
+	todo_list->current = todo_list->nr = todo_list->total_nr = 0;
 
 	for (i = 1; *p; i++, p = next_p) {
 		char *eol = strchrnul(p, '\n');
@@ -2677,10 +2736,13 @@
 			item->commit = NULL;
 		}
 
+		if (item->command != TODO_COMMENT)
+			todo_list->total_nr++;
+
 		if (fixup_okay)
 			; /* do nothing */
 		else if (is_fixup(item->command))
-			return error(_("cannot '%s' without a previous commit"),
+			res = error(_("cannot '%s' without a previous commit"),
 				command_to_string(item->command));
 		else if (!is_noop(item->command))
 			fixup_okay = 1;
@@ -2764,7 +2826,7 @@
 
 	if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
 		if (!refs_delete_ref(get_main_ref_store(r), "",
-				     "CHERRY_PICK_HEAD", NULL, 0) &&
+				     "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF) &&
 		    verbose)
 			warning(_("cancelling a cherry picking in progress"));
 		opts.action = REPLAY_PICK;
@@ -2773,14 +2835,15 @@
 
 	if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
 		if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
-				     NULL, 0) &&
+				     NULL, REF_NO_DEREF) &&
 		    verbose)
 			warning(_("cancelling a revert in progress"));
 		opts.action = REPLAY_REVERT;
 		need_cleanup = 1;
 	}
 
-	unlink(git_path_auto_merge(r));
+	refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+			NULL, REF_NO_DEREF);
 
 	if (!need_cleanup)
 		return;
@@ -2867,7 +2930,9 @@
 	return 0;
 }
 
-static int populate_opts_cb(const char *key, const char *value, void *data)
+static int populate_opts_cb(const char *key, const char *value,
+			    const struct config_context *ctx,
+			    void *data)
 {
 	struct replay_opts *opts = data;
 	int error_flag = 1;
@@ -2875,36 +2940,38 @@
 	if (!value)
 		error_flag = 0;
 	else if (!strcmp(key, "options.no-commit"))
-		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+		opts->no_commit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.edit"))
-		opts->edit = git_config_bool_or_int(key, value, &error_flag);
+		opts->edit = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-empty"))
 		opts->allow_empty =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+	else if (!strcmp(key, "options.drop-redundant-commits"))
+		opts->drop_redundant_commits =
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
-			git_config_bool_or_int(key, value, &error_flag);
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.signoff"))
-		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+		opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.record-origin"))
-		opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+		opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.allow-ff"))
-		opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+		opts->allow_ff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.mainline"))
-		opts->mainline = git_config_int(key, value);
+		opts->mainline = git_config_int(key, value, ctx->kvi);
 	else if (!strcmp(key, "options.strategy"))
 		git_config_string_dup(&opts->strategy, key, value);
 	else if (!strcmp(key, "options.gpg-sign"))
 		git_config_string_dup(&opts->gpg_sign, key, value);
 	else if (!strcmp(key, "options.strategy-option")) {
-		ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
-		opts->xopts[opts->xopts_nr++] = xstrdup(value);
+		strvec_push(&opts->xopts, value);
 	} else if (!strcmp(key, "options.allow-rerere-auto"))
 		opts->allow_rerere_auto =
-			git_config_bool_or_int(key, value, &error_flag) ?
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag) ?
 				RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
 	else if (!strcmp(key, "options.default-msg-cleanup")) {
 		opts->explicit_cleanup = 1;
@@ -2918,22 +2985,27 @@
 	return 0;
 }
 
-void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
+static void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
 {
 	int i;
+	int count;
+	const char **argv;
 	char *strategy_opts_string = raw_opts;
 
 	if (*strategy_opts_string == ' ')
 		strategy_opts_string++;
 
-	opts->xopts_nr = split_cmdline(strategy_opts_string,
-				       (const char ***)&opts->xopts);
-	for (i = 0; i < opts->xopts_nr; i++) {
-		const char *arg = opts->xopts[i];
+	count = split_cmdline(strategy_opts_string, &argv);
+	if (count < 0)
+		BUG("could not split '%s': %s", strategy_opts_string,
+			    split_cmdline_strerror(count));
+	for (i = 0; i < count; i++) {
+		const char *arg = argv[i];
 
 		skip_prefix(arg, "--", &arg);
-		opts->xopts[i] = xstrdup(arg);
+		strvec_push(&opts->xopts, arg);
 	}
+	free(argv);
 }
 
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
@@ -2941,7 +3013,6 @@
 	strbuf_reset(buf);
 	if (!read_oneliner(buf, rebase_path_strategy(), 0))
 		return;
-	free(opts->strategy);
 	opts->strategy = strbuf_detach(buf, NULL);
 	if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
 		return;
@@ -3022,7 +3093,7 @@
 		}
 
 		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
-			if (get_oid_committish(buf.buf, &opts->squash_onto) < 0) {
+			if (repo_get_oid_committish(the_repository, buf.buf, &opts->squash_onto) < 0) {
 				ret = error(_("unusable squash-onto"));
 				goto done_rebase_i;
 			}
@@ -3050,12 +3121,13 @@
 
 static void write_strategy_opts(struct replay_opts *opts)
 {
-	int i;
 	struct strbuf buf = STRBUF_INIT;
 
-	for (i = 0; i < opts->xopts_nr; ++i)
-		strbuf_addf(&buf, " --%s", opts->xopts[i]);
-
+	/*
+	 * Quote strategy options so that they can be read correctly
+	 * by split_cmdline().
+	 */
+	quote_cmdline(&buf, opts->xopts.v);
 	write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
 	strbuf_release(&buf);
 }
@@ -3078,7 +3150,7 @@
 		write_file(rebase_path_verbose(), "%s", "");
 	if (opts->strategy)
 		write_file(rebase_path_strategy(), "%s\n", opts->strategy);
-	if (opts->xopts_nr > 0)
+	if (opts->xopts.nr > 0)
 		write_strategy_opts(opts);
 
 	if (opts->allow_rerere_auto == RERERE_AUTOUPDATE)
@@ -3122,7 +3194,9 @@
 
 	while ((commit = get_revision(opts->revs))) {
 		struct todo_item *item = append_new_todo(todo_list);
-		const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
+		const char *commit_buffer = repo_logmsg_reencode(the_repository,
+								 commit, NULL,
+								 encoding);
 		const char *subject;
 		int subject_len;
 
@@ -3133,8 +3207,10 @@
 		item->offset_in_buf = todo_list->buf.len;
 		subject_len = find_commit_subject(commit_buffer, &subject);
 		strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string,
-			short_commit_name(commit), subject_len, subject);
-		unuse_commit_buffer(commit, commit_buffer);
+			short_commit_name(the_repository, commit),
+			subject_len, subject);
+		repo_unuse_commit_buffer(the_repository, commit,
+					 commit_buffer);
 	}
 
 	if (!todo_list->nr)
@@ -3184,25 +3260,7 @@
 
 static int save_head(const char *head)
 {
-	struct lock_file head_lock = LOCK_INIT;
-	struct strbuf buf = STRBUF_INIT;
-	int fd;
-	ssize_t written;
-
-	fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
-	if (fd < 0)
-		return error_errno(_("could not lock HEAD"));
-	strbuf_addf(&buf, "%s\n", head);
-	written = write_in_full(fd, buf.buf, buf.len);
-	strbuf_release(&buf);
-	if (written < 0) {
-		error_errno(_("could not write to '%s'"), git_path_head_file());
-		rollback_lock_file(&head_lock);
-		return -1;
-	}
-	if (commit_lock_file(&head_lock) < 0)
-		return error(_("failed to finalize '%s'"), git_path_head_file());
-	return 0;
+	return write_message(head, strlen(head), git_path_head_file(), 1);
 }
 
 static int rollback_is_safe(void)
@@ -3223,7 +3281,7 @@
 	else
 		die_errno(_("could not read '%s'"), git_path_abort_safety_file());
 
-	if (get_oid("HEAD", &actual_head))
+	if (repo_get_oid(the_repository, "HEAD", &actual_head))
 		oidclr(&actual_head);
 
 	return oideq(&actual_head, &expected_head);
@@ -3379,7 +3437,8 @@
 	return -1;
 }
 
-static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
+static int save_todo(struct todo_list *todo_list, struct replay_opts *opts,
+		     int reschedule)
 {
 	struct lock_file todo_lock = LOCK_INIT;
 	const char *todo_path = get_todo_path(opts);
@@ -3389,7 +3448,7 @@
 	 * rebase -i writes "git-rebase-todo" without the currently executing
 	 * command, appending it to "done" instead.
 	 */
-	if (is_rebase_i(opts))
+	if (is_rebase_i(opts) && !reschedule)
 		next++;
 
 	fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
@@ -3402,7 +3461,7 @@
 	if (commit_lock_file(&todo_lock) < 0)
 		return error(_("failed to finalize '%s'"), todo_path);
 
-	if (is_rebase_i(opts) && next > 0) {
+	if (is_rebase_i(opts) && !reschedule && next > 0) {
 		const char *done = rebase_path_done();
 		int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666);
 		int ret = 0;
@@ -3427,57 +3486,57 @@
 
 	if (opts->no_commit)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.no-commit", "true");
+					"options.no-commit", NULL, "true");
 	if (opts->edit >= 0)
-		res |= git_config_set_in_file_gently(opts_file, "options.edit",
+		res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL,
 						     opts->edit ? "true" : "false");
 	if (opts->allow_empty)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.allow-empty", "true");
+					"options.allow-empty", NULL, "true");
 	if (opts->allow_empty_message)
 		res |= git_config_set_in_file_gently(opts_file,
-				"options.allow-empty-message", "true");
+				"options.allow-empty-message", NULL, "true");
+	if (opts->drop_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.drop-redundant-commits", NULL, "true");
 	if (opts->keep_redundant_commits)
 		res |= git_config_set_in_file_gently(opts_file,
-				"options.keep-redundant-commits", "true");
+				"options.keep-redundant-commits", NULL, "true");
 	if (opts->signoff)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.signoff", "true");
+					"options.signoff", NULL, "true");
 	if (opts->record_origin)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.record-origin", "true");
+					"options.record-origin", NULL, "true");
 	if (opts->allow_ff)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.allow-ff", "true");
+					"options.allow-ff", NULL, "true");
 	if (opts->mainline) {
 		struct strbuf buf = STRBUF_INIT;
 		strbuf_addf(&buf, "%d", opts->mainline);
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.mainline", buf.buf);
+					"options.mainline", NULL, buf.buf);
 		strbuf_release(&buf);
 	}
 	if (opts->strategy)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.strategy", opts->strategy);
+					"options.strategy", NULL, opts->strategy);
 	if (opts->gpg_sign)
 		res |= git_config_set_in_file_gently(opts_file,
-					"options.gpg-sign", opts->gpg_sign);
-	if (opts->xopts) {
-		int i;
-		for (i = 0; i < opts->xopts_nr; i++)
-			res |= git_config_set_multivar_in_file_gently(opts_file,
-					"options.strategy-option",
-					opts->xopts[i], "^$", 0);
-	}
+					"options.gpg-sign", NULL, opts->gpg_sign);
+	for (size_t i = 0; i < opts->xopts.nr; i++)
+		res |= git_config_set_multivar_in_file_gently(opts_file,
+				"options.strategy-option",
+				opts->xopts.v[i], "^$", NULL, 0);
 	if (opts->allow_rerere_auto)
 		res |= git_config_set_in_file_gently(opts_file,
-				"options.allow-rerere-auto",
+				"options.allow-rerere-auto", NULL,
 				opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
 				"true" : "false");
 
 	if (opts->explicit_cleanup)
 		res |= git_config_set_in_file_gently(opts_file,
-				"options.default-msg-cleanup",
+				"options.default-msg-cleanup", NULL,
 				describe_cleanup_mode(opts->default_msg_cleanup));
 	return res;
 }
@@ -3486,18 +3545,19 @@
 		      struct commit *commit,
 		      struct replay_opts *opts)
 {
-	struct strbuf buf = STRBUF_INIT;
 	struct rev_info log_tree_opt;
 	const char *subject;
 	char hex[GIT_MAX_HEXSZ + 1];
 	int res = 0;
 
+	if (!is_rebase_i(opts))
+		BUG("make_patch should only be called when rebasing");
+
 	oid_to_hex_r(hex, &commit->object.oid);
 	if (write_message(hex, strlen(hex), rebase_path_stopped_sha(), 1) < 0)
 		return -1;
 	res |= write_rebase_head(&commit->object.oid);
 
-	strbuf_addf(&buf, "%s/patch", get_dir(opts));
 	memset(&log_tree_opt, 0, sizeof(log_tree_opt));
 	repo_init_revisions(r, &log_tree_opt, NULL);
 	log_tree_opt.abbrev = 0;
@@ -3505,25 +3565,26 @@
 	log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
 	log_tree_opt.disable_stdin = 1;
 	log_tree_opt.no_commit_id = 1;
-	log_tree_opt.diffopt.file = fopen(buf.buf, "w");
+	log_tree_opt.diffopt.file = fopen(rebase_path_patch(), "w");
 	log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;
 	if (!log_tree_opt.diffopt.file)
-		res |= error_errno(_("could not open '%s'"), buf.buf);
+		res |= error_errno(_("could not open '%s'"),
+				   rebase_path_patch());
 	else {
 		res |= log_tree_commit(&log_tree_opt, commit);
 		fclose(log_tree_opt.diffopt.file);
 	}
-	strbuf_reset(&buf);
 
-	strbuf_addf(&buf, "%s/message", get_dir(opts));
-	if (!file_exists(buf.buf)) {
+	if (!file_exists(rebase_path_message())) {
 		const char *encoding = get_commit_output_encoding();
-		const char *commit_buffer = logmsg_reencode(commit, NULL, encoding);
+		const char *commit_buffer = repo_logmsg_reencode(r,
+								 commit, NULL,
+								 encoding);
 		find_commit_subject(commit_buffer, &subject);
-		res |= write_message(subject, strlen(subject), buf.buf, 1);
-		unuse_commit_buffer(commit, commit_buffer);
+		res |= write_message(subject, strlen(subject), rebase_path_message(), 1);
+		repo_unuse_commit_buffer(r, commit,
+					 commit_buffer);
 	}
-	strbuf_release(&buf);
 	release_revisions(&log_tree_opt);
 
 	return res;
@@ -3534,7 +3595,7 @@
 	struct object_id head;
 	char *p;
 
-	if (get_oid("HEAD", &head))
+	if (repo_get_oid(the_repository, "HEAD", &head))
 		return error(_("cannot read HEAD"));
 
 	p = oid_to_hex(&head);
@@ -3571,7 +3632,7 @@
 	} else if (exit_code) {
 		if (commit)
 			fprintf_ln(stderr, _("Could not apply %s... %.*s"),
-				   short_commit_name(commit), subject_len, subject);
+				   short_commit_name(r, commit), subject_len, subject);
 		else
 			/*
 			 * We don't have the hash of the parent so
@@ -3609,6 +3670,7 @@
 	fprintf(stderr, _("Executing: %s\n"), command_line);
 	cmd.use_shell = 1;
 	strvec_push(&cmd.args, command_line);
+	strvec_push(&cmd.env, "GIT_CHERRY_PICK_HELP");
 	status = run_command(&cmd);
 
 	/* force re-reading of the cache */
@@ -3625,14 +3687,14 @@
 			  "  git rebase --continue\n"
 			  "\n"),
 			command_line,
-			dirty ? N_("and made changes to the index and/or the "
-				"working tree\n") : "");
+			dirty ? _("and made changes to the index and/or the "
+				"working tree.\n") : "");
 		if (status == 127)
 			/* command not found */
 			status = 1;
 	} else if (dirty) {
 		warning(_("execution succeeded: %s\nbut "
-			  "left changes to the index and/or the working tree\n"
+			  "left changes to the index and/or the working tree.\n"
 			  "Commit or stash your changes, and then run\n"
 			  "\n"
 			  "  git rebase --continue\n"
@@ -3673,7 +3735,6 @@
 	}
 	if (commit_lock_file(&lock) < 0) {
 		strbuf_release(&buf);
-		rollback_lock_file(&lock);
 		return error(_("failed to finalize '%s'"), filename);
 	}
 
@@ -3700,7 +3761,7 @@
 	if (!transaction) {
 		error("%s", err.buf);
 		ret = -1;
-	} else if (get_oid("HEAD", &head_oid)) {
+	} else if (repo_get_oid(r, "HEAD", &head_oid)) {
 		error(_("could not read HEAD"));
 		ret = -1;
 	} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
@@ -3850,6 +3911,8 @@
 	}
 
 	tree = parse_tree_indirect(&oid);
+	if (!tree)
+		return error(_("unable to read tree (%s)"), oid_to_hex(&oid));
 	prime_cache_tree(r, r->index, tree);
 
 	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
@@ -3876,9 +3939,9 @@
 	int run_commit_flags = 0;
 	struct strbuf ref_name = STRBUF_INIT;
 	struct commit *head_commit, *merge_commit, *i;
-	struct commit_list *bases, *j;
+	struct commit_list *bases = NULL, *j;
 	struct commit_list *to_merge = NULL, **tail = &to_merge;
-	const char *strategy = !opts->xopts_nr &&
+	const char *strategy = !opts->xopts.nr &&
 		(!opts->strategy ||
 		 !strcmp(opts->strategy, "recursive") ||
 		 !strcmp(opts->strategy, "ort")) ?
@@ -3988,7 +4051,8 @@
 
 	if (commit) {
 		const char *encoding = get_commit_output_encoding();
-		const char *message = logmsg_reencode(commit, NULL, encoding);
+		const char *message = repo_logmsg_reencode(r, commit, NULL,
+							   encoding);
 		const char *body;
 		int len;
 
@@ -4001,7 +4065,7 @@
 		find_commit_subject(message, &body);
 		len = strlen(body);
 		ret = write_message(body, len, git_path_merge_msg(r), 0);
-		unuse_commit_buffer(commit, message);
+		repo_unuse_commit_buffer(r, commit, message);
 		if (ret) {
 			error_errno(_("could not write '%s'"),
 				    git_path_merge_msg(r));
@@ -4061,9 +4125,9 @@
 			strvec_push(&cmd.args, "octopus");
 		else {
 			strvec_push(&cmd.args, strategy);
-			for (k = 0; k < opts->xopts_nr; k++)
+			for (k = 0; k < opts->xopts.nr; k++)
 				strvec_pushf(&cmd.args,
-					     "-X%s", opts->xopts[k]);
+					     "-X%s", opts->xopts.v[k]);
 		}
 		if (!(flags & TODO_EDIT_MERGE_MSG))
 			strvec_push(&cmd.args, "--no-edit");
@@ -4086,7 +4150,7 @@
 
 		strbuf_release(&ref_name);
 		refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
-				NULL, 0);
+				NULL, REF_NO_DEREF);
 		rollback_lock_file(&lock);
 
 		ret = run_command(&cmd);
@@ -4101,7 +4165,11 @@
 	}
 
 	merge_commit = to_merge->item;
-	bases = get_merge_bases(head_commit, merge_commit);
+	if (repo_get_merge_bases(r, head_commit, merge_commit, &bases) < 0) {
+		ret = -1;
+		goto leave_merge;
+	}
+
 	if (bases && oideq(&merge_commit->object.oid,
 			   &bases->item->object.oid)) {
 		ret = 0;
@@ -4141,6 +4209,7 @@
 	if (ret < 0) {
 		error(_("could not even attempt to merge '%.*s'"),
 		      merge_arg_len, arg);
+		unlink(git_path_merge_msg(r));
 		goto leave_merge;
 	}
 	/*
@@ -4269,7 +4338,7 @@
 		if (!is_null_oid(&rec->after))
 			continue;
 
-		for (j = 0; !found && j < todo_list->total_nr; j++) {
+		for (j = 0; !found && j < todo_list->nr; j++) {
 			struct todo_item *item = &todo_list->items[j];
 			const char *arg = todo_list->buf.buf + item->arg_offset;
 
@@ -4299,7 +4368,7 @@
 	 * For each todo_item, check if its ref is in the update_refs list.
 	 * If not, then add it as an un-updated ref.
 	 */
-	for (i = 0; i < todo_list->total_nr; i++) {
+	for (i = 0; i < todo_list->nr; i++) {
 		struct todo_item *item = &todo_list->items[i];
 		const char *arg = todo_list->buf.buf + item->arg_offset;
 		int j, found = 0;
@@ -4430,12 +4499,17 @@
 	return -1;
 }
 
-void create_autostash(struct repository *r, const char *path)
+static void create_autostash_internal(struct repository *r,
+				      const char *path,
+				      const char *refname)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct lock_file lock_file = LOCK_INIT;
 	int fd;
 
+	if (path && refname)
+		BUG("can only pass path or refname");
+
 	fd = repo_hold_locked_index(r, &lock_file, 0);
 	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
@@ -4456,16 +4530,22 @@
 		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
 			die(_("Cannot autostash"));
 		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
+		if (repo_get_oid(r, buf.buf, &oid))
 			die(_("Unexpected stash response: '%s'"),
 			    buf.buf);
 		strbuf_reset(&buf);
 		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
 
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
+		if (path) {
+			if (safe_create_leading_directories_const(path))
+				die(_("Could not create directory for '%s'"),
+				    path);
+			write_file(path, "%s", oid_to_hex(&oid));
+		} else {
+			refs_update_ref(get_main_ref_store(r), "", refname,
+					&oid, null_oid(), 0, UPDATE_REFS_DIE_ON_ERR);
+		}
+
 		printf(_("Created autostash: %s\n"), buf.buf);
 		if (reset_head(r, &ropts) < 0)
 			die(_("could not reset --hard"));
@@ -4476,6 +4556,16 @@
 	strbuf_release(&buf);
 }
 
+void create_autostash(struct repository *r, const char *path)
+{
+	create_autostash_internal(r, path, NULL);
+}
+
+void create_autostash_ref(struct repository *r, const char *refname)
+{
+	create_autostash_internal(r, NULL, refname);
+}
+
 static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
@@ -4553,6 +4643,41 @@
 	return apply_save_autostash_oid(stash_oid, 1);
 }
 
+static int apply_save_autostash_ref(struct repository *r, const char *refname,
+				    int attempt_apply)
+{
+	struct object_id stash_oid;
+	char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+	int flag, ret;
+
+	if (!refs_ref_exists(get_main_ref_store(r), refname))
+		return 0;
+
+	if (!refs_resolve_ref_unsafe(get_main_ref_store(r), refname,
+				     RESOLVE_REF_READING, &stash_oid, &flag))
+		return -1;
+	if (flag & REF_ISSYMREF)
+		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);
+
+	refs_delete_ref(get_main_ref_store(r), "", refname,
+			&stash_oid, REF_NO_DEREF);
+
+	return ret;
+}
+
+int save_autostash_ref(struct repository *r, const char *refname)
+{
+	return apply_save_autostash_ref(r, refname, 0);
+}
+
+int apply_autostash_ref(struct repository *r, const char *refname)
+{
+	return apply_save_autostash_ref(r, refname, 1);
+}
+
 static int checkout_onto(struct repository *r, struct replay_opts *opts,
 			 const char *onto_name, const struct object_id *onto,
 			 const struct object_id *orig_head)
@@ -4581,9 +4706,9 @@
 	struct commit *commit;
 	struct commit_message message;
 
-	if (get_oid("HEAD", &head) ||
+	if (repo_get_oid(r, "HEAD", &head) ||
 	    !(commit = lookup_commit(r, &head)) ||
-	    parse_commit(commit) || get_message(commit, &message))
+	    repo_parse_commit(r, commit) || get_message(commit, &message))
 		fprintf(stderr, _("Stopped at HEAD\n"));
 	else {
 		fprintf(stderr, _("Stopped at %s\n"), message.label);
@@ -4628,6 +4753,68 @@
 "    git rebase --edit-todo\n"
 "    git rebase --continue\n");
 
+static int pick_one_commit(struct repository *r,
+			   struct todo_list *todo_list,
+			   struct replay_opts *opts,
+			   int *check_todo, int* reschedule)
+{
+	int res;
+	struct todo_item *item = todo_list->items + todo_list->current;
+	const char *arg = todo_item_get_arg(todo_list, item);
+	if (is_rebase_i(opts))
+		opts->reflog_message = reflog_message(
+			opts, command_to_string(item->command), NULL);
+
+	res = do_pick_commit(r, item, opts, is_final_fixup(todo_list),
+			     check_todo);
+	if (is_rebase_i(opts) && res < 0) {
+		/* Reschedule */
+		*reschedule = 1;
+		return -1;
+	}
+	if (item->command == TODO_EDIT) {
+		struct commit *commit = item->commit;
+		if (!res) {
+			if (!opts->verbose)
+				term_clear_line();
+			fprintf(stderr, _("Stopped at %s...  %.*s\n"),
+				short_commit_name(r, commit), item->arg_len, arg);
+		}
+		return error_with_patch(r, commit,
+					arg, item->arg_len, opts, res, !res);
+	}
+	if (is_rebase_i(opts) && !res)
+		record_in_rewritten(&item->commit->object.oid,
+				    peek_command(todo_list, 1));
+	if (res && is_fixup(item->command)) {
+		if (res == 1)
+			intend_to_amend();
+		return error_failed_squash(r, item->commit, opts,
+					   item->arg_len, arg);
+	} else if (res && is_rebase_i(opts) && item->commit) {
+		int to_amend = 0;
+		struct object_id oid;
+
+		/*
+		 * If we are rewording and have either
+		 * fast-forwarded already, or are about to
+		 * create a new root commit, we want to amend,
+		 * otherwise we do not.
+		 */
+		if (item->command == TODO_REWORD &&
+		    !repo_get_oid(r, "HEAD", &oid) &&
+		    (oideq(&item->commit->object.oid, &oid) ||
+		     (opts->have_squash_onto &&
+		      oideq(&opts->squash_onto, &oid))))
+			to_amend = 1;
+
+		return res | error_with_patch(r, item->commit,
+					      arg, item->arg_len, opts,
+					      res, to_amend);
+	}
+	return res;
+}
+
 static int pick_commits(struct repository *r,
 			struct todo_list *todo_list,
 			struct replay_opts *opts)
@@ -4643,12 +4830,17 @@
 	if (read_and_refresh_cache(r, opts))
 		return -1;
 
+	unlink(rebase_path_message());
+	unlink(rebase_path_stopped_sha());
+	unlink(rebase_path_amend());
+	unlink(rebase_path_patch());
+
 	while (todo_list->current < todo_list->nr) {
 		struct todo_item *item = todo_list->items + todo_list->current;
 		const char *arg = todo_item_get_arg(todo_list, item);
 		int check_todo = 0;
 
-		if (save_todo(todo_list, opts))
+		if (save_todo(todo_list, opts, reschedule))
 			return -1;
 		if (is_rebase_i(opts)) {
 			if (item->command != TODO_COMMENT) {
@@ -4666,13 +4858,12 @@
 						todo_list->total_nr,
 						opts->verbose ? "\n" : "\r");
 			}
-			unlink(rebase_path_message());
 			unlink(rebase_path_author_script());
-			unlink(rebase_path_stopped_sha());
-			unlink(rebase_path_amend());
 			unlink(git_path_merge_head(r));
-			unlink(git_path_auto_merge(r));
-			delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+			refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+					NULL, REF_NO_DEREF);
+			refs_delete_ref(get_main_ref_store(r), "", "REBASE_HEAD",
+					NULL, REF_NO_DEREF);
 
 			if (item->command == TODO_BREAK) {
 				if (!opts->verbose)
@@ -4681,66 +4872,10 @@
 			}
 		}
 		if (item->command <= TODO_SQUASH) {
-			if (is_rebase_i(opts))
-				opts->reflog_message = reflog_message(opts,
-				      command_to_string(item->command), NULL);
-
-			res = do_pick_commit(r, item, opts,
-					     is_final_fixup(todo_list),
-					     &check_todo);
-			if (is_rebase_i(opts) && res < 0) {
-				/* Reschedule */
-				advise(_(rescheduled_advice),
-				       get_item_line_length(todo_list,
-							    todo_list->current),
-				       get_item_line(todo_list,
-						     todo_list->current));
-				todo_list->current--;
-				if (save_todo(todo_list, opts))
-					return -1;
-			}
-			if (item->command == TODO_EDIT) {
-				struct commit *commit = item->commit;
-				if (!res) {
-					if (!opts->verbose)
-						term_clear_line();
-					fprintf(stderr,
-						_("Stopped at %s...  %.*s\n"),
-						short_commit_name(commit),
-						item->arg_len, arg);
-				}
-				return error_with_patch(r, commit,
-					arg, item->arg_len, opts, res, !res);
-			}
-			if (is_rebase_i(opts) && !res)
-				record_in_rewritten(&item->commit->object.oid,
-					peek_command(todo_list, 1));
-			if (res && is_fixup(item->command)) {
-				if (res == 1)
-					intend_to_amend();
-				return error_failed_squash(r, item->commit, opts,
-					item->arg_len, arg);
-			} else if (res && is_rebase_i(opts) && item->commit) {
-				int to_amend = 0;
-				struct object_id oid;
-
-				/*
-				 * If we are rewording and have either
-				 * fast-forwarded already, or are about to
-				 * create a new root commit, we want to amend,
-				 * otherwise we do not.
-				 */
-				if (item->command == TODO_REWORD &&
-				    !get_oid("HEAD", &oid) &&
-				    (oideq(&item->commit->object.oid, &oid) ||
-				     (opts->have_squash_onto &&
-				      oideq(&opts->squash_onto, &oid))))
-					to_amend = 1;
-
-				return res | error_with_patch(r, item->commit,
-						arg, item->arg_len, opts,
-						res, to_amend);
-			}
+			res = pick_one_commit(r, todo_list, opts, &check_todo,
+					      &reschedule);
+			if (!res && item->command == TODO_EDIT)
+				return 0;
 		} else if (item->command == TODO_EXEC) {
 			char *end_of_arg = (char *)(arg + item->arg_len);
 			int saved = *end_of_arg;
@@ -4788,39 +4923,32 @@
 			       get_item_line_length(todo_list,
 						    todo_list->current),
 			       get_item_line(todo_list, todo_list->current));
-			todo_list->current--;
-			if (save_todo(todo_list, opts))
+			if (save_todo(todo_list, opts, reschedule))
 				return -1;
 			if (item->commit)
-				return error_with_patch(r,
-							item->commit,
-							arg, item->arg_len,
-							opts, res, 0);
+				write_rebase_head(&item->commit->object.oid);
 		} else if (is_rebase_i(opts) && check_todo && !res &&
 			   reread_todo_if_changed(r, todo_list, opts)) {
 			return -1;
 		}
 
-		todo_list->current++;
 		if (res)
 			return res;
+
+		todo_list->current++;
 	}
 
 	if (is_rebase_i(opts)) {
 		struct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;
 		struct stat st;
 
-		/* Stopped in the middle, as planned? */
-		if (todo_list->current < todo_list->nr)
-			return 0;
-
 		if (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&
 				starts_with(head_ref.buf, "refs/")) {
 			const char *msg;
 			struct object_id head, orig;
 			int res;
 
-			if (get_oid("HEAD", &head)) {
+			if (repo_get_oid(r, "HEAD", &head)) {
 				res = error(_("cannot read HEAD"));
 cleanup_head_ref:
 				strbuf_release(&head_ref);
@@ -4867,8 +4995,8 @@
 			log_tree_opt.disable_stdin = 1;
 
 			if (read_oneliner(&buf, rebase_path_orig_head(), 0) &&
-			    !get_oid(buf.buf, &orig) &&
-			    !get_oid("HEAD", &head)) {
+			    !repo_get_oid(r, buf.buf, &orig) &&
+			    !repo_get_oid(r, "HEAD", &head)) {
 				diff_tree_oid(&orig, &head, "",
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
@@ -4956,11 +5084,16 @@
 
 	is_clean = !has_uncommitted_changes(r, 0);
 
+	if (!is_clean && !file_exists(rebase_path_message())) {
+		const char *gpg_opt = gpg_sign_opt_quoted(opts);
+
+		return error(_(staged_changes_advice), gpg_opt, gpg_opt);
+	}
 	if (file_exists(rebase_path_amend())) {
 		struct strbuf rev = STRBUF_INIT;
 		struct object_id head, to_amend;
 
-		if (get_oid("HEAD", &head))
+		if (repo_get_oid(r, "HEAD", &head))
 			return error(_("cannot amend non-existing commit"));
 		if (!read_oneliner(&rev, rebase_path_amend(), 0))
 			return error(_("invalid file: '%s'"), rebase_path_amend());
@@ -5035,18 +5168,31 @@
 				 * We need to update the squash message to skip
 				 * the latest commit message.
 				 */
+				int res = 0;
 				struct commit *commit;
+				const char *msg;
 				const char *path = rebase_path_squash_msg();
 				const char *encoding = get_commit_output_encoding();
 
-				if (parse_head(r, &commit) ||
-				    !(p = logmsg_reencode(commit, NULL, encoding)) ||
-				    write_message(p, strlen(p), path, 0)) {
-					unuse_commit_buffer(commit, p);
-					return error(_("could not write file: "
-						       "'%s'"), path);
+				if (parse_head(r, &commit))
+					return error(_("could not parse HEAD"));
+
+				p = repo_logmsg_reencode(r, commit, NULL, encoding);
+				if (!p)  {
+					res = error(_("could not parse commit %s"),
+						    oid_to_hex(&commit->object.oid));
+					goto unuse_commit_buffer;
 				}
-				unuse_commit_buffer(commit, p);
+				find_commit_subject(p, &msg);
+				if (write_message(msg, strlen(msg), path, 0)) {
+					res = error(_("could not write file: "
+						       "'%s'"), path);
+					goto unuse_commit_buffer;
+				}
+			unuse_commit_buffer:
+				repo_unuse_commit_buffer(r, commit, p);
+				if (res)
+					return res;
 			}
 		}
 
@@ -5058,7 +5204,7 @@
 		if (refs_ref_exists(get_main_ref_store(r),
 				    "CHERRY_PICK_HEAD") &&
 		    refs_delete_ref(get_main_ref_store(r), "",
-				    "CHERRY_PICK_HEAD", NULL, 0))
+				    "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF))
 			return error(_("could not remove CHERRY_PICK_HEAD"));
 		if (unlink(git_path_merge_msg(r)) && errno != ENOENT)
 			return error_errno(_("could not remove '%s'"),
@@ -5072,7 +5218,8 @@
 		return error(_("could not commit staged changes."));
 	unlink(rebase_path_amend());
 	unlink(git_path_merge_head(r));
-	unlink(git_path_auto_merge(r));
+	refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+			NULL, REF_NO_DEREF);
 	if (final_fixup) {
 		unlink(rebase_path_fixup_msg());
 		unlink(rebase_path_squash_msg());
@@ -5185,7 +5332,7 @@
 		if (!strlen(name))
 			continue;
 
-		if (!get_oid(name, &oid)) {
+		if (!repo_get_oid(r, name, &oid)) {
 			if (!lookup_commit_reference_gently(r, &oid, 1)) {
 				enum object_type type = oid_object_info(r,
 									&oid,
@@ -5228,7 +5375,7 @@
 	if (walk_revs_populate_todo(&todo_list, opts) ||
 			create_seq_dir(r) < 0)
 		return -1;
-	if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
+	if (repo_get_oid(r, "HEAD", &oid) && (opts->action == REPLAY_REVERT))
 		return error(_("can't revert as initial commit"));
 	if (save_head(oid_to_hex(&oid)))
 		return -1;
@@ -5326,6 +5473,7 @@
 	struct oidmap commit2label;
 	struct hashmap labels;
 	struct strbuf buf;
+	int max_label_length;
 };
 
 static const char *label_oid(struct object_id *oid, const char *label,
@@ -5344,7 +5492,7 @@
 	 * For "uninteresting" commits, i.e. commits that are not to be
 	 * rebased, and which can therefore not be labeled, we use a unique
 	 * abbreviation of the commit name. This is slightly more complicated
-	 * than calling find_unique_abbrev() because we also need to make
+	 * than calling repo_find_unique_abbrev() because we also need to make
 	 * sure that the abbreviation does not conflict with any other
 	 * label.
 	 *
@@ -5360,7 +5508,8 @@
 		strbuf_grow(&state->buf, GIT_MAX_HEXSZ);
 		label = p = state->buf.buf;
 
-		find_unique_abbrev_r(p, oid, default_abbrev);
+		repo_find_unique_abbrev_r(the_repository, p, oid,
+					  default_abbrev);
 
 		/*
 		 * We may need to extend the abbreviated hash so that there is
@@ -5381,6 +5530,8 @@
 		}
 	} else {
 		struct strbuf *buf = &state->buf;
+		int label_is_utf8 = 1; /* start with this assumption */
+		size_t max_len = buf->len + state->max_label_length;
 
 		/*
 		 * Sanitize labels by replacing non-alpha-numeric characters
@@ -5389,14 +5540,34 @@
 		 *
 		 * Note that we retain non-ASCII UTF-8 characters (identified
 		 * via the most significant bit). They should be all acceptable
-		 * in file names. We do not validate the UTF-8 here, that's not
-		 * the job of this function.
+		 * in file names.
+		 *
+		 * As we will use the labels as names of (loose) refs, it is
+		 * vital that the name not be longer than the maximum component
+		 * size of the file system (`NAME_MAX`). We are careful to
+		 * truncate the label accordingly, allowing for the `.lock`
+		 * suffix and for the label to be UTF-8 encoded (i.e. we avoid
+		 * truncating in the middle of a character).
 		 */
-		for (; *label; label++)
-			if ((*label & 0x80) || isalnum(*label))
+		for (; *label && buf->len + 1 < max_len; label++)
+			if (isalnum(*label) ||
+			    (!label_is_utf8 && (*label & 0x80)))
 				strbuf_addch(buf, *label);
+			else if (*label & 0x80) {
+				const char *p = label;
+
+				utf8_width(&p, NULL);
+				if (p) {
+					if (buf->len + (p - label) > max_len)
+						break;
+					strbuf_add(buf, label, p - label);
+					label = p - 1;
+				} else {
+					label_is_utf8 = 0;
+					strbuf_addch(buf, *label);
+				}
 			/* avoid leading dash and double-dashes */
-			else if (buf->len && buf->buf[buf->len - 1] != '-')
+			} else if (buf->len && buf->buf[buf->len - 1] != '-')
 				strbuf_addch(buf, '-');
 		if (!buf->len) {
 			strbuf_addstr(buf, "rev-");
@@ -5458,7 +5629,8 @@
 	struct string_entry *entry;
 	struct oidset interesting = OIDSET_INIT, child_seen = OIDSET_INIT,
 		shown = OIDSET_INIT;
-	struct label_state state = { OIDMAP_INIT, { NULL }, STRBUF_INIT };
+	struct label_state state =
+		{ OIDMAP_INIT, { NULL }, STRBUF_INIT, GIT_MAX_LABEL_LENGTH };
 
 	int abbr = flags & TODO_LIST_ABBREVIATE_CMDS;
 	const char *cmd_pick = abbr ? "p" : "pick",
@@ -5466,6 +5638,8 @@
 		*cmd_reset = abbr ? "t" : "reset",
 		*cmd_merge = abbr ? "m" : "merge";
 
+	git_config_get_int("rebase.maxlabellength", &state.max_label_length);
+
 	oidmap_init(&commit2todo, 0);
 	oidmap_init(&state.commit2label, 0);
 	hashmap_init(&state.labels, labels_cmp, NULL, 0);
@@ -5502,7 +5676,7 @@
 		if (!is_empty && (commit->object.flags & PATCHSAME)) {
 			if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
 				warning(_("skipped previously applied commit %s"),
-					short_commit_name(commit));
+					short_commit_name(the_repository, commit));
 			skipped_commit = 1;
 			continue;
 		}
@@ -5520,8 +5694,8 @@
 				    oid_to_hex(&commit->object.oid),
 				    oneline.buf);
 			if (is_empty)
-				strbuf_addf(&buf, " %c empty",
-					    comment_line_char);
+				strbuf_addf(&buf, " %s empty",
+					    comment_line_str);
 
 			FLEX_ALLOC_STR(entry, string, buf.buf);
 			oidcpy(&entry->entry.oid, &commit->object.oid);
@@ -5611,7 +5785,7 @@
 		entry = oidmap_get(&state.commit2label, &commit->object.oid);
 
 		if (entry)
-			strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+			strbuf_addf(out, "\n%s Branch %s\n", comment_line_str, entry->string);
 		else
 			strbuf_addch(out, '\n');
 
@@ -5738,7 +5912,7 @@
 		if (!is_empty && (commit->object.flags & PATCHSAME)) {
 			if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
 				warning(_("skipped previously applied commit %s"),
-					short_commit_name(commit));
+					short_commit_name(r, commit));
 			skipped_commit = 1;
 			continue;
 		}
@@ -5748,7 +5922,7 @@
 			    oid_to_hex(&commit->object.oid));
 		pretty_print_commit(&pp, commit, out);
 		if (is_empty)
-			strbuf_addf(out, " %c empty", comment_line_char);
+			strbuf_addf(out, " %s empty", comment_line_str);
 		strbuf_addch(out, '\n');
 	}
 	if (skipped_commit)
@@ -5830,7 +6004,8 @@
 	todo_list->alloc = alloc;
 }
 
-static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+static void todo_list_to_strbuf(struct repository *r,
+				struct todo_list *todo_list,
 				struct strbuf *buf, int num, unsigned flags)
 {
 	struct todo_item *item;
@@ -5859,7 +6034,7 @@
 		/* add commit id */
 		if (item->commit) {
 			const char *oid = flags & TODO_LIST_SHORTEN_IDS ?
-					  short_commit_name(item->commit) :
+					  short_commit_name(r, item->commit) :
 					  oid_to_hex(&item->commit->object.oid);
 
 			if (item->command == TODO_FIXUP) {
@@ -5922,7 +6097,7 @@
 			continue;
 		if (item->command != TODO_PICK)
 			break;
-		if (parse_commit(item->commit)) {
+		if (repo_parse_commit(r, item->commit)) {
 			return error(_("could not parse commit '%s'"),
 				oid_to_hex(&item->commit->object.oid));
 		}
@@ -6093,7 +6268,8 @@
 	struct object_id oid = onto->object.oid;
 	int res;
 
-	find_unique_abbrev_r(shortonto, &oid, DEFAULT_ABBREV);
+	repo_find_unique_abbrev_r(r, shortonto, &oid,
+				  DEFAULT_ABBREV);
 
 	if (buf->len == 0) {
 		struct todo_item *item = append_new_todo(todo_list);
@@ -6144,7 +6320,8 @@
 	todo_list_to_strbuf(r, &new_todo, &buf2, -1, 0);
 	strbuf_swap(&new_todo.buf, &buf2);
 	strbuf_release(&buf2);
-	new_todo.total_nr -= new_todo.nr;
+	/* Nothing is done yet, and we're reparsing, so let's reset the count */
+	new_todo.total_nr = 0;
 	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
 		BUG("invalid todo list after expanding IDs:\n%s",
 		    new_todo.buf.buf);
@@ -6165,7 +6342,7 @@
 	if (checkout_onto(r, opts, onto_name, &oid, orig_head))
 		goto cleanup;
 
-	if (require_clean_work_tree(r, "rebase", "", 1, 1))
+	if (require_clean_work_tree(r, "rebase", NULL, 1, 1))
 		goto cleanup;
 
 	todo_list_write_total_nr(&new_todo);
@@ -6216,7 +6393,7 @@
 int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
 	struct hashmap subject2item;
-	int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
+	int rearranged = 0, *next, *tail, i, nr = 0;
 	char **subjects;
 	struct commit_todo_item commit_todo;
 	struct todo_item *items = NULL;
@@ -6254,12 +6431,15 @@
 			return error(_("the script was already rearranged."));
 		}
 
-		parse_commit(item->commit);
-		commit_buffer = logmsg_reencode(item->commit, NULL, "UTF-8");
+		repo_parse_commit(the_repository, item->commit);
+		commit_buffer = repo_logmsg_reencode(the_repository,
+						     item->commit, NULL,
+						     "UTF-8");
 		find_commit_subject(commit_buffer, &subject);
 		format_subject(&buf, subject, " ");
 		subject = subjects[i] = strbuf_detach(&buf, &subject_len);
-		unuse_commit_buffer(item->commit, commit_buffer);
+		repo_unuse_commit_buffer(the_repository, item->commit,
+					 commit_buffer);
 		if (skip_fixupish(subject, &p)) {
 			struct commit *commit2;
 
@@ -6325,6 +6505,8 @@
 	}
 
 	if (rearranged) {
+		ALLOC_ARRAY(items, todo_list->nr);
+
 		for (i = 0; i < todo_list->nr; i++) {
 			enum todo_command command = todo_list->items[i].command;
 			int cur = i;
@@ -6337,16 +6519,15 @@
 				continue;
 
 			while (cur >= 0) {
-				ALLOC_GROW(items, nr + 1, alloc);
 				items[nr++] = todo_list->items[cur];
 				cur = next[cur];
 			}
 		}
 
+		assert(nr == todo_list->nr);
+		todo_list->alloc = nr;
 		FREE_AND_NULL(todo_list->items);
 		todo_list->items = items;
-		todo_list->nr = nr;
-		todo_list->alloc = alloc;
 	}
 
 	free(next);
@@ -6369,8 +6550,8 @@
 		if (file_exists(git_path_seq_dir()))
 			*whence = FROM_CHERRY_PICK_MULTI;
 		if (file_exists(rebase_path()) &&
-		    !get_oid("REBASE_HEAD", &rebase_head) &&
-		    !get_oid("CHERRY_PICK_HEAD", &cherry_pick_head) &&
+		    !repo_get_oid(r, "REBASE_HEAD", &rebase_head) &&
+		    !repo_get_oid(r, "CHERRY_PICK_HEAD", &cherry_pick_head) &&
 		    oideq(&rebase_head, &cherry_pick_head))
 			*whence = FROM_REBASE_PICK;
 		else
diff --git a/sequencer.h b/sequencer.h
index 3bcdfa1..437eabd 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -1,11 +1,12 @@
 #ifndef SEQUENCER_H
 #define SEQUENCER_H
 
-#include "cache.h"
 #include "strbuf.h"
+#include "strvec.h"
 #include "wt-status.h"
 
 struct commit;
+struct index_state;
 struct repository;
 
 const char *git_path_commit_editmsg(void);
@@ -13,6 +14,8 @@
 const char *rebase_path_todo_backup(void);
 const char *rebase_path_dropped(void);
 
+extern const char *rebase_resolvemsg;
+
 #define APPEND_SIGNOFF_DEDUP (1u << 0)
 
 enum replay_action {
@@ -60,8 +63,7 @@
 	/* Merge strategy */
 	char *default_strategy;  /* from config options */
 	char *strategy;
-	char **xopts;
-	size_t xopts_nr, xopts_alloc;
+	struct strvec xopts;
 
 	/* Reflog */
 	char *reflog_action;
@@ -80,7 +82,12 @@
 	/* Private use */
 	const char *reflog_message;
 };
-#define REPLAY_OPTS_INIT { .edit = -1, .action = -1, .current_fixups = STRBUF_INIT }
+#define REPLAY_OPTS_INIT {			\
+	.edit = -1,				\
+	.action = -1,				\
+	.current_fixups = STRBUF_INIT,		\
+	.xopts = STRVEC_INIT,			\
+}
 
 /*
  * Note that ordering matters in this enum. Not only must it match the mapping
@@ -220,9 +227,12 @@
 			 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);
 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);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
@@ -247,7 +257,6 @@
 	const char *path, unsigned flags);
 int read_author_script(const char *path, char **name, char **email, char **date,
 		       int allow_missing);
-void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
 int write_basic_state(struct replay_opts *opts, const char *head_name,
 		      struct commit *onto, const struct object_id *orig_head);
 void sequencer_post_commit_cleanup(struct repository *r, int verbose);
diff --git a/serve.c b/serve.c
index cbf4a14..aa651b7 100644
--- a/serve.c
+++ b/serve.c
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "repository.h"
 #include "config.h"
+#include "hash-ll.h"
 #include "pkt-line.h"
 #include "version.h"
 #include "ls-refs.h"
@@ -8,17 +9,19 @@
 #include "serve.h"
 #include "upload-pack.h"
 #include "bundle-uri.h"
+#include "trace2.h"
 
 static int advertise_sid = -1;
+static int advertise_object_info = -1;
 static int client_hash_algo = GIT_HASH_SHA1;
 
-static int always_advertise(struct repository *r,
-			    struct strbuf *value)
+static int always_advertise(struct repository *r UNUSED,
+			    struct strbuf *value UNUSED)
 {
 	return 1;
 }
 
-static int agent_advertise(struct repository *r,
+static int agent_advertise(struct repository *r UNUSED,
 			   struct strbuf *value)
 {
 	if (value)
@@ -34,7 +37,7 @@
 	return 1;
 }
 
-static void object_format_receive(struct repository *r,
+static void object_format_receive(struct repository *r UNUSED,
 				  const char *algo_name)
 {
 	if (!algo_name)
@@ -48,7 +51,7 @@
 static int session_id_advertise(struct repository *r, struct strbuf *value)
 {
 	if (advertise_sid == -1 &&
-	    git_config_get_bool("transfer.advertisesid", &advertise_sid))
+	    repo_config_get_bool(r, "transfer.advertisesid", &advertise_sid))
 		advertise_sid = 0;
 	if (!advertise_sid)
 		return 0;
@@ -57,7 +60,7 @@
 	return 1;
 }
 
-static void session_id_receive(struct repository *r,
+static void session_id_receive(struct repository *r UNUSED,
 			       const char *client_sid)
 {
 	if (!client_sid)
@@ -65,6 +68,17 @@
 	trace2_data_string("transfer", NULL, "client-sid", client_sid);
 }
 
+static int object_info_advertise(struct repository *r, struct strbuf *value UNUSED)
+{
+	if (advertise_object_info == -1 &&
+	    repo_config_get_bool(r, "transfer.advertiseobjectinfo",
+				 &advertise_object_info)) {
+		/* disabled by default */
+		advertise_object_info = 0;
+	}
+	return advertise_object_info;
+}
+
 struct protocol_capability {
 	/*
 	 * The name of the capability.  The server uses this name when
@@ -133,7 +147,7 @@
 	},
 	{
 		.name = "object-info",
-		.advertise = always_advertise,
+		.advertise = object_info_advertise,
 		.command = cap_object_info,
 	},
 	{
diff --git a/server-info.c b/server-info.c
index 0ec6c0c..e2fe0f9 100644
--- a/server-info.c
+++ b/server-info.c
@@ -1,12 +1,17 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
+#include "environment.h"
+#include "hex.h"
 #include "repository.h"
 #include "refs.h"
 #include "object.h"
 #include "commit.h"
 #include "tag.h"
 #include "packfile.h"
-#include "object-store.h"
+#include "path.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "server-info.h"
 #include "strbuf.h"
 
 struct update_info_ctx {
diff --git a/server-info.h b/server-info.h
new file mode 100644
index 0000000..13bbde2
--- /dev/null
+++ b/server-info.h
@@ -0,0 +1,7 @@
+#ifndef SERVER_INFO_H
+#define SERVER_INFO_H
+
+/* Dumb servers support */
+int update_server_info(int);
+
+#endif /* SERVER_INFO_H */
diff --git a/setup.c b/setup.c
index cefd5f6..f4b32f7 100644
--- a/setup.c
+++ b/setup.c
@@ -1,11 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "copy.h"
+#include "environment.h"
+#include "exec-cmd.h"
+#include "gettext.h"
+#include "object-name.h"
+#include "refs.h"
 #include "repository.h"
 #include "config.h"
 #include "dir.h"
+#include "setup.h"
 #include "string-list.h"
 #include "chdir-notify.h"
-#include "promisor-remote.h"
+#include "path.h"
 #include "quote.h"
+#include "trace2.h"
+#include "worktree.h"
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
@@ -510,7 +520,9 @@
 	startup_info->original_cwd = NULL;
 }
 
-static int read_worktree_config(const char *var, const char *value, void *vdata)
+static int read_worktree_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *vdata)
 {
 	struct repository_format *data = vdata;
 
@@ -546,6 +558,8 @@
 			data->precious_objects = git_config_bool(var, value);
 			return EXTENSION_OK;
 		} else if (!strcmp(ext, "partialclone")) {
+			if (!value)
+				return config_error_nonbool(var);
 			data->partial_clone = xstrdup(value);
 			return EXTENSION_OK;
 		} else if (!strcmp(ext, "worktreeconfig")) {
@@ -577,17 +591,48 @@
 				     "extensions.objectformat", value);
 		data->hash_algo = format;
 		return EXTENSION_OK;
+	} else if (!strcmp(ext, "compatobjectformat")) {
+		struct string_list_item *item;
+		int format;
+
+		if (!value)
+			return config_error_nonbool(var);
+		format = hash_algo_by_name(value);
+		if (format == GIT_HASH_UNKNOWN)
+			return error(_("invalid value for '%s': '%s'"),
+				     "extensions.compatobjectformat", value);
+		/* For now only support compatObjectFormat being specified once. */
+		for_each_string_list_item(item, &data->v1_only_extensions) {
+			if (!strcmp(item->string, "compatobjectformat"))
+				return error(_("'%s' already specified as '%s'"),
+					"extensions.compatobjectformat",
+					hash_algos[data->compat_hash_algo].name);
+		}
+		data->compat_hash_algo = format;
+		return EXTENSION_OK;
+	} else if (!strcmp(ext, "refstorage")) {
+		unsigned int format;
+
+		if (!value)
+			return config_error_nonbool(var);
+		format = ref_storage_format_by_name(value);
+		if (format == REF_STORAGE_FORMAT_UNKNOWN)
+			return error(_("invalid value for '%s': '%s'"),
+				     "extensions.refstorage", value);
+		data->ref_storage_format = format;
+		return EXTENSION_OK;
 	}
 	return EXTENSION_UNKNOWN;
 }
 
-static int check_repo_format(const char *var, const char *value, void *vdata)
+static int check_repo_format(const char *var, const char *value,
+			     const struct config_context *ctx, void *vdata)
 {
 	struct repository_format *data = vdata;
 	const char *ext;
 
 	if (strcmp(var, "core.repositoryformatversion") == 0)
-		data->version = git_config_int(var, value);
+		data->version = git_config_int(var, value, ctx->kvi);
 	else if (skip_prefix(var, "extensions.", &ext)) {
 		switch (handle_extension_v0(var, value, ext, data)) {
 		case EXTENSION_ERROR:
@@ -610,7 +655,7 @@
 		}
 	}
 
-	return read_worktree_config(var, value, vdata);
+	return read_worktree_config(var, value, ctx, vdata);
 }
 
 static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
@@ -643,11 +688,10 @@
 	}
 
 	repository_format_precious_objects = candidate->precious_objects;
-	repository_format_worktree_config = candidate->worktree_config;
 	string_list_clear(&candidate->unknown_extensions, 0);
 	string_list_clear(&candidate->v1_only_extensions, 0);
 
-	if (repository_format_worktree_config) {
+	if (candidate->worktree_config) {
 		/*
 		 * pick up core.bare and core.worktree from per-worktree
 		 * config if present
@@ -680,29 +724,39 @@
 	struct strbuf err = STRBUF_INIT;
 	struct strbuf repo_version = STRBUF_INIT;
 	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+	int ret;
 
 	strbuf_git_common_path(&sb, the_repository, "config");
 	read_repository_format(&repo_fmt, sb.buf);
 	strbuf_release(&sb);
 
-	if (repo_fmt.version >= target_version)
-		return 0;
+	if (repo_fmt.version >= target_version) {
+		ret = 0;
+		goto out;
+	}
 
 	if (verify_repository_format(&repo_fmt, &err) < 0) {
-		error("cannot upgrade repository format from %d to %d: %s",
-		      repo_fmt.version, target_version, err.buf);
-		strbuf_release(&err);
-		return -1;
+		ret = error("cannot upgrade repository format from %d to %d: %s",
+			    repo_fmt.version, target_version, err.buf);
+		goto out;
 	}
-	if (!repo_fmt.version && repo_fmt.unknown_extensions.nr)
-		return error("cannot upgrade repository format: "
-			     "unknown extension %s",
-			     repo_fmt.unknown_extensions.items[0].string);
+	if (!repo_fmt.version && repo_fmt.unknown_extensions.nr) {
+		ret = error("cannot upgrade repository format: "
+			    "unknown extension %s",
+			    repo_fmt.unknown_extensions.items[0].string);
+		goto out;
+	}
 
 	strbuf_addf(&repo_version, "%d", target_version);
 	git_config_set("core.repositoryformatversion", repo_version.buf);
+
+	ret = 1;
+
+out:
+	clear_repository_format(&repo_fmt);
 	strbuf_release(&repo_version);
-	return 1;
+	strbuf_release(&err);
+	return ret;
 }
 
 static void init_repository_format(struct repository_format *format)
@@ -1109,7 +1163,8 @@
 	int is_safe;
 };
 
-static int safe_directory_cb(const char *key, const char *value, void *d)
+static int safe_directory_cb(const char *key, const char *value,
+			     const struct config_context *ctx UNUSED, void *d)
 {
 	struct safe_directory_data *data = d;
 
@@ -1165,7 +1220,9 @@
 	return data.is_safe;
 }
 
-static int allowed_bare_repo_cb(const char *key, const char *value, void *d)
+static int allowed_bare_repo_cb(const char *key, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *d)
 {
 	enum allowed_bare_repo *allowed_bare_repo = d;
 
@@ -1205,18 +1262,31 @@
 	return NULL;
 }
 
-enum discovery_result {
-	GIT_DIR_NONE = 0,
-	GIT_DIR_EXPLICIT,
-	GIT_DIR_DISCOVERED,
-	GIT_DIR_BARE,
-	/* these are errors */
-	GIT_DIR_HIT_CEILING = -1,
-	GIT_DIR_HIT_MOUNT_POINT = -2,
-	GIT_DIR_INVALID_GITFILE = -3,
-	GIT_DIR_INVALID_OWNERSHIP = -4,
-	GIT_DIR_DISALLOWED_BARE = -5,
-};
+static int is_implicit_bare_repo(const char *path)
+{
+	/*
+	 * what we found is a ".git" directory at the root of
+	 * the working tree.
+	 */
+	if (ends_with_path_components(path, ".git"))
+		return 1;
+
+	/*
+	 * we are inside $GIT_DIR of a secondary worktree of a
+	 * non-bare repository.
+	 */
+	if (strstr(path, "/.git/worktrees/"))
+		return 1;
+
+	/*
+	 * we are inside $GIT_DIR of a worktree of a non-embedded
+	 * submodule, whose superproject is not a bare repository.
+	 */
+	if (strstr(path, "/.git/modules/"))
+		return 1;
+
+	return 0;
+}
 
 /*
  * We cannot decide in this function whether we are in the work tree or
@@ -1345,7 +1415,9 @@
 		}
 
 		if (is_git_directory(dir->buf)) {
-			if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT)
+			trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
+			if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT &&
+			    !is_implicit_bare_repo(dir->buf))
 				return GIT_DIR_DISALLOWED_BARE;
 			if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
 				return GIT_DIR_INVALID_OWNERSHIP;
@@ -1368,21 +1440,23 @@
 	}
 }
 
-int discover_git_directory(struct strbuf *commondir,
-			   struct strbuf *gitdir)
+enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
+						    struct strbuf *gitdir)
 {
 	struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
 	size_t gitdir_offset = gitdir->len, cwd_len;
 	size_t commondir_offset = commondir->len;
 	struct repository_format candidate = REPOSITORY_FORMAT_INIT;
+	enum discovery_result result;
 
 	if (strbuf_getcwd(&dir))
-		return -1;
+		return GIT_DIR_CWD_FAILURE;
 
 	cwd_len = dir.len;
-	if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) {
+	result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);
+	if (result <= 0) {
 		strbuf_release(&dir);
-		return -1;
+		return result;
 	}
 
 	/*
@@ -1412,16 +1486,11 @@
 		strbuf_setlen(commondir, commondir_offset);
 		strbuf_setlen(gitdir, gitdir_offset);
 		clear_repository_format(&candidate);
-		return -1;
+		return GIT_DIR_INVALID_FORMAT;
 	}
 
-	/* take ownership of candidate.partial_clone */
-	the_repository->repository_format_partial_clone =
-		candidate.partial_clone;
-	candidate.partial_clone = NULL;
-
 	clear_repository_format(&candidate);
-	return 0;
+	return result;
 }
 
 const char *setup_git_directory_gently(int *nongit_ok)
@@ -1503,10 +1572,11 @@
 		}
 		*nongit_ok = 1;
 		break;
-	case GIT_DIR_NONE:
+	case GIT_DIR_CWD_FAILURE:
+	case GIT_DIR_INVALID_FORMAT:
 		/*
 		 * As a safeguard against setup_git_directory_gently_1 returning
-		 * this value, fallthrough to BUG. Otherwise it is possible to
+		 * these values, fallthrough to BUG. Otherwise it is possible to
 		 * set startup_info->have_repository to 1 when we did nothing to
 		 * find a repository.
 		 */
@@ -1552,6 +1622,12 @@
 		}
 		if (startup_info->have_repository) {
 			repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+			repo_set_compat_hash_algo(the_repository,
+						  repo_fmt.compat_hash_algo);
+			repo_set_ref_storage_format(the_repository,
+						    repo_fmt.ref_storage_format);
+			the_repository->repository_format_worktree_config =
+				repo_fmt.worktree_config;
 			/* take ownership of repo_fmt.partial_clone */
 			the_repository->repository_format_partial_clone =
 				repo_fmt.partial_clone;
@@ -1643,6 +1719,11 @@
 	check_repository_format_gently(get_git_dir(), 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);
+	the_repository->repository_format_worktree_config =
+		fmt->worktree_config;
 	the_repository->repository_format_partial_clone =
 		xstrdup_or_null(fmt->partial_clone);
 	clear_repository_format(&repo_fmt);
@@ -1699,3 +1780,517 @@
 	return 0;
 #endif
 }
+
+#ifdef NO_TRUSTABLE_FILEMODE
+#define TEST_FILEMODE 0
+#else
+#define TEST_FILEMODE 1
+#endif
+
+#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
+
+static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
+			     DIR *dir)
+{
+	size_t path_baselen = path->len;
+	size_t template_baselen = template_path->len;
+	struct dirent *de;
+
+	/* Note: if ".git/hooks" file exists in the repository being
+	 * re-initialized, /etc/core-git/templates/hooks/update would
+	 * cause "git init" to fail here.  I think this is sane but
+	 * it means that the set of templates we ship by default, along
+	 * with the way the namespace under .git/ is organized, should
+	 * be really carefully chosen.
+	 */
+	safe_create_dir(path->buf, 1);
+	while ((de = readdir(dir)) != NULL) {
+		struct stat st_git, st_template;
+		int exists = 0;
+
+		strbuf_setlen(path, path_baselen);
+		strbuf_setlen(template_path, template_baselen);
+
+		if (de->d_name[0] == '.')
+			continue;
+		strbuf_addstr(path, de->d_name);
+		strbuf_addstr(template_path, de->d_name);
+		if (lstat(path->buf, &st_git)) {
+			if (errno != ENOENT)
+				die_errno(_("cannot stat '%s'"), path->buf);
+		}
+		else
+			exists = 1;
+
+		if (lstat(template_path->buf, &st_template))
+			die_errno(_("cannot stat template '%s'"), template_path->buf);
+
+		if (S_ISDIR(st_template.st_mode)) {
+			DIR *subdir = opendir(template_path->buf);
+			if (!subdir)
+				die_errno(_("cannot opendir '%s'"), template_path->buf);
+			strbuf_addch(path, '/');
+			strbuf_addch(template_path, '/');
+			copy_templates_1(path, template_path, subdir);
+			closedir(subdir);
+		}
+		else if (exists)
+			continue;
+		else if (S_ISLNK(st_template.st_mode)) {
+			struct strbuf lnk = STRBUF_INIT;
+			if (strbuf_readlink(&lnk, template_path->buf,
+					    st_template.st_size) < 0)
+				die_errno(_("cannot readlink '%s'"), template_path->buf);
+			if (symlink(lnk.buf, path->buf))
+				die_errno(_("cannot symlink '%s' '%s'"),
+					  lnk.buf, path->buf);
+			strbuf_release(&lnk);
+		}
+		else if (S_ISREG(st_template.st_mode)) {
+			if (copy_file(path->buf, template_path->buf, st_template.st_mode))
+				die_errno(_("cannot copy '%s' to '%s'"),
+					  template_path->buf, path->buf);
+		}
+		else
+			error(_("ignoring template %s"), template_path->buf);
+	}
+}
+
+static void copy_templates(const char *template_dir, const char *init_template_dir)
+{
+	struct strbuf path = STRBUF_INIT;
+	struct strbuf template_path = STRBUF_INIT;
+	size_t template_len;
+	struct repository_format template_format = REPOSITORY_FORMAT_INIT;
+	struct strbuf err = STRBUF_INIT;
+	DIR *dir;
+	char *to_free = NULL;
+
+	if (!template_dir)
+		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+	if (!template_dir)
+		template_dir = init_template_dir;
+	if (!template_dir)
+		template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+	if (!template_dir[0]) {
+		free(to_free);
+		return;
+	}
+
+	strbuf_addstr(&template_path, template_dir);
+	strbuf_complete(&template_path, '/');
+	template_len = template_path.len;
+
+	dir = opendir(template_path.buf);
+	if (!dir) {
+		warning(_("templates not found in %s"), template_dir);
+		goto free_return;
+	}
+
+	/* Make sure that template is from the correct vintage */
+	strbuf_addstr(&template_path, "config");
+	read_repository_format(&template_format, template_path.buf);
+	strbuf_setlen(&template_path, template_len);
+
+	/*
+	 * No mention of version at all is OK, but anything else should be
+	 * verified.
+	 */
+	if (template_format.version >= 0 &&
+	    verify_repository_format(&template_format, &err) < 0) {
+		warning(_("not copying templates from '%s': %s"),
+			  template_dir, err.buf);
+		strbuf_release(&err);
+		goto close_free_return;
+	}
+
+	strbuf_addstr(&path, get_git_common_dir());
+	strbuf_complete(&path, '/');
+	copy_templates_1(&path, &template_path, dir);
+close_free_return:
+	closedir(dir);
+free_return:
+	free(to_free);
+	strbuf_release(&path);
+	strbuf_release(&template_path);
+	clear_repository_format(&template_format);
+}
+
+/*
+ * If the git_dir is not directly inside the working tree, then git will not
+ * find it by default, and we need to set the worktree explicitly.
+ */
+static int needs_work_tree_config(const char *git_dir, const char *work_tree)
+{
+	if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
+		return 0;
+	if (skip_prefix(git_dir, work_tree, &git_dir) &&
+	    !strcmp(git_dir, "/.git"))
+		return 0;
+	return 1;
+}
+
+void initialize_repository_version(int hash_algo,
+				   unsigned int ref_storage_format,
+				   int reinit)
+{
+	char repo_version_string[10];
+	int repo_version = GIT_REPO_VERSION;
+
+	/*
+	 * Note that we initialize the repository version to 1 when the ref
+	 * storage format is unknown. This is on purpose so that we can add the
+	 * correct object format to the config during git-clone(1). The format
+	 * version will get adjusted by git-clone(1) once it has learned about
+	 * the remote repository's format.
+	 */
+	if (hash_algo != GIT_HASH_SHA1 ||
+	    ref_storage_format != REF_STORAGE_FORMAT_FILES)
+		repo_version = GIT_REPO_VERSION_READ;
+
+	/* This forces creation of new config file */
+	xsnprintf(repo_version_string, sizeof(repo_version_string),
+		  "%d", repo_version);
+	git_config_set("core.repositoryformatversion", repo_version_string);
+
+	if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)
+		git_config_set("extensions.objectformat",
+			       hash_algos[hash_algo].name);
+	else if (reinit)
+		git_config_set_gently("extensions.objectformat", NULL);
+
+	if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+		git_config_set("extensions.refstorage",
+			       ref_storage_format_to_name(ref_storage_format));
+}
+
+static int is_reinit(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	char junk[2];
+	int ret;
+
+	git_path_buf(&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(unsigned int ref_storage_format,
+			       const char *initial_branch, int quiet)
+{
+	struct strbuf err = STRBUF_INIT;
+	int reinit = is_reinit();
+
+	repo_set_ref_storage_format(the_repository, ref_storage_format);
+	if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
+		die("failed to set up refs db: %s", err.buf);
+
+	/*
+	 * Point the HEAD symref to the initial branch with if HEAD does
+	 * not yet exist.
+	 */
+	if (!reinit) {
+		char *ref;
+
+		if (!initial_branch)
+			initial_branch = git_default_branch_name(quiet);
+
+		ref = xstrfmt("refs/heads/%s", initial_branch);
+		if (check_refname_format(ref, 0) < 0)
+			die(_("invalid initial branch name: '%s'"),
+			    initial_branch);
+
+		if (create_symref("HEAD", ref, NULL) < 0)
+			exit(1);
+		free(ref);
+	}
+
+	if (reinit && initial_branch)
+		warning(_("re-init: ignored --initial-branch=%s"),
+			initial_branch);
+
+	strbuf_release(&err);
+}
+
+static int create_default_files(const char *template_path,
+				const char *original_git_dir,
+				const struct repository_format *fmt,
+				int init_shared_repository)
+{
+	struct stat st1;
+	struct strbuf buf = STRBUF_INIT;
+	char *path;
+	int reinit;
+	int filemode;
+	const char *init_template_dir = NULL;
+	const char *work_tree = get_git_work_tree();
+
+	/*
+	 * First copy the templates -- we might have the default
+	 * config file there, in which case we would want to read
+	 * from it after installing.
+	 *
+	 * Before reading that config, we also need to clear out any cached
+	 * values (since we've just potentially changed what's available on
+	 * disk).
+	 */
+	git_config_get_pathname("init.templatedir", &init_template_dir);
+	copy_templates(template_path, init_template_dir);
+	free((char *)init_template_dir);
+	git_config_clear();
+	reset_shared_repository();
+	git_config(git_default_config, NULL);
+
+	reinit = is_reinit();
+
+	/*
+	 * 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)
+		set_shared_repository(init_shared_repository);
+
+	is_bare_repository_cfg = !work_tree;
+
+	/*
+	 * We would have created the above under user's umask -- under
+	 * shared-repository settings, we would need to fix them up.
+	 */
+	if (get_shared_repository()) {
+		adjust_shared_perm(get_git_dir());
+	}
+
+	initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
+
+	/* Check filemode trustability */
+	path = git_path_buf(&buf, "config");
+	filemode = TEST_FILEMODE;
+	if (TEST_FILEMODE && !lstat(path, &st1)) {
+		struct stat st2;
+		filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+				!lstat(path, &st2) &&
+				st1.st_mode != st2.st_mode &&
+				!chmod(path, st1.st_mode));
+		if (filemode && !reinit && (st1.st_mode & S_IXUSR))
+			filemode = 0;
+	}
+	git_config_set("core.filemode", filemode ? "true" : "false");
+
+	if (is_bare_repository())
+		git_config_set("core.bare", "true");
+	else {
+		git_config_set("core.bare", "false");
+		/* allow template config file to override the default */
+		if (log_all_ref_updates == LOG_REFS_UNSET)
+			git_config_set("core.logallrefupdates", "true");
+		if (needs_work_tree_config(original_git_dir, work_tree))
+			git_config_set("core.worktree", work_tree);
+	}
+
+	if (!reinit) {
+		/* Check if symlink is supported in the work tree */
+		path = git_path_buf(&buf, "tXXXXXX");
+		if (!close(xmkstemp(path)) &&
+		    !unlink(path) &&
+		    !symlink("testing", path) &&
+		    !lstat(path, &st1) &&
+		    S_ISLNK(st1.st_mode))
+			unlink(path); /* good */
+		else
+			git_config_set("core.symlinks", "false");
+
+		/* Check if the filesystem is case-insensitive */
+		path = git_path_buf(&buf, "CoNfIg");
+		if (!access(path, F_OK))
+			git_config_set("core.ignorecase", "true");
+		probe_utf8_pathname_composition();
+	}
+
+	strbuf_release(&buf);
+	return reinit;
+}
+
+static void create_object_directory(void)
+{
+	struct strbuf path = STRBUF_INIT;
+	size_t baselen;
+
+	strbuf_addstr(&path, get_object_directory());
+	baselen = path.len;
+
+	safe_create_dir(path.buf, 1);
+
+	strbuf_setlen(&path, baselen);
+	strbuf_addstr(&path, "/pack");
+	safe_create_dir(path.buf, 1);
+
+	strbuf_setlen(&path, baselen);
+	strbuf_addstr(&path, "/info");
+	safe_create_dir(path.buf, 1);
+
+	strbuf_release(&path);
+}
+
+static void separate_git_dir(const char *git_dir, const char *git_link)
+{
+	struct stat st;
+
+	if (!stat(git_link, &st)) {
+		const char *src;
+
+		if (S_ISREG(st.st_mode))
+			src = read_gitfile(git_link);
+		else if (S_ISDIR(st.st_mode))
+			src = git_link;
+		else
+			die(_("unable to handle file type %d"), (int)st.st_mode);
+
+		if (rename(src, git_dir))
+			die_errno(_("unable to move %s to %s"), src, git_dir);
+		repair_worktrees(NULL, NULL);
+	}
+
+	write_file(git_link, "gitdir: %s", git_dir);
+}
+
+static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
+{
+	const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
+	/*
+	 * If we already have an initialized repo, don't allow the user to
+	 * specify a different algorithm, as that could cause corruption.
+	 * Otherwise, if the user has specified one on the command line, use it.
+	 */
+	if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
+		die(_("attempt to reinitialize repository with different hash"));
+	else if (hash != GIT_HASH_UNKNOWN)
+		repo_fmt->hash_algo = hash;
+	else if (env) {
+		int env_algo = hash_algo_by_name(env);
+		if (env_algo == GIT_HASH_UNKNOWN)
+			die(_("unknown hash algorithm '%s'"), env);
+		repo_fmt->hash_algo = env_algo;
+	}
+}
+
+static void validate_ref_storage_format(struct repository_format *repo_fmt,
+					unsigned int format)
+{
+	const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
+
+	if (repo_fmt->version >= 0 &&
+	    format != REF_STORAGE_FORMAT_UNKNOWN &&
+	    format != repo_fmt->ref_storage_format) {
+		die(_("attempt to reinitialize repository with different reference storage format"));
+	} else if (format != REF_STORAGE_FORMAT_UNKNOWN) {
+		repo_fmt->ref_storage_format = format;
+	} else if (name) {
+		format = ref_storage_format_by_name(name);
+		if (format == REF_STORAGE_FORMAT_UNKNOWN)
+			die(_("unknown ref storage format '%s'"), name);
+		repo_fmt->ref_storage_format = format;
+	}
+}
+
+int init_db(const char *git_dir, const char *real_git_dir,
+	    const char *template_dir, int hash,
+	    unsigned int ref_storage_format,
+	    const char *initial_branch,
+	    int init_shared_repository, unsigned int flags)
+{
+	int reinit;
+	int exist_ok = flags & INIT_DB_EXIST_OK;
+	char *original_git_dir = real_pathdup(git_dir, 1);
+	struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+
+	if (real_git_dir) {
+		struct stat st;
+
+		if (!exist_ok && !stat(git_dir, &st))
+			die(_("%s already exists"), 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 = get_git_dir();
+		separate_git_dir(git_dir, original_git_dir);
+	}
+	else {
+		set_git_dir(git_dir, 1);
+		git_dir = get_git_dir();
+	}
+	startup_info->have_repository = 1;
+
+	/* Ensure `core.hidedotfiles` is processed */
+	git_config(platform_core_config, NULL);
+
+	safe_create_dir(git_dir, 0);
+
+
+	/* Check to see if the repository version is right.
+	 * Note that a newly created repository does not have
+	 * 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);
+
+	validate_hash_algorithm(&repo_fmt, hash);
+	validate_ref_storage_format(&repo_fmt, ref_storage_format);
+
+	reinit = create_default_files(template_dir, original_git_dir,
+				      &repo_fmt, init_shared_repository);
+
+	/*
+	 * Now that we have set up both the hash algorithm and the ref storage
+	 * format we can update the repository's settings accordingly.
+	 */
+	repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+	repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
+
+	if (!(flags & INIT_DB_SKIP_REFDB))
+		create_reference_database(repo_fmt.ref_storage_format,
+					  initial_branch, flags & INIT_DB_QUIET);
+	create_object_directory();
+
+	if (get_shared_repository()) {
+		char buf[10];
+		/* We do not spell "group" and such, so that
+		 * the configuration can be read by older version
+		 * of git. Note, we use octal numbers for new share modes,
+		 * and compatibility values for PERM_GROUP and
+		 * PERM_EVERYBODY.
+		 */
+		if (get_shared_repository() < 0)
+			/* force to the mode value */
+			xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
+		else if (get_shared_repository() == PERM_GROUP)
+			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
+		else if (get_shared_repository() == PERM_EVERYBODY)
+			xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
+		else
+			BUG("invalid value for shared_repository");
+		git_config_set("core.sharedrepository", buf);
+		git_config_set("receive.denyNonFastforwards", "true");
+	}
+
+	if (!(flags & INIT_DB_QUIET)) {
+		int len = strlen(git_dir);
+
+		if (reinit)
+			printf(get_shared_repository()
+			       ? _("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(get_shared_repository()
+			       ? _("Initialized empty shared Git repository in %s%s\n")
+			       : _("Initialized empty Git repository in %s%s\n"),
+			       git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+	}
+
+	clear_repository_format(&repo_fmt);
+	free(original_git_dir);
+	return 0;
+}
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..d88bb37
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,215 @@
+#ifndef SETUP_H
+#define SETUP_H
+
+#include "string-list.h"
+
+int is_inside_git_dir(void);
+int is_inside_work_tree(void);
+int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
+int get_common_dir(struct strbuf *sb, const char *gitdir);
+
+/*
+ * Return true if the given path is a git directory; note that this _just_
+ * looks at the directory itself. If you want to know whether "foo/.git"
+ * is a repository, you must feed that path, not just "foo".
+ */
+int is_git_directory(const char *path);
+
+/*
+ * Return 1 if the given path is the root of a git repository or
+ * submodule, else 0. Will not return 1 for bare repositories with the
+ * exception of creating a bare repository in "foo/.git" and calling
+ * is_git_repository("foo").
+ *
+ * If we run into read errors, we err on the side of saying "yes, it is",
+ * as we usually consider sub-repos precious, and would prefer to err on the
+ * side of not disrupting or deleting them.
+ */
+int is_nonbare_repository_dir(struct strbuf *path);
+
+#define READ_GITFILE_ERR_STAT_FAILED 1
+#define READ_GITFILE_ERR_NOT_A_FILE 2
+#define READ_GITFILE_ERR_OPEN_FAILED 3
+#define READ_GITFILE_ERR_READ_FAILED 4
+#define READ_GITFILE_ERR_INVALID_FORMAT 5
+#define READ_GITFILE_ERR_NO_PATH 6
+#define READ_GITFILE_ERR_NOT_A_REPO 7
+#define READ_GITFILE_ERR_TOO_LARGE 8
+void read_gitfile_error_die(int error_code, const char *path, const char *dir);
+const char *read_gitfile_gently(const char *path, int *return_error_code);
+#define read_gitfile(path) read_gitfile_gently((path), NULL)
+const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
+#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
+
+void setup_work_tree(void);
+
+/*
+ * discover_git_directory_reason() is similar to discover_git_directory(),
+ * except it returns an enum value instead. It is important to note that
+ * a zero-valued return here is actually GIT_DIR_NONE, which is different
+ * from discover_git_directory.
+ */
+enum discovery_result {
+	GIT_DIR_EXPLICIT = 1,
+	GIT_DIR_DISCOVERED = 2,
+	GIT_DIR_BARE = 3,
+	/* these are errors */
+	GIT_DIR_HIT_CEILING = -1,
+	GIT_DIR_HIT_MOUNT_POINT = -2,
+	GIT_DIR_INVALID_GITFILE = -3,
+	GIT_DIR_INVALID_OWNERSHIP = -4,
+	GIT_DIR_DISALLOWED_BARE = -5,
+	GIT_DIR_INVALID_FORMAT = -6,
+	GIT_DIR_CWD_FAILURE = -7,
+};
+enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
+						    struct strbuf *gitdir);
+
+/*
+ * Find the commondir and gitdir of the repository that contains the current
+ * working directory, without changing the working directory or other global
+ * state. The result is appended to commondir and gitdir.  If the discovered
+ * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
+ * both have the same result appended to the buffer.  The return value is
+ * either 0 upon success and -1 if no repository was found.
+ */
+static inline int discover_git_directory(struct strbuf *commondir,
+					 struct strbuf *gitdir)
+{
+	if (discover_git_directory_reason(commondir, gitdir) <= 0)
+		return -1;
+	return 0;
+}
+
+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);
+
+int check_filename(const char *prefix, const char *name);
+void verify_filename(const char *prefix,
+		     const char *name,
+		     int diagnose_misspelt_rev);
+void verify_non_filename(const char *prefix, const char *name);
+int path_inside_repo(const char *prefix, const char *path);
+
+void sanitize_stdfds(void);
+int daemonize(void);
+
+/*
+ * GIT_REPO_VERSION is the version we write by default. The
+ * _READ variant is the highest number we know how to
+ * handle.
+ */
+#define GIT_REPO_VERSION 0
+#define GIT_REPO_VERSION_READ 1
+
+/*
+ * You _have_ to initialize a `struct repository_format` using
+ * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
+ */
+struct repository_format {
+	int version;
+	int precious_objects;
+	char *partial_clone; /* value of extensions.partialclone */
+	int worktree_config;
+	int is_bare;
+	int hash_algo;
+	int compat_hash_algo;
+	unsigned int ref_storage_format;
+	int sparse_index;
+	char *work_tree;
+	struct string_list unknown_extensions;
+	struct string_list v1_only_extensions;
+};
+
+/*
+ * Always use this to initialize a `struct repository_format`
+ * to a well-defined, default state before calling
+ * `read_repository()`.
+ */
+#define REPOSITORY_FORMAT_INIT \
+{ \
+	.version = -1, \
+	.is_bare = -1, \
+	.hash_algo = GIT_HASH_SHA1, \
+	.ref_storage_format = REF_STORAGE_FORMAT_FILES, \
+	.unknown_extensions = STRING_LIST_INIT_DUP, \
+	.v1_only_extensions = STRING_LIST_INIT_DUP, \
+}
+
+/*
+ * Read the repository format characteristics from the config file "path" into
+ * "format" struct. Returns the numeric version. On error, or if no version is
+ * found in the configuration, -1 is returned, format->version is set to -1,
+ * and all other fields in the struct are set to the default configuration
+ * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
+ * REPOSITORY_FORMAT_INIT before calling this function.
+ */
+int read_repository_format(struct repository_format *format, const char *path);
+
+/*
+ * Free the memory held onto by `format`, but not the struct itself.
+ * (No need to use this after `read_repository_format()` fails.)
+ */
+void clear_repository_format(struct repository_format *format);
+
+/*
+ * Verify that the repository described by repository_format is something we
+ * can read. If it is, return 0. Otherwise, return -1, and "err" will describe
+ * any errors encountered.
+ */
+int verify_repository_format(const struct repository_format *format,
+			     struct strbuf *err);
+
+/*
+ * Check the repository format version in the path found in get_git_dir(),
+ * 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);
+
+#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,
+	    const char *template_dir, int hash_algo,
+	    unsigned int ref_storage_format,
+	    const char *initial_branch, int init_shared_repository,
+	    unsigned int flags);
+void initialize_repository_version(int hash_algo,
+				   unsigned int ref_storage_format,
+				   int reinit);
+void create_reference_database(unsigned int ref_storage_format,
+			       const char *initial_branch, int quiet);
+
+/*
+ * NOTE NOTE NOTE!!
+ *
+ * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
+ * not be changed. Old repositories have core.sharedrepository written in
+ * numeric format, and therefore these values are preserved for compatibility
+ * reasons.
+ */
+enum sharedrepo {
+	PERM_UMASK          = 0,
+	OLD_PERM_GROUP      = 1,
+	OLD_PERM_EVERYBODY  = 2,
+	PERM_GROUP          = 0660,
+	PERM_EVERYBODY      = 0664
+};
+int git_config_perm(const char *var, const char *value);
+
+struct startup_info {
+	int have_repository;
+	const char *prefix;
+	const char *original_cwd;
+};
+extern struct startup_info *startup_info;
+extern const char *tmp_original_cwd;
+
+#endif /* SETUP_H */
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
index 133496b..f69fd16 100644
--- a/sh-i18n--envsubst.c
+++ b/sh-i18n--envsubst.c
@@ -31,7 +31,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 /* closeout.c - close standard output and standard error
    Copyright (C) 1998-2007 Free Software Foundation, Inc.
@@ -47,7 +47,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
 #include <stdio.h>
diff --git a/sha1/openssl.h b/sha1/openssl.h
new file mode 100644
index 0000000..006c1f4
--- /dev/null
+++ b/sha1/openssl.h
@@ -0,0 +1,49 @@
+/* wrappers for the EVP API of OpenSSL 3+ */
+#ifndef SHA1_OPENSSL_H
+#define SHA1_OPENSSL_H
+#include <openssl/evp.h>
+
+struct openssl_SHA1_CTX {
+	EVP_MD_CTX *ectx;
+};
+
+typedef struct openssl_SHA1_CTX openssl_SHA1_CTX;
+
+static inline void openssl_SHA1_Init(struct openssl_SHA1_CTX *ctx)
+{
+	const EVP_MD *type = EVP_sha1();
+
+	ctx->ectx = EVP_MD_CTX_new();
+	if (!ctx->ectx)
+		die("EVP_MD_CTX_new: out of memory");
+
+	EVP_DigestInit_ex(ctx->ectx, type, NULL);
+}
+
+static inline void openssl_SHA1_Update(struct openssl_SHA1_CTX *ctx,
+					const void *data,
+					size_t len)
+{
+	EVP_DigestUpdate(ctx->ectx, data, len);
+}
+
+static inline void openssl_SHA1_Final(unsigned char *digest,
+				       struct openssl_SHA1_CTX *ctx)
+{
+	EVP_DigestFinal_ex(ctx->ectx, digest, NULL);
+	EVP_MD_CTX_free(ctx->ectx);
+}
+
+static inline void openssl_SHA1_Clone(struct openssl_SHA1_CTX *dst,
+					const struct openssl_SHA1_CTX *src)
+{
+	EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
+}
+
+#define platform_SHA_CTX openssl_SHA1_CTX
+#define platform_SHA1_Init openssl_SHA1_Init
+#define platform_SHA1_Clone openssl_SHA1_Clone
+#define platform_SHA1_Update openssl_SHA1_Update
+#define platform_SHA1_Final openssl_SHA1_Final
+
+#endif /* SHA1_OPENSSL_H */
diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c
index dede2cb..f993ef9 100644
--- a/sha1dc/sha1.c
+++ b/sha1dc/sha1.c
@@ -88,7 +88,7 @@
 /*
  * Should define Big Endian for a whitelist of known processors. See
  * https://sourceforge.net/p/predef/wiki/Endianness/ and
- * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html
+ * https://web.archive.org/web/20140421151132/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html
  */
 #define SHA1DC_BIGENDIAN
 
diff --git a/sha1dc_git.c b/sha1dc_git.c
index 5c300e8..9b675a0 100644
--- a/sha1dc_git.c
+++ b/sha1dc_git.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "sha1dc_git.h"
+#include "hex.h"
 
 #ifdef DC_SHA1_EXTERNAL
 /*
diff --git a/sha256/gcrypt.h b/sha256/gcrypt.h
index 501da5e..17a90f1 100644
--- a/sha256/gcrypt.h
+++ b/sha256/gcrypt.h
@@ -7,22 +7,25 @@
 
 typedef gcry_md_hd_t gcrypt_SHA256_CTX;
 
-inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx)
+static inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx)
 {
-	gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+	gcry_error_t err = gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+	if (err)
+		die("gcry_md_open: %s", gcry_strerror(err));
 }
 
-inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len)
+static inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len)
 {
 	gcry_md_write(*ctx, data, len);
 }
 
-inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
+static inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
 {
 	memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE);
+	gcry_md_close(*ctx);
 }
 
-inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src)
+static inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src)
 {
 	gcry_md_copy(dst, *src);
 }
diff --git a/sha256/openssl.h b/sha256/openssl.h
new file mode 100644
index 0000000..c1083d9
--- /dev/null
+++ b/sha256/openssl.h
@@ -0,0 +1,49 @@
+/* wrappers for the EVP API of OpenSSL 3+ */
+#ifndef SHA256_OPENSSL_H
+#define SHA256_OPENSSL_H
+#include <openssl/evp.h>
+
+struct openssl_SHA256_CTX {
+	EVP_MD_CTX *ectx;
+};
+
+typedef struct openssl_SHA256_CTX openssl_SHA256_CTX;
+
+static inline void openssl_SHA256_Init(struct openssl_SHA256_CTX *ctx)
+{
+	const EVP_MD *type = EVP_sha256();
+
+	ctx->ectx = EVP_MD_CTX_new();
+	if (!ctx->ectx)
+		die("EVP_MD_CTX_new: out of memory");
+
+	EVP_DigestInit_ex(ctx->ectx, type, NULL);
+}
+
+static inline void openssl_SHA256_Update(struct openssl_SHA256_CTX *ctx,
+					const void *data,
+					size_t len)
+{
+	EVP_DigestUpdate(ctx->ectx, data, len);
+}
+
+static inline void openssl_SHA256_Final(unsigned char *digest,
+				       struct openssl_SHA256_CTX *ctx)
+{
+	EVP_DigestFinal_ex(ctx->ectx, digest, NULL);
+	EVP_MD_CTX_free(ctx->ectx);
+}
+
+static inline void openssl_SHA256_Clone(struct openssl_SHA256_CTX *dst,
+					const struct openssl_SHA256_CTX *src)
+{
+	EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
+}
+
+#define platform_SHA256_CTX openssl_SHA256_CTX
+#define platform_SHA256_Init openssl_SHA256_Init
+#define platform_SHA256_Clone openssl_SHA256_Clone
+#define platform_SHA256_Update openssl_SHA256_Update
+#define platform_SHA256_Final openssl_SHA256_Final
+
+#endif /* SHA256_OPENSSL_H */
diff --git a/shallow.c b/shallow.c
index 17f9bcd..7ff50dd 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,20 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex.h"
 #include "repository.h"
 #include "tempfile.h"
 #include "lockfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
-#include "remote.h"
 #include "refs.h"
 #include "oid-array.h"
+#include "path.h"
 #include "diff.h"
 #include "revision.h"
 #include "commit-slab.h"
 #include "list-objects.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "statinfo.h"
+#include "trace.h"
 
 void set_alternate_shallow_file(struct repository *r, const char *path, int override)
 {
@@ -30,12 +33,14 @@
 {
 	struct commit_graft *graft =
 		xmalloc(sizeof(struct commit_graft));
-	struct commit *commit = lookup_commit(the_repository, oid);
+	struct commit *commit = lookup_commit(r, oid);
 
 	oidcpy(&graft->oid, oid);
 	graft->nr_parent = -1;
-	if (commit && commit->object.parsed)
+	if (commit && commit->object.parsed) {
+		free_commit_list(commit->parents);
 		commit->parents = NULL;
+	}
 	return register_commit_graft(r, graft, 0);
 }
 
@@ -247,7 +252,7 @@
 		struct commit *c = p->item;
 		struct commit_list *parent;
 
-		if (parse_commit(c))
+		if (repo_parse_commit(the_repository, c))
 			die("unable to parse commit %s",
 			    oid_to_hex(&c->object.oid));
 
@@ -301,7 +306,7 @@
 	if (graft->nr_parent != -1)
 		return 0;
 	if (data->flags & QUICK) {
-		if (!has_object_file(&graft->oid))
+		if (!repo_has_object_file(the_repository, &graft->oid))
 			return 0;
 	} else if (data->flags & SEEN_ONLY) {
 		struct commit *c = lookup_commit(the_repository, &graft->oid);
@@ -466,7 +471,7 @@
 	ALLOC_ARRAY(info->ours, sa->nr);
 	ALLOC_ARRAY(info->theirs, sa->nr);
 	for (i = 0; i < sa->nr; i++) {
-		if (has_object_file(sa->oid + i)) {
+		if (repo_has_object_file(the_repository, sa->oid + i)) {
 			struct commit_graft *graft;
 			graft = lookup_commit_graft(the_repository,
 						    &sa->oid[i]);
@@ -494,7 +499,7 @@
 	for (i = dst = 0; i < info->nr_theirs; i++) {
 		if (i != dst)
 			info->theirs[dst] = info->theirs[i];
-		if (has_object_file(oid + info->theirs[i]))
+		if (repo_has_object_file(the_repository, oid + info->theirs[i]))
 			dst++;
 	}
 	info->nr_theirs = dst;
@@ -583,7 +588,7 @@
 		if (c->object.flags & BOTTOM)
 			continue;
 
-		if (parse_commit(c))
+		if (repo_parse_commit(the_repository, c))
 			die("unable to parse commit %s",
 			    oid_to_hex(&c->object.oid));
 
@@ -789,12 +794,16 @@
 		if (!*bitmap)
 			continue;
 		for (j = 0; j < bitmap_nr; j++)
-			if (bitmap[0][j] &&
-			    /* Step 7, reachability test at commit level */
-			    !in_merge_bases_many(c, ca.nr, ca.commits)) {
-				update_refstatus(ref_status, info->ref->nr, *bitmap);
-				dst++;
-				break;
+			if (bitmap[0][j]) {
+				/* Step 7, reachability test at commit level */
+				int ret = repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1);
+				if (ret < 0)
+					exit(128);
+				if (!ret) {
+					update_refstatus(ref_status, info->ref->nr, *bitmap);
+					dst++;
+					break;
+				}
 			}
 	}
 	info->nr_ours = dst;
@@ -819,9 +828,13 @@
 			si->nr_commits = ca.nr;
 		}
 
-		si->reachable[c] = in_merge_bases_many(commit,
-						       si->nr_commits,
-						       si->commits);
+		si->reachable[c] = repo_in_merge_bases_many(the_repository,
+							    commit,
+							    si->nr_commits,
+							    si->commits,
+							    1);
+		if (si->reachable[c] < 0)
+			exit(128);
 		si->need_reachability_test[c] = 0;
 	}
 	return si->reachable[c];
diff --git a/shallow.h b/shallow.h
index aba6ff5..e9ca7e4 100644
--- a/shallow.h
+++ b/shallow.h
@@ -6,6 +6,8 @@
 #include "repository.h"
 #include "strbuf.h"
 
+struct oid_array;
+
 void set_alternate_shallow_file(struct repository *r, const char *path, int override);
 int register_shallow(struct repository *r, const struct object_id *oid);
 int unregister_shallow(const struct object_id *oid);
diff --git a/shared.mak b/shared.mak
index aeb80fc..29bebd3 100644
--- a/shared.mak
+++ b/shared.mak
@@ -108,3 +108,11 @@
 define mkdir_p_parent_template
 $(if $(wildcard $(@D)),,$(QUIET_MKDIR_P_PARENT)$(shell mkdir -p $(@D)))
 endef
+
+## Getting sick of writing -L$(SOMELIBDIR) $(CC_LD_DYNPATH)$(SOMELIBDIR)?
+## Write $(call libpath_template,$(SOMELIBDIR)) instead, perhaps?
+## With CC_LD_DYNPATH set to either an empty string or to "-L", the
+## the directory is not shown the second time.
+define libpath_template
+-L$(1) $(if $(filter-out -L,$(CC_LD_DYNPATH)),$(CC_LD_DYNPATH)$(1))
+endef
diff --git a/shell.c b/shell.c
index af0d7c7..2ece8b1 100644
--- a/shell.c
+++ b/shell.c
@@ -1,10 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "quote.h"
 #include "exec-cmd.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "alias.h"
-#include "prompt.h"
 
 #define COMMAND_DIR "git-shell-commands"
 #define HELP_COMMAND COMMAND_DIR "/help"
diff --git a/sideband.c b/sideband.c
index 85bddfd..5d89071 100644
--- a/sideband.c
+++ b/sideband.c
@@ -1,9 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "color.h"
 #include "config.h"
+#include "editor.h"
+#include "gettext.h"
 #include "sideband.h"
 #include "help.h"
 #include "pkt-line.h"
+#include "write-or-die.h"
 
 struct keyword_entry {
 	/*
@@ -66,7 +69,10 @@
  * of the line. This should be called for a single line only, which is
  * passed as the first N characters of the SRC array.
  *
- * NEEDSWORK: use "size_t n" instead for clarity.
+ * It is fine to use "int n" here instead of "size_t n" as all calls to this
+ * function pass an 'int' parameter. Additionally, the buffer involved in
+ * storing these 'int' values takes input from a packet via the pkt-line
+ * interface, which is capable of transferring only 64kB at a time.
  */
 static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 {
@@ -214,7 +220,7 @@
 			}
 
 			strbuf_addch(scratch, *brk);
-			xwrite(2, scratch->buf, scratch->len);
+			write_in_full(2, scratch->buf, scratch->len);
 			strbuf_reset(scratch);
 
 			b = brk + 1;
@@ -241,7 +247,7 @@
 		die("%s", scratch->buf);
 	if (scratch->len) {
 		strbuf_addch(scratch, '\n');
-		xwrite(2, scratch->buf, scratch->len);
+		write_in_full(2, scratch->buf, scratch->len);
 	}
 	strbuf_release(scratch);
 	return 1;
diff --git a/sigchain.c b/sigchain.c
index 022677b..66123bd 100644
--- a/sigchain.c
+++ b/sigchain.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "sigchain.h"
 
 #define SIGCHAIN_MAX_SIGNALS 32
diff --git a/sparse-index.c b/sparse-index.c
index 147a133..e48e40c 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -1,4 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
+#include "gettext.h"
+#include "name-hash.h"
+#include "read-cache-ll.h"
 #include "repository.h"
 #include "sparse-index.h"
 #include "tree.h"
@@ -7,7 +11,7 @@
 #include "cache-tree.h"
 #include "config.h"
 #include "dir.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
 
 struct modify_index_context {
 	struct index_state *write;
@@ -387,7 +391,7 @@
 		strbuf_setlen(&base, 0);
 		strbuf_add(&base, ce->name, strlen(ce->name));
 
-		read_tree_at(istate->repo, tree, &base, &ps,
+		read_tree_at(istate->repo, tree, &base, 0, &ps,
 			     add_path_to_index, &ctx);
 
 		/* free directory entries. full entries are re-used */
@@ -575,8 +579,9 @@
 		replace++;
 		temp = *replace;
 		*replace = '\0';
+		substr_len = replace - path_mutable.buf;
 		if (index_file_exists(istate, path_mutable.buf,
-				      path_mutable.len, icase)) {
+				      substr_len, icase)) {
 			/*
 			 * We found a parent directory in the name-hash
 			 * hashtable, because only sparse directory entries
@@ -589,7 +594,6 @@
 		}
 
 		*replace = temp;
-		substr_len = replace - path_mutable.buf;
 	}
 
 cleanup:
diff --git a/sparse-index.h b/sparse-index.h
index 59a92d8..a16f3e6 100644
--- a/sparse-index.h
+++ b/sparse-index.h
@@ -37,4 +37,6 @@
  */
 void expand_index(struct index_state *istate, struct pattern_list *pl);
 
+void ensure_full_index(struct index_state *istate);
+
 #endif
diff --git a/split-index.c b/split-index.c
index 5d0f047..8c38687 100644
--- a/split-index.c
+++ b/split-index.c
@@ -1,5 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hash.h"
+#include "mem-pool.h"
+#include "read-cache-ll.h"
 #include "split-index.h"
+#include "strbuf.h"
 #include "ewah/ewok.h"
 
 struct split_index *init_split_index(struct index_state *istate)
diff --git a/split-index.h b/split-index.h
index 7a435ca..15a29cd 100644
--- a/split-index.h
+++ b/split-index.h
@@ -1,7 +1,7 @@
 #ifndef SPLIT_INDEX_H
 #define SPLIT_INDEX_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
 struct index_state;
 struct strbuf;
diff --git a/statinfo.c b/statinfo.c
new file mode 100644
index 0000000..3c6bc04
--- /dev/null
+++ b/statinfo.c
@@ -0,0 +1,130 @@
+#include "git-compat-util.h"
+#include "environment.h"
+#include "statinfo.h"
+
+/*
+ * Munge st_size into an unsigned int.
+ */
+static unsigned int munge_st_size(off_t st_size) {
+	unsigned int sd_size = st_size;
+
+	/*
+	 * If the file is an exact multiple of 4 GiB, modify the value so it
+	 * doesn't get marked as racily clean (zero).
+	 */
+	if (!sd_size && st_size)
+		return 0x80000000;
+	else
+		return sd_size;
+}
+
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+	sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+	sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+	sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+	sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+	sd->sd_dev = st->st_dev;
+	sd->sd_ino = st->st_ino;
+	sd->sd_uid = st->st_uid;
+	sd->sd_gid = st->st_gid;
+	sd->sd_size = munge_st_size(st->st_size);
+}
+
+static void set_times(struct stat *st, const struct stat_data *sd)
+{
+	st->st_ctime = sd->sd_ctime.sec;
+	st->st_mtime = sd->sd_mtime.sec;
+#ifdef NO_NSEC
+	; /* nothing */
+#else
+#ifdef USE_ST_TIMESPEC
+	st->st_ctimespec.tv_nsec = sd->sd_ctime.nsec;
+	st->st_mtimespec.tv_nsec = sd->sd_mtime.nsec;
+#else
+	st->st_ctim.tv_nsec = sd->sd_ctime.nsec;
+	st->st_mtim.tv_nsec = sd->sd_mtime.nsec;
+#endif
+#endif
+}
+
+void fake_lstat_data(const struct stat_data *sd, struct stat *st)
+{
+	set_times(st, sd);
+	st->st_dev = sd->sd_dev;
+	st->st_ino = sd->sd_ino;
+	st->st_uid = sd->sd_uid;
+	st->st_gid = sd->sd_gid;
+	st->st_size = sd->sd_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+	int changed = 0;
+
+	if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+		changed |= MTIME_CHANGED;
+	if (trust_ctime && check_stat &&
+	    sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+		changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+	if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+		changed |= MTIME_CHANGED;
+	if (trust_ctime && check_stat &&
+	    sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+		changed |= CTIME_CHANGED;
+#endif
+
+	if (check_stat) {
+		if (sd->sd_uid != (unsigned int) st->st_uid ||
+			sd->sd_gid != (unsigned int) st->st_gid)
+			changed |= OWNER_CHANGED;
+		if (sd->sd_ino != (unsigned int) st->st_ino)
+			changed |= INODE_CHANGED;
+	}
+
+#ifdef USE_STDEV
+	/*
+	 * st_dev breaks on network filesystems where different
+	 * clients will have different views of what "device"
+	 * the filesystem is on
+	 */
+	if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+			changed |= INODE_CHANGED;
+#endif
+
+	if (sd->sd_size != munge_st_size(st->st_size))
+		changed |= DATA_CHANGED;
+
+	return changed;
+}
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+	FREE_AND_NULL(sv->sd);
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+	struct stat st;
+
+	if (stat(path, &st) < 0)
+		return sv->sd == NULL;
+	if (!sv->sd)
+		return 0;
+	return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+	struct stat st;
+
+	if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+		stat_validity_clear(sv);
+	else {
+		if (!sv->sd)
+			CALLOC_ARRAY(sv->sd, 1);
+		fill_stat_data(sv->sd, &st);
+	}
+}
diff --git a/statinfo.h b/statinfo.h
new file mode 100644
index 0000000..5b21a30
--- /dev/null
+++ b/statinfo.h
@@ -0,0 +1,97 @@
+#ifndef STATINFO_H
+#define STATINFO_H
+
+struct index_state;
+
+/*
+ * The "cache_time" is just the low 32 bits of the
+ * time. It doesn't matter if it overflows - we only
+ * check it for equality in the 32 bits we save.
+ */
+struct cache_time {
+	uint32_t sec;
+	uint32_t nsec;
+};
+
+struct stat_data {
+	struct cache_time sd_ctime;
+	struct cache_time sd_mtime;
+	unsigned int sd_dev;
+	unsigned int sd_ino;
+	unsigned int sd_uid;
+	unsigned int sd_gid;
+	unsigned int sd_size;
+};
+
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+	struct stat_data *sd;
+};
+
+#define MTIME_CHANGED	0x0001
+#define CTIME_CHANGED	0x0002
+#define OWNER_CHANGED	0x0004
+#define MODE_CHANGED    0x0008
+#define INODE_CHANGED   0x0010
+#define DATA_CHANGED    0x0020
+#define TYPE_CHANGED    0x0040
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * The inverse of the above.  When we know the cache_entry that
+ * contains sd is up-to-date, but still need to pretend we called
+ * lstat() to learn that fact, this function fills "st" enough to
+ * fool ie_match_stat().
+ */
+void fake_lstat_data(const struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled.  If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+int match_stat_data(const struct stat_data *sd, struct stat *st);
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise.  A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
+#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
+#define DTYPE(de)	((de)->d_type)
+#else
+#undef DT_UNKNOWN
+#undef DT_DIR
+#undef DT_REG
+#undef DT_LNK
+#define DT_UNKNOWN	0
+#define DT_DIR		1
+#define DT_REG		2
+#define DT_LNK		3
+#define DTYPE(de)	DT_UNKNOWN
+#endif
+
+#endif
diff --git a/strbuf.c b/strbuf.c
index c383f41..1492a08 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,5 +1,7 @@
-#include "cache.h"
-#include "refs.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex-ll.h"
+#include "strbuf.h"
 #include "string-list.h"
 #include "utf8.h"
 #include "date.h"
@@ -22,6 +24,17 @@
 			return 0;
 }
 
+int starts_with_mem(const char *str, size_t len, const char *prefix)
+{
+	const char *end = str + len;
+	for (; ; str++, prefix++) {
+		if (!*prefix)
+			return 1;
+		else if (str == end || *str != *prefix)
+			return 0;
+	}
+}
+
 int skip_to_optional_arg_default(const char *str, const char *prefix,
 				 const char **arg, const char *def)
 {
@@ -338,18 +351,17 @@
 }
 
 static void add_lines(struct strbuf *out,
-			const char *prefix1,
-			const char *prefix2,
-			const char *buf, size_t size)
+			const char *prefix,
+			const char *buf, size_t size,
+			int space_after_prefix)
 {
 	while (size) {
-		const char *prefix;
 		const char *next = memchr(buf, '\n', size);
 		next = next ? (next + 1) : (buf + size);
 
-		prefix = ((prefix2 && (buf[0] == '\n' || buf[0] == '\t'))
-			  ? prefix2 : prefix1);
 		strbuf_addstr(out, prefix);
+		if (space_after_prefix && buf[0] != '\n' && buf[0] != '\t')
+			strbuf_addch(out, ' ');
 		strbuf_add(out, buf, next - buf);
 		size -= next - buf;
 		buf = next;
@@ -357,19 +369,14 @@
 	strbuf_complete_line(out);
 }
 
-void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size)
+void strbuf_add_commented_lines(struct strbuf *out, const char *buf,
+				size_t size, const char *comment_prefix)
 {
-	static char prefix1[3];
-	static char prefix2[2];
-
-	if (prefix1[0] != comment_line_char) {
-		xsnprintf(prefix1, sizeof(prefix1), "%c ", comment_line_char);
-		xsnprintf(prefix2, sizeof(prefix2), "%c", comment_line_char);
-	}
-	add_lines(out, prefix1, prefix2, buf, size);
+	add_lines(out, comment_prefix, buf, size, 1);
 }
 
-void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...)
+void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix,
+			   const char *fmt, ...)
 {
 	va_list params;
 	struct strbuf buf = STRBUF_INIT;
@@ -379,7 +386,7 @@
 	strbuf_vaddf(&buf, fmt, params);
 	va_end(params);
 
-	strbuf_add_commented_lines(sb, buf.buf, buf.len);
+	strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_prefix);
 	if (incomplete_line)
 		sb->buf[--sb->len] = '\0';
 
@@ -407,36 +414,19 @@
 	strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
-		   void *context)
+int strbuf_expand_step(struct strbuf *sb, const char **formatp)
 {
-	for (;;) {
-		const char *percent;
-		size_t consumed;
+	const char *format = *formatp;
+	const char *percent = strchrnul(format, '%');
 
-		percent = strchrnul(format, '%');
-		strbuf_add(sb, format, percent - format);
-		if (!*percent)
-			break;
-		format = percent + 1;
-
-		if (*format == '%') {
-			strbuf_addch(sb, '%');
-			format++;
-			continue;
-		}
-
-		consumed = fn(sb, format, context);
-		if (consumed)
-			format += consumed;
-		else
-			strbuf_addch(sb, '%');
-	}
+	strbuf_add(sb, format, percent - format);
+	if (!*percent)
+		return 0;
+	*formatp = percent + 1;
+	return 1;
 }
 
-size_t strbuf_expand_literal_cb(struct strbuf *sb,
-				const char *placeholder,
-				void *context UNUSED)
+size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder)
 {
 	int ch;
 
@@ -455,20 +445,24 @@
 	return 0;
 }
 
-size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
-		void *context)
+void strbuf_expand_bad_format(const char *format, const char *command)
 {
-	struct strbuf_expand_dict_entry *e = context;
-	size_t len;
+	const char *end;
 
-	for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
-		if (!strncmp(placeholder, e->placeholder, len)) {
-			if (e->value)
-				strbuf_addstr(sb, e->value);
-			return len;
-		}
-	}
-	return 0;
+	if (*format != '(')
+		/* TRANSLATORS: The first %s is a command like "ls-tree". */
+		die(_("bad %s format: element '%s' does not start with '('"),
+		    command, format);
+
+	end = strchr(format + 1, ')');
+	if (!end)
+		/* TRANSLATORS: The first %s is a command like "ls-tree". */
+		die(_("bad %s format: element '%s' does not end in ')'"),
+		    command, format);
+
+	/* TRANSLATORS: %s is a command like "ls-tree". */
+	die(_("bad %s format: %%%.*s"),
+	    command, (int)(end - format + 1), format);
 }
 
 void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
@@ -713,11 +707,11 @@
 	return 0;
 }
 
-int strbuf_getline(struct strbuf *sb, FILE *fp)
+int strbuf_getdelim_strip_crlf(struct strbuf *sb, FILE *fp, int term)
 {
-	if (strbuf_getwholeline(sb, fp, '\n'))
+	if (strbuf_getwholeline(sb, fp, term))
 		return EOF;
-	if (sb->buf[sb->len - 1] == '\n') {
+	if (term == '\n' && sb->buf[sb->len - 1] == '\n') {
 		strbuf_setlen(sb, sb->len - 1);
 		if (sb->len && sb->buf[sb->len - 1] == '\r')
 			strbuf_setlen(sb, sb->len - 1);
@@ -725,6 +719,11 @@
 	return 0;
 }
 
+int strbuf_getline(struct strbuf *sb, FILE *fp)
+{
+	return strbuf_getdelim_strip_crlf(sb, fp, '\n');
+}
+
 int strbuf_getline_lf(struct strbuf *sb, FILE *fp)
 {
 	return strbuf_getdelim(sb, fp, '\n');
@@ -774,7 +773,7 @@
 void strbuf_add_lines(struct strbuf *out, const char *prefix,
 		      const char *buf, size_t size)
 {
-	add_lines(out, prefix, NULL, buf, size);
+	add_lines(out, prefix, buf, size, 0);
 }
 
 void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
@@ -803,25 +802,6 @@
 	}
 }
 
-int is_rfc3986_reserved_or_unreserved(char ch)
-{
-	if (is_rfc3986_unreserved(ch))
-		return 1;
-	switch (ch) {
-		case '!': case '*': case '\'': case '(': case ')': case ';':
-		case ':': case '@': case '&': case '=': case '+': case '$':
-		case ',': case '/': case '?': case '#': case '[': case ']':
-			return 1;
-	}
-	return 0;
-}
-
-int is_rfc3986_unreserved(char ch)
-{
-	return isalnum(ch) ||
-		ch == '-' || ch == '_' || ch == '.' || ch == '~';
-}
-
 static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
 				 char_predicate allow_unencoded_fn)
 {
@@ -892,42 +872,6 @@
 	strbuf_humanise(buf, bytes, 1);
 }
 
-void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
-{
-	if (!*path)
-		die("The empty string is not a valid path");
-	if (!is_absolute_path(path)) {
-		struct stat cwd_stat, pwd_stat;
-		size_t orig_len = sb->len;
-		char *cwd = xgetcwd();
-		char *pwd = getenv("PWD");
-		if (pwd && strcmp(pwd, cwd) &&
-		    !stat(cwd, &cwd_stat) &&
-		    (cwd_stat.st_dev || cwd_stat.st_ino) &&
-		    !stat(pwd, &pwd_stat) &&
-		    pwd_stat.st_dev == cwd_stat.st_dev &&
-		    pwd_stat.st_ino == cwd_stat.st_ino)
-			strbuf_addstr(sb, pwd);
-		else
-			strbuf_addstr(sb, cwd);
-		if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
-			strbuf_addch(sb, '/');
-		free(cwd);
-	}
-	strbuf_addstr(sb, path);
-}
-
-void strbuf_add_real_path(struct strbuf *sb, const char *path)
-{
-	if (sb->len) {
-		struct strbuf resolved = STRBUF_INIT;
-		strbuf_realpath(&resolved, path, 1);
-		strbuf_addbuf(sb, &resolved);
-		strbuf_release(&resolved);
-	} else
-		strbuf_realpath(sb, path, 1);
-}
-
 int printf_ln(const char *fmt, ...)
 {
 	int ret;
@@ -1014,37 +958,20 @@
 	 * we want for %z, but the computation for %s has to convert to number
 	 * of seconds.
 	 */
-	for (;;) {
-		const char *percent = strchrnul(fmt, '%');
-		strbuf_add(&munged_fmt, fmt, percent - fmt);
-		if (!*percent)
-			break;
-		fmt = percent + 1;
-		switch (*fmt) {
-		case '%':
+	while (strbuf_expand_step(&munged_fmt, &fmt)) {
+		if (skip_prefix(fmt, "%", &fmt))
 			strbuf_addstr(&munged_fmt, "%%");
-			fmt++;
-			break;
-		case 's':
+		else if (skip_prefix(fmt, "s", &fmt))
 			strbuf_addf(&munged_fmt, "%"PRItime,
 				    (timestamp_t)tm_to_time_t(tm) -
 				    3600 * (tz_offset / 100) -
 				    60 * (tz_offset % 100));
-			fmt++;
-			break;
-		case 'z':
+		else if (skip_prefix(fmt, "z", &fmt))
 			strbuf_addf(&munged_fmt, "%+05d", tz_offset);
-			fmt++;
-			break;
-		case 'Z':
-			if (suppress_tz_name) {
-				fmt++;
-				break;
-			}
-			/* FALLTHROUGH */
-		default:
+		else if (suppress_tz_name && skip_prefix(fmt, "Z", &fmt))
+			; /* nothing */
+		else
 			strbuf_addch(&munged_fmt, '%');
-		}
 	}
 	fmt = munged_fmt.buf;
 
@@ -1072,21 +999,6 @@
 	strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
-				   const struct object_id *oid, int abbrev_len)
-{
-	int r;
-	strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
-	r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
-	strbuf_setlen(sb, sb->len + r);
-}
-
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
-			      int abbrev_len)
-{
-	strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
-}
-
 /*
  * Returns the length of a line, without trailing spaces.
  *
@@ -1116,10 +1028,10 @@
  *
  * If last line does not have a newline at the end, one is added.
  *
- * Enable skip_comments to skip every line starting with comment
- * character.
+ * Pass a non-NULL comment_prefix to skip every line starting
+ * with it.
  */
-void strbuf_stripspace(struct strbuf *sb, int skip_comments)
+void strbuf_stripspace(struct strbuf *sb, const char *comment_prefix)
 {
 	size_t empties = 0;
 	size_t i, j, len, newlen;
@@ -1132,7 +1044,8 @@
 		eol = memchr(sb->buf + i, '\n', sb->len - i);
 		len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
 
-		if (skip_comments && len && sb->buf[i] == comment_line_char) {
+		if (comment_prefix && len &&
+		    starts_with(sb->buf + i, comment_prefix)) {
 			newlen = 0;
 			continue;
 		}
@@ -1153,54 +1066,6 @@
 	strbuf_setlen(sb, j);
 }
 
-int strbuf_normalize_path(struct strbuf *src)
-{
-	struct strbuf dst = STRBUF_INIT;
-
-	strbuf_grow(&dst, src->len);
-	if (normalize_path_copy(dst.buf, src->buf) < 0) {
-		strbuf_release(&dst);
-		return -1;
-	}
-
-	/*
-	 * normalize_path does not tell us the new length, so we have to
-	 * compute it by looking for the new NUL it placed
-	 */
-	strbuf_setlen(&dst, strlen(dst.buf));
-	strbuf_swap(src, &dst);
-	strbuf_release(&dst);
-	return 0;
-}
-
-int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
-			      const char *const *env)
-{
-	char *path2 = NULL;
-	int fd, res = 0;
-
-	if (!is_absolute_path(path))
-		path = path2 = xstrdup(git_path("%s", path));
-
-	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-	if (fd < 0)
-		res = error_errno(_("could not open '%s' for writing"), path);
-	else if (write_in_full(fd, buffer->buf, buffer->len) < 0) {
-		res = error_errno(_("could not write to '%s'"), path);
-		close(fd);
-	} else if (close(fd) < 0)
-		res = error_errno(_("could not close '%s'"), path);
-	else {
-		strbuf_reset(buffer);
-		if (launch_editor(path, buffer, env) < 0)
-			res = error_errno(_("could not edit '%s'"), path);
-		unlink(path);
-	}
-
-	free(path2);
-	return res;
-}
-
 void strbuf_strip_file_from_path(struct strbuf *sb)
 {
 	char *path_sep = find_last_dir_sep(sb->buf);
diff --git a/strbuf.h b/strbuf.h
index f6dbb96..97fa4a3 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -1,12 +1,20 @@
 #ifndef STRBUF_H
 #define STRBUF_H
 
+/*
+ * NOTE FOR STRBUF DEVELOPERS
+ *
+ * strbuf is a low-level primitive; as such it should interact only
+ * with other low-level primitives. Do not introduce new functions
+ * which interact with higher-level APIs.
+ */
+
 struct string_list;
 
 /**
- * strbuf's are meant to be used with all the usual C string and memory
+ * strbufs are meant to be used with all the usual C string and memory
  * APIs. Given that the length of the buffer is known, it's often better to
- * use the mem* functions than a str* one (memchr vs. strchr e.g.).
+ * use the mem* functions than a str* one (e.g., memchr vs. strchr).
  * Though, one has to be careful about the fact that str* functions often
  * stop on NULs and that strbufs may have embedded NULs.
  *
@@ -16,7 +24,7 @@
  * strbufs have some invariants that are very important to keep in mind:
  *
  *  - The `buf` member is never NULL, so it can be used in any usual C
- *    string operations safely. strbuf's _have_ to be initialized either by
+ *    string operations safely. strbufs _have_ to be initialized either by
  *    `strbuf_init()` or by `= STRBUF_INIT` before the invariants, though.
  *
  *    Do *not* assume anything on what `buf` really is (e.g. if it is
@@ -29,7 +37,7 @@
  *
  *  - The `buf` member is a byte array that has at least `len + 1` bytes
  *    allocated. The extra byte is used to store a `'\0'`, allowing the
- *    `buf` member to be a valid C-string. Every strbuf function ensure this
+ *    `buf` member to be a valid C-string. All strbuf functions ensure this
  *    invariant is preserved.
  *
  *    NOTE: It is OK to "play" with the buffer directly if you work it this
@@ -72,10 +80,6 @@
 extern char strbuf_slopbuf[];
 #define STRBUF_INIT  { .buf = strbuf_slopbuf }
 
-/*
- * Predeclare this here, since cache.h includes this file before it defines the
- * struct.
- */
 struct object_id;
 
 /**
@@ -283,7 +287,8 @@
  * by a comment character and a blank.
  */
 void strbuf_add_commented_lines(struct strbuf *out,
-				const char *buf, size_t size);
+				const char *buf, size_t size,
+				const char *comment_prefix);
 
 
 /**
@@ -318,58 +323,24 @@
 			     const char **argv, char delim);
 
 /**
- * This function can be used to expand a format string containing
- * placeholders. To that end, it parses the string and calls the specified
- * function for every percent sign found.
- *
- * The callback function is given a pointer to the character after the `%`
- * and a pointer to the struct strbuf.  It is expected to add the expanded
- * version of the placeholder to the strbuf, e.g. to add a newline
- * character if the letter `n` appears after a `%`.  The function returns
- * the length of the placeholder recognized and `strbuf_expand()` skips
- * over it.
- *
- * The format `%%` is automatically expanded to a single `%` as a quoting
- * mechanism; callers do not need to handle the `%` placeholder themselves,
- * and the callback function will not be invoked for this placeholder.
- *
- * All other characters (non-percent and not skipped ones) are copied
- * verbatim to the strbuf.  If the callback returned zero, meaning that the
- * placeholder is unknown, then the percent sign is copied, too.
- *
- * In order to facilitate caching and to make it possible to give
- * parameters to the callback, `strbuf_expand()` passes a context
- * pointer with any kind of data.
+ * Used with `strbuf_expand_step` to expand the literals %n and %x
+ * followed by two hexadecimal digits. Returns the number of recognized
+ * characters.
  */
-typedef size_t (*expand_fn_t) (struct strbuf *sb,
-			       const char *placeholder,
-			       void *context);
-void strbuf_expand(struct strbuf *sb,
-		   const char *format,
-		   expand_fn_t fn,
-		   void *context);
+size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder);
 
 /**
- * Used as callback for `strbuf_expand` to only expand literals
- * (i.e. %n and %xNN). The context argument is ignored.
+ * If the string pointed to by `formatp` contains a percent sign ("%"),
+ * advance it to point to the character following the next one and
+ * return 1, otherwise return 0.  Append the substring before that
+ * percent sign to `sb`, or the whole string if there is none.
  */
-size_t strbuf_expand_literal_cb(struct strbuf *sb,
-				const char *placeholder,
-				void *context);
+int strbuf_expand_step(struct strbuf *sb, const char **formatp);
 
 /**
- * Used as callback for `strbuf_expand()`, expects an array of
- * struct strbuf_expand_dict_entry as context, i.e. pairs of
- * placeholder and replacement string.  The array needs to be
- * terminated by an entry with placeholder set to NULL.
+ * Used with `strbuf_expand_step` to report unknown placeholders.
  */
-struct strbuf_expand_dict_entry {
-	const char *placeholder;
-	const char *value;
-};
-size_t strbuf_expand_dict_cb(struct strbuf *sb,
-			     const char *placeholder,
-			     void *context);
+void strbuf_expand_bad_format(const char *format, const char *command);
 
 /**
  * Append the contents of one strbuf to another, quoting any
@@ -412,8 +383,8 @@
  * Add a formatted string prepended by a comment character and a
  * blank to the buffer.
  */
-__attribute__((format (printf, 2, 3)))
-void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
+void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix, const char *fmt, ...);
 
 __attribute__((format (printf,2,0)))
 void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
@@ -476,6 +447,18 @@
 ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 
 /**
+ * Read from a FILE * until the specified terminator is encountered,
+ * overwriting the existing contents of the strbuf.
+ *
+ * Reading stops after the terminator or at EOF.  The terminator is
+ * removed from the buffer before returning.  If the terminator is LF
+ * and if it is preceded by a CR, then the whole CRLF is stripped.
+ * Returns 0 unless there was nothing left before EOF, in which case
+ * it returns `EOF`.
+ */
+int strbuf_getdelim_strip_crlf(struct strbuf *sb, FILE *fp, int term);
+
+/**
  * Read a line from a FILE *, overwriting the existing contents of
  * the strbuf.  The strbuf_getline*() family of functions share
  * this signature, but have different line termination conventions.
@@ -528,28 +511,6 @@
 int strbuf_getcwd(struct strbuf *sb);
 
 /**
- * Add a path to a buffer, converting a relative path to an
- * absolute one in the process.  Symbolic links are not
- * resolved.
- */
-void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
-
-/**
- * Canonize `path` (make it absolute, resolve symlinks, remove extra
- * slashes) and append it to `sb`.  Die with an informative error
- * message if there is a problem.
- *
- * The directory part of `path` (i.e., everything up to the last
- * dir_sep) must denote a valid, existing directory, but the last
- * component need not exist.
- *
- * Callers that don't mind links should use the more lightweight
- * strbuf_add_absolute_path() instead.
- */
-void strbuf_add_real_path(struct strbuf *sb, const char *path);
-
-
-/**
  * Normalize in-place the path contained in the strbuf. See
  * normalize_path_copy() for details. If an error occurs, the contents of "sb"
  * are left untouched, and -1 is returned.
@@ -557,10 +518,11 @@
 int strbuf_normalize_path(struct strbuf *sb);
 
 /**
- * Strip whitespace from a buffer. The second parameter controls if
- * comments are considered contents to be removed or not.
+ * Strip whitespace from a buffer. If comment_prefix is non-NULL,
+ * then lines beginning with that character are considered comments,
+ * thus removed.
  */
-void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+void strbuf_stripspace(struct strbuf *buf, const char *comment_prefix);
 
 static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 {
@@ -630,40 +592,6 @@
  */
 void strbuf_list_free(struct strbuf **list);
 
-/**
- * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
- * the strbuf `sb`.
- */
-struct repository;
-void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
-				   const struct object_id *oid, int abbrev_len);
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
-			      int abbrev_len);
-
-/**
- * Launch the user preferred editor to edit a file and fill the buffer
- * with the file's contents upon the user completing their editing. The
- * third argument can be used to set the environment which the editor is
- * run in. If the buffer is NULL the editor is launched as usual but the
- * file's contents are not read into the buffer upon completion.
- */
-int launch_editor(const char *path, struct strbuf *buffer,
-		  const char *const *env);
-
-int launch_sequence_editor(const char *path, struct strbuf *buffer,
-			   const char *const *env);
-
-/*
- * In contrast to `launch_editor()`, this function writes out the contents
- * of the specified file first, then clears the `buffer`, then launches
- * the editor and reads back in the file contents into the `buffer`.
- * Finally, it deletes the temporary file.
- *
- * If `path` is relative, it refers to a file in the `.git` directory.
- */
-int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
-			      const char *const *env);
-
 /*
  * Remove the filename from the provided path string. If the path
  * contains a trailing separator, then the path is considered a directory
@@ -706,14 +634,14 @@
 
 /*
  * Copy "name" to "sb", expanding any special @-marks as handled by
- * interpret_branch_name(). The result is a non-qualified branch name
+ * repo_interpret_branch_name(). The result is a non-qualified branch name
  * (so "foo" or "origin/master" instead of "refs/heads/foo" or
  * "refs/remotes/origin/master").
  *
  * Note that the resulting name may not be a syntactically valid refname.
  *
  * If "allowed" is non-zero, restrict the set of allowed expansions. See
- * interpret_branch_name() for details.
+ * repo_interpret_branch_name() for details.
  */
 void strbuf_branchname(struct strbuf *sb, const char *name,
 		       unsigned allowed);
@@ -728,9 +656,6 @@
 
 typedef int (*char_predicate)(char ch);
 
-int is_rfc3986_unreserved(char ch);
-int is_rfc3986_reserved_or_unreserved(char ch);
-
 void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
 			     char_predicate allow_unencoded_fn);
 
@@ -751,4 +676,37 @@
 __attribute__((format (printf, 1, 2)))
 char *xstrfmt(const char *fmt, ...);
 
+int starts_with(const char *str, const char *prefix);
+int istarts_with(const char *str, const char *prefix);
+int starts_with_mem(const char *str, size_t len, const char *prefix);
+
+/*
+ * If the string "str" is the same as the string in "prefix", then the "arg"
+ * parameter is set to the "def" parameter and 1 is returned.
+ * If the string "str" begins with the string found in "prefix" and then a
+ * "=" sign, then the "arg" parameter is set to "str + strlen(prefix) + 1"
+ * (i.e., to the point in the string right after the prefix and the "=" sign),
+ * and 1 is returned.
+ *
+ * Otherwise, return 0 and leave "arg" untouched.
+ *
+ * When we accept both a "--key" and a "--key=<val>" option, this function
+ * can be used instead of !strcmp(arg, "--key") and then
+ * skip_prefix(arg, "--key=", &arg) to parse such an option.
+ */
+int skip_to_optional_arg_default(const char *str, const char *prefix,
+				 const char **arg, const char *def);
+
+static inline int skip_to_optional_arg(const char *str, const char *prefix,
+				       const char **arg)
+{
+	return skip_to_optional_arg_default(str, prefix, arg, "");
+}
+
+static inline int ends_with(const char *str, const char *suffix)
+{
+	size_t len;
+	return strip_suffix(str, suffix, &len);
+}
+
 #endif /* STRBUF_H */
diff --git a/streaming.c b/streaming.c
index 27841dc..10adf62 100644
--- a/streaming.c
+++ b/streaming.c
@@ -1,10 +1,13 @@
 /*
  * Copyright (c) 2011, Google Inc.
  */
-#include "cache.h"
+#include "git-compat-util.h"
+#include "convert.h"
+#include "environment.h"
 #include "streaming.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-store-ll.h"
 #include "replace-object.h"
 #include "packfile.h"
 
diff --git a/streaming.h b/streaming.h
index 5e4e6ac..bd27f59 100644
--- a/streaming.h
+++ b/streaming.h
@@ -3,10 +3,12 @@
  */
 #ifndef STREAMING_H
 #define STREAMING_H 1
-#include "cache.h"
+
+#include "object.h"
 
 /* opaque */
 struct git_istream;
+struct stream_filter;
 
 struct git_istream *open_istream(struct repository *, const struct object_id *,
 				 enum object_type *, unsigned long *,
diff --git a/string-list.c b/string-list.c
index 42bacae..954569f 100644
--- a/string-list.c
+++ b/string-list.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "string-list.h"
 
 void string_list_init_nodup(struct string_list *list)
@@ -202,6 +202,15 @@
 	list->nr = list->alloc = 0;
 }
 
+void string_list_setlen(struct string_list *list, size_t nr)
+{
+	if (list->strdup_strings)
+		BUG("cannot setlen a string_list which owns its entries");
+	if (nr > list->nr)
+		BUG("cannot grow a string_list with setlen");
+	list->nr = nr;
+}
+
 struct string_list_item *string_list_append_nodup(struct string_list *list,
 						  char *string)
 {
@@ -300,7 +309,7 @@
 }
 
 int string_list_split_in_place(struct string_list *list, char *string,
-			       int delim, int maxsplit)
+			       const char *delim, int maxsplit)
 {
 	int count = 0;
 	char *p = string, *end;
@@ -314,7 +323,7 @@
 			string_list_append(list, p);
 			return count;
 		}
-		end = strchr(p, delim);
+		end = strpbrk(p, delim);
 		if (end) {
 			*end = '\0';
 			string_list_append(list, p);
diff --git a/string-list.h b/string-list.h
index c7b0d5d..122b318 100644
--- a/string-list.h
+++ b/string-list.h
@@ -134,6 +134,16 @@
 /** Call a custom clear function on each util pointer */
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
+/*
+ * Set the length of a string_list to `nr`, provided that (a) `list`
+ * does not own its own storage, and (b) that `nr` is no larger than
+ * `list->nr`.
+ *
+ * Useful when "shrinking" `list` to write over existing entries that
+ * are no longer used without reallocating.
+ */
+void string_list_setlen(struct string_list *list, size_t nr);
+
 /**
  * Apply `func` to each item. If `func` returns nonzero, the
  * iteration aborts and the return value is propagated.
@@ -270,5 +280,5 @@
  * list->strdup_strings must *not* be set.
  */
 int string_list_split_in_place(struct string_list *list, char *string,
-			       int delim, int maxsplit);
+			       const char *delim, int maxsplit);
 #endif /* STRING_LIST_H */
diff --git a/strvec.c b/strvec.c
index 61a76ce..178f4f3 100644
--- a/strvec.c
+++ b/strvec.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "strvec.h"
 #include "strbuf.h"
 
diff --git a/strvec.h b/strvec.h
index 9f55c87..4715d3e 100644
--- a/strvec.h
+++ b/strvec.h
@@ -4,8 +4,8 @@
 /**
  * The strvec API allows one to dynamically build and store
  * NULL-terminated arrays of strings. A strvec maintains the invariant that the
- * `items` member always points to a non-NULL array, and that the array is
- * always NULL-terminated at the element pointed to by `items[nr]`. This
+ * `v` member always points to a non-NULL array, and that the array is
+ * always NULL-terminated at the element pointed to by `v[nr]`. This
  * makes the result suitable for passing to functions expecting to receive
  * argv from main().
  *
@@ -22,7 +22,7 @@
 
 /**
  * A single array. This should be initialized by assignment from
- * `STRVEC_INIT`, or by calling `strvec_init`. The `items`
+ * `STRVEC_INIT`, or by calling `strvec_init`. The `v`
  * member contains the actual array; the `nr` member contains the
  * number of elements in the array, not including the terminating
  * NULL.
@@ -80,7 +80,7 @@
 void strvec_clear(struct strvec *);
 
 /**
- * Disconnect the `items` member from the `strvec` struct and
+ * Disconnect the `v` member from the `strvec` struct and
  * return it. The caller is responsible for freeing the memory used
  * by the array, and by the strings it references. After detaching,
  * the `strvec` is in a reinitialized state and can be pushed
diff --git a/sub-process.c b/sub-process.c
index 6d42322..1daf5a9 100644
--- a/sub-process.c
+++ b/sub-process.c
@@ -1,6 +1,7 @@
 /*
  * Generic implementation of background process infrastructure.
  */
+#include "git-compat-util.h"
 #include "sub-process.h"
 #include "sigchain.h"
 #include "pkt-line.h"
diff --git a/sub-process.h b/sub-process.h
index e85f21f..6a61638 100644
--- a/sub-process.h
+++ b/sub-process.h
@@ -1,7 +1,6 @@
 #ifndef SUBPROCESS_H
 #define SUBPROCESS_H
 
-#include "git-compat-util.h"
 #include "hashmap.h"
 #include "run-command.h"
 
diff --git a/submodule-config.c b/submodule-config.c
index 4dc61b3..11428b4 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -1,13 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "path.h"
 #include "repository.h"
 #include "config.h"
 #include "submodule-config.h"
 #include "submodule.h"
 #include "strbuf.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "parse-options.h"
+#include "thread-utils.h"
 #include "tree-walk.h"
+#include "url.h"
+#include "urlmatch.h"
 
 /*
  * submodule cache lookup structure
@@ -222,6 +230,144 @@
 	return 0;
 }
 
+static int starts_with_dot_slash(const char *const path)
+{
+	return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
+				PATH_MATCH_XPLATFORM);
+}
+
+static int starts_with_dot_dot_slash(const char *const path)
+{
+	return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
+				PATH_MATCH_XPLATFORM);
+}
+
+static int submodule_url_is_relative(const char *url)
+{
+	return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
+}
+
+/*
+ * Count directory components that a relative submodule URL should chop
+ * from the remote_url it is to be resolved against.
+ *
+ * In other words, this counts "../" components at the start of a
+ * submodule URL.
+ *
+ * Returns the number of directory components to chop and writes a
+ * pointer to the next character of url after all leading "./" and
+ * "../" components to out.
+ */
+static int count_leading_dotdots(const char *url, const char **out)
+{
+	int result = 0;
+	while (1) {
+		if (starts_with_dot_dot_slash(url)) {
+			result++;
+			url += strlen("../");
+			continue;
+		}
+		if (starts_with_dot_slash(url)) {
+			url += strlen("./");
+			continue;
+		}
+		*out = url;
+		return result;
+	}
+}
+/*
+ * Check whether a transport is implemented by git-remote-curl.
+ *
+ * If it is, returns 1 and writes the URL that would be passed to
+ * git-remote-curl to the "out" parameter.
+ *
+ * Otherwise, returns 0 and leaves "out" untouched.
+ *
+ * Examples:
+ *   http::https://example.com/repo.git -> 1, https://example.com/repo.git
+ *   https://example.com/repo.git -> 1, https://example.com/repo.git
+ *   git://example.com/repo.git -> 0
+ *
+ * This is for use in checking for previously exploitable bugs that
+ * required a submodule URL to be passed to git-remote-curl.
+ */
+static int url_to_curl_url(const char *url, const char **out)
+{
+	/*
+	 * We don't need to check for case-aliases, "http.exe", and so
+	 * on because in the default configuration, is_transport_allowed
+	 * prevents URLs with those schemes from being cloned
+	 * automatically.
+	 */
+	if (skip_prefix(url, "http::", out) ||
+	    skip_prefix(url, "https::", out) ||
+	    skip_prefix(url, "ftp::", out) ||
+	    skip_prefix(url, "ftps::", out))
+		return 1;
+	if (starts_with(url, "http://") ||
+	    starts_with(url, "https://") ||
+	    starts_with(url, "ftp://") ||
+	    starts_with(url, "ftps://")) {
+		*out = url;
+		return 1;
+	}
+	return 0;
+}
+
+int check_submodule_url(const char *url)
+{
+	const char *curl_url;
+
+	if (looks_like_command_line_option(url))
+		return -1;
+
+	if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
+		char *decoded;
+		const char *next;
+		int has_nl;
+
+		/*
+		 * This could be appended to an http URL and url-decoded;
+		 * check for malicious characters.
+		 */
+		decoded = url_decode(url);
+		has_nl = !!strchr(decoded, '\n');
+
+		free(decoded);
+		if (has_nl)
+			return -1;
+
+		/*
+		 * URLs which escape their root via "../" can overwrite
+		 * the host field and previous components, resolving to
+		 * URLs like https::example.com/submodule.git and
+		 * https:///example.com/submodule.git that were
+		 * susceptible to CVE-2020-11008.
+		 */
+		if (count_leading_dotdots(url, &next) > 0 &&
+		    (*next == ':' || *next == '/'))
+			return -1;
+	}
+
+	else if (url_to_curl_url(url, &curl_url)) {
+		int ret = 0;
+		char *normalized = url_normalize(curl_url, NULL);
+		if (normalized) {
+			char *decoded = url_decode(normalized);
+			if (strchr(decoded, '\n'))
+				ret = -1;
+			free(normalized);
+			free(decoded);
+		} else {
+			ret = -1;
+		}
+
+		return ret;
+	}
+
+	return 0;
+}
+
 static int name_and_item_from_var(const char *var, struct strbuf *name,
 				  struct strbuf *item)
 {
@@ -298,9 +444,10 @@
 	}
 }
 
-int parse_submodule_fetchjobs(const char *var, const char *value)
+int parse_submodule_fetchjobs(const char *var, const char *value,
+			      const struct key_value_info *kvi)
 {
-	int fetchjobs = git_config_int(var, value);
+	int fetchjobs = git_config_int(var, value, kvi);
 	if (fetchjobs < 0)
 		die(_("negative values not allowed for submodule.fetchJobs"));
 	if (!fetchjobs)
@@ -420,7 +567,8 @@
  * config store (.git/config, etc).  Callers are responsible for
  * checking for overrides in the main config store when appropriate.
  */
-static int parse_config(const char *var, const char *value, void *data)
+static int parse_config(const char *var, const char *value,
+			const struct config_context *ctx UNUSED, void *data)
 {
 	struct parse_config_parameter *me = data;
 	struct submodule *submodule;
@@ -508,7 +656,9 @@
 			submodule->recommend_shallow =
 				git_config_bool(var, value);
 	} else if (!strcmp(item.buf, "branch")) {
-		if (!me->overwrite && submodule->branch)
+		if (!value)
+			ret = config_error_nonbool(var);
+		else if (!me->overwrite && submodule->branch)
 			warn_multiple_config(me->treeish_name, submodule->name,
 					     "branch");
 		else {
@@ -535,7 +685,7 @@
 	}
 
 	strbuf_addf(rev, "%s:.gitmodules", oid_to_hex(treeish_name));
-	if (get_oid(rev->buf, gitmodules_oid) >= 0)
+	if (repo_get_oid(the_repository, rev->buf, gitmodules_oid) >= 0)
 		ret = 1;
 
 	return ret;
@@ -588,7 +738,8 @@
 	if (submodule)
 		goto out;
 
-	config = read_object_file(&oid, &type, &config_size);
+	config = repo_read_object_file(the_repository, &oid, &type,
+				       &config_size);
 	if (!config || type != OBJ_BLOB)
 		goto out;
 
@@ -598,7 +749,7 @@
 	parameter.gitmodules_oid = &oid;
 	parameter.overwrite = 0;
 	git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
-			config, config_size, &parameter, NULL);
+			    config, config_size, &parameter, CONFIG_SCOPE_UNKNOWN, NULL);
 	strbuf_release(&rev);
 	free(config);
 
@@ -652,7 +803,6 @@
 			config_source.file = file;
 		} else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||
 			   repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {
-			config_source.repo = repo;
 			config_source.blob = oidstr = xstrdup(oid_to_hex(&oid));
 			if (repo != the_repository)
 				add_submodule_odb_by_path(repo->objects->odb->path);
@@ -660,7 +810,7 @@
 			goto out;
 		}
 
-		config_with_options(fn, data, &config_source, &opts);
+		config_with_options(fn, data, &config_source, repo, &opts);
 
 out:
 		free(oidstr);
@@ -668,7 +818,8 @@
 	}
 }
 
-static int gitmodules_cb(const char *var, const char *value, void *data)
+static int gitmodules_cb(const char *var, const char *value,
+			 const struct config_context *ctx, void *data)
 {
 	struct repository *repo = data;
 	struct parse_config_parameter parameter;
@@ -678,7 +829,7 @@
 	parameter.gitmodules_oid = null_oid();
 	parameter.overwrite = 1;
 
-	return parse_config(var, value, &parameter);
+	return parse_config(var, value, ctx, &parameter);
 }
 
 void repo_read_gitmodules(struct repository *repo, int skip_if_read)
@@ -706,7 +857,8 @@
 
 	if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
 		git_config_from_blob_oid(gitmodules_cb, rev.buf,
-					 the_repository, &oid, the_repository);
+					 the_repository, &oid, the_repository,
+					 CONFIG_SCOPE_UNKNOWN);
 	}
 	strbuf_release(&rev);
 
@@ -795,7 +947,9 @@
 		submodule_cache_clear(r->submodule_cache);
 }
 
-static int config_print_callback(const char *var, const char *value, void *cb_data)
+static int config_print_callback(const char *var, const char *value,
+				 const struct config_context *ctx UNUSED,
+				 void *cb_data)
 {
 	char *wanted_key = cb_data;
 
@@ -824,7 +978,7 @@
 {
 	int ret;
 
-	ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
+	ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value);
 	if (ret < 0)
 		/* Maybe the user already did that, don't error out here */
 		warning(_("Could not update .gitmodules entry %s"), key);
@@ -837,13 +991,15 @@
 	int *recurse_submodules;
 };
 
-static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+static int gitmodules_fetch_config(const char *var, const char *value,
+				   const struct config_context *ctx,
+				   void *cb)
 {
 	struct fetch_config *config = cb;
 	if (!strcmp(var, "submodule.fetchjobs")) {
 		if (config->max_children)
 			*(config->max_children) =
-				parse_submodule_fetchjobs(var, value);
+				parse_submodule_fetchjobs(var, value, ctx->kvi);
 		return 0;
 	} else if (!strcmp(var, "fetch.recursesubmodules")) {
 		if (config->recurse_submodules)
@@ -865,11 +1021,12 @@
 }
 
 static int gitmodules_update_clone_config(const char *var, const char *value,
+					  const struct config_context *ctx,
 					  void *cb)
 {
 	int *max_jobs = cb;
 	if (!strcmp(var, "submodule.fetchjobs"))
-		*max_jobs = parse_submodule_fetchjobs(var, value);
+		*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);
 	return 0;
 }
 
diff --git a/submodule-config.h b/submodule-config.h
index 28a8ca6..b6133af 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -1,11 +1,8 @@
 #ifndef SUBMODULE_CONFIG_CACHE_H
 #define SUBMODULE_CONFIG_CACHE_H
 
-#include "cache.h"
 #include "config.h"
-#include "hashmap.h"
 #include "submodule.h"
-#include "strbuf.h"
 #include "tree-walk.h"
 
 /**
@@ -51,7 +48,8 @@
 
 void submodule_cache_free(struct submodule_cache *cache);
 
-int parse_submodule_fetchjobs(const char *var, const char *value);
+int parse_submodule_fetchjobs(const char *var, const char *value,
+			      const struct key_value_info *kvi);
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 struct option;
 int option_fetch_parse_recurse_submodules(const struct option *opt,
@@ -91,6 +89,9 @@
  */
 int check_submodule_name(const char *name);
 
+/* Returns 0 if the URL valid per RFC3986 and -1 otherwise. */
+int check_submodule_url(const char *url);
+
 /*
  * Note: these helper functions exist solely to maintain backward
  * compatibility with 'fetch' and 'update_clone' storing configuration in
diff --git a/submodule.c b/submodule.c
index 3a0dfc4..ce2d032 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1,5 +1,5 @@
-
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "repository.h"
 #include "config.h"
 #include "submodule-config.h"
@@ -7,6 +7,9 @@
 #include "dir.h"
 #include "diff.h"
 #include "commit.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "revision.h"
 #include "run-command.h"
 #include "diffcore.h"
@@ -14,15 +17,18 @@
 #include "string-list.h"
 #include "oid-array.h"
 #include "strvec.h"
-#include "blob.h"
 #include "thread-utils.h"
-#include "quote.h"
+#include "path.h"
 #include "remote.h"
 #include "worktree.h"
 #include "parse-options.h"
-#include "object-store.h"
+#include "object-file.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "commit-reach.h"
-#include "shallow.h"
+#include "read-cache-ll.h"
+#include "setup.h"
+#include "trace2.h"
 
 static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
 static int initialized_fetch_ref_tips;
@@ -65,7 +71,7 @@
 {
 	struct object_id oid;
 	return file_exists(GITMODULES_FILE) ||
-		(get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
+		(repo_get_oid(the_repository, GITMODULES_INDEX, &oid) < 0 && repo_get_oid(the_repository, GITMODULES_HEAD, &oid) < 0);
 }
 
 /*
@@ -274,8 +280,7 @@
 	free(key);
 
 	/* submodule.active is set */
-	sl = repo_config_get_value_multi(repo, "submodule.active");
-	if (sl) {
+	if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) {
 		struct pathspec ps;
 		struct strvec args = STRVEC_INIT;
 		const struct string_list_item *item;
@@ -587,7 +592,12 @@
 	     (!is_null_oid(two) && !*right))
 		message = "(commits not present)";
 
-	*merge_bases = repo_get_merge_bases(sub, *left, *right);
+	*merge_bases = NULL;
+	if (repo_get_merge_bases(sub, *left, *right, merge_bases) < 0) {
+		message = "(corrupt repository)";
+		goto output_header;
+	}
+
 	if (*merge_bases) {
 		if ((*merge_bases)->item == *left)
 			fast_forward = 1;
@@ -1625,7 +1635,7 @@
 		if (!task->repo) {
 			strbuf_addf(err, _("Could not access submodule '%s' at commit %s\n"),
 				    cs_data->path,
-				    find_unique_abbrev(cs_data->super_oid, DEFAULT_ABBREV));
+				    repo_find_unique_abbrev(the_repository, cs_data->super_oid, DEFAULT_ABBREV));
 
 			fetch_task_release(task);
 			free(task);
@@ -1636,8 +1646,8 @@
 			strbuf_addf(err,
 				    _("Fetching submodule %s%s at commit %s\n"),
 				    spf->prefix, task->sub->path,
-				    find_unique_abbrev(cs_data->super_oid,
-						       DEFAULT_ABBREV));
+				    repo_find_unique_abbrev(the_repository, cs_data->super_oid,
+							    DEFAULT_ABBREV));
 
 		spf->changed_count++;
 		/*
@@ -1682,8 +1692,6 @@
 		task = get_fetch_task_from_changed(spf, err);
 
 	if (task) {
-		struct strbuf submodule_prefix = STRBUF_INIT;
-
 		child_process_init(cp);
 		cp->dir = task->repo->gitdir;
 		prepare_submodule_repo_env_in_gitdir(&cp->env);
@@ -1693,15 +1701,11 @@
 			strvec_pushv(&cp->args, task->git_args.v);
 		strvec_pushv(&cp->args, spf->args.v);
 		strvec_push(&cp->args, task->default_argv);
-		strvec_push(&cp->args, "--submodule-prefix");
+		strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+			     spf->prefix, task->sub->path);
 
-		strbuf_addf(&submodule_prefix, "%s%s/",
-						spf->prefix,
-						task->sub->path);
-		strvec_push(&cp->args, submodule_prefix.buf);
 		*task_cb = task;
 
-		strbuf_release(&submodule_prefix);
 		string_list_insert(&spf->seen_submodule_names, task->sub->name);
 		return 1;
 	}
@@ -1709,12 +1713,8 @@
 	if (spf->oid_fetch_tasks_nr) {
 		struct fetch_task *task =
 			spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1];
-		struct strbuf submodule_prefix = STRBUF_INIT;
 		spf->oid_fetch_tasks_nr--;
 
-		strbuf_addf(&submodule_prefix, "%s%s/",
-			    spf->prefix, task->sub->path);
-
 		child_process_init(cp);
 		prepare_submodule_repo_env_in_gitdir(&cp->env);
 		cp->git_cmd = 1;
@@ -1723,8 +1723,8 @@
 		strvec_init(&cp->args);
 		strvec_pushv(&cp->args, spf->args.v);
 		strvec_push(&cp->args, "on-demand");
-		strvec_push(&cp->args, "--submodule-prefix");
-		strvec_push(&cp->args, submodule_prefix.buf);
+		strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+			     spf->prefix, task->sub->path);
 
 		/* NEEDSWORK: have get_default_remote from submodule--helper */
 		strvec_push(&cp->args, "origin");
@@ -1732,14 +1732,13 @@
 					  append_oid_to_argv, &cp->args);
 
 		*task_cb = task;
-		strbuf_release(&submodule_prefix);
 		return 1;
 	}
 
 	return 0;
 }
 
-static int fetch_start_failure(struct strbuf *err,
+static int fetch_start_failure(struct strbuf *err UNUSED,
 			       void *cb, void *task_cb)
 {
 	struct submodule_parallel_fetch *spf = cb;
@@ -1760,7 +1759,7 @@
 	return type != OBJ_COMMIT;
 }
 
-static int fetch_finish(int retvalue, struct strbuf *err,
+static int fetch_finish(int retvalue, struct strbuf *err UNUSED,
 			void *cb, void *task_cb)
 {
 	struct submodule_parallel_fetch *spf = cb;
@@ -2047,7 +2046,7 @@
 	submodule_name_to_gitdir(&config_path, the_repository, sub->name);
 	strbuf_addstr(&config_path, "/config");
 
-	if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL))
+	if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL))
 		warning(_("Could not unset core.worktree setting in submodule '%s'"),
 			  sub->path);
 
diff --git a/symlinks.c b/symlinks.c
index c667baa..b29e340 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -1,4 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "setup.h"
+#include "symlinks.h"
 
 static int threaded_check_leading_path(struct cache_def *cache, const char *name,
 				       int len, int warn_on_lstat_err);
diff --git a/symlinks.h b/symlinks.h
new file mode 100644
index 0000000..7ae3d5b
--- /dev/null
+++ b/symlinks.h
@@ -0,0 +1,28 @@
+#ifndef SYMLINKS_H
+#define SYMLINKS_H
+
+#include "strbuf.h"
+
+struct cache_def {
+	struct strbuf path;
+	int flags;
+	int track_flags;
+	int prefix_len_stat_func;
+};
+#define CACHE_DEF_INIT { \
+	.path = STRBUF_INIT, \
+}
+static inline void cache_def_clear(struct cache_def *cache)
+{
+	strbuf_release(&cache->path);
+}
+
+int has_symlink_leading_path(const char *name, int len);
+int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
+int check_leading_path(const char *name, int len, int warn_on_lstat_err);
+int has_dirs_only_path(const char *name, int len, int prefix_len);
+void invalidate_lstat_cache(void);
+void schedule_dir_for_removal(const char *name, int len);
+void remove_scheduled_dirs(void);
+
+#endif /* SYMLINKS_H */
diff --git a/t/.gitattributes b/t/.gitattributes
index 9930e28..b9cea17 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -22,3 +22,4 @@
 /t7500/* eol=lf
 /t8005/*.txt eol=lf
 /t9*/*.dump eol=lf
+/t0040*.sh whitespace=-indent-with-non-tab
diff --git a/t/Makefile b/t/Makefile
index 2c2b252..2d95046 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
 # Import tree-wide shared Makefile behavior and libraries
 include ../shared.mak
 
@@ -6,6 +9,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
+-include ../config.mak.uname
 -include ../config.mak.autogen
 -include ../config.mak
 
@@ -17,6 +21,7 @@
 RM ?= rm -f
 PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
+DEFAULT_UNIT_TEST_TARGET ?= unit-tests-raw
 TEST_LINT ?= test-lint
 
 ifdef TEST_OUTPUT_DIRECTORY
@@ -41,13 +46,16 @@
 TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh))
 CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
 CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
+UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c)
+UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES))
+UNIT_TESTS = $(sort $(filter-out unit-tests/bin/t-basic%,$(UNIT_TEST_PROGRAMS)))
 
 # `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
 # checks all tests in all scripts via a single invocation, so tell individual
-# scripts not to "chainlint" themselves
-CHAINLINTSUPPRESS = GIT_TEST_CHAIN_LINT=0 && export GIT_TEST_CHAIN_LINT &&
+# scripts not to run the external "chainlint.pl" script themselves
+CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&
 
-all: $(DEFAULT_TEST_TARGET)
+all:: $(DEFAULT_TEST_TARGET)
 
 test: pre-clean check-chainlint $(TEST_LINT)
 	$(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup
@@ -65,6 +73,17 @@
 $(T):
 	@echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
+$(UNIT_TESTS):
+	@echo "*** $@ ***"; $@
+
+.PHONY: unit-tests unit-tests-raw unit-tests-prove
+unit-tests: $(DEFAULT_UNIT_TEST_TARGET)
+
+unit-tests-raw: $(UNIT_TESTS)
+
+unit-tests-prove:
+	@echo "*** prove - unit tests ***"; $(PROVE) $(GIT_PROVE_OPTS) $(UNIT_TESTS)
+
 pre-clean:
 	$(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
@@ -90,20 +109,12 @@
 		echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
 		for i in $(CHAINLINTTESTS); do \
 			echo "# chainlint: $$i" && \
-			sed -e '/^[ 	]*$$/d' chainlint/$$i.expect; \
+			cat chainlint/$$i.expect; \
 		done \
 	} >'$(CHAINLINTTMP_SQ)'/expect && \
 	$(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
-		sed -e 's/^[1-9][0-9]* //;/^[ 	]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
-	if test -f ../GIT-BUILD-OPTIONS; then \
-		. ../GIT-BUILD-OPTIONS; \
-	fi && \
-	if test -x ../git$$X; then \
-		DIFFW="../git$$X --no-pager diff -w --no-index"; \
-	else \
-		DIFFW="diff -w -u"; \
-	fi && \
-	$$DIFFW '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
+		sed -e 's/^[1-9][0-9]* //' >'$(CHAINLINTTMP_SQ)'/actual && \
+	diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
 
 test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
 	test-lint-filenames
@@ -140,9 +151,7 @@
 	$(MAKE) clean
 
 aggregate-results:
-	for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
-		echo "$$f"; \
-	done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
+	@'$(SHELL_PATH_SQ)' ./aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
 
 valgrind:
 	$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
@@ -151,4 +160,4 @@
 	$(MAKE) -C perf/ all
 
 .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
-	check-chainlint clean-chainlint test-chainlint
+	check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
diff --git a/t/README b/t/README
index 29576c3..d9e0e07 100644
--- a/t/README
+++ b/t/README
@@ -32,7 +32,14 @@
     ok 2 - plain with GIT_WORK_TREE
     ok 3 - plain bare
 
-Since the tests all output TAP (see http://testanything.org) they can
+t/Makefile defines a target for each test file, such that you can also use
+shell pattern matching to run a subset of the tests:
+
+    make *checkout*
+
+will run all tests with 'checkout' in their filename.
+
+Since the tests all output TAP (see https://testanything.org) they can
 be run with any TAP harness. Here's an example of parallel testing
 powered by a recent version of prove(1):
 
@@ -262,8 +269,8 @@
 substrings or globs or individual test numbers or ranges with an
 optional negation prefix (of '!') that define what tests in a test
 suite to include (or exclude, if negated) in the run.  A range is two
-numbers separated with a dash and matches a range of tests with both
-ends been included.  You may omit the first or the second number to
+numbers separated with a dash and specifies an inclusive range of tests
+to run.  You may omit the first or the second number to
 mean "from the first test" or "up to the very last test" respectively.
 
 The argument to --run is split on commas into separate strings,
@@ -274,10 +281,10 @@
 on all tests that match either the glob *rebase* or the glob
 *merge?cherry-pick*.
 
-If --run starts with an unprefixed number or range the initial
-set of tests to run is empty. If the first item starts with '!'
+If --run starts with an unprefixed number or range, the initial
+set of tests to run is empty.  If the first item starts with '!',
 all the tests are added to the initial set.  After initial set is
-determined every test number or range is added or excluded from
+determined, every test number or range is added or excluded from
 the set one by one, from left to right.
 
 For example, to run only tests up to a specific test (21), one
@@ -442,6 +449,10 @@
 for the index version specified.  Can be set to any valid version
 (currently 2, 3, or 4).
 
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=<boolean> if enabled will
+use the boundary-based bitmap traversal algorithm. See the documentation
+of `pack.useBitmapBoundaryTraversal` for more details.
+
 GIT_TEST_PACK_SPARSE=<boolean> if disabled will default the pack-objects
 builtin to use the non-sparse object walk. This can still be overridden by
 the --sparse command-line argument.
@@ -475,7 +486,10 @@
 use in the test scripts. Recognized values for <hash-algo> are "sha1"
 and "sha256".
 
-GIT_TEST_WRITE_REV_INDEX=<boolean>, when true enables the
+GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format
+to use in the test scripts. Recognized values for <format> are "files".
+
+GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
 'pack.writeReverseIndex' setting.
 
 GIT_TEST_SPARSE_INDEX=<boolean>, when true enables index writes to use the
@@ -575,11 +589,11 @@
 
 Recommended style
 -----------------
-Here are some recommented styles when writing test case.
 
- - Keep test title the same line with test helper function itself.
+ - Keep the test_expect_* function call and test title on
+   the same line.
 
-   Take test_expect_success helper for example, write it like:
+   For example, with test_expect_success, write it like:
 
   test_expect_success 'test title' '
       ... test body ...
@@ -591,10 +605,9 @@
       'test title' \
       '... test body ...'
 
+ - End the line with an opening single quote.
 
- - End the line with a single quote.
-
- - Indent the body of here-document, and use "<<-" instead of "<<"
+ - Indent here-document bodies, and use "<<-" instead of "<<"
    to strip leading TABs used for indentation:
 
   test_expect_success 'test something' '
@@ -620,7 +633,7 @@
   '
 
  - Quote or escape the EOF delimiter that begins a here-document if
-   there is no parameter and other expansion in it, to signal readers
+   there is no parameter or other expansion in it, to signal readers
    that they can skim it more casually:
 
   cmd <<-\EOF
@@ -634,7 +647,7 @@
 Here are a few examples of things you probably should and shouldn't do
 when writing tests.
 
-Here are the "do's:"
+The "do's:"
 
  - Put all code inside test_expect_success and other assertions.
 
@@ -718,6 +731,26 @@
    Note that we still &&-chain the loop to propagate failures from
    earlier commands.
 
+ - Repeat tests with slightly different arguments in a loop.
+
+   In some cases it may make sense to re-run the same set of tests with
+   different options or commands to ensure that the command behaves
+   despite the different parameters. This can be achieved by looping
+   around a specific parameter:
+
+	for arg in '' "--foo"
+	do
+		test_expect_success "test command ${arg:-without arguments}" '
+			command $arg
+		'
+	done
+
+   Note that while the test title uses double quotes ("), the test body
+   should continue to use single quotes (') to avoid breakage in case the
+   values contain e.g. quoting characters. The loop variable will be
+   accessible regardless of the single quotes as the test body is passed
+   to `eval`.
+
 
 And here are the "don'ts:"
 
@@ -884,7 +917,7 @@
    rare case where your test depends on more than one:
 
 	test_expect_success PERL,PYTHON 'yo dawg' \
-	    ' test $(perl -E 'print eval "1 +" . qx[python -c "print 2"]') == "4" '
+	    ' test $(perl -E '\''print eval "1 +" . qx[python -c "print(2)"]'\'') = "4" '
 
  - test_expect_failure [<prereq>] <message> <script>
 
@@ -1098,6 +1131,12 @@
    the symbolic link in the file system and a part that does; then only
    the latter part need be protected by a SYMLINKS prerequisite (see below).
 
+ - test_path_is_executable
+
+   This tests whether a file is executable and prints an error message
+   if not. This must be used only under the POSIXPERM prerequisite
+   (see below).
+
  - test_oid_init
 
    This function loads facts and useful object IDs related to the hash
@@ -1227,8 +1266,8 @@
 because the things the very basic core test tries to achieve is
 to serve as a basis for people who are changing the Git internals
 drastically.  For these people, after making certain changes,
-not seeing failures from the basic test _is_ a failure.  And
-such drastic changes to the core Git that even changes these
+not seeing failures from the basic test _is_ a failure.  Any
+Git core changes so drastic that they change even these
 otherwise supposedly stable object IDs should be accompanied by
 an update to t0000-basic.sh.
 
@@ -1238,7 +1277,7 @@
 hardcoded the object IDs like t0000-basic.sh does, that defeats
 the purpose of t0000-basic.sh, which is to isolate that level of
 validation in one place.  Your test also ends up needing
-updating when such a change to the internal happens, so do _not_
+an update whenever the internals change, so do _not_
 do it and leave the low level of validation to t0000-basic.sh.
 
 Test coverage
@@ -1269,7 +1308,7 @@
    sudo aptitude install libdevel-cover-perl
 
    # From the CPAN with cpanminus
-   curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+   curl -L https://cpanmin.us/ | perl - --sudo --self-upgrade
    cpanm --sudo Devel::Cover
 
 Then, at the top-level:
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index 7f2b83b..6e3bcc4 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -8,7 +8,7 @@
 total=0
 missing_prereq=
 
-while read file
+for file in "$1"/t*-*.counts
 do
 	while read type value
 	do
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index f1b9a6c..8757245 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -72,6 +72,32 @@
 	check_count A 2
 '
 
+test_expect_success 'blame working copy' '
+	test_when_finished "git restore file" &&
+	echo "1A quick brown fox jumps over the" >file &&
+	echo "another lazy dog" >>file &&
+	check_count A 1 "Not Committed Yet" 1
+'
+
+test_expect_success 'blame with --contents' '
+	check_count --contents=file A 2
+'
+
+test_expect_success 'blame with --contents in a bare repo' '
+	git clone --bare . bare-contents.git &&
+	(
+		cd bare-contents.git &&
+		echo "1A quick brown fox jumps over the" >contents &&
+		check_count --contents=contents A 1
+	)
+'
+
+test_expect_success 'blame with --contents changed' '
+	echo "1A quick brown fox jumps over the" >contents &&
+	echo "another lazy dog" >>contents &&
+	check_count --contents=contents A 1 "External file (--contents)" 1
+'
+
 test_expect_success 'blame in a bare repo without starting commit' '
 	git clone --bare . bare.git &&
 	(
@@ -98,6 +124,10 @@
 	check_count A 2 B 2
 '
 
+test_expect_success 'blame with --contents and revision' '
+	check_count -h testTag --contents=file A 2 "External file (--contents)" 2
+'
+
 test_expect_success 'setup B1 lines (branch1)' '
 	git checkout -b branch1 main &&
 	echo "3A slow green fox jumps into the" >>file &&
@@ -502,7 +532,7 @@
 		"$(cat file.template)" &&
 	test_commit --author "B <B@test.git>" \
 		"change" "$fortran_file" \
-		"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+		"$(sed -e s/ChangeMe/IWasChanged/ file.template)" &&
 	check_count -f "$fortran_file" -L:RIGHT A 3 B 1
 '
 
diff --git a/t/chainlint.pl b/t/chainlint.pl
index e966412..556ee91 100755
--- a/t/chainlint.pl
+++ b/t/chainlint.pl
@@ -80,7 +80,8 @@
 	return "<<$indented" unless $token;
 	my $tag = $token->[0];
 	$tag =~ s/['"\\]//g;
-	push(@{$self->{heretags}}, $indented ? "\t$tag" : "$tag");
+	$$token[0] = $indented ? "\t$tag" : "$tag";
+	push(@{$self->{heretags}}, $token);
 	return "<<$indented$tag";
 }
 
@@ -169,10 +170,18 @@
 	my $tags = $self->{heretags};
 	while (my $tag = shift @$tags) {
 		my $start = pos($$b);
-		my $indent = $tag =~ s/^\t// ? '\\s*' : '';
-		$$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
+		my $indent = $$tag[0] =~ s/^\t// ? '\\s*' : '';
+		$$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc;
+		if (pos($$b) > $start) {
+			my $body = substr($$b, $start, pos($$b) - $start);
+			$self->{lineno} += () = $body =~ /\n/sg;
+			next;
+		}
+		push(@{$self->{parser}->{problems}}, ['UNCLOSED-HEREDOC', $tag]);
+		$$b =~ /(?:\G|\n).*\z/gc; # consume rest of input
 		my $body = substr($$b, $start, pos($$b) - $start);
 		$self->{lineno} += () = $body =~ /\n/sg;
+		last;
 	}
 }
 
diff --git a/t/chainlint/blank-line-before-esac.expect b/t/chainlint/blank-line-before-esac.expect
index 48ed4eb..056e030 100644
--- a/t/chainlint/blank-line-before-esac.expect
+++ b/t/chainlint/blank-line-before-esac.expect
@@ -1,11 +1,11 @@
-test_done ( ) {
+test_done () {
 	case "$test_failure" in
-	0 )
+	0)
 		test_at_end_hook_
 
 		exit 0 ;;
 
-	* )
+	*)
 		if test $test_external_has_tap -eq 0
 		then
 			say_color error "# failed $test_failure among $msg"
@@ -14,5 +14,5 @@
 
 		exit 1 ;;
 
-		esac
+	esac
 }
diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
index f76fde1..b47827d 100644
--- a/t/chainlint/blank-line.expect
+++ b/t/chainlint/blank-line.expect
@@ -1,4 +1,8 @@
 (
+
 	nothing &&
+
 	something
+
+
 )
diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
index a3bcea4..1c87326 100644
--- a/t/chainlint/block.expect
+++ b/t/chainlint/block.expect
@@ -12,9 +12,9 @@
 ) &&
 
 {
-	echo a ; ?!AMP?! echo b
+	echo a; ?!AMP?! echo b
 } &&
-{ echo a ; ?!AMP?! echo b ; } &&
+{ echo a; ?!AMP?! echo b; } &&
 
 {
 	echo "${var}9" &&
diff --git a/t/chainlint/chain-break-background.expect b/t/chainlint/chain-break-background.expect
index 28f9114..20d0bb5 100644
--- a/t/chainlint/chain-break-background.expect
+++ b/t/chainlint/chain-break-background.expect
@@ -1,9 +1,9 @@
 JGIT_DAEMON_PID= &&
 git init --bare empty.git &&
-> empty.git/git-daemon-export-ok &&
+>empty.git/git-daemon-export-ok &&
 mkfifo jgit_daemon_output &&
 {
-	jgit daemon --port="$JGIT_DAEMON_PORT" . > jgit_daemon_output &
+	jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
 	JGIT_DAEMON_PID=$!
 } &&
 test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-return-exit.expect b/t/chainlint/chain-break-return-exit.expect
index 1732d22..4cd18e2 100644
--- a/t/chainlint/chain-break-return-exit.expect
+++ b/t/chainlint/chain-break-return-exit.expect
@@ -1,16 +1,16 @@
 case "$(git ls-files)" in
-one ) echo pass one ;;
-* ) echo bad one ; return 1 ;;
+one) echo pass one ;;
+*) echo bad one; return 1 ;;
 esac &&
 (
 	case "$(git ls-files)" in
-	two ) echo pass two ;;
-	* ) echo bad two ; exit 1 ;;
-esac
+	two) echo pass two ;;
+	*) echo bad two; exit 1 ;;
+	esac
 ) &&
 case "$(git ls-files)" in
-dir/two"$LF"one ) echo pass both ;;
-* ) echo bad ; return 1 ;;
+dir/two"$LF"one) echo pass both ;;
+*) echo bad; return 1 ;;
 esac &&
 
 for i in 1 2 3 4 ; do
diff --git a/t/chainlint/chain-break-status.expect b/t/chainlint/chain-break-status.expect
index f4bada9..e6b3b21 100644
--- a/t/chainlint/chain-break-status.expect
+++ b/t/chainlint/chain-break-status.expect
@@ -1,7 +1,7 @@
-OUT=$(( ( large_git ; echo $? 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
 test_match_signal 13 "$OUT" &&
 
-{ test-tool sigchain > actual ; ret=$? ; } &&
+{ test-tool sigchain >actual; ret=$?; } &&
 {
 	test_match_signal 15 "$ret" ||
 	test "$ret" = 3
diff --git a/t/chainlint/chained-subshell.expect b/t/chainlint/chained-subshell.expect
index af0369d..83810ea 100644
--- a/t/chainlint/chained-subshell.expect
+++ b/t/chainlint/chained-subshell.expect
@@ -4,7 +4,7 @@
 	nuff said
 ) &&
 
-cut "-d " -f actual | ( read s1 s2 s3 &&
+cut "-d " -f actual | (read s1 s2 s3 &&
 test -f $s1 ?!AMP?!
 test $(cat $s2) = tree2path1 &&
-test $(cat $s3) = tree3path1 )
+test $(cat $s3) = tree3path1)
diff --git a/t/chainlint/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
index ab2f79e..ec42f2c 100644
--- a/t/chainlint/command-substitution-subsubshell.expect
+++ b/t/chainlint/command-substitution-subsubshell.expect
@@ -1,2 +1,2 @@
-OUT=$(( ( large_git 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
 test_match_signal 13 "$OUT"
diff --git a/t/chainlint/dqstring-line-splice.expect b/t/chainlint/dqstring-line-splice.expect
index bf9ced6..37eab80 100644
--- a/t/chainlint/dqstring-line-splice.expect
+++ b/t/chainlint/dqstring-line-splice.expect
@@ -1,3 +1,5 @@
-echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' > expect &&
-test_must_fail git commit --fixup=reword:HEAD~ $1 2 > actual &&
+
+echo 'fatal: reword option of --fixup is mutually exclusive with'	'--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
 test_cmp expect actual
+
diff --git a/t/chainlint/dqstring-no-interpolate.expect b/t/chainlint/dqstring-no-interpolate.expect
index 1072498..087eda1 100644
--- a/t/chainlint/dqstring-no-interpolate.expect
+++ b/t/chainlint/dqstring-no-interpolate.expect
@@ -6,6 +6,7 @@
 (
 	cd client$version &&
 	GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
-) > output &&
-	cut -d ' ' -f 2 < output | sort > actual &&
+) >output &&
+	cut -d ' ' -f 2 <output | sort >actual &&
 	test_cmp expect actual
+
diff --git a/t/chainlint/empty-here-doc.expect b/t/chainlint/empty-here-doc.expect
index e8733c9..8507721 100644
--- a/t/chainlint/empty-here-doc.expect
+++ b/t/chainlint/empty-here-doc.expect
@@ -1,4 +1,4 @@
-git ls-tree $tree path > current &&
-cat > expected <<\EOF &&
+git ls-tree $tree path >current &&
+cat >expected <<\EOF &&
 EOF
 test_output
diff --git a/t/chainlint/exclamation.expect b/t/chainlint/exclamation.expect
index 2d961a5..765a35b 100644
--- a/t/chainlint/exclamation.expect
+++ b/t/chainlint/exclamation.expect
@@ -1,4 +1,4 @@
-if ! condition ; then echo nope ; else yep ; fi &&
+if ! condition; then echo nope; else yep; fi &&
 test_prerequisite !MINGW &&
 mail uucp!address &&
 echo !whatever!
diff --git a/t/chainlint/for-loop-abbreviated.expect b/t/chainlint/for-loop-abbreviated.expect
index a21007a..02c0d15 100644
--- a/t/chainlint/for-loop-abbreviated.expect
+++ b/t/chainlint/for-loop-abbreviated.expect
@@ -1,5 +1,5 @@
 for it
 do
-	path=$(expr "$it" : ( [^:]*) ) &&
+	path=$(expr "$it" : ([^:]*)) &&
 	git update-index --add "$path" || exit
 done
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
index d65c821..d2237f1 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -6,6 +6,7 @@
 		bar
 		EOF
 	done ?!AMP?!
+
 	for i in a b c; do
 		echo $i &&
 		cat $i ?!LOOP?!
diff --git a/t/chainlint/function.expect b/t/chainlint/function.expect
index a14388e..dd7c997 100644
--- a/t/chainlint/function.expect
+++ b/t/chainlint/function.expect
@@ -1,8 +1,8 @@
-sha1_file ( ) {
+sha1_file() {
 	echo "$*" | sed "s#..#.git/objects/&/#"
 } &&
 
-remove_object ( ) {
+remove_object() {
 	file=$(sha1_file "$*") &&
 	test -e "$file" ?!AMP?!
 	rm -f "$file"
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 1df3f78..91b9612 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,6 +1,6 @@
 boodle wobba \
-	gorgo snoot \
-	wafta snurb <<EOF &&
+       gorgo snoot \
+       wafta snurb <<EOF &&
 quoth the raven,
 nevermore...
 EOF
diff --git a/t/chainlint/loop-detect-status.expect b/t/chainlint/loop-detect-status.expect
index 24da9e8..7ce3a34 100644
--- a/t/chainlint/loop-detect-status.expect
+++ b/t/chainlint/loop-detect-status.expect
@@ -1,18 +1,18 @@
-( while test $i -le $blobcount
-do
-	printf "Generating blob $i/$blobcount\r" >& 2 &&
+(while test $i -le $blobcount
+ do
+	printf "Generating blob $i/$blobcount\r" >&2 &&
 	printf "blob\nmark :$i\ndata $blobsize\n" &&
 	#test-tool genrandom $i $blobsize &&
 	printf "%-${blobsize}s" $i &&
 	echo "M 100644 :$i $i" >> commit &&
 	i=$(($i+1)) ||
 	echo $? > exit-status
-done &&
-echo "commit refs/heads/main" &&
-echo "author A U Thor <author@email.com> 123456789 +0000" &&
-echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
-echo "data 5" &&
-echo ">2gb" &&
-cat commit ) |
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
 git fast-import --big-file-threshold=2 &&
 test ! -f exit-status
diff --git a/t/chainlint/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
index 2a86885..3836049 100644
--- a/t/chainlint/nested-cuddled-subshell.expect
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -2,18 +2,24 @@
 	(cd foo &&
 		bar
 	) &&
+
 	(cd foo &&
 		bar
 	) ?!AMP?!
+
 	(
 		cd foo &&
 		bar) &&
+
 	(
 		cd foo &&
 		bar) ?!AMP?!
+
 	(cd foo &&
 		bar) &&
+
 	(cd foo &&
 		bar) ?!AMP?!
+
 	foobar
 )
diff --git a/t/chainlint/nested-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
index 4793a0e..3461df4 100644
--- a/t/chainlint/nested-loop-detect-failure.expect
+++ b/t/chainlint/nested-loop-detect-failure.expect
@@ -1,31 +1,31 @@
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-	for j in 0 1 2 3 4 5 6 7 8 9 ;
+	for j in 0 1 2 3 4 5 6 7 8 9;
 	do
-		echo "$i$j" > "path$i$j" ?!LOOP?!
+		echo "$i$j" >"path$i$j" ?!LOOP?!
 	done ?!LOOP?!
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-	for j in 0 1 2 3 4 5 6 7 8 9 ;
+	for j in 0 1 2 3 4 5 6 7 8 9;
 	do
-		echo "$i$j" > "path$i$j" || return 1
+		echo "$i$j" >"path$i$j" || return 1
 	done
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-	for j in 0 1 2 3 4 5 6 7 8 9 ;
+	for j in 0 1 2 3 4 5 6 7 8 9;
 	do
-		echo "$i$j" > "path$i$j" ?!LOOP?!
+		echo "$i$j" >"path$i$j" ?!LOOP?!
 	done || return 1
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-	for j in 0 1 2 3 4 5 6 7 8 9 ;
+	for j in 0 1 2 3 4 5 6 7 8 9;
 	do
-		echo "$i$j" > "path$i$j" || return 1
+		echo "$i$j" >"path$i$j" || return 1
 	done || return 1
 done
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
index 02e0a9f..73ff285 100644
--- a/t/chainlint/nested-subshell.expect
+++ b/t/chainlint/nested-subshell.expect
@@ -4,6 +4,7 @@
 		echo a &&
 		echo b
 	) >file &&
+
 	cd foo &&
 	(
 		echo a ?!AMP?!
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
index 2cfc028..811971b 100644
--- a/t/chainlint/pipe.expect
+++ b/t/chainlint/pipe.expect
@@ -2,7 +2,9 @@
 	foo |
 	bar |
 	baz &&
+
 	fish |
 	cow ?!AMP?!
+
 	sunder
 )
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 5278927..75d6f60 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -1,7 +1,7 @@
 (
 	echo wobba \
-		gorgo snoot \
-		wafta snurb <<-EOF &&
+	       gorgo snoot \
+	       wafta snurb <<-EOF &&
 	quoth the raven,
 	nevermore...
 	EOF
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
index b701536..8f69499 100644
--- a/t/chainlint/subshell-one-liner.expect
+++ b/t/chainlint/subshell-one-liner.expect
@@ -2,13 +2,18 @@
 	(foo && bar) &&
 	(foo && bar) |
 	(foo && bar) >baz &&
+
 	(foo; ?!AMP?! bar) &&
 	(foo; ?!AMP?! bar) |
 	(foo; ?!AMP?! bar) >baz &&
+
 	(foo || exit 1) &&
 	(foo || exit 1) |
 	(foo || exit 1) >baz &&
+
 	(foo && bar) ?!AMP?!
+
 	(foo && bar; ?!AMP?! baz) ?!AMP?!
+
 	foobar
 )
diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
index 71b3b3b..02f3129 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -15,6 +15,7 @@
 $chkms
 TXT
 ) &&
+
 	subfiles=$(git ls-files) &&
 	check_equal "$subfiles" "$chkms
 $chks"
diff --git a/t/chainlint/token-pasting.expect b/t/chainlint/token-pasting.expect
index 342360b..6a38791 100644
--- a/t/chainlint/token-pasting.expect
+++ b/t/chainlint/token-pasting.expect
@@ -4,22 +4,22 @@
 {
     echo "*.t filter=rot13" ?!AMP?!
     echo "*.i ident"
-} > .gitattributes &&
+} >.gitattributes &&
 
 {
     echo a b c d e f g h i j k l m ?!AMP?!
     echo n o p q r s t u v w x y z ?!AMP?!
     echo '$Id$'
-} > test &&
-cat test > test.t &&
-cat test > test.o &&
-cat test > test.i &&
+} >test &&
+cat test >test.t &&
+cat test >test.o &&
+cat test >test.i &&
 git add test test.t test.i &&
 rm -f test test.t test.i &&
 git checkout -- test test.t test.i &&
 
-echo "content-test2" > test2.o &&
-echo "content-test3 - filename with special characters" > "test3 'sq',$x=.o" ?!AMP?!
+echo "content-test2" >test2.o &&
+echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?!
 
 downstream_url_for_sed=$(
 	printf "%sn" "$downstream_url" |
diff --git a/t/chainlint/unclosed-here-doc-indent.expect b/t/chainlint/unclosed-here-doc-indent.expect
new file mode 100644
index 0000000..7c30a1a
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc-indent.expect
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc-indent.test b/t/chainlint/unclosed-here-doc-indent.test
new file mode 100644
index 0000000..5c841a9
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc-indent.test
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.expect b/t/chainlint/unclosed-here-doc.expect
new file mode 100644
index 0000000..d65e50f
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc.expect
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! &&
+	we try to end the here-doc below,
+	but the indentation throws us off
+	since the operator is not "<<-".
+	EOF
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.test b/t/chainlint/unclosed-here-doc.test
new file mode 100644
index 0000000..69d3786
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc.test
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF &&
+	we try to end the here-doc below,
+	but the indentation throws us off
+	since the operator is not "<<-".
+	EOF
+command_which_is_gobbled
diff --git a/t/chainlint/while-loop.expect b/t/chainlint/while-loop.expect
index 1f5eaea..06c1567 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -6,6 +6,7 @@
 		bar
 		EOF
 	done ?!AMP?!
+
 	while true; do
 		echo foo &&
 		cat bar ?!LOOP?!
diff --git a/t/helper/test-advise.c b/t/helper/test-advise.c
index cb88113..8a3fd00 100644
--- a/t/helper/test-advise.c
+++ b/t/helper/test-advise.c
@@ -1,7 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "advice.h"
 #include "config.h"
+#include "setup.h"
 
 int cmd__advise_if_enabled(int argc, const char **argv)
 {
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
index ff35f59..af43ee1 100644
--- a/t/helper/test-bitmap.c
+++ b/t/helper/test-bitmap.c
@@ -1,6 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "git-compat-util.h"
 #include "pack-bitmap.h"
+#include "setup.h"
 
 static int bitmap_list_commits(void)
 {
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index 6c900ca..1281e66 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -1,7 +1,9 @@
-#include "git-compat-util.h"
-#include "bloom.h"
 #include "test-tool.h"
+#include "bloom.h"
+#include "hex.h"
 #include "commit.h"
+#include "repository.h"
+#include "setup.h"
 
 static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
 
@@ -38,7 +40,6 @@
 {
 	struct commit *c;
 	struct bloom_filter *filter;
-	setup_git_directory();
 	c = lookup_commit(the_repository, commit_oid);
 	filter = get_or_compute_bloom_filter(the_repository, c, 1,
 					     &settings,
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
index b18e760..09dc787 100644
--- a/t/helper/test-bundle-uri.c
+++ b/t/helper/test-bundle-uri.c
@@ -1,12 +1,11 @@
 #include "test-tool.h"
 #include "parse-options.h"
 #include "bundle-uri.h"
+#include "gettext.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "transport.h"
-#include "ref-filter.h"
 #include "remote.h"
-#include "refs.h"
 
 enum input_mode {
 	KEY_VALUE_PAIRS,
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
index 9159a17..e723639 100644
--- a/t/helper/test-cache-tree.c
+++ b/t/helper/test-cache-tree.c
@@ -1,9 +1,13 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "gettext.h"
+#include "hex.h"
 #include "tree.h"
 #include "cache-tree.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 static char const * const test_cache_tree_usage[] = {
 	N_("test-tool cache-tree <options> (control|prime|update)"),
diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c
index dc28890..0e55388 100644
--- a/t/helper/test-chmtime.c
+++ b/t/helper/test-chmtime.c
@@ -94,7 +94,7 @@
 	if (timespec_arg(argv[i], &set_time, &set_eq)) {
 		++i;
 	} else {
-		if (get == 0) {
+		if (get == 0 && verbose == 0) {
 			fprintf(stderr, "Not a base-10 integer: %s\n", argv[i] + 1);
 			goto usage;
 		}
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 4ba9eb6..ed444ca 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "config.h"
+#include "setup.h"
 #include "string-list.h"
 
 /*
@@ -14,6 +14,8 @@
  * get_value_multi -> prints all values for the entered key in increasing order
  *		     of priority
  *
+ * get -> print return value for the entered key
+ *
  * get_int -> print integer value for the entered key or die
  *
  * get_bool -> print bool value for the entered key or die
@@ -30,6 +32,9 @@
  * iterate -> iterate over all values using git_config(), and print some
  *            data for each
  *
+ * git_config_int -> iterate over all values using git_config() and print the
+ *                   integer value for the entered key or die
+ *
  * Examples:
  *
  * To print the value with highest priority for key "foo.bAr Baz.rock":
@@ -37,8 +42,11 @@
  *
  */
 
-static int iterate_cb(const char *var, const char *value, void *data UNUSED)
+static int iterate_cb(const char *var, const char *value,
+		      const struct config_context *ctx,
+		      void *data UNUSED)
 {
+	const struct key_value_info *kvi = ctx->kvi;
 	static int nr;
 
 	if (nr++)
@@ -46,15 +54,29 @@
 
 	printf("key=%s\n", var);
 	printf("value=%s\n", value ? value : "(null)");
-	printf("origin=%s\n", current_config_origin_type());
-	printf("name=%s\n", current_config_name());
-	printf("lno=%d\n", current_config_line());
-	printf("scope=%s\n", config_scope_name(current_config_scope()));
+	printf("origin=%s\n", config_origin_type_name(kvi->origin_type));
+	printf("name=%s\n", kvi->filename ? kvi->filename : "");
+	printf("lno=%d\n", kvi->linenr);
+	printf("scope=%s\n", config_scope_name(kvi->scope));
 
 	return 0;
 }
 
-static int early_config_cb(const char *var, const char *value, void *vdata)
+static int parse_int_cb(const char *var, const char *value,
+			const struct config_context *ctx, void *data)
+{
+	const char *key_to_match = data;
+
+	if (!strcmp(key_to_match, var)) {
+		int parsed = git_config_int(value, value, ctx->kvi);
+		printf("%d\n", parsed);
+	}
+	return 0;
+}
+
+static int early_config_cb(const char *var, const char *value,
+			   const struct config_context *ctx UNUSED,
+			   void *vdata)
 {
 	const char *key = vdata;
 
@@ -95,8 +117,7 @@
 			goto exit1;
 		}
 	} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
-		strptr = git_config_get_value_multi(argv[2]);
-		if (strptr) {
+		if (!git_config_get_value_multi(argv[2], &strptr)) {
 			for (i = 0; i < strptr->nr; i++) {
 				v = strptr->items[i].string;
 				if (!v)
@@ -109,6 +130,26 @@
 			printf("Value not found for \"%s\"\n", argv[2]);
 			goto exit1;
 		}
+	} else if (argc == 3 && !strcmp(argv[1], "get")) {
+		int ret;
+
+		if (!(ret = git_config_get(argv[2])))
+			goto exit0;
+		else if (ret == 1)
+			printf("Value not found for \"%s\"\n", argv[2]);
+		else if (ret == -CONFIG_INVALID_KEY)
+			printf("Key \"%s\" is invalid\n", argv[2]);
+		else if (ret == -CONFIG_NO_SECTION_OR_NAME)
+			printf("Key \"%s\" has no section\n", argv[2]);
+		else
+			/*
+			 * A normal caller should just check "ret <
+			 * 0", but for our own tests let's BUG() if
+			 * our whitelist of git_config_parse_key()
+			 * return values isn't exhaustive.
+			 */
+			BUG("Key \"%s\" has unknown return %d", argv[2], ret);
+		goto exit1;
 	} else if (argc == 3 && !strcmp(argv[1], "get_int")) {
 		if (!git_config_get_int(argv[2], &val)) {
 			printf("%d\n", val);
@@ -141,7 +182,7 @@
 				goto exit2;
 			}
 		}
-		if (!git_configset_get_value(&cs, argv[2], &v)) {
+		if (!git_configset_get_value(&cs, argv[2], &v, NULL)) {
 			if (!v)
 				printf("(NULL)\n");
 			else
@@ -159,8 +200,7 @@
 				goto exit2;
 			}
 		}
-		strptr = git_configset_get_value_multi(&cs, argv[2]);
-		if (strptr) {
+		if (!git_configset_get_value_multi(&cs, argv[2], &strptr)) {
 			for (i = 0; i < strptr->nr; i++) {
 				v = strptr->items[i].string;
 				if (!v)
@@ -176,6 +216,9 @@
 	} else if (!strcmp(argv[1], "iterate")) {
 		git_config(iterate_cb, NULL);
 		goto exit0;
+	} else if (argc == 3 && !strcmp(argv[1], "git_config_int")) {
+		git_config(parse_int_cb, (void *) argv[2]);
+		goto exit0;
 	}
 
 	die("%s: Please check the syntax and the function name", argv[0]);
diff --git a/t/helper/test-crontab.c b/t/helper/test-crontab.c
index e6c1b1e..597027a 100644
--- a/t/helper/test-crontab.c
+++ b/t/helper/test-crontab.c
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 
 /*
  * Usage: test-tool crontab <file> -l|<input>
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644
index b21bd67..0000000
--- a/t/helper/test-ctype.c
+++ /dev/null
@@ -1,69 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-
-static int rc;
-
-static void report_error(const char *class, int ch)
-{
-	printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
-	rc = 1;
-}
-
-static int is_in(const char *s, int ch)
-{
-	/*
-	 * We can't find NUL using strchr. Accept it as the first
-	 * character in the spec -- there are no empty classes.
-	 */
-	if (ch == '\0')
-		return ch == *s;
-	if (*s == '\0')
-		s++;
-	return !!strchr(s, ch);
-}
-
-#define TEST_CLASS(t,s) {			\
-	int i;					\
-	for (i = 0; i < 256; i++) {		\
-		if (is_in(s, i) != t(i))	\
-			report_error(#t, i);	\
-	}					\
-}
-
-#define DIGIT "0123456789"
-#define LOWER "abcdefghijklmnopqrstuvwxyz"
-#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
-#define ASCII \
-	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
-	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
-	"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
-	"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
-	"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
-	"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
-	"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
-	"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
-#define CNTRL \
-	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
-	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
-	"\x7f"
-
-int cmd__ctype(int argc, const char **argv)
-{
-	TEST_CLASS(isdigit, DIGIT);
-	TEST_CLASS(isspace, " \n\r\t");
-	TEST_CLASS(isalpha, LOWER UPPER);
-	TEST_CLASS(isalnum, LOWER UPPER DIGIT);
-	TEST_CLASS(is_glob_special, "*?[\\");
-	TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
-	TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
-	TEST_CLASS(isascii, ASCII);
-	TEST_CLASS(islower, LOWER);
-	TEST_CLASS(isupper, UPPER);
-	TEST_CLASS(iscntrl, CNTRL);
-	TEST_CLASS(ispunct, PUNCT);
-	TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
-	TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
-
-	return rc;
-}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 45951b1..0683d46 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "date.h"
+#include "trace.h"
 
 static const char *usage_msg = "\n"
 "  test-tool date relative [time_t]...\n"
@@ -81,7 +81,7 @@
 {
 	for (; *argv; argv++) {
 		timestamp_t t;
-		t = approxidate_relative(*argv);
+		t = approxidate(*argv);
 		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
 	}
 }
@@ -90,7 +90,7 @@
 {
 	for (; *argv; argv++) {
 		timestamp_t t;
-		t = approxidate_relative(*argv);
+		t = approxidate(*argv);
 		printf("%s -> %"PRItime"\n", *argv, t);
 	}
 }
@@ -104,7 +104,7 @@
 	printf("%lf\n", seconds);
 }
 
-int cmd__date(int argc, const char **argv)
+int cmd__date(int argc UNUSED, const char **argv)
 {
 	const char *x;
 
diff --git a/t/helper/test-delete-gpgsig.c b/t/helper/test-delete-gpgsig.c
new file mode 100644
index 0000000..e36831a
--- /dev/null
+++ b/t/helper/test-delete-gpgsig.c
@@ -0,0 +1,62 @@
+#include "test-tool.h"
+#include "gpg-interface.h"
+#include "strbuf.h"
+
+
+int cmd__delete_gpgsig(int argc, const char **argv)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *pattern = "gpgsig";
+	const char *bufptr, *tail, *eol;
+	int deleting = 0;
+	size_t plen;
+
+	if (argc >= 2) {
+		pattern = argv[1];
+		argv++;
+		argc--;
+	}
+
+	plen = strlen(pattern);
+	strbuf_read(&buf, 0, 0);
+
+	if (!strcmp(pattern, "trailer")) {
+		size_t payload_size = parse_signed_buffer(buf.buf, buf.len);
+		fwrite(buf.buf, 1, payload_size, stdout);
+		fflush(stdout);
+		return 0;
+	}
+
+	bufptr = buf.buf;
+	tail = bufptr + buf.len;
+
+	while (bufptr < tail) {
+		/* Find the end of the line */
+		eol = memchr(bufptr, '\n', tail - bufptr);
+		if (!eol)
+			eol = tail;
+
+		/* Drop continuation lines */
+		if (deleting && (bufptr < eol) && (bufptr[0] == ' ')) {
+			bufptr = eol + 1;
+			continue;
+		}
+		deleting = 0;
+
+		/* Does the line match the prefix? */
+		if (((bufptr + plen) < eol) &&
+		    !memcmp(bufptr, pattern, plen) &&
+		    (bufptr[plen] == ' ')) {
+			deleting = 1;
+			bufptr = eol + 1;
+			continue;
+		}
+
+		/* Print all other lines */
+		fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
+		bufptr = eol + 1;
+	}
+	fflush(stdout);
+
+	return 0;
+}
diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c
index b15481e..6bc787a 100644
--- a/t/helper/test-delta.c
+++ b/t/helper/test-delta.c
@@ -11,7 +11,6 @@
 #include "test-tool.h"
 #include "git-compat-util.h"
 #include "delta.h"
-#include "cache.h"
 
 static const char usage_str[] =
 	"test-tool delta (-d|-p) <from_file> <data_file> <out_file>";
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
index e37396d..73e551c 100644
--- a/t/helper/test-drop-caches.c
+++ b/t/helper/test-drop-caches.c
@@ -155,7 +155,7 @@
 
 #endif
 
-int cmd__drop_caches(int argc, const char **argv)
+int cmd__drop_caches(int argc UNUSED, const char **argv UNUSED)
 {
 	cmd_sync();
 	return cmd_dropcaches();
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 454f17b..c38f546 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -1,9 +1,12 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "hash.h"
+#include "hex.h"
 #include "tree.h"
 #include "cache-tree.h"
-
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
 {
@@ -56,7 +59,7 @@
 	return errs;
 }
 
-int cmd__dump_cache_tree(int ac, const char **av)
+int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
 {
 	struct index_state istate;
 	struct cache_tree *another = cache_tree();
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 975f0ac..4f215fe 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -1,7 +1,9 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
-int cmd__dump_fsmonitor(int ac, const char **av)
+int cmd__dump_fsmonitor(int ac UNUSED, const char **av UNUSED)
 {
 	struct index_state *istate = the_repository->index;
 	int i;
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index 0ea97b8..f29d18e 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -1,15 +1,18 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "split-index.h"
 #include "ewah/ewok.h"
 
-static void show_bit(size_t pos, void *data)
+static void show_bit(size_t pos, void *data UNUSED)
 {
 	printf(" %d", (int)pos);
 }
 
-int cmd__dump_split_index(int ac, const char **av)
+int cmd__dump_split_index(int ac UNUSED, const char **av)
 {
 	struct split_index *si;
 	int i;
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 6d53683..b4af971 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,7 +1,10 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "dir.h"
+#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 static int compare_untracked(const void *a_, const void *b_)
 {
@@ -40,7 +43,7 @@
 	strbuf_setlen(base, len);
 }
 
-int cmd__dump_untracked_cache(int ac, const char **av)
+int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
 {
 	struct untracked_cache *uc;
 	struct strbuf base = STRBUF_INIT;
diff --git a/t/helper/test-env-helper.c b/t/helper/test-env-helper.c
index 66c88b8..1c48688 100644
--- a/t/helper/test-env-helper.c
+++ b/t/helper/test-env-helper.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "config.h"
+#include "parse.h"
 #include "parse-options.h"
 
 static char const * const env__helper_usage[] = {
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index b9d1200..8f59f6b 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -1,9 +1,10 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "git-compat-util.h"
 #include "object.h"
 #include "decorate.h"
+#include "repository.h"
 
-int cmd__example_decorate(int argc, const char **argv)
+int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
 {
 	struct decoration n;
 	struct object_id one_oid = { {1} };
@@ -71,5 +72,7 @@
 	if (objects_noticed != 2)
 		BUG("should have 2 objects");
 
+	clear_decoration(&n, NULL);
+
 	return 0;
 }
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
deleted file mode 100644
index efc82dd..0000000
--- a/t/helper/test-fast-rebase.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * "git fast-rebase" builtin command
- *
- * FAST: Forking Any Subprocesses (is) Taboo
- *
- * This is meant SOLELY as a demo of what is possible.  sequencer.c and
- * rebase.c should be refactored to use the ideas here, rather than attempting
- * to extend this file to replace those (unless Phillip or Dscho say that
- * refactoring is too hard and we need a clean slate, but I'm guessing that
- * refactoring is the better route).
- */
-
-#define USE_THE_INDEX_VARIABLE
-#include "test-tool.h"
-
-#include "cache-tree.h"
-#include "commit.h"
-#include "lockfile.h"
-#include "merge-ort.h"
-#include "refs.h"
-#include "revision.h"
-#include "sequencer.h"
-#include "strvec.h"
-#include "tree.h"
-
-static const char *short_commit_name(struct commit *commit)
-{
-	return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV);
-}
-
-static struct commit *peel_committish(const char *name)
-{
-	struct object *obj;
-	struct object_id oid;
-
-	if (get_oid(name, &oid))
-		return NULL;
-	obj = parse_object(the_repository, &oid);
-	return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
-static char *get_author(const char *message)
-{
-	size_t len;
-	const char *a;
-
-	a = find_commit_header(message, "author", &len);
-	if (a)
-		return xmemdupz(a, len);
-
-	return NULL;
-}
-
-static struct commit *create_commit(struct tree *tree,
-				    struct commit *based_on,
-				    struct commit *parent)
-{
-	struct object_id ret;
-	struct object *obj;
-	struct commit_list *parents = NULL;
-	char *author;
-	char *sign_commit = NULL;
-	struct commit_extra_header *extra;
-	struct strbuf msg = STRBUF_INIT;
-	const char *out_enc = get_commit_output_encoding();
-	const char *message = logmsg_reencode(based_on, NULL, out_enc);
-	const char *orig_message = NULL;
-	const char *exclude_gpgsig[] = { "gpgsig", NULL };
-
-	commit_list_insert(parent, &parents);
-	extra = read_commit_extra_headers(based_on, exclude_gpgsig);
-	find_commit_subject(message, &orig_message);
-	strbuf_addstr(&msg, orig_message);
-	author = get_author(message);
-	reset_ident_date();
-	if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
-				 &ret, author, NULL, sign_commit, extra)) {
-		error(_("failed to write commit object"));
-		return NULL;
-	}
-	free(author);
-	strbuf_release(&msg);
-
-	obj = parse_object(the_repository, &ret);
-	return (struct commit *)obj;
-}
-
-int cmd__fast_rebase(int argc, const char **argv)
-{
-	struct commit *onto;
-	struct commit *last_commit = NULL, *last_picked_commit = NULL;
-	struct object_id head;
-	struct lock_file lock = LOCK_INIT;
-	struct strvec rev_walk_args = STRVEC_INIT;
-	struct rev_info revs;
-	struct commit *commit;
-	struct merge_options merge_opt;
-	struct tree *next_tree, *base_tree, *head_tree;
-	struct merge_result result;
-	struct strbuf reflog_msg = STRBUF_INIT;
-	struct strbuf branch_name = STRBUF_INIT;
-	int ret = 0;
-
-	/*
-	 * test-tool stuff doesn't set up the git directory by default; need to
-	 * do that manually.
-	 */
-	setup_git_directory();
-
-	if (argc == 2 && !strcmp(argv[1], "-h")) {
-		printf("Sorry, I am not a psychiatrist; I can not give you the help you need.  Oh, you meant usage...\n");
-		exit(129);
-	}
-
-	if (argc != 5 || strcmp(argv[1], "--onto"))
-		die("usage: read the code, figure out how to use it, then do so");
-
-	onto = peel_committish(argv[2]);
-	strbuf_addf(&branch_name, "refs/heads/%s", argv[4]);
-
-	/* Sanity check */
-	if (get_oid("HEAD", &head))
-		die(_("Cannot read HEAD"));
-	assert(oideq(&onto->object.oid, &head));
-
-	repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
-	if (repo_read_index(the_repository) < 0)
-		BUG("Could not read index");
-
-	repo_init_revisions(the_repository, &revs, NULL);
-	revs.verbose_header = 1;
-	revs.max_parents = 1;
-	revs.cherry_mark = 1;
-	revs.limited = 1;
-	revs.reverse = 1;
-	revs.right_only = 1;
-	revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
-	revs.topo_order = 1;
-	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
-
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
-		ret = error(_("unhandled options"));
-		goto cleanup;
-	}
-
-	strvec_clear(&rev_walk_args);
-
-	if (prepare_revision_walk(&revs) < 0) {
-		ret = error(_("error preparing revisions"));
-		goto cleanup;
-	}
-
-	init_merge_options(&merge_opt, the_repository);
-	memset(&result, 0, sizeof(result));
-	merge_opt.show_rename_progress = 1;
-	merge_opt.branch1 = "HEAD";
-	head_tree = get_commit_tree(onto);
-	result.tree = head_tree;
-	last_commit = onto;
-	while ((commit = get_revision(&revs))) {
-		struct commit *base;
-
-		fprintf(stderr, "Rebasing %s...\r",
-			oid_to_hex(&commit->object.oid));
-		assert(commit->parents && !commit->parents->next);
-		base = commit->parents->item;
-
-		next_tree = get_commit_tree(commit);
-		base_tree = get_commit_tree(base);
-
-		merge_opt.branch2 = short_commit_name(commit);
-		merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2);
-
-		merge_incore_nonrecursive(&merge_opt,
-					  base_tree,
-					  result.tree,
-					  next_tree,
-					  &result);
-
-		free((char*)merge_opt.ancestor);
-		merge_opt.ancestor = NULL;
-		if (!result.clean)
-			break;
-		last_picked_commit = commit;
-		last_commit = create_commit(result.tree, commit, last_commit);
-	}
-
-	merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean);
-
-	if (result.clean < 0)
-		exit(128);
-
-	if (result.clean) {
-		fprintf(stderr, "\nDone.\n");
-		strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
-			    oid_to_hex(&last_picked_commit->object.oid),
-			    oid_to_hex(&last_commit->object.oid));
-		if (update_ref(reflog_msg.buf, branch_name.buf,
-			       &last_commit->object.oid,
-			       &last_picked_commit->object.oid,
-			       REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
-			error(_("could not update %s"), argv[4]);
-			die("Failed to update %s", argv[4]);
-		}
-		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
-			die(_("unable to update HEAD"));
-
-		prime_cache_tree(the_repository, the_repository->index,
-				 result.tree);
-	} else {
-		fprintf(stderr, "\nAborting: Hit a conflict.\n");
-		strbuf_addf(&reflog_msg, "rebase progress up to %s",
-			    oid_to_hex(&last_picked_commit->object.oid));
-		if (update_ref(reflog_msg.buf, "HEAD",
-			       &last_commit->object.oid,
-			       &head,
-			       REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
-			error(_("could not update %s"), argv[4]);
-			die("Failed to update %s", argv[4]);
-		}
-	}
-	if (write_locked_index(&the_index, &lock,
-			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-		die(_("unable to write %s"), get_index_file());
-
-	ret = (result.clean == 0);
-cleanup:
-	strbuf_release(&reflog_msg);
-	strbuf_release(&branch_name);
-	release_revisions(&revs);
-	return ret;
-}
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
new file mode 100644
index 0000000..e8bd793
--- /dev/null
+++ b/t/helper/test-find-pack.c
@@ -0,0 +1,50 @@
+#include "test-tool.h"
+#include "object-name.h"
+#include "object-store.h"
+#include "packfile.h"
+#include "parse-options.h"
+#include "setup.h"
+
+/*
+ * Display the path(s), one per line, of the packfile(s) containing
+ * the given object.
+ *
+ * If '--check-count <n>' is passed, then error out if the number of
+ * packfiles containing the object is not <n>.
+ */
+
+static const char *find_pack_usage[] = {
+	"test-tool find-pack [--check-count <n>] <object>",
+	NULL
+};
+
+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();
+
+	struct option options[] = {
+		OPT_INTEGER('c', "check-count", &count, "expected number of packs"),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, options, find_pack_usage, 0);
+	if (argc != 1)
+		usage(find_pack_usage[0]);
+
+	if (repo_get_oid(the_repository, argv[0], &oid))
+		die("cannot parse %s as an object name", argv[0]);
+
+	for (p = get_all_packs(the_repository); p; p = p->next)
+		if (find_pack_entry_one(oid.hash, p)) {
+			printf("%s\n", p->pack_name);
+			actual_count++;
+		}
+
+	if (count > -1 && count != actual_count)
+		die("bad packfile count %d instead of %d", actual_count, count);
+
+	return 0;
+}
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index 54a4856..8280984 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -4,14 +4,16 @@
  */
 
 #include "test-tool.h"
-#include "cache.h"
 #include "parse-options.h"
 #include "fsmonitor-ipc.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "thread-utils.h"
 #include "trace2.h"
 
 #ifndef HAVE_FSMONITOR_DAEMON_BACKEND
-int cmd__fsmonitor_client(int argc, const char **argv)
+int cmd__fsmonitor_client(int argc UNUSED, const char **argv UNUSED)
 {
 	die("fsmonitor--daemon not available on this platform");
 }
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
index f40d9ad..b235da5 100644
--- a/t/helper/test-hash-speed.c
+++ b/t/helper/test-hash-speed.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
 
 #define NUM_SECONDS 3
 
diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c
index 5860dab..45d829c 100644
--- a/t/helper/test-hash.c
+++ b/t/helper/test-hash.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 
 int cmd_hash_impl(int ac, const char **av, int algo)
 {
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 36ff07b..0eb0b3d 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -2,6 +2,7 @@
 #include "git-compat-util.h"
 #include "hashmap.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 struct test_entry
 {
@@ -150,6 +151,7 @@
  */
 int cmd__hashmap(int argc, const char **argv)
 {
+	struct string_list parts = STRING_LIST_INIT_NODUP;
 	struct strbuf line = STRBUF_INIT;
 	int icase;
 	struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase);
@@ -159,21 +161,26 @@
 
 	/* process commands from stdin */
 	while (strbuf_getline(&line, stdin) != EOF) {
-		char *cmd, *p1 = NULL, *p2 = NULL;
+		char *cmd, *p1, *p2;
 		unsigned int hash = 0;
 		struct test_entry *entry;
 
 		/* break line into command and up to two parameters */
-		cmd = strtok(line.buf, DELIM);
+		string_list_setlen(&parts, 0);
+		string_list_split_in_place(&parts, line.buf, DELIM, 2);
+		string_list_remove_empty_items(&parts, 0);
+
 		/* ignore empty lines */
-		if (!cmd || *cmd == '#')
+		if (!parts.nr)
+			continue;
+		if (!*parts.items[0].string || *parts.items[0].string == '#')
 			continue;
 
-		p1 = strtok(NULL, DELIM);
-		if (p1) {
+		cmd = parts.items[0].string;
+		p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
+		p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
+		if (p1)
 			hash = icase ? strihash(p1) : strhash(p1);
-			p2 = strtok(NULL, DELIM);
-		}
 
 		if (!strcmp("add", cmd) && p1 && p2) {
 
@@ -260,6 +267,7 @@
 		}
 	}
 
+	string_list_clear(&parts, 0);
 	strbuf_release(&line);
 	hashmap_clear_and_free(&map, struct test_entry, ent);
 	return 0;
diff --git a/t/helper/test-hexdump.c b/t/helper/test-hexdump.c
index 811e89c..05f55ec 100644
--- a/t/helper/test-hexdump.c
+++ b/t/helper/test-hexdump.c
@@ -4,7 +4,7 @@
 /*
  * Read stdin and print a hexdump to stdout.
  */
-int cmd__hexdump(int argc, const char **argv)
+int cmd__hexdump(int argc UNUSED, const char **argv UNUSED)
 {
 	char buf[1024];
 	ssize_t i, len;
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
deleted file mode 100644
index fcd1096..0000000
--- a/t/helper/test-index-version.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-
-int cmd__index_version(int argc, const char **argv)
-{
-	struct cache_header hdr;
-	int version;
-
-	memset(&hdr,0,sizeof(hdr));
-	if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
-		return 0;
-	version = ntohl(hdr.hdr_version);
-	printf("%d\n", version);
-	return 0;
-}
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index 8c3edac..afe393f 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "json-writer.h"
+#include "string-list.h"
 
 static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
 static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
@@ -395,35 +395,41 @@
 	return 0;
 }
 
-static void get_s(int line_nr, char **s_in)
+struct line {
+	struct string_list *parts;
+	size_t consumed_nr;
+	int nr;
+};
+
+static void get_s(struct line *line, char **s_in)
 {
-	*s_in = strtok(NULL, " ");
-	if (!*s_in)
-		die("line[%d]: expected: <s>", line_nr);
+	if (line->consumed_nr > line->parts->nr)
+		die("line[%d]: expected: <s>", line->nr);
+	*s_in = line->parts->items[line->consumed_nr++].string;
 }
 
-static void get_i(int line_nr, intmax_t *s_in)
+static void get_i(struct line *line, intmax_t *s_in)
 {
 	char *s;
 	char *endptr;
 
-	get_s(line_nr, &s);
+	get_s(line, &s);
 
 	*s_in = strtol(s, &endptr, 10);
 	if (*endptr || errno == ERANGE)
-		die("line[%d]: invalid integer value", line_nr);
+		die("line[%d]: invalid integer value", line->nr);
 }
 
-static void get_d(int line_nr, double *s_in)
+static void get_d(struct line *line, double *s_in)
 {
 	char *s;
 	char *endptr;
 
-	get_s(line_nr, &s);
+	get_s(line, &s);
 
 	*s_in = strtod(s, &endptr);
 	if (*endptr || errno == ERANGE)
-		die("line[%d]: invalid float value", line_nr);
+		die("line[%d]: invalid float value", line->nr);
 }
 
 static int pretty;
@@ -454,6 +460,7 @@
 
 static int scripted(void)
 {
+	struct string_list parts = STRING_LIST_INIT_NODUP;
 	struct json_writer jw = JSON_WRITER_INIT;
 	char buf[MAX_LINE_LENGTH];
 	char *line;
@@ -471,66 +478,77 @@
 		die("expected first line to be 'object' or 'array'");
 
 	while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
+		struct line state = { 0 };
 		char *verb;
 		char *key;
 		char *s_value;
 		intmax_t i_value;
 		double d_value;
 
-		line_nr++;
+		state.parts = &parts;
+		state.nr = ++line_nr;
 
-		verb = strtok(line, " ");
+		/* break line into command and zero or more tokens */
+		string_list_setlen(&parts, 0);
+		string_list_split_in_place(&parts, line, " ", -1);
+		string_list_remove_empty_items(&parts, 0);
+
+		/* ignore empty lines */
+		if (!parts.nr || !*parts.items[0].string)
+			continue;
+
+		verb = parts.items[state.consumed_nr++].string;
 
 		if (!strcmp(verb, "end")) {
 			jw_end(&jw);
 		}
 		else if (!strcmp(verb, "object-string")) {
-			get_s(line_nr, &key);
-			get_s(line_nr, &s_value);
+			get_s(&state, &key);
+			get_s(&state, &s_value);
 			jw_object_string(&jw, key, s_value);
 		}
 		else if (!strcmp(verb, "object-int")) {
-			get_s(line_nr, &key);
-			get_i(line_nr, &i_value);
+			get_s(&state, &key);
+			get_i(&state, &i_value);
 			jw_object_intmax(&jw, key, i_value);
 		}
 		else if (!strcmp(verb, "object-double")) {
-			get_s(line_nr, &key);
-			get_i(line_nr, &i_value);
-			get_d(line_nr, &d_value);
+			get_s(&state, &key);
+			get_i(&state, &i_value);
+			get_d(&state, &d_value);
 			jw_object_double(&jw, key, i_value, d_value);
 		}
 		else if (!strcmp(verb, "object-true")) {
-			get_s(line_nr, &key);
+			get_s(&state, &key);
 			jw_object_true(&jw, key);
 		}
 		else if (!strcmp(verb, "object-false")) {
-			get_s(line_nr, &key);
+			get_s(&state, &key);
 			jw_object_false(&jw, key);
 		}
 		else if (!strcmp(verb, "object-null")) {
-			get_s(line_nr, &key);
+			get_s(&state, &key);
 			jw_object_null(&jw, key);
 		}
 		else if (!strcmp(verb, "object-object")) {
-			get_s(line_nr, &key);
+			get_s(&state, &key);
 			jw_object_inline_begin_object(&jw, key);
 		}
 		else if (!strcmp(verb, "object-array")) {
-			get_s(line_nr, &key);
+			get_s(&state, &key);
 			jw_object_inline_begin_array(&jw, key);
 		}
 		else if (!strcmp(verb, "array-string")) {
-			get_s(line_nr, &s_value);
+			get_s(&state, &s_value);
 			jw_array_string(&jw, s_value);
 		}
 		else if (!strcmp(verb, "array-int")) {
-			get_i(line_nr, &i_value);
+			get_i(&state, &i_value);
 			jw_array_intmax(&jw, i_value);
 		}
 		else if (!strcmp(verb, "array-double")) {
-			get_i(line_nr, &i_value);
-			get_d(line_nr, &d_value);
+			get_i(&state, &i_value);
+			get_d(&state, &d_value);
 			jw_array_double(&jw, i_value, d_value);
 		}
 		else if (!strcmp(verb, "array-true"))
@@ -553,6 +571,7 @@
 	printf("%s\n", jw.json.buf);
 
 	jw_release(&jw);
+	string_list_clear(&parts, 0);
 	return 0;
 }
 
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index ab86c14..187a115 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -1,7 +1,12 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
+#include "environment.h"
+#include "name-hash.h"
 #include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
+#include "trace.h"
 
 static int single;
 static int multi;
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index 4079fde..d0db5ff 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -1,17 +1,21 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "match-trees.h"
+#include "object-name.h"
+#include "repository.h"
+#include "setup.h"
 #include "tree.h"
 
-int cmd__match_trees(int ac, const char **av)
+int cmd__match_trees(int ac UNUSED, const char **av)
 {
 	struct object_id hash1, hash2, shifted;
 	struct tree *one, *two;
 
 	setup_git_directory();
 
-	if (get_oid(av[1], &hash1))
+	if (repo_get_oid(the_repository, av[1], &hash1))
 		die("cannot parse %s as an object name", av[1]);
-	if (get_oid(av[2], &hash2))
+	if (repo_get_oid(the_repository, av[2], &hash2))
 		die("cannot parse %s as an object name", av[2]);
 	one = parse_tree_indirect(&hash1);
 	if (!one)
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
index 335e5bb..42ccc87 100644
--- a/t/helper/test-mergesort.c
+++ b/t/helper/test-mergesort.c
@@ -1,6 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "mem-pool.h"
 #include "mergesort.h"
+#include "strbuf.h"
 
 static uint32_t minstd_rand(uint32_t *state)
 {
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index d1324d0..aafe398 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -1,14 +1,16 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "oid-array.h"
+#include "setup.h"
+#include "strbuf.h"
 
-static int print_oid(const struct object_id *oid, void *data)
+static int print_oid(const struct object_id *oid, void *data UNUSED)
 {
 	puts(oid_to_hex(oid));
 	return 0;
 }
 
-int cmd__oid_array(int argc, const char **argv)
+int cmd__oid_array(int argc UNUSED, const char **argv UNUSED)
 {
 	struct oid_array array = OID_ARRAY_INIT;
 	struct strbuf line = STRBUF_INIT;
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
index 0acf999..bd30244 100644
--- a/t/helper/test-oidmap.c
+++ b/t/helper/test-oidmap.c
@@ -1,7 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
+#include "object-name.h"
 #include "oidmap.h"
+#include "repository.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 /* key is an oid and value is a name (could be a refname for example) */
 struct test_entry {
@@ -21,8 +25,9 @@
  * iterate -> oidkey1 namevalue1\noidkey2 namevalue2\n...
  *
  */
-int cmd__oidmap(int argc, const char **argv)
+int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
 {
+	struct string_list parts = STRING_LIST_INIT_NODUP;
 	struct strbuf line = STRBUF_INIT;
 	struct oidmap map = OIDMAP_INIT;
 
@@ -33,23 +38,28 @@
 
 	/* process commands from stdin */
 	while (strbuf_getline(&line, stdin) != EOF) {
-		char *cmd, *p1 = NULL, *p2 = NULL;
+		char *cmd, *p1, *p2;
 		struct test_entry *entry;
 		struct object_id oid;
 
 		/* break line into command and up to two parameters */
-		cmd = strtok(line.buf, DELIM);
+		string_list_setlen(&parts, 0);
+		string_list_split_in_place(&parts, line.buf, DELIM, 2);
+		string_list_remove_empty_items(&parts, 0);
+
 		/* ignore empty lines */
-		if (!cmd || *cmd == '#')
+		if (!parts.nr)
+			continue;
+		if (!*parts.items[0].string || *parts.items[0].string == '#')
 			continue;
 
-		p1 = strtok(NULL, DELIM);
-		if (p1)
-			p2 = strtok(NULL, DELIM);
+		cmd = parts.items[0].string;
+		p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
+		p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
 
 		if (!strcmp("put", cmd) && p1 && p2) {
 
-			if (get_oid(p1, &oid)) {
+			if (repo_get_oid(the_repository, p1, &oid)) {
 				printf("Unknown oid: %s\n", p1);
 				continue;
 			}
@@ -67,7 +77,7 @@
 
 		} else if (!strcmp("get", cmd) && p1) {
 
-			if (get_oid(p1, &oid)) {
+			if (repo_get_oid(the_repository, p1, &oid)) {
 				printf("Unknown oid: %s\n", p1);
 				continue;
 			}
@@ -80,7 +90,7 @@
 
 		} else if (!strcmp("remove", cmd) && p1) {
 
-			if (get_oid(p1, &oid)) {
+			if (repo_get_oid(the_repository, p1, &oid)) {
 				printf("Unknown oid: %s\n", p1);
 				continue;
 			}
@@ -106,6 +116,7 @@
 		}
 	}
 
+	string_list_clear(&parts, 0);
 	strbuf_release(&line);
 	oidmap_free(&map, 1);
 	return 0;
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
index d48a409..c7a1d4c 100644
--- a/t/helper/test-oidtree.c
+++ b/t/helper/test-oidtree.c
@@ -1,14 +1,16 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "oidtree.h"
+#include "setup.h"
+#include "strbuf.h"
 
-static enum cb_next print_oid(const struct object_id *oid, void *data)
+static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
 {
 	puts(oid_to_hex(oid));
 	return CB_CONTINUE;
 }
 
-int cmd__oidtree(int argc, const char **argv)
+int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
 {
 	struct oidtree ot;
 	struct strbuf line = STRBUF_INIT;
diff --git a/t/helper/test-online-cpus.c b/t/helper/test-online-cpus.c
index 8cb0d53..47dc211 100644
--- a/t/helper/test-online-cpus.c
+++ b/t/helper/test-online-cpus.c
@@ -2,7 +2,7 @@
 #include "git-compat-util.h"
 #include "thread-utils.h"
 
-int cmd__online_cpus(int argc, const char **argv)
+int cmd__online_cpus(int argc UNUSED, const char **argv UNUSED)
 {
 	printf("%d\n", online_cpus());
 	return 0;
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index f7b79da..67a964e 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -1,9 +1,10 @@
-#include "git-compat-util.h"
 #include "test-tool.h"
+#include "hex.h"
 #include "strbuf.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "packfile.h"
 #include "pack-mtimes.h"
+#include "setup.h"
 
 static void dump_mtimes(struct packed_git *p)
 {
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 5068355..ded8116 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "parse-options.h"
+#include "strbuf.h"
 #include "string-list.h"
 #include "trace2.h"
 
@@ -21,6 +21,19 @@
 	int unset;
 } length_cb;
 
+static int mode34_callback(const struct option *opt, const char *arg, int unset)
+{
+	if (unset)
+		*(int *)opt->value = 0;
+	else if (!strcmp(arg, "3"))
+		*(int *)opt->value = 3;
+	else if (!strcmp(arg, "4"))
+		*(int *)opt->value = 4;
+	else
+		return error("invalid value for '%s': '%s'", "--mode34", arg);
+	return 0;
+}
+
 static int length_callback(const struct option *opt, const char *arg, int unset)
 {
 	length_cb.called = 1;
@@ -124,6 +137,9 @@
 		OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
 		OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
 		OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
+		OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)",
+			"set integer to 3 or 4 (cmdmode option)",
+			PARSE_OPT_CMDMODE, mode34_callback),
 		OPT_CALLBACK('L', "length", &integer, "str",
 			"get length of <str>", length_callback),
 		OPT_FILENAME('F', "file", &file, "set file to <file>"),
@@ -133,6 +149,8 @@
 		OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
 		OPT_STRING('o', NULL, &string, "str", "get another string"),
 		OPT_NOOP_NOARG(0, "obsolete"),
+		OPT_SET_INT_F(0, "longhelp", &integer, "help text of this entry\n"
+			      "spans multiple lines", 0, PARSE_OPT_NONEG),
 		OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
 		OPT_GROUP("Magic arguments"),
 		OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
@@ -263,14 +281,14 @@
 	return parse_options_flags__cmd(argc, argv, test_flags);
 }
 
-static int subcmd_one(int argc, const char **argv, const char *prefix)
+static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED)
 {
 	printf("fn: subcmd_one\n");
 	print_args(argc, argv);
 	return 0;
 }
 
-static int subcmd_two(int argc, const char **argv, const char *prefix)
+static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED)
 {
 	printf("fn: subcmd_two\n");
 	print_args(argc, argv);
diff --git a/t/helper/test-parse-pathspec-file.c b/t/helper/test-parse-pathspec-file.c
index b3e08ce..89ecefd 100644
--- a/t/helper/test-parse-pathspec-file.c
+++ b/t/helper/test-parse-pathspec-file.c
@@ -1,12 +1,11 @@
 #include "test-tool.h"
 #include "parse-options.h"
 #include "pathspec.h"
-#include "gettext.h"
 
 int cmd__parse_pathspec_file(int argc, const char **argv)
 {
 	struct pathspec pathspec;
-	const char *pathspec_from_file = NULL;
+	char *pathspec_from_file = NULL;
 	int pathspec_file_nul = 0, i;
 
 	static const char *const usage[] = {
@@ -29,5 +28,6 @@
 		printf("%s\n", pathspec.items[i].original);
 
 	clear_pathspec(&pathspec);
+	free(pathspec_from_file);
 	return 0;
 }
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index 3f102cf..910a128 100644
--- a/t/helper/test-partial-clone.c
+++ b/t/helper/test-partial-clone.c
@@ -1,7 +1,8 @@
-#include "cache.h"
 #include "test-tool.h"
+#include "hex.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "setup.h"
 
 /*
  * Prints the size of the object corresponding to the given hash in a specific
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index f69709d..70396fa 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -1,6 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "abspath.h"
+#include "environment.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "setup.h"
 #include "string-list.h"
+#include "trace.h"
 #include "utf8.h"
 
 /*
diff --git a/t/helper/test-pcre2-config.c b/t/helper/test-pcre2-config.c
index 5258fdd..5d0b2a2 100644
--- a/t/helper/test-pcre2-config.c
+++ b/t/helper/test-pcre2-config.c
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "grep.h"
 
 int cmd__pcre2_config(int argc, const char **argv)
diff --git a/t/helper/test-pkt-line.c b/t/helper/test-pkt-line.c
index c5e052e..4daa82f 100644
--- a/t/helper/test-pkt-line.c
+++ b/t/helper/test-pkt-line.c
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "test-tool.h"
 #include "pkt-line.h"
+#include "sideband.h"
+#include "write-or-die.h"
+#include "parse-options.h"
 
 static void pack_line(const char *line)
 {
@@ -63,12 +66,33 @@
 	}
 }
 
-static void unpack_sideband(void)
+static void unpack_sideband(int argc, const char **argv)
 {
 	struct packet_reader reader;
-	packet_reader_init(&reader, 0, NULL, 0,
-			   PACKET_READ_GENTLE_ON_EOF |
-			   PACKET_READ_CHOMP_NEWLINE);
+	int options = PACKET_READ_GENTLE_ON_EOF;
+	int chomp_newline = 1;
+	int reader_use_sideband = 0;
+	const char *const unpack_sideband_usage[] = {
+		"test_tool unpack_sideband [options...]", NULL
+	};
+	struct option cmd_options[] = {
+		OPT_BOOL(0, "reader-use-sideband", &reader_use_sideband,
+			 "set use_sideband bit for packet reader (Default: off)"),
+		OPT_BOOL(0, "chomp-newline", &chomp_newline,
+			 "chomp newline in packet (Default: on)"),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, "", cmd_options, unpack_sideband_usage,
+			     0);
+	if (argc > 0)
+		usage_msg_opt(_("too many arguments"), unpack_sideband_usage,
+			      cmd_options);
+
+	if (chomp_newline)
+		options |= PACKET_READ_CHOMP_NEWLINE;
+	packet_reader_init(&reader, 0, NULL, 0, options);
+	reader.use_sideband = reader_use_sideband;
 
 	while (packet_reader_read(&reader) != PACKET_READ_EOF) {
 		int band;
@@ -78,6 +102,17 @@
 		case PACKET_READ_EOF:
 			break;
 		case PACKET_READ_NORMAL:
+			/*
+			 * When the "use_sideband" field of the reader is turned
+			 * on, sideband packets other than the payload have been
+			 * parsed and consumed in packet_reader_read(), and only
+			 * the payload arrives here.
+			 */
+			if (reader.use_sideband) {
+				write_or_die(1, reader.line, reader.pktlen - 1);
+				break;
+			}
+
 			band = reader.line[0] & 0xff;
 			if (band < 1 || band > 2)
 				continue; /* skip non-sideband packets */
@@ -96,15 +131,31 @@
 
 static int send_split_sideband(void)
 {
+	const char *foo = "Foo.\n";
+	const char *bar = "Bar.\n";
 	const char *part1 = "Hello,";
 	const char *primary = "\001primary: regular output\n";
 	const char *part2 = " world!\n";
 
+	/* Each sideband message has a trailing newline character. */
+	send_sideband(1, 2, foo, strlen(foo), LARGE_PACKET_MAX);
+	send_sideband(1, 2, bar, strlen(bar), LARGE_PACKET_MAX);
+
+	/*
+	 * One sideband message is divided into part1 and part2
+	 * by the primary message.
+	 */
 	send_sideband(1, 2, part1, strlen(part1), LARGE_PACKET_MAX);
 	packet_write(1, primary, strlen(primary));
 	send_sideband(1, 2, part2, strlen(part2), LARGE_PACKET_MAX);
 	packet_response_end(1);
 
+	/*
+	 * We use unpack_sideband() to consume packets. A flush packet
+	 * is required to end parsing.
+	 */
+	packet_flush(1);
+
 	return 0;
 }
 
@@ -125,7 +176,7 @@
 	else if (!strcmp(argv[1], "unpack"))
 		unpack();
 	else if (!strcmp(argv[1], "unpack-sideband"))
-		unpack_sideband();
+		unpack_sideband(argc - 1, argv + 1);
 	else if (!strcmp(argv[1], "send-split-sideband"))
 		send_split_sideband();
 	else if (!strcmp(argv[1], "receive-sideband"))
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
deleted file mode 100644
index 133b5e6..0000000
--- a/t/helper/test-prio-queue.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include "test-tool.h"
-#include "cache.h"
-#include "prio-queue.h"
-
-static int intcmp(const void *va, const void *vb, void *data)
-{
-	const int *a = va, *b = vb;
-	return *a - *b;
-}
-
-static void show(int *v)
-{
-	if (!v)
-		printf("NULL\n");
-	else
-		printf("%d\n", *v);
-	free(v);
-}
-
-int cmd__prio_queue(int argc, const char **argv)
-{
-	struct prio_queue pq = { intcmp };
-
-	while (*++argv) {
-		if (!strcmp(*argv, "get")) {
-			void *peek = prio_queue_peek(&pq);
-			void *get = prio_queue_get(&pq);
-			if (peek != get)
-				BUG("peek and get results do not match");
-			show(get);
-		} else if (!strcmp(*argv, "dump")) {
-			void *peek;
-			void *get;
-			while ((peek = prio_queue_peek(&pq))) {
-				get = prio_queue_get(&pq);
-				if (peek != get)
-					BUG("peek and get results do not match");
-				show(get);
-			}
-		} else if (!strcmp(*argv, "stack")) {
-			pq.compare = NULL;
-		} else {
-			int *v = xmalloc(sizeof(*v));
-			*v = atoi(*argv);
-			prio_queue_put(&pq, v);
-		}
-	}
-
-	clear_prio_queue(&pq);
-
-	return 0;
-}
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
index a4b305f..f30022d 100644
--- a/t/helper/test-proc-receive.c
+++ b/t/helper/test-proc-receive.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "test-tool.h"
 #include "connect.h"
+#include "hex.h"
 #include "parse-options.h"
 #include "pkt-line.h"
+#include "setup.h"
 #include "sigchain.h"
-#include "test-tool.h"
 
 static const char *proc_receive_usage[] = {
 	"test-tool proc-receive [<options>]",
diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c
index 6cc9735..66acb6a 100644
--- a/t/helper/test-progress.c
+++ b/t/helper/test-progress.c
@@ -19,7 +19,6 @@
  */
 #define GIT_TEST_PROGRESS_ONLY
 #include "test-tool.h"
-#include "gettext.h"
 #include "parse-options.h"
 #include "progress.h"
 #include "strbuf.h"
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 2f65c7f..1e3b431 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -1,10 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "commit.h"
 #include "commit-reach.h"
-#include "config.h"
-#include "parse-options.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
 #include "ref-filter.h"
+#include "setup.h"
 #include "string-list.h"
 #include "tag.h"
 
@@ -57,7 +58,7 @@
 		if (buf.len < 3)
 			continue;
 
-		if (get_oid_committish(buf.buf + 2, &oid))
+		if (repo_get_oid_committish(the_repository, buf.buf + 2, &oid))
 			die("failed to resolve %s", buf.buf + 2);
 
 		orig = parse_object(r, &oid);
@@ -106,13 +107,20 @@
 	if (!strcmp(av[1], "ref_newer"))
 		printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B));
 	else if (!strcmp(av[1], "in_merge_bases"))
-		printf("%s(A,B):%d\n", av[1], in_merge_bases(A, B));
+		printf("%s(A,B):%d\n", av[1],
+		       repo_in_merge_bases(the_repository, A, B));
 	else if (!strcmp(av[1], "in_merge_bases_many"))
-		printf("%s(A,X):%d\n", av[1], in_merge_bases_many(A, X_nr, X_array));
+		printf("%s(A,X):%d\n", av[1],
+		       repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
 	else if (!strcmp(av[1], "is_descendant_of"))
 		printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
 	else if (!strcmp(av[1], "get_merge_bases_many")) {
-		struct commit_list *list = get_merge_bases_many(A, X_nr, X_array);
+		struct commit_list *list = NULL;
+		if (repo_get_merge_bases_many(the_repository,
+					      A, X_nr,
+					      X_array,
+					      &list) < 0)
+			exit(128);
 		printf("%s(A,X):\n", av[1]);
 		print_sorted_commit_ids(list);
 	} else if (!strcmp(av[1], "reduce_heads")) {
@@ -131,7 +139,7 @@
 
 		printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
 	} else if (!strcmp(av[1], "commit_contains")) {
-		struct ref_filter filter;
+		struct ref_filter filter = REF_FILTER_INIT;
 		struct contains_cache cache;
 		init_contains_cache(&cache);
 
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index 23e9e27..1acd362 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,7 +1,9 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "config.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 int cmd__read_cache(int argc, const char **argv)
 {
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 98b73bb..8c7a83f 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -1,11 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "commit-graph.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "bloom.h"
+#include "setup.h"
 
-int cmd__read_graph(int argc, const char **argv)
+int cmd__read_graph(int argc UNUSED, const char **argv UNUSED)
 {
 	struct commit_graph *graph = NULL;
 	struct object_directory *odb;
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 27072ba..4acae41 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -1,9 +1,12 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "midx.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "pack-bitmap.h"
+#include "packfile.h"
+#include "setup.h"
+#include "gettext.h"
 
 static int read_midx_file(const char *object_dir, int show_objects)
 {
@@ -77,7 +80,7 @@
 static int read_midx_preferred_pack(const char *object_dir)
 {
 	struct multi_pack_index *midx = NULL;
-	struct bitmap_index *bitmap = NULL;
+	uint32_t preferred_pack;
 
 	setup_git_directory();
 
@@ -85,23 +88,45 @@
 	if (!midx)
 		return 1;
 
-	bitmap = prepare_bitmap_git(the_repository);
-	if (!bitmap)
-		return 1;
-	if (!bitmap_is_midx(bitmap)) {
-		free_bitmap_index(bitmap);
+	if (midx_preferred_pack(midx, &preferred_pack) < 0) {
+		warning(_("could not determine MIDX preferred pack"));
 		return 1;
 	}
 
-	printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
-	free_bitmap_index(bitmap);
+	printf("%s\n", midx->pack_names[preferred_pack]);
+	return 0;
+}
+
+static int read_midx_bitmapped_packs(const char *object_dir)
+{
+	struct multi_pack_index *midx = NULL;
+	struct bitmapped_pack pack;
+	uint32_t i;
+
+	setup_git_directory();
+
+	midx = load_multi_pack_index(object_dir, 1);
+	if (!midx)
+		return 1;
+
+	for (i = 0; i < midx->num_packs; i++) {
+		if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0)
+			return 1;
+
+		printf("%s\n", pack_basename(pack.p));
+		printf("  bitmap_pos: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_pos);
+		printf("  bitmap_nr: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_nr);
+	}
+
+	close_midx(midx);
+
 	return 0;
 }
 
 int cmd__read_midx(int argc, const char **argv)
 {
 	if (!(argc == 2 || argc == 3))
-		usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>");
+		usage("read-midx [--show-objects|--checksum|--preferred-pack|--bitmap] <object-dir>");
 
 	if (!strcmp(argv[1], "--show-objects"))
 		return read_midx_file(argv[2], 1);
@@ -109,5 +134,7 @@
 		return read_midx_checksum(argv[2]);
 	else if (!strcmp(argv[1], "--preferred-pack"))
 		return read_midx_preferred_pack(argv[2]);
+	else if (!strcmp(argv[1], "--bitmap"))
+		return read_midx_bitmapped_packs(argv[2]);
 	return read_midx_file(argv[1], 0);
 }
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index ae8a564..82bbf6e 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -1,9 +1,13 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hex.h"
 #include "refs.h"
+#include "setup.h"
 #include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
 #include "repository.h"
+#include "strbuf.h"
+#include "revision.h"
 
 struct flag_definition {
 	const char *name;
@@ -108,17 +112,6 @@
 	return argv + 1;
 }
 
-static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
-					       FLAG_DEF(PACK_REFS_ALL),
-					       { NULL, 0 } };
-
-static int cmd_pack_refs(struct ref_store *refs, const char **argv)
-{
-	unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
-
-	return refs_pack_refs(refs, flags);
-}
-
 static int cmd_create_symref(struct ref_store *refs, const char **argv)
 {
 	const char *refname = notnull(*argv++, "refname");
@@ -174,6 +167,15 @@
 	return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
 }
 
+static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv)
+{
+	const char *prefix = notnull(*argv++, "prefix");
+	const char **exclude_patterns = argv;
+
+	return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref,
+					NULL);
+}
+
 static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
 {
 	struct object_id oid = *null_oid();
@@ -200,14 +202,21 @@
 	return ret;
 }
 
-static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
+static int each_reflog(const char *refname, void *cb_data UNUSED)
 {
-	return refs_for_each_reflog(refs, each_ref, NULL);
+	printf("%s\n", refname);
+	return 0;
 }
 
-static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
-		       const char *committer, timestamp_t timestamp,
-		       int tz, const char *msg, void *cb_data UNUSED)
+static int cmd_for_each_reflog(struct ref_store *refs,
+			       const char **argv UNUSED)
+{
+	return refs_for_each_reflog(refs, each_reflog, NULL);
+}
+
+static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
+			   const char *committer, timestamp_t timestamp,
+			   int tz, const char *msg, void *cb_data UNUSED)
 {
 	printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid),
 	       oid_to_hex(new_oid), committer, timestamp, tz,
@@ -219,14 +228,14 @@
 {
 	const char *refname = notnull(*argv++, "refname");
 
-	return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
+	return refs_for_each_reflog_ent(refs, refname, each_reflog_ent, refs);
 }
 
 static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
 {
 	const char *refname = notnull(*argv++, "refname");
 
-	return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
+	return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog_ent, refs);
 }
 
 static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
@@ -255,11 +264,6 @@
 	return refs_delete_reflog(refs, refname);
 }
 
-static int cmd_reflog_expire(struct ref_store *refs, const char **argv)
-{
-	die("not supported yet");
-}
-
 static int cmd_delete_ref(struct ref_store *refs, const char **argv)
 {
 	const char *msg = notnull(*argv++, "msg");
@@ -281,16 +285,19 @@
 	const char *new_sha1_buf = notnull(*argv++, "new-sha1");
 	const char *old_sha1_buf = notnull(*argv++, "old-sha1");
 	unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
-	struct object_id old_oid;
+	struct object_id old_oid, *old_oid_ptr = NULL;
 	struct object_id new_oid;
 
-	if (get_oid_hex(old_sha1_buf, &old_oid))
-		die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+	if (*old_sha1_buf) {
+		if (get_oid_hex(old_sha1_buf, &old_oid))
+			die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+		old_oid_ptr = &old_oid;
+	}
 	if (get_oid_hex(new_sha1_buf, &new_oid))
 		die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name);
 
 	return refs_update_ref(refs, msg, refname,
-			       &new_oid, &old_oid,
+			       &new_oid, old_oid_ptr,
 			       flags, UPDATE_REFS_DIE_ON_ERR);
 }
 
@@ -300,11 +307,11 @@
 };
 
 static struct command commands[] = {
-	{ "pack-refs", cmd_pack_refs },
 	{ "create-symref", cmd_create_symref },
 	{ "delete-refs", cmd_delete_refs },
 	{ "rename-ref", cmd_rename_ref },
 	{ "for-each-ref", cmd_for_each_ref },
+	{ "for-each-ref--exclude", cmd_for_each_ref__exclude },
 	{ "resolve-ref", cmd_resolve_ref },
 	{ "verify-ref", cmd_verify_ref },
 	{ "for-each-reflog", cmd_for_each_reflog },
@@ -313,7 +320,6 @@
 	{ "reflog-exists", cmd_reflog_exists },
 	{ "create-reflog", cmd_create_reflog },
 	{ "delete-reflog", cmd_delete_reflog },
-	{ "reflog-expire", cmd_reflog_expire },
 	/*
 	 * backend transaction functions can't be tested separately
 	 */
@@ -322,7 +328,7 @@
 	{ NULL, NULL }
 };
 
-int cmd__ref_store(int argc, const char **argv)
+int cmd__ref_store(int argc UNUSED, const char **argv)
 {
 	struct ref_store *refs;
 	const char *func;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 1f0a28c..00237ef 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -1,3 +1,4 @@
+#include "reftable/system.h"
 #include "reftable/reftable-tests.h"
 #include "test-tool.h"
 
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index bd871a7..80042ea 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -30,7 +30,7 @@
 	if (regexec(&r, str, 1, m, 0))
 		die("no match of pattern '%s' to string '%s'", pat, str);
 
-	/* http://sourceware.org/bugzilla/show_bug.cgi?id=3957  */
+	/* https://sourceware.org/bugzilla/show_bug.cgi?id=3957 */
 	if (m[0].rm_so == 3) /* matches '\n' when it should not */
 		die("regex bug confirmed: re-build git with NO_REGEX=1");
 
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 56f0e3c..0c7c5aa 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -1,11 +1,11 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "commit-graph.h"
 #include "commit.h"
-#include "config.h"
-#include "object-store.h"
+#include "environment.h"
+#include "hex.h"
 #include "object.h"
 #include "repository.h"
+#include "setup.h"
 #include "tree.h"
 
 static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 4a45d5b..f346951 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -9,17 +9,19 @@
  */
 
 #include "test-tool.h"
-#include "cache.h"
 #include "commit.h"
 #include "diff.h"
+#include "repository.h"
 #include "revision.h"
+#include "setup.h"
 
 static void print_commit(struct commit *commit)
 {
 	struct strbuf sb = STRBUF_INIT;
 	struct pretty_print_context ctx = {0};
 	ctx.date_mode.type = DATE_NORMAL;
-	format_commit_message(commit, " %m %s", &sb, &ctx);
+	repo_format_commit_message(the_repository, commit, " %m %s", &sb,
+				   &ctx);
 	printf("%s\n", sb.buf);
 	strbuf_release(&sb);
 }
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index 3ecb830..c0ed872 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -9,8 +9,6 @@
  */
 
 #include "test-tool.h"
-#include "git-compat-util.h"
-#include "cache.h"
 #include "run-command.h"
 #include "strvec.h"
 #include "strbuf.h"
@@ -18,13 +16,12 @@
 #include "string-list.h"
 #include "thread-utils.h"
 #include "wildmatch.h"
-#include "gettext.h"
 
 static int number_callbacks;
 static int parallel_next(struct child_process *cp,
 			 struct strbuf *err,
 			 void *cb,
-			 void **task_cb)
+			 void **task_cb UNUSED)
 {
 	struct child_process *d = cb;
 	if (number_callbacks >= 4)
@@ -40,10 +37,10 @@
 	return 1;
 }
 
-static int no_job(struct child_process *cp,
+static int no_job(struct child_process *cp UNUSED,
 		  struct strbuf *err,
-		  void *cb,
-		  void **task_cb)
+		  void *cb UNUSED,
+		  void **task_cb UNUSED)
 {
 	if (err)
 		strbuf_addstr(err, "no further jobs available\n");
@@ -52,10 +49,10 @@
 	return 0;
 }
 
-static int task_finished(int result,
+static int task_finished(int result UNUSED,
 			 struct strbuf *err,
-			 void *pp_cb,
-			 void *pp_task_cb)
+			 void *pp_cb UNUSED,
+			 void *pp_task_cb UNUSED)
 {
 	if (err)
 		strbuf_addstr(err, "asking for a quick stop\n");
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index a26107e..0a816a9 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -1,11 +1,13 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 #include "tree.h"
 #include "cache-tree.h"
 
-int cmd__scrap_cache_tree(int ac, const char **av)
+int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
 {
 	struct lock_file index_lock = LOCK_INIT;
 
diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c
index 824e5c0..054cbcf 100644
--- a/t/helper/test-serve-v2.c
+++ b/t/helper/test-serve-v2.c
@@ -1,7 +1,8 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "gettext.h"
 #include "parse-options.h"
 #include "serve.h"
+#include "setup.h"
 
 static char const * const serve_usage[] = {
 	N_("test-tool serve-v2 [<options>]"),
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index 71fe5c6..dcb7f6c 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
 
 int cmd__sha1(int ac, const char **av)
 {
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
index 0ac6a99..08cf149 100644
--- a/t/helper/test-sha256.c
+++ b/t/helper/test-sha256.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
 
 int cmd__sha256(int ac, const char **av)
 {
diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c
index d013bcc..2d5ecf7 100644
--- a/t/helper/test-sigchain.c
+++ b/t/helper/test-sigchain.c
@@ -1,5 +1,4 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "sigchain.h"
 
 #define X(f) \
@@ -14,7 +13,7 @@
 X(three)
 #undef X
 
-int cmd__sigchain(int argc, const char **argv)
+int cmd__sigchain(int argc UNUSED, const char **argv UNUSED)
 {
 	sigchain_push(SIGTERM, one);
 	sigchain_push(SIGTERM, two);
diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c
index 28365ff..fb59277 100644
--- a/t/helper/test-simple-ipc.c
+++ b/t/helper/test-simple-ipc.c
@@ -3,13 +3,13 @@
  */
 
 #include "test-tool.h"
-#include "cache.h"
-#include "strbuf.h"
+#include "gettext.h"
 #include "simple-ipc.h"
 #include "parse-options.h"
 #include "thread-utils.h"
 #include "strvec.h"
 #include "run-command.h"
+#include "trace2.h"
 
 #ifndef SUPPORTS_SIMPLE_IPC
 int cmd__simple_ipc(int argc, const char **argv)
@@ -277,7 +277,8 @@
 
 static start_bg_wait_cb bg_wait_cb;
 
-static int bg_wait_cb(const struct child_process *cp, void *cb_data)
+static int bg_wait_cb(const struct child_process *cp UNUSED,
+		      void *cb_data UNUSED)
 {
 	int s = ipc_get_active_state(cl_args.path);
 
diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c
index 44e4a6d..d8473cf 100644
--- a/t/helper/test-strcmp-offset.c
+++ b/t/helper/test-strcmp-offset.c
@@ -1,7 +1,7 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
 
-int cmd__strcmp_offset(int argc, const char **argv)
+int cmd__strcmp_offset(int argc UNUSED, const char **argv)
 {
 	int result;
 	size_t offset;
diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c
index 2123dda..e2aad61 100644
--- a/t/helper/test-string-list.c
+++ b/t/helper/test-string-list.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "strbuf.h"
 #include "string-list.h"
 
 /*
@@ -62,7 +62,7 @@
 		struct string_list list = STRING_LIST_INIT_NODUP;
 		int i;
 		char *s = xstrdup(argv[2]);
-		int delim = *argv[3];
+		const char *delim = argv[3];
 		int maxsplit = atoi(argv[4]);
 
 		i = string_list_split_in_place(&list, s, delim, maxsplit);
@@ -111,7 +111,7 @@
 		 */
 		if (sb.len && sb.buf[sb.len - 1] == '\n')
 			strbuf_setlen(&sb, sb.len - 1);
-		string_list_split_in_place(&list, sb.buf, '\n', -1);
+		string_list_split_in_place(&list, sb.buf, "\n", -1);
 
 		string_list_sort(&list);
 
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index 22a41c4..9df2f03 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -1,10 +1,13 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "config.h"
+#include "hash.h"
+#include "object-name.h"
+#include "repository.h"
+#include "setup.h"
 #include "submodule-config.h"
 #include "submodule.h"
 
-static void die_usage(int argc, const char **argv, const char *msg)
+static void die_usage(int argc UNUSED, const char **argv, const char *msg)
 {
 	fprintf(stderr, "%s\n", msg);
 	fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
@@ -42,7 +45,7 @@
 
 		if (commit[0] == '\0')
 			oidclr(&commit_oid);
-		else if (get_oid(commit, &commit_oid) < 0)
+		else if (repo_get_oid(the_repository, commit, &commit_oid) < 0)
 			die_usage(argc, argv, "Commit not found.");
 
 		if (lookup_name) {
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index dc1c14b..ecd40de 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -1,4 +1,6 @@
 #include "test-tool.h"
+#include "repository.h"
+#include "setup.h"
 #include "submodule-config.h"
 
 static void die_usage(const char **argv, const char *msg)
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index e060cc6..7197969 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -1,18 +1,27 @@
 #include "test-tool.h"
 #include "test-tool-utils.h"
-#include "cache.h"
 #include "parse-options.h"
 #include "remote.h"
+#include "repository.h"
+#include "setup.h"
+#include "strbuf.h"
 #include "submodule-config.h"
 #include "submodule.h"
 
 #define TEST_TOOL_CHECK_NAME_USAGE \
-	"test-tool submodule check-name <name>"
+	"test-tool submodule check-name"
 static const char *submodule_check_name_usage[] = {
 	TEST_TOOL_CHECK_NAME_USAGE,
 	NULL
 };
 
+#define TEST_TOOL_CHECK_URL_USAGE \
+	"test-tool submodule check-url"
+static const char *submodule_check_url_usage[] = {
+	TEST_TOOL_CHECK_URL_USAGE,
+	NULL
+};
+
 #define TEST_TOOL_IS_ACTIVE_USAGE \
 	"test-tool submodule is-active <name>"
 static const char *submodule_is_active_usage[] = {
@@ -29,31 +38,26 @@
 
 static const char *submodule_usage[] = {
 	TEST_TOOL_CHECK_NAME_USAGE,
+	TEST_TOOL_CHECK_URL_USAGE,
 	TEST_TOOL_IS_ACTIVE_USAGE,
 	TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
 	NULL
 };
 
+typedef int (*check_fn_t)(const char *);
+
 /*
- * Exit non-zero if any of the submodule names given on the command line is
- * invalid. If no names are given, filter stdin to print only valid names
- * (which is primarily intended for testing).
+ * Apply 'check_fn' to each line of stdin, printing values that pass the check
+ * to stdout.
  */
-static int check_name(int argc, const char **argv)
+static int check_submodule(check_fn_t check_fn)
 {
-	if (argc > 1) {
-		while (*++argv) {
-			if (check_submodule_name(*argv) < 0)
-				return 1;
-		}
-	} else {
-		struct strbuf buf = STRBUF_INIT;
-		while (strbuf_getline(&buf, stdin) != EOF) {
-			if (!check_submodule_name(buf.buf))
-				printf("%s\n", buf.buf);
-		}
-		strbuf_release(&buf);
+	struct strbuf buf = STRBUF_INIT;
+	while (strbuf_getline(&buf, stdin) != EOF) {
+		if (!check_fn(buf.buf))
+			printf("%s\n", buf.buf);
 	}
+	strbuf_release(&buf);
 	return 0;
 }
 
@@ -67,7 +71,20 @@
 	if (argc)
 		usage_with_options(submodule_check_name_usage, options);
 
-	return check_name(argc, argv);
+	return check_submodule(check_submodule_name);
+}
+
+static int cmd__submodule_check_url(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END()
+	};
+	argc = parse_options(argc, argv, "test-tools", options,
+			     submodule_check_url_usage, 0);
+	if (argc)
+		usage_with_options(submodule_check_url_usage, options);
+
+	return check_submodule(check_submodule_url);
 }
 
 static int cmd__submodule_is_active(int argc, const char **argv)
@@ -174,7 +191,7 @@
 	usage_with_options(usage, options);
 }
 
-static int cmd__submodule_config_writeable(int argc, const char **argv)
+static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
 {
 	struct option options[] = {
 		OPT_END()
@@ -193,6 +210,7 @@
 
 static struct test_cmd cmds[] = {
 	{ "check-name", cmd__submodule_check_name },
+	{ "check-url", cmd__submodule_check_url },
 	{ "is-active", cmd__submodule_is_active },
 	{ "resolve-relative-url", cmd__submodule_resolve_relative_url},
 	{ "config-list", cmd__submodule_config_list },
diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c
index ff22f2f..c344f16 100644
--- a/t/helper/test-subprocess.c
+++ b/t/helper/test-subprocess.c
@@ -1,6 +1,6 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "run-command.h"
+#include "setup.h"
 
 int cmd__subprocess(int argc, const char **argv)
 {
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index abe8a78..80a946b 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -19,8 +19,8 @@
 	{ "config", cmd__config },
 	{ "crontab", cmd__crontab },
 	{ "csprng", cmd__csprng },
-	{ "ctype", cmd__ctype },
 	{ "date", cmd__date },
+	{ "delete-gpgsig", cmd__delete_gpgsig },
 	{ "delta", cmd__delta },
 	{ "dir-iterator", cmd__dir_iterator },
 	{ "drop-caches", cmd__drop_caches },
@@ -30,7 +30,7 @@
 	{ "dump-untracked-cache", cmd__dump_untracked_cache },
 	{ "env-helper", cmd__env_helper },
 	{ "example-decorate", cmd__example_decorate },
-	{ "fast-rebase", cmd__fast_rebase },
+	{ "find-pack", cmd__find_pack },
 	{ "fsmonitor-client", cmd__fsmonitor_client },
 	{ "genrandom", cmd__genrandom },
 	{ "genzeros", cmd__genzeros },
@@ -38,7 +38,6 @@
 	{ "hashmap", cmd__hashmap },
 	{ "hash-speed", cmd__hash_speed },
 	{ "hexdump", cmd__hexdump },
-	{ "index-version", cmd__index_version },
 	{ "json-writer", cmd__json_writer },
 	{ "lazy-init-name-hash", cmd__lazy_init_name_hash },
 	{ "match-trees", cmd__match_trees },
@@ -57,7 +56,6 @@
 	{ "path-utils", cmd__path_utils },
 	{ "pcre2-config", cmd__pcre2_config },
 	{ "pkt-line", cmd__pkt_line },
-	{ "prio-queue", cmd__prio_queue },
 	{ "proc-receive", cmd__proc_receive },
 	{ "progress", cmd__progress },
 	{ "reach", cmd__reach },
@@ -86,6 +84,7 @@
 	{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
 	{ "subprocess", cmd__subprocess },
 	{ "trace2", cmd__trace2 },
+	{ "truncate", cmd__truncate },
 	{ "userdiff", cmd__userdiff },
 	{ "urlmatch-normalization", cmd__urlmatch_normalization },
 	{ "xml-encode", cmd__xml_encode },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index ea26724..2808b92 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -12,9 +12,9 @@
 int cmd__config(int argc, const char **argv);
 int cmd__crontab(int argc, const char **argv);
 int cmd__csprng(int argc, const char **argv);
-int cmd__ctype(int argc, const char **argv);
 int cmd__date(int argc, const char **argv);
 int cmd__delta(int argc, const char **argv);
+int cmd__delete_gpgsig(int argc, const char **argv);
 int cmd__dir_iterator(int argc, const char **argv);
 int cmd__drop_caches(int argc, const char **argv);
 int cmd__dump_cache_tree(int argc, const char **argv);
@@ -24,7 +24,7 @@
 int cmd__dump_reftable(int argc, const char **argv);
 int cmd__env_helper(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
-int cmd__fast_rebase(int argc, const char **argv);
+int cmd__find_pack(int argc, const char **argv);
 int cmd__fsmonitor_client(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
 int cmd__genzeros(int argc, const char **argv);
@@ -32,7 +32,6 @@
 int cmd__hashmap(int argc, const char **argv);
 int cmd__hash_speed(int argc, const char **argv);
 int cmd__hexdump(int argc, const char **argv);
-int cmd__index_version(int argc, const char **argv);
 int cmd__json_writer(int argc, const char **argv);
 int cmd__lazy_init_name_hash(int argc, const char **argv);
 int cmd__match_trees(int argc, const char **argv);
@@ -50,7 +49,6 @@
 int cmd__path_utils(int argc, const char **argv);
 int cmd__pcre2_config(int argc, const char **argv);
 int cmd__pkt_line(int argc, const char **argv);
-int cmd__prio_queue(int argc, const char **argv);
 int cmd__proc_receive(int argc, const char **argv);
 int cmd__progress(int argc, const char **argv);
 int cmd__reach(int argc, const char **argv);
@@ -79,6 +77,7 @@
 int cmd__submodule_nested_repo_config(int argc, const char **argv);
 int cmd__subprocess(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);
 int cmd__urlmatch_normalization(int argc, const char **argv);
 int cmd__xml_encode(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index f374c21..1adac29 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -1,9 +1,10 @@
 #include "test-tool.h"
-#include "cache.h"
 #include "strvec.h"
 #include "run-command.h"
 #include "exec-cmd.h"
 #include "config.h"
+#include "repository.h"
+#include "trace2.h"
 
 typedef int(fn_unit_test)(int argc, const char **argv);
 
@@ -44,7 +45,7 @@
  * [] "def_param" events for all of the "interesting" pre-defined
  * config settings.
  */
-static int ut_001return(int argc, const char **argv)
+static int ut_001return(int argc UNUSED, const char **argv)
 {
 	int rc;
 
@@ -64,7 +65,7 @@
  * [] "def_param" events for all of the "interesting" pre-defined
  * config settings.
  */
-static int ut_002exit(int argc, const char **argv)
+static int ut_002exit(int argc UNUSED, const char **argv)
 {
 	int rc;
 
@@ -200,7 +201,7 @@
 	return 0;
 }
 
-static int ut_007BUG(int argc, const char **argv)
+static int ut_007BUG(int argc UNUSED, const char **argv UNUSED)
 {
 	/*
 	 * Exercise BUG() to ensure that the message is printed to trace2.
@@ -208,7 +209,7 @@
 	BUG("the bug message");
 }
 
-static int ut_008bug(int argc, const char **argv)
+static int ut_008bug(int argc UNUSED, const char **argv UNUSED)
 {
 	bug("a bug message");
 	bug("another bug message");
@@ -216,7 +217,7 @@
 	return 0;
 }
 
-static int ut_009bug_BUG(int argc, const char **argv)
+static int ut_009bug_BUG(int argc UNUSED, const char **argv UNUSED)
 {
 	bug("a bug message");
 	bug("another bug message");
@@ -224,7 +225,7 @@
 	return 0;
 }
 
-static int ut_010bug_BUG(int argc, const char **argv)
+static int ut_010bug_BUG(int argc UNUSED, const char **argv UNUSED)
 {
 	bug("a %s message", "bug");
 	BUG("a %s message", "BUG");
@@ -411,6 +412,56 @@
 	return 0;
 }
 
+static int ut_300redact_start(int argc, const char **argv)
+{
+	if (!argc)
+		die("expect <argv...>");
+
+	trace2_cmd_start(argv);
+
+	return 0;
+}
+
+static int ut_301redact_child_start(int argc, const char **argv)
+{
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	int k;
+
+	if (!argc)
+		die("expect <argv...>");
+
+	for (k = 0; argv[k]; k++)
+		strvec_push(&cmd.args, argv[k]);
+
+	trace2_child_start(&cmd);
+
+	strvec_clear(&cmd.args);
+
+	return 0;
+}
+
+static int ut_302redact_exec(int argc, const char **argv)
+{
+	if (!argc)
+		die("expect <exe> <argv...>");
+
+	trace2_exec(argv[0], &argv[1]);
+
+	return 0;
+}
+
+static int ut_303redact_def_param(int argc, const char **argv)
+{
+	struct key_value_info kvi = KVI_INIT;
+
+	if (argc < 2)
+		die("expect <key> <value>");
+
+	trace2_def_param(argv[0], argv[1], &kvi);
+
+	return 0;
+}
+
 /*
  * Usage:
  *     test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -437,6 +488,11 @@
 
 	{ ut_200counter,  "200counter", "<v1> [<v2> [<v3> [...]]]" },
 	{ ut_201counter,  "201counter", "<v1> <v2> <threads>" },
+
+	{ ut_300redact_start,       "300redact_start",       "<argv...>" },
+	{ ut_301redact_child_start, "301redact_child_start", "<argv...>" },
+	{ ut_302redact_exec,        "302redact_exec",        "<exe> <argv...>" },
+	{ ut_303redact_def_param,   "303redact_def_param",   "<key> <value>" },
 };
 /* clang-format on */
 
diff --git a/t/helper/test-truncate.c b/t/helper/test-truncate.c
new file mode 100644
index 0000000..3931dea
--- /dev/null
+++ b/t/helper/test-truncate.c
@@ -0,0 +1,25 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+/*
+ * Truncate a file to the given size.
+ */
+int cmd__truncate(int argc, const char **argv)
+{
+	char *p = NULL;
+	uintmax_t sz = 0;
+	int fd = -1;
+
+	if (argc != 3)
+		die("expected filename and size");
+
+	sz = strtoumax(argv[2], &p, 0);
+	if (*p)
+		die("invalid size");
+
+	fd = xopen(argv[1], O_WRONLY | O_CREAT, 0600);
+
+	if (ftruncate(fd, (off_t) sz) < 0)
+		die_errno("failed to truncate file");
+	return 0;
+}
diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
index a2b56b9..0ce31ce 100644
--- a/t/helper/test-userdiff.c
+++ b/t/helper/test-userdiff.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "setup.h"
 #include "userdiff.h"
 #include "config.h"
 
@@ -12,7 +12,9 @@
 	return 0;
 }
 
-static int cmd__userdiff_config(const char *var, const char *value, void *cb UNUSED)
+static int cmd__userdiff_config(const char *var, const char *value,
+				const struct config_context *ctx UNUSED,
+				void *cb UNUSED)
 {
 	if (userdiff_config(var, value) < 0)
 		return -1;
diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c
index 2c103d1..b4ff5f9 100644
--- a/t/helper/test-wildmatch.c
+++ b/t/helper/test-wildmatch.c
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "cache.h"
+#include "wildmatch.h"
 
 int cmd__wildmatch(int argc, const char **argv)
 {
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index 7d45cd6..f084034 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -1,7 +1,9 @@
 #define USE_THE_INDEX_VARIABLE
 #include "test-tool.h"
-#include "cache.h"
 #include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
+#include "setup.h"
 
 int cmd__write_cache(int argc, const char **argv)
 {
diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c
index a648bbd..b2f330d 100644
--- a/t/helper/test-xml-encode.c
+++ b/t/helper/test-xml-encode.c
@@ -6,7 +6,7 @@
  * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded
  * in an XML file.
  */
-int cmd__xml_encode(int argc, const char **argv)
+int cmd__xml_encode(int argc UNUSED, const char **argv UNUSED)
 {
 	unsigned char buf[1024], tmp[4], *tmp2 = NULL;
 	ssize_t cur = 0, len = 1, remaining = 0;
diff --git a/t/lib-chunk.sh b/t/lib-chunk.sh
new file mode 100644
index 0000000..a7cd9c3
--- /dev/null
+++ b/t/lib-chunk.sh
@@ -0,0 +1,17 @@
+# Shell library for working with "chunk" files (commit-graph, midx, etc).
+
+# corrupt_chunk_file <fn> <chunk> <offset> <bytes>
+#
+# Corrupt a chunk-based file (like a commit-graph) by overwriting the bytes
+# found in the chunk specified by the 4-byte <chunk> identifier. If <offset> is
+# "clear", replace the chunk entirely. Otherwise, overwrite data <offset> bytes
+# into the chunk.
+#
+# The <bytes> are interpreted as pairs of hex digits (so "000000FE" would be
+# big-endian 254).
+corrupt_chunk_file () {
+	fn=$1; shift
+	perl "$TEST_DIRECTORY"/lib-chunk/corrupt-chunk-file.pl \
+		"$@" <"$fn" >"$fn.tmp" &&
+	mv "$fn.tmp" "$fn"
+}
diff --git a/t/lib-chunk/corrupt-chunk-file.pl b/t/lib-chunk/corrupt-chunk-file.pl
new file mode 100644
index 0000000..0e11aad
--- /dev/null
+++ b/t/lib-chunk/corrupt-chunk-file.pl
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+my ($chunk, $seek, $bytes) = @ARGV;
+$bytes =~ s/../chr(hex($&))/ge;
+
+binmode STDIN;
+binmode STDOUT;
+
+# A few helpers to read bytes, or read and copy them to the
+# output.
+sub get {
+	my $n = shift;
+	return unless $n;
+	read(STDIN, my $buf, $n)
+		or die "read error or eof: $!\n";
+	return $buf;
+}
+sub copy {
+	my $buf = get(@_);
+	print $buf;
+	return $buf;
+}
+
+# Some platforms' perl builds don't support 64-bit integers, and hence do not
+# allow packing/unpacking quadwords with "Q". The chunk format uses 64-bit file
+# offsets to support files of any size, but in practice our test suite will
+# only use small files. So we can fake it by asking for two 32-bit values and
+# discarding the first (most significant) one, which is equivalent as long as
+# it's just zero.
+sub unpack_quad {
+	my $bytes = shift;
+	my ($n1, $n2) = unpack("NN", $bytes);
+	die "quad value exceeds 32 bits" if $n1;
+	return $n2;
+}
+sub pack_quad {
+	my $n = shift;
+	my $ret = pack("NN", 0, $n);
+	# double check that our original $n did not exceed the 32-bit limit.
+	# This is presumably impossible on a 32-bit system (which would have
+	# truncated much earlier), but would still alert us on a 64-bit build
+	# of a new test that would fail on a 32-bit build (though we'd
+	# presumably see the die() from unpack_quad() in such a case).
+	die "quad round-trip failed" if unpack_quad($ret) != $n;
+	return $ret;
+}
+
+# read until we find table-of-contents entry for chunk;
+# note that we cheat a bit by assuming 4-byte alignment and
+# that no ToC entry will accidentally look like a header.
+#
+# If we don't find the entry, copy() will hit EOF and exit
+# (which should cause the caller to fail the test).
+while (copy(4) ne $chunk) { }
+my $offset = unpack_quad(copy(8));
+
+# In clear mode, our length will change. So figure out
+# the length by comparing to the offset of the next chunk, and
+# then adjust that offset (and all subsequent) ones.
+my $len;
+if ($seek eq "clear") {
+	my $id;
+	do {
+		$id = copy(4);
+		my $next = unpack_quad(get(8));
+		if (!defined $len) {
+			$len = $next - $offset;
+		}
+		print pack_quad($next - $len + length($bytes));
+	} while (unpack("N", $id));
+}
+
+# and now copy up to our existing chunk data
+copy($offset - tell(STDIN));
+if ($seek eq "clear") {
+	# if clearing, skip past existing data
+	get($len);
+} else {
+	# otherwise, copy up to the requested offset,
+	# and skip past the overwritten bytes
+	copy($seek);
+	get(length($bytes));
+}
+
+# now write out the requested bytes, along
+# with any other remaining data
+print $bytes;
+while (read(STDIN, my $buf, 4096)) {
+	print $buf;
+}
diff --git a/t/lib-commit-graph.sh b/t/lib-commit-graph.sh
index 5d79e1a..89b2667 100755
--- a/t/lib-commit-graph.sh
+++ b/t/lib-commit-graph.sh
@@ -14,24 +14,37 @@
 	test_cmp expect output
 }
 
+# graph_git_behavior <name> <directory> <branch> <compare>
+#
+# Ensures that a handful of traversal operations produce the same
+# results with and without the commit-graph in use.
+#
+# NOTE: it is a bug to call this function with <directory> containing
+# any characters in $IFS.
 graph_git_behavior() {
 	MSG=$1
 	DIR=$2
 	BRANCH=$3
 	COMPARE=$4
 	test_expect_success "check normal git operations: $MSG" '
-		cd "$TRASH_DIRECTORY/$DIR" &&
-		graph_git_two_modes "log --oneline $BRANCH" &&
-		graph_git_two_modes "log --topo-order $BRANCH" &&
-		graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
-		graph_git_two_modes "branch -vv" &&
-		graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
+		graph_git_two_modes "${DIR:+-C $DIR} log --oneline $BRANCH" &&
+		graph_git_two_modes "${DIR:+-C $DIR} log --topo-order $BRANCH" &&
+		graph_git_two_modes "${DIR:+-C $DIR} log --graph $COMPARE..$BRANCH" &&
+		graph_git_two_modes "${DIR:+-C $DIR} branch -vv" &&
+		graph_git_two_modes "${DIR:+-C $DIR} merge-base -a $BRANCH $COMPARE"
 	'
 }
 
 graph_read_expect() {
 	OPTIONAL=""
 	NUM_CHUNKS=3
+	DIR="."
+	if test "$1" = -C
+	then
+		shift
+		DIR="$1"
+		shift
+	fi
 	if test -n "$2"
 	then
 		OPTIONAL=" $2"
@@ -47,12 +60,15 @@
 	then
 		OPTIONS=" read_generation_data"
 	fi
-	cat >expect <<- EOF
+	cat >"$DIR/expect" <<-EOF
 	header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
 	num_commits: $1
 	chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
 	options:$OPTIONS
 	EOF
-	test-tool read-graph >output &&
-	test_cmp expect output
+	(
+		cd "$DIR" &&
+		test-tool read-graph >output &&
+		test_cmp expect output
+	)
 }
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 5ea8bc9..44799c0 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -43,6 +43,14 @@
 	reject $1 https example.com store-user
 	reject $1 https example.com user1
 	reject $1 https example.com user2
+	reject $1 https example.com user-expiry
+	reject $1 https example.com user-expiry-overwrite
+	reject $1 https example.com user4
+	reject $1 https example.com user-distinct-pass
+	reject $1 https example.com user-overwrite
+	reject $1 https example.com user-erase1
+	reject $1 https example.com user-erase2
+	reject $1 https victim.example.com user
 	reject $1 http path.tld user
 	reject $1 https timeout.tld user
 	reject $1 https sso.tld
@@ -166,6 +174,49 @@
 		EOF
 	'
 
+	test_expect_success "helper ($HELPER) overwrites on store" '
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		password=pass1
+		EOF
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		password=pass2
+		EOF
+		check fill $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		--
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		password=pass2
+		EOF
+		check reject $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		password=pass2
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		--
+		protocol=https
+		host=example.com
+		username=user-overwrite
+		password=askpass-password
+		--
+		askpass: Password for '\''https://user-overwrite@example.com'\'':
+		EOF
+	'
+
 	test_expect_success "helper ($HELPER) can forget host" '
 		check reject $HELPER <<-\EOF &&
 		protocol=https
@@ -220,6 +271,31 @@
 		EOF
 	'
 
+	test_expect_success "helper ($HELPER) does not erase a password distinct from input" '
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-distinct-pass
+		password=pass1
+		EOF
+		check reject $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-distinct-pass
+		password=pass2
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user-distinct-pass
+		--
+		protocol=https
+		host=example.com
+		username=user-distinct-pass
+		password=pass1
+		EOF
+	'
+
 	test_expect_success "helper ($HELPER) can forget user" '
 		check reject $HELPER <<-\EOF &&
 		protocol=https
@@ -270,6 +346,66 @@
 		password=
 		EOF
 	'
+
+	test_expect_success "helper ($HELPER) erases all matching credentials" '
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-erase1
+		password=pass1
+		EOF
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-erase2
+		password=pass1
+		EOF
+		check reject $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		--
+		protocol=https
+		host=example.com
+		username=askpass-username
+		password=askpass-password
+		--
+		askpass: Username for '\''https://example.com'\'':
+		askpass: Password for '\''https://askpass-username@example.com'\'':
+		EOF
+	'
+
+	: ${GIT_TEST_LONG_CRED_BUFFER:=1024}
+	# 23 bytes accounts for "wwwauth[]=basic realm=" plus NUL
+	LONG_VALUE_LEN=$((GIT_TEST_LONG_CRED_BUFFER - 23))
+	LONG_VALUE=$(perl -e 'print "a" x shift' $LONG_VALUE_LEN)
+
+	test_expect_success "helper ($HELPER) not confused by long header" '
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=victim.example.com
+		username=user
+		password=to-be-stolen
+		EOF
+
+		check fill $HELPER <<-EOF
+		protocol=https
+		host=badguy.example.com
+		wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+		--
+		protocol=https
+		host=badguy.example.com
+		username=askpass-username
+		password=askpass-password
+		wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+		--
+		askpass: Username for '\''https://badguy.example.com'\'':
+		askpass: Password for '\''https://askpass-username@badguy.example.com'\'':
+		EOF
+	'
 }
 
 helper_test_timeout() {
@@ -298,6 +434,110 @@
 	'
 }
 
+helper_test_password_expiry_utc() {
+	HELPER=$1
+
+	test_expect_success "helper ($HELPER) stores password_expiry_utc" '
+		check approve $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user-expiry
+		password=pass
+		password_expiry_utc=9999999999
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) gets password_expiry_utc" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user-expiry
+		--
+		protocol=https
+		host=example.com
+		username=user-expiry
+		password=pass
+		password_expiry_utc=9999999999
+		--
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) overwrites when password_expiry_utc changes" '
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		password=pass1
+		password_expiry_utc=9999999998
+		EOF
+		check approve $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		password=pass2
+		password_expiry_utc=9999999999
+		EOF
+		check fill $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		--
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		password=pass2
+		password_expiry_utc=9999999999
+		EOF
+		check reject $HELPER <<-\EOF &&
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		password=pass2
+		EOF
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		--
+		protocol=https
+		host=example.com
+		username=user-expiry-overwrite
+		password=askpass-password
+		--
+		askpass: Password for '\''https://user-expiry-overwrite@example.com'\'':
+		EOF
+	'
+}
+
+helper_test_oauth_refresh_token() {
+	HELPER=$1
+
+	test_expect_success "helper ($HELPER) stores oauth_refresh_token" '
+		check approve $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user4
+		password=pass
+		oauth_refresh_token=xyzzy
+		EOF
+	'
+
+	test_expect_success "helper ($HELPER) gets oauth_refresh_token" '
+		check fill $HELPER <<-\EOF
+		protocol=https
+		host=example.com
+		username=user4
+		--
+		protocol=https
+		host=example.com
+		username=user4
+		password=pass
+		oauth_refresh_token=xyzzy
+		--
+		EOF
+	'
+}
+
 write_script askpass <<\EOF
 echo >&2 askpass: $*
 what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 32b3473..57b9b2d 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -71,8 +71,8 @@
 		find . -type d -name .git -prune -o -type f -print
 	) | sort >module-git-"$1".list &&
 	test_cmp module-cvs-"$1".list module-git-"$1".list &&
-	cat module-cvs-"$1".list | while read f
+	while read f
 	do
 		test_cmp_branch_file "$1" "$f" || return 1
-	done
+	done <module-cvs-"$1".list
 }
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
index a8f5d32..c4dc2d4 100644
--- a/t/lib-diff-alternative.sh
+++ b/t/lib-diff-alternative.sh
@@ -112,15 +112,36 @@
 
 	STRATEGY=$1
 
-	test_expect_success "$STRATEGY diff from attributes" '
+	test_expect_success "setup attributes files for tests with $STRATEGY" '
+		git checkout -b master &&
 		echo "file* diff=driver" >.gitattributes &&
-		git config diff.driver.algorithm "$STRATEGY" &&
-		test_must_fail git diff --no-index file1 file2 > output &&
-		cat expect &&
-		cat output &&
+		git add file1 file2 .gitattributes &&
+		git commit -m "adding files" &&
+		git checkout -b branchA &&
+		echo "file* diff=driverA" >.gitattributes &&
+		git add .gitattributes &&
+		git commit -m "adding driverA as diff driver" &&
+		git checkout master &&
+		git clone --bare --no-local . bare.git
+	'
+
+	test_expect_success "$STRATEGY diff from attributes" '
+		test_must_fail git -c diff.driver.algorithm=$STRATEGY diff --no-index file1 file2 > output &&
 		test_cmp expect output
 	'
 
+	test_expect_success "diff from attributes with bare repo with source" '
+		git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
+			-c diff.driverA.algorithm=$STRATEGY \
+			diff HEAD:file1 HEAD:file2 >output &&
+		test_cmp expect output
+	'
+
+	test_expect_success "diff from attributes with bare repo with invalid source" '
+		test_must_fail git -C bare.git --attr-source=invalid-branch diff \
+			HEAD:file1 HEAD:file2
+	'
+
 	test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
 		echo "file* diff=driver" >.gitattributes &&
 		git config diff.driver.algorithm "$STRATEGY" &&
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 1147855..add11e8 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -13,7 +13,7 @@
 	gpg_version=$(gpg --version 2>&1)
 	test $? != 127 || exit 1
 
-	# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+	# As said here: https://web.archive.org/web/20130212022238/https://www.gnupg.org/faq/gnupg-faq.html#why-does-gnupg-1.0.6-bail-out-on-keyrings-used-with-1.0.7
 	# the gpg version 1.0.6 did not parse trust packets correctly, so for
 	# that version, creation of signed tags using the generated key fails.
 	case "$gpg_version" in
@@ -45,6 +45,28 @@
 			"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
 		gpg --homedir "${GNUPGHOME}" --import-ownertrust \
 			"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
+		gpg --homedir "${GNUPGHOME}" --update-trustdb &&
+		gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
+			--sign -u committer@example.com
+		;;
+	esac
+'
+
+test_lazy_prereq GPG2 '
+	gpg_version=$(gpg --version 2>&1)
+	test $? != 127 || exit 1
+
+	case "$gpg_version" in
+	"gpg (GnuPG) "[01].*)
+		say "This test requires a GPG version >= v2.0.0"
+		exit 1
+		;;
+	*)
+		(gpgconf --kill all || : ) &&
+		gpg --homedir "${GNUPGHOME}" --import \
+			"$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
+		gpg --homedir "${GNUPGHOME}" --import-ownertrust \
+			"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
 		gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \
 			--sign -u committer@example.com
 		;;
@@ -135,8 +157,9 @@
 '
 
 test_lazy_prereq GPGSSH_VERIFYTIME '
+	test_have_prereq GPGSSH &&
 	# Check if ssh-keygen has a verify-time option by passing an invalid date to it
-	ssh-keygen -Overify-time=INVALID -Y check-novalidate -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" &&
+	ssh-keygen -Overify-time=INVALID -Y check-novalidate -n "git" -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" &&
 
 	# Set up keys with key lifetimes
 	ssh-keygen -t ed25519 -N "" -C "timeboxed valid key" -f "${GPGSSH_KEY_TIMEBOXEDVALID}" >/dev/null &&
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 09cf5ed..d83bafe 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -55,21 +55,31 @@
 
 HTTPD_PARA=""
 
-for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' \
+			  '/usr/sbin/apache2' \
+			  "$(command -v httpd)" \
+			  "$(command -v apache2)"
 do
-	if test -x "$DEFAULT_HTTPD_PATH"
+	if test -n "$DEFAULT_HTTPD_PATH" && test -x "$DEFAULT_HTTPD_PATH"
 	then
 		break
 	fi
 done
 
+if test -x "$DEFAULT_HTTPD_PATH"
+then
+	DETECTED_HTTPD_ROOT="$("$DEFAULT_HTTPD_PATH" -V 2>/dev/null | sed -n 's/^ -D HTTPD_ROOT="\(.*\)"$/\1/p')"
+fi
+
 for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
 				 '/usr/lib/apache2/modules' \
 				 '/usr/lib64/httpd/modules' \
 				 '/usr/lib/httpd/modules' \
-				 '/usr/libexec/httpd'
+				 '/usr/libexec/httpd' \
+				 '/usr/lib/apache2' \
+				 "${DETECTED_HTTPD_ROOT:+${DETECTED_HTTPD_ROOT}/modules}"
 do
-	if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+	if test -n "$DEFAULT_HTTPD_MODULE_PATH" && test -d "$DEFAULT_HTTPD_MODULE_PATH"
 	then
 		break
 	fi
@@ -127,6 +137,20 @@
 		"Could not identify web server at '$LIB_HTTPD_PATH'"
 fi
 
+if test -n "$LIB_HTTPD_DAV" && test -f /etc/os-release
+then
+	case "$(grep "^ID=" /etc/os-release | cut -d= -f2-)" in
+	alpine)
+		# The WebDAV module in Alpine Linux is broken at least up to
+		# Alpine v3.16 as the default DBM driver is missing.
+		#
+		# https://gitlab.alpinelinux.org/alpine/aports/-/issues/13112
+		test_skip_or_die GIT_TEST_HTTPD \
+			"Apache WebDAV module does not have default DBM backend driver"
+		;;
+	esac
+fi
+
 install_script () {
 	write_script "$HTTPD_ROOT_PATH/$1" <"$TEST_PATH/$1"
 }
@@ -142,6 +166,7 @@
 	install_script error-smart-http.sh
 	install_script error.sh
 	install_script apply-one-time-perl.sh
+	install_script nph-custom-auth.sh
 
 	ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
 
@@ -190,6 +215,20 @@
 	test_set_prereq HTTP2
 }
 
+enable_cgipassauth () {
+	# We are looking for 2.4.13 or more recent. Since we only support
+	# 2.4 and up, no need to check for older major/minor.
+	if test "$HTTPD_VERSION_MAJOR" = 2 &&
+	   test "$HTTPD_VERSION_MINOR" = 4 &&
+	   test "$(echo $HTTPD_VERSION | cut -d. -f3)" -lt 13
+	then
+		echo >&4 "apache $HTTPD_VERSION too old for CGIPassAuth"
+		return
+	fi
+	HTTPD_PARA="$HTTPD_PARA -DUSE_CGIPASSAUTH"
+	test_set_prereq CGIPASSAUTH
+}
+
 start_httpd() {
 	prepare_httpd >&3 2>&4
 
@@ -227,8 +266,12 @@
 		git commit -a -m path2 --amend &&
 
 		test_must_fail git push -v origin >output 2>&1 &&
-		(cd "$REMOTE_REPO" &&
-		 test $HEAD = $(git rev-parse --verify HEAD))
+		(
+			cd "$REMOTE_REPO" &&
+			echo "$HEAD" >expect &&
+			git rev-parse --verify HEAD >actual &&
+			test_cmp expect actual
+		)
 	'
 
 	test_expect_success 'non-fast-forward push show ref status' '
@@ -236,7 +279,7 @@
 	'
 
 	test_expect_success 'non-fast-forward push shows help message' '
-		test_i18ngrep "Updates were rejected because" output
+		test_grep "Updates were rejected because" output
 	'
 
 	test_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' '
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 31f82fa..022276a 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -92,6 +92,7 @@
 PassEnv GNUPGHOME
 PassEnv ASAN_OPTIONS
 PassEnv LSAN_OPTIONS
+PassEnv UBSAN_OPTIONS
 PassEnv GIT_TRACE
 PassEnv GIT_CONFIG_NOSYSTEM
 PassEnv GIT_TEST_SIDEBAND_ALL
@@ -101,6 +102,8 @@
 Alias /dumb/ www/
 Alias /auth/dumb/ www/auth/dumb/
 
+SetEnv PERL_PATH ${PERL_PATH}
+
 <LocationMatch /smart/>
 	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
 	SetEnv GIT_HTTP_EXPORT_ALL
@@ -141,6 +144,13 @@
 	SetEnv GIT_HTTP_EXPORT_ALL
 	SetEnv GIT_PROTOCOL
 </LocationMatch>
+<LocationMatch /custom_auth/>
+	SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+	SetEnv GIT_HTTP_EXPORT_ALL
+	<IfDefine USE_CGIPASSAUTH>
+	CGIPassAuth on
+	</IfDefine>
+</LocationMatch>
 ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
 ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
 ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
@@ -150,6 +160,7 @@
 ScriptAlias /error_smart/ error-smart-http.sh/
 ScriptAlias /error/ error.sh/
 ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
+ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
 <Directory ${GIT_EXEC_PATH}>
 	Options FollowSymlinks
 </Directory>
diff --git a/t/lib-httpd/apply-one-time-perl.sh b/t/lib-httpd/apply-one-time-perl.sh
index 09a0abd..d7f9fed 100644
--- a/t/lib-httpd/apply-one-time-perl.sh
+++ b/t/lib-httpd/apply-one-time-perl.sh
@@ -13,7 +13,7 @@
 	export LC_ALL
 
 	"$GIT_EXEC_PATH/git-http-backend" >out
-	perl -pe "$(cat one-time-perl)" out >out_modified
+	"$PERL_PATH" -pe "$(cat one-time-perl)" out >out_modified
 
 	if cmp -s out out_modified
 	then
diff --git a/t/lib-httpd/nph-custom-auth.sh b/t/lib-httpd/nph-custom-auth.sh
new file mode 100644
index 0000000..f5345e7
--- /dev/null
+++ b/t/lib-httpd/nph-custom-auth.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+VALID_CREDS_FILE=custom-auth.valid
+CHALLENGE_FILE=custom-auth.challenge
+
+#
+# If $VALID_CREDS_FILE exists in $HTTPD_ROOT_PATH, consider each line as a valid
+# credential for the current request. Each line in the file is considered a
+# valid HTTP Authorization header value. For example:
+#
+# Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+#
+# If $CHALLENGE_FILE exists in $HTTPD_ROOT_PATH, output the contents as headers
+# in a 401 response if no valid authentication credentials were included in the
+# request. For example:
+#
+# WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+# WWW-Authenticate: Basic realm="example.com"
+#
+
+if test -n "$HTTP_AUTHORIZATION" && \
+	grep -Fqsx "${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
+then
+	# Note that although git-http-backend returns a status line, it
+	# does so using a CGI 'Status' header. Because this script is an
+	# No Parsed Headers (NPH) script, we must return a real HTTP
+	# status line.
+	# This is only a test script, so we don't bother to check for
+	# the actual status from git-http-backend and always return 200.
+	echo 'HTTP/1.1 200 OK'
+	exec "$GIT_EXEC_PATH"/git-http-backend
+fi
+
+echo 'HTTP/1.1 401 Authorization Required'
+if test -f "$CHALLENGE_FILE"
+then
+	cat "$CHALLENGE_FILE"
+fi
+echo
diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd
index 99a34d6..d9c122f 100644
--- a/t/lib-httpd/passwd
+++ b/t/lib-httpd/passwd
@@ -1 +1 @@
-user@host:xb4E8pqD81KQs
+user@host:$apr1$LGPmCZWj$9vxEwj5Z5GzQLBMxp3mCx1
diff --git a/t/lib-httpd/proxy-passwd b/t/lib-httpd/proxy-passwd
index 77c2513..2ad7705 100644
--- a/t/lib-httpd/proxy-passwd
+++ b/t/lib-httpd/proxy-passwd
@@ -1 +1 @@
-proxuser:2x7tAukjAED5M
+proxuser:$apr1$RxS6MLkD$DYsqQdflheq4GPNxzJpx5.
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
index cfd76bf..89ca1f7 100644
--- a/t/lib-patch-mode.sh
+++ b/t/lib-patch-mode.sh
@@ -29,8 +29,12 @@
 
 # verify_state <path> <expected-worktree-content> <expected-index-content>
 verify_state () {
-	test "$(cat "$1")" = "$2" &&
-	test "$(git show :"$1")" = "$3"
+	echo "$2" >expect &&
+	test_cmp expect "$1" &&
+
+	echo "$3" >expect &&
+	git show :"$1" >actual &&
+	test_cmp expect actual
 }
 
 # verify_saved_state <path>
@@ -46,5 +50,6 @@
 }
 
 verify_saved_head () {
-	test "$(cat _head)" = "$(git rev-parse HEAD)"
+	git rev-parse HEAD >actual &&
+	test_cmp _head actual
 }
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 7ca5b91..11d2dc9 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -8,18 +8,21 @@
 # - check that non-commit messages have a certain line count with $EXPECT_COUNT
 # - check the commit count in the commit message header with $EXPECT_HEADER_COUNT
 # - rewrite a rebase -i script as directed by $FAKE_LINES.
-#   $FAKE_LINES consists of a sequence of words separated by spaces.
-#   The following word combinations are possible:
+#   $FAKE_LINES consists of a sequence of words separated by spaces;
+#   spaces inside the words are encoded as underscores.
+#   The following words are possible:
 #
-#   "<lineno>" -- add a "pick" line with the SHA1 taken from the
-#       specified line.
+#   "<cmd>" -- override the command for the next line specification. Can be
+#       "pick", "squash", "fixup[_-(c|C)]", "edit", "reword", "drop",
+#       "merge[_-(c|C)_<rev>]", or "bad" for an invalid command.
 #
-#   "<cmd> <lineno>" -- add a line with the specified command
-#       ("pick", "squash", "fixup"|"fixup_-C"|"fixup_-c", "edit", "reword" or "drop")
-#       and the SHA1 taken from the specified line.
+#   "<lineno>" -- add a command, using the specified line as a template.
+#       If the command has not been overridden, the line will be copied
+#       verbatim, usually resulting in a "pick" line.
 #
-#   "_" -- add a space, like "fixup_-C" implies "fixup -C" and
-#       "exec_cmd_with_args" add an "exec cmd with args" line.
+#   "fakesha" -- add a command ("pick" by default), using a fake SHA1.
+#
+#   "exec_[command...]", "break" -- add the specified command.
 #
 #   "#" -- Add a comment line.
 #
@@ -49,7 +52,7 @@
 	action=\&
 	for line in $FAKE_LINES; do
 		case $line in
-		pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m)
+		pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|t|merge|m)
 			action="$line";;
 		exec_*|x_*|break|b)
 			echo "$line" | sed 's/_/ /g' >> "$1";;
@@ -64,7 +67,7 @@
 		fakesha)
 			test \& != "$action" || action=pick
 			echo "$action XXXXXXX False commit" >> "$1"
-			action=pick;;
+			action=\&;;
 		*)
 			sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1"
 			action=\&;;
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 2d31fcf..36f767c 100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -168,20 +168,16 @@
 # Note that this only supports submodules at the root level of the
 # superproject, with the default name, i.e. same as its path.
 test_git_directory_is_unchanged () {
-	(
-		cd ".git/modules/$1" &&
-		# does core.worktree point at the right place?
-		test "$(git config core.worktree)" = "../../../$1" &&
-		# remove it temporarily before comparing, as
-		# "$1/.git/config" lacks it...
-		git config --unset core.worktree
-	) &&
+	# does core.worktree point at the right place?
+	echo "../../../$1" >expect &&
+	git -C ".git/modules/$1" config core.worktree >actual &&
+	test_cmp expect actual &&
+	# remove it temporarily before comparing, as
+	# "$1/.git/config" lacks it...
+	git -C ".git/modules/$1" config --unset core.worktree &&
 	diff -r ".git/modules/$1" "$1/.git" &&
-	(
-		# ... and then restore.
-		cd ".git/modules/$1" &&
-		git config core.worktree "../../../$1"
-	)
+	# ... and then restore.
+	git -C ".git/modules/$1" config core.worktree "../../../$1"
 }
 
 test_git_directory_exists () {
@@ -189,7 +185,9 @@
 	if test -f sub1/.git
 	then
 		# does core.worktree point at the right place?
-		test "$(git -C .git/modules/$1 config core.worktree)" = "../../../$1"
+		echo "../../../$1" >expect &&
+		git -C ".git/modules/$1" config core.worktree >actual &&
+		test_cmp expect actual
 	fi
 }
 
@@ -804,7 +802,7 @@
 			git branch -t no_submodule origin/no_submodule &&
 			$command no_submodule &&
 			test_superproject_content origin/no_submodule &&
-			! test_path_is_dir sub1 &&
+			test_path_is_missing sub1 &&
 			test_must_fail git config -f .git/modules/sub1/config core.worktree &&
 			test_must_fail git config -f .git/modules/sub1/modules/sub2/config core.worktree
 		)
@@ -832,7 +830,7 @@
 			cd submodule_update &&
 			git branch -t invalid_sub1 origin/invalid_sub1 &&
 			test_must_fail $command invalid_sub1 2>err &&
-			test_i18ngrep sub1 err &&
+			test_grep sub1 err &&
 			test_superproject_content origin/add_sub1 &&
 			test_submodule_content sub1 origin/add_sub1
 		)
diff --git a/t/oid-info/hash-info b/t/oid-info/hash-info
index d0736dd..b8a5bcb 100644
--- a/t/oid-info/hash-info
+++ b/t/oid-info/hash-info
@@ -15,3 +15,15 @@
 
 empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
 empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
+
+blob17_1 sha1:263
+blob17_1 sha256:34
+
+blob17_2 sha1:410
+blob17_2 sha256:174
+
+blob17_3 sha1:523
+blob17_3 sha256:313
+
+blob17_4 sha1:790
+blob17_4 sha256:481
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
new file mode 100755
index 0000000..e14e762
--- /dev/null
+++ b/t/perf/p1500-graph-walks.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='Commit walk performance tests'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+	git for-each-ref --format="%(refname)" "refs/heads/*" "refs/tags/*" >allrefs &&
+	sort -r allrefs | head -n 50 >refs &&
+	for ref in $(cat refs)
+	do
+		git branch -f ref-$ref $ref &&
+		echo ref-$ref ||
+		return 1
+	done >branches &&
+	for ref in $(cat refs)
+	do
+		git tag -f tag-$ref $ref &&
+		echo tag-$ref ||
+		return 1
+	done >tags &&
+	git commit-graph write --reachable
+'
+
+test_perf 'ahead-behind counts: git for-each-ref' '
+	git for-each-ref --format="%(ahead-behind:HEAD)" --stdin <refs
+'
+
+test_perf 'ahead-behind counts: git branch' '
+	xargs git branch -l --format="%(ahead-behind:HEAD)" <branches
+'
+
+test_perf 'ahead-behind counts: git tag' '
+	xargs git tag -l --format="%(ahead-behind:HEAD)" <tags
+'
+
+test_perf 'contains: git for-each-ref --merged' '
+	git for-each-ref --merged=HEAD --stdin <refs
+'
+
+test_perf 'contains: git branch --merged' '
+	xargs git branch --merged=HEAD <branches
+'
+
+test_perf 'contains: git tag --merged' '
+	xargs git tag --merged=HEAD <tags
+'
+
+test_done
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index 3242cfe..39e92b0 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -43,6 +43,7 @@
 	done &&
 
 	git sparse-checkout init --cone &&
+	git tag -a v1.0 -m "Final" &&
 	git sparse-checkout set $SPARSE_CONE &&
 	git checkout -b wide $OLD_COMMIT &&
 
@@ -124,6 +125,15 @@
 test_perf_on_all git checkout-index -f --all
 test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
 test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
-test_perf_on_all git grep --cached --sparse bogus -- "f2/f1/f1/*"
+test_perf_on_all git grep --cached bogus -- "f2/f1/f1/*"
+test_perf_on_all git write-tree
+test_perf_on_all git describe --dirty
+test_perf_on_all 'echo >>new && git describe --dirty'
+test_perf_on_all git diff-files
+test_perf_on_all git diff-files -- $SPARSE_CONE/a
+test_perf_on_all git diff-tree HEAD
+test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a
+test_perf_on_all "git worktree add ../temp && git worktree remove ../temp"
+test_perf_on_all git check-attr -a -- $SPARSE_CONE/a
 
 test_done
diff --git a/t/perf/p5312-pack-bitmaps-revs.sh b/t/perf/p5312-pack-bitmaps-revs.sh
index 0684b69..ceec606 100755
--- a/t/perf/p5312-pack-bitmaps-revs.sh
+++ b/t/perf/p5312-pack-bitmaps-revs.sh
@@ -12,8 +12,7 @@
 	test_perf_large_repo
 
 	test_expect_success 'setup bitmap config' '
-		git config pack.writebitmaps true &&
-		git config pack.writeReverseIndex true
+		git config pack.writebitmaps true
 	'
 
 	# we need to create the tag up front such that it is covered by the repack and
diff --git a/t/perf/p5332-multi-pack-reuse.sh b/t/perf/p5332-multi-pack-reuse.sh
new file mode 100755
index 0000000..5c6c575
--- /dev/null
+++ b/t/perf/p5332-multi-pack-reuse.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='tests pack performance with multi-pack reuse'
+
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-pack.sh"
+
+packdir=.git/objects/pack
+
+test_perf_large_repo
+
+find_pack () {
+	for idx in $packdir/pack-*.idx
+	do
+		if git show-index <$idx | grep -q "$1"
+		then
+			basename $idx
+		fi || return 1
+	done
+}
+
+repack_into_n_chunks () {
+	git repack -adk &&
+
+	test "$1" -eq 1 && return ||
+
+	find $packdir -type f | sort >packs.before &&
+
+	# partition the repository into $1 chunks of consecutive commits, and
+	# then create $1 packs with the objects reachable from each chunk
+	# (excluding any objects reachable from the previous chunks)
+	sz="$(($(git rev-list --count --all) / $1))"
+	for rev in $(git rev-list --all | awk "NR % $sz == 0" | tac)
+	do
+		pack="$(echo "$rev" | git pack-objects --revs \
+			--honor-pack-keep --delta-base-offset $packdir/pack)" &&
+		touch $packdir/pack-$pack.keep || return 1
+	done
+
+	# grab any remaining objects not packed by the previous step(s)
+	git pack-objects --revs --all --honor-pack-keep --delta-base-offset \
+		$packdir/pack &&
+
+	find $packdir -type f | sort >packs.after &&
+
+	# and install the whole thing
+	for f in $(comm -12 packs.before packs.after)
+	do
+		rm -f "$f" || return 1
+	done
+	rm -fr $packdir/*.keep
+}
+
+for nr_packs in 1 10 100
+do
+	test_expect_success "create $nr_packs-pack scenario" '
+		repack_into_n_chunks $nr_packs
+	'
+
+	test_expect_success "setup bitmaps for $nr_packs-pack scenario" '
+		find $packdir -type f -name "*.idx" | sed -e "s/.*\/\(.*\)$/+\1/g" |
+		git multi-pack-index write --stdin-packs --bitmap \
+			--preferred-pack="$(find_pack $(git rev-parse HEAD))"
+	'
+
+	for reuse in single multi
+	do
+		test_perf "clone for $nr_packs-pack scenario ($reuse-pack reuse)" "
+			git for-each-ref --format='%(objectname)' refs/heads refs/tags >in &&
+			git -c pack.allowPackReuse=$reuse pack-objects \
+				--revs --delta-base-offset --use-bitmap-index \
+				--stdout <in >result
+		"
+
+		test_size "clone size for $nr_packs-pack scenario ($reuse-pack reuse)" '
+			wc -c <result
+		'
+	done
+done
+
+test_done
diff --git a/t/perf/p6300-for-each-ref.sh b/t/perf/p6300-for-each-ref.sh
new file mode 100755
index 0000000..fa7289c
--- /dev/null
+++ b/t/perf/p6300-for-each-ref.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance of for-each-ref'
+. ./perf-lib.sh
+
+test_perf_fresh_repo
+
+ref_count_per_type=10000
+test_iteration_count=10
+
+test_expect_success "setup" '
+	test_commit_bulk $(( 1 + $ref_count_per_type )) &&
+
+	# Create refs
+	test_seq $ref_count_per_type |
+		sed "s,.*,update refs/heads/branch_& HEAD~&\nupdate refs/custom/special_& HEAD~&," |
+		git update-ref --stdin &&
+
+	# Create annotated tags
+	for i in $(test_seq $ref_count_per_type)
+	do
+		# Base tags
+		echo "tag tag_$i" &&
+		echo "mark :$i" &&
+		echo "from HEAD~$i" &&
+		printf "tagger %s <%s> %s\n" \
+			"$GIT_COMMITTER_NAME" \
+			"$GIT_COMMITTER_EMAIL" \
+			"$GIT_COMMITTER_DATE" &&
+		echo "data <<EOF" &&
+		echo "tag $i" &&
+		echo "EOF" &&
+
+		# Nested tags
+		echo "tag nested_$i" &&
+		echo "from :$i" &&
+		printf "tagger %s <%s> %s\n" \
+			"$GIT_COMMITTER_NAME" \
+			"$GIT_COMMITTER_EMAIL" \
+			"$GIT_COMMITTER_DATE" &&
+		echo "data <<EOF" &&
+		echo "nested tag $i" &&
+		echo "EOF" || return 1
+	done | git fast-import
+'
+
+test_for_each_ref () {
+	title="for-each-ref"
+	if test $# -gt 0; then
+		title="$title ($1)"
+		shift
+	fi
+	args="$@"
+
+	test_perf "$title" "
+		for i in \$(test_seq $test_iteration_count); do
+			git for-each-ref $args >/dev/null
+		done
+	"
+}
+
+run_tests () {
+	test_for_each_ref "$1"
+	test_for_each_ref "$1, no sort" --no-sort
+	test_for_each_ref "$1, --count=1" --count=1
+	test_for_each_ref "$1, --count=1, no sort" --no-sort --count=1
+	test_for_each_ref "$1, tags" refs/tags/
+	test_for_each_ref "$1, tags, no sort" --no-sort refs/tags/
+	test_for_each_ref "$1, tags, dereferenced" '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+	test_for_each_ref "$1, tags, dereferenced, no sort" --no-sort '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+
+	test_perf "for-each-ref ($1, tags) + cat-file --batch-check (dereferenced)" "
+		for i in \$(test_seq $test_iteration_count); do
+			git for-each-ref --format='%(objectname)^{} %(refname) %(objectname)' refs/tags/ | \
+				git cat-file --batch-check='%(objectname) %(rest)' >/dev/null
+		done
+	"
+}
+
+run_tests "loose"
+
+test_expect_success 'pack refs' '
+	git pack-refs --all
+'
+run_tests "packed"
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index e778677..ab0c763 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -15,7 +15,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # These variables must be set before the inclusion of test-lib.sh below,
 # because it will change our working directory.
@@ -31,7 +31,7 @@
 GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
 export GIT_CONFIG_SYSTEM
 
-if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+if test -n "$GIT_TEST_INSTALLED" && test -z "$PERF_SET_GIT_TEST_INSTALLED"
 then
 	error "Do not use GIT_TEST_INSTALLED with the perf tests.
 
diff --git a/t/perf/repos/inflate-repo.sh b/t/perf/repos/inflate-repo.sh
index fcfc992..412e4b4 100755
--- a/t/perf/repos/inflate-repo.sh
+++ b/t/perf/repos/inflate-repo.sh
@@ -33,7 +33,7 @@
 done
 
 git ls-tree -r HEAD >GEN_src_list
-nr_src_files=$(cat GEN_src_list | wc -l)
+nr_src_files=$(wc -l <GEN_src_list)
 
 src_branch=$(git symbolic-ref --short HEAD)
 
diff --git a/t/perf/run b/t/perf/run
index 34115ed..486ead2 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -91,10 +91,10 @@
 run_dirs_helper () {
 	mydir=${1%/}
 	shift
-	while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+	while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
 		shift
 	done
-	if test $# -gt 0 -a "$1" = --; then
+	if test $# -gt 0 && test "$1" = --; then
 		shift
 	fi
 
@@ -124,7 +124,7 @@
 }
 
 run_dirs () {
-	while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+	while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
 		run_dirs_helper "$@"
 		shift
 	done
@@ -180,7 +180,8 @@
 	GIT_PERF_AGGREGATING_LATER=t
 	export GIT_PERF_AGGREGATING_LATER
 
-	if test $# = 0 -o "$1" = -- -o -f "$1"; then
+	if test $# = 0 || test "$1" = -- || test -f "$1"
+	then
 		set -- . "$@"
 	fi
 
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 8ea31d1..6e300be 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -1014,7 +1014,7 @@
 '
 
 test_expect_success 'showing tree with git ls-tree' '
-    git ls-tree $tree >current
+	git ls-tree $tree >current
 '
 
 test_expect_success 'git ls-tree output for a known tree' '
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index d479303..b131d66 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -168,8 +168,8 @@
 		git -c init.defaultBranch=initial init >out1 2>err1 &&
 		git init >out2 2>err2
 	) &&
-	test_i18ngrep "Initialized empty" again/out1 &&
-	test_i18ngrep "Reinitialized existing" again/out2 &&
+	test_grep "Initialized empty" again/out1 &&
+	test_grep "Reinitialized existing" again/out2 &&
 	test_must_be_empty again/err1 &&
 	test_must_be_empty again/err2
 '
@@ -332,7 +332,7 @@
 
 test_expect_success 'explicit bare & --separate-git-dir incompatible' '
 	test_must_fail git init --bare --separate-git-dir goop.git bare.git 2>err &&
-	test_i18ngrep "cannot be used together" err
+	test_grep "cannot be used together" err
 '
 
 test_expect_success 'implicit bare & --separate-git-dir incompatible' '
@@ -340,7 +340,7 @@
 	mkdir -p bare.git &&
 	test_must_fail env GIT_DIR=. \
 		git -C bare.git init --separate-git-dir goop.git 2>err &&
-	test_i18ngrep "incompatible" err
+	test_grep "incompatible" err
 '
 
 test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
@@ -349,7 +349,7 @@
 	git clone --bare . bare.git &&
 	git -C bare.git worktree add --detach ../linkwt &&
 	test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err &&
-	test_i18ngrep "incompatible" err
+	test_grep "incompatible" err
 '
 
 test_lazy_prereq GETCWD_IGNORES_PERMS '
@@ -532,6 +532,76 @@
 	test_must_fail git -C sha256 init --object-format=sha1
 '
 
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
+	test_when_finished "rm -rf refstorage" &&
+	git init refstorage &&
+	git -C refstorage config extensions.refStorage files &&
+	test_must_fail git -C refstorage rev-parse 2>err &&
+	grep "repo version is 0, but v1-only extension found" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
+	test_when_finished "rm -rf refstorage" &&
+	git init refstorage &&
+	git -C refstorage config core.repositoryformatversion 1 &&
+	git -C refstorage config extensions.refStorage files &&
+	test_commit -C refstorage A &&
+	git -C refstorage rev-parse --verify HEAD
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
+	test_when_finished "rm -rf refstorage" &&
+	git init refstorage &&
+	git -C refstorage config core.repositoryformatversion 1 &&
+	git -C refstorage config extensions.refStorage garbage &&
+	test_must_fail git -C refstorage rev-parse 2>err &&
+	grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' '
+	test_when_finished "rm -rf refformat" &&
+	GIT_DEFAULT_REF_FORMAT=files git init refformat &&
+	echo 0 >expect &&
+	git -C refformat config core.repositoryformatversion >actual &&
+	test_cmp expect actual &&
+	test_must_fail git -C refformat config extensions.refstorage
+'
+
+test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' '
+	test_when_finished "rm -rf refformat" &&
+	cat >expect <<-EOF &&
+	fatal: unknown ref storage format ${SQ}garbage${SQ}
+	EOF
+	test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err &&
+	test_cmp expect err
+'
+
+test_expect_success 'init with --ref-format=files' '
+	test_when_finished "rm -rf refformat" &&
+	git init --ref-format=files refformat &&
+	echo files >expect &&
+	git -C refformat rev-parse --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 're-init with same format' '
+	test_when_finished "rm -rf refformat" &&
+	git init --ref-format=files refformat &&
+	git init --ref-format=files refformat &&
+	echo files >expect &&
+	git -C refformat rev-parse --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'init with --ref-format=garbage' '
+	test_when_finished "rm -rf refformat" &&
+	cat >expect <<-EOF &&
+	fatal: unknown ref storage format ${SQ}garbage${SQ}
+	EOF
+	test_must_fail git init --ref-format=garbage refformat 2>err &&
+	test_cmp expect err
+'
+
 test_expect_success MINGW 'core.hidedotfiles = false' '
 	git config --global core.hidedotfiles false &&
 	rm -rf newdir &&
@@ -563,7 +633,7 @@
 
 	: re-initializing should not change the branch name &&
 	git init --initial-branch=ignore initial-branch-option 2>err &&
-	test_i18ngrep "ignored --initial-branch" err &&
+	test_grep "ignored --initial-branch" err &&
 	git -C initial-branch-option symbolic-ref HEAD >actual &&
 	grep hello actual
 '
@@ -579,7 +649,7 @@
 	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= git -c color.advice=always \
 		init unconfigured-default-branch-name 2>err &&
 	test_decode_color <err >decoded &&
-	test_i18ngrep "<YELLOW>hint: " decoded
+	test_grep "<YELLOW>hint: " decoded
 '
 
 test_expect_success 'overridden default main branch name (env)' '
@@ -592,15 +662,20 @@
 test_expect_success 'invalid default branch name' '
 	test_must_fail env GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME="with space" \
 		git init initial-branch-invalid 2>err &&
-	test_i18ngrep "invalid branch name" err
+	test_grep "invalid branch name" err
 '
 
 test_expect_success 'branch -m with the initial branch' '
 	git init rename-initial &&
 	git -C rename-initial branch -m renamed &&
-	test renamed = $(git -C rename-initial symbolic-ref --short HEAD) &&
+	echo renamed >expect &&
+	git -C rename-initial symbolic-ref --short HEAD >actual &&
+	test_cmp expect actual &&
+
 	git -C rename-initial branch -m renamed again &&
-	test again = $(git -C rename-initial symbolic-ref --short HEAD)
+	echo again >expect &&
+	git -C rename-initial symbolic-ref --short HEAD >actual &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 26eaca0..bf3bf60 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -22,23 +22,25 @@
 test_expect_success 'bad setup: invalid .git file format' '
 	echo "gitdir $REAL" >.git &&
 	test_must_fail git rev-parse 2>.err &&
-	test_i18ngrep "invalid gitfile format" .err
+	test_grep "invalid gitfile format" .err
 '
 
 test_expect_success 'bad setup: invalid .git file path' '
 	echo "gitdir: $REAL.not" >.git &&
 	test_must_fail git rev-parse 2>.err &&
-	test_i18ngrep "not a git repository" .err
+	test_grep "not a git repository" .err
 '
 
 test_expect_success 'final setup + check rev-parse --git-dir' '
 	echo "gitdir: $REAL" >.git &&
-	test "$REAL" = "$(git rev-parse --git-dir)"
+	echo "$REAL" >expect &&
+	git rev-parse --git-dir >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'check hash-object' '
 	echo "foo" >bar &&
-	SHA=$(cat bar | git hash-object -w --stdin) &&
+	SHA=$(git hash-object -w --stdin <bar) &&
 	test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 89b306c..774b52c 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -19,6 +19,20 @@
 	test_must_be_empty err
 }
 
+attr_check_object_mode_basic () {
+	path="$1" &&
+	expect="$2" &&
+	check_opts="$3" &&
+	git check-attr $check_opts builtin_objectmode -- "$path" >actual 2>err &&
+	echo "$path: builtin_objectmode: $expect" >expect &&
+	test_cmp expect actual
+}
+
+attr_check_object_mode () {
+	attr_check_object_mode_basic "$@" &&
+	test_must_be_empty err
+}
+
 attr_check_quote () {
 	path="$1" quoted_path="$2" expect="$3" &&
 
@@ -30,8 +44,21 @@
 attr_check_source () {
 	path="$1" expect="$2" source="$3" git_opts="$4" &&
 
-	git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
 	echo "$path: test: $expect" >expect &&
+
+	git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+	test_cmp expect actual &&
+	test_must_be_empty err &&
+
+	git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
+	test_cmp expect actual &&
+	test_must_be_empty err
+
+	git $git_opts -c "attr.tree=$source" check-attr test -- "$path" >actual 2>err &&
+	test_cmp expect actual &&
+	test_must_be_empty err
+
+	GIT_ATTR_SOURCE="$source" git $git_opts check-attr test -- "$path" >actual 2>err &&
 	test_cmp expect actual &&
 	test_must_be_empty err
 }
@@ -250,7 +277,7 @@
 test_expect_success 'negative patterns' '
 	echo "!f test=bar" >.gitattributes &&
 	git check-attr test -- '"'"'!f'"'"' 2>errors &&
-	test_i18ngrep "Negative patterns are ignored" errors
+	test_grep "Negative patterns are ignored" errors
 '
 
 test_expect_success 'patterns starting with exclamation' '
@@ -333,6 +360,74 @@
 	)
 '
 
+bad_attr_source_err="fatal: bad --attr-source or GIT_ATTR_SOURCE"
+
+test_expect_success '--attr-source is bad' '
+	test_when_finished rm -rf empty &&
+	git init empty &&
+	(
+		cd empty &&
+		echo "$bad_attr_source_err" >expect_err &&
+		test_must_fail git --attr-source=HEAD check-attr test -- f/path 2>err &&
+		test_cmp expect_err err
+	)
+'
+
+test_expect_success 'attr.tree when HEAD is unborn' '
+	test_when_finished rm -rf empty &&
+	git init empty &&
+	(
+		cd empty &&
+		echo "f/path: test: unspecified" >expect &&
+		git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
+		test_must_be_empty err &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'bad attr source defaults to reading .gitattributes file' '
+	test_when_finished rm -rf empty &&
+	git init empty &&
+	(
+		cd empty &&
+		echo "f/path test=val" >.gitattributes &&
+		echo "f/path: test: val" >expect &&
+		git -c attr.tree=HEAD check-attr test -- f/path >actual 2>err &&
+		test_must_be_empty err &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'bare repo defaults to reading .gitattributes from HEAD' '
+	test_when_finished rm -rf test bare_with_gitattribute &&
+	git init test &&
+	test_commit -C test gitattributes .gitattributes "f/path test=val" &&
+	git clone --bare test bare_with_gitattribute &&
+	echo "f/path: test: val" >expect &&
+	git -C bare_with_gitattribute check-attr test -- f/path >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'precedence of --attr-source, GIT_ATTR_SOURCE, then attr.tree' '
+	test_when_finished rm -rf empty &&
+	git init empty &&
+	(
+		cd empty &&
+		git checkout -b attr-source &&
+		test_commit "val1" .gitattributes "f/path test=val1" &&
+		git checkout -b attr-tree &&
+		test_commit "val2" .gitattributes "f/path test=val2" &&
+		git checkout attr-source &&
+		echo "f/path: test: val1" >expect &&
+		GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree --attr-source=attr-source \
+		check-attr test -- f/path >actual &&
+		test_cmp expect actual &&
+		GIT_ATTR_SOURCE=attr-source git -c attr.tree=attr-tree \
+		check-attr test -- f/path >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'bare repository: with --source' '
 	(
 		cd bare.git &&
@@ -415,7 +510,7 @@
 	mkdir subdir &&
 	ln -s ../attr subdir/.gitattributes &&
 	attr_check_basic subdir/file unspecified &&
-	test_i18ngrep "unable to access.*gitattributes" err
+	test_grep "unable to access.*gitattributes" err
 '
 
 test_expect_success 'large attributes line ignored in tree' '
@@ -477,4 +572,66 @@
 	test_cmp expect err
 '
 
+test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
+	>normal &&
+	attr_check_object_mode normal 100644 &&
+	mkdir dir &&
+	attr_check_object_mode dir 040000
+'
+
+test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
+	>exec &&
+	chmod +x exec &&
+	attr_check_object_mode exec 100755
+'
+
+test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
+	ln -s to_sym sym &&
+	attr_check_object_mode sym 120000
+'
+
+test_expect_success 'native object mode attributes work with --cached' '
+	>normal &&
+	git add normal &&
+	empty_blob=$(git rev-parse :normal) &&
+	git update-index --index-info <<-EOF &&
+	100755 $empty_blob 0	exec
+	120000 $empty_blob 0	symlink
+	EOF
+	attr_check_object_mode normal 100644 --cached &&
+	attr_check_object_mode exec 100755 --cached &&
+	attr_check_object_mode symlink 120000 --cached
+'
+
+test_expect_success 'check object mode attributes work for submodules' '
+	mkdir sub &&
+	(
+		cd sub &&
+		git init &&
+		mv .git .real &&
+		echo "gitdir: .real" >.git &&
+		test_commit first
+	) &&
+	attr_check_object_mode sub 160000 &&
+	attr_check_object_mode sub unspecified --cached &&
+	git add sub &&
+	attr_check_object_mode sub 160000 --cached
+'
+
+test_expect_success 'we do not allow user defined builtin_* attributes' '
+	echo "foo* builtin_foo" >.gitattributes &&
+	git add .gitattributes 2>actual &&
+	echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'user defined builtin_objectmode values are ignored' '
+	echo "foo* builtin_objectmode=12345" >.gitattributes &&
+	git add .gitattributes &&
+	>foo_1 &&
+	attr_check_object_mode_basic foo_1 100644 &&
+	echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
+	test_cmp expect err
+'
+
 test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index e18b160..3031256 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -46,6 +46,7 @@
 TIME='1466000000 +0200'
 check_show iso8601 "$TIME" '2016-06-15 16:13:20 +0200'
 check_show iso8601-strict "$TIME" '2016-06-15T16:13:20+02:00'
+check_show iso8601-strict "$(echo "$TIME" | sed 's/+0200$/+0000/')" '2016-06-15T14:13:20Z'
 check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
 check_show short "$TIME" '2016-06-15'
 check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
@@ -69,6 +70,14 @@
 check_show 'format:%s' '123456789 -1234' 123456789
 check_show 'format-local:%s' '123456789 -1234' 123456789
 
+# negative TZ offset
+TIME='1466000000 -0200'
+check_show iso8601 "$TIME" '2016-06-15 12:13:20 -0200'
+check_show iso8601-strict "$TIME" '2016-06-15T12:13:20-02:00'
+check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 12:13:20 -0200'
+check_show default "$TIME" 'Wed Jun 15 12:13:20 2016 -0200'
+check_show raw "$TIME" '1466000000 -0200'
+
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
 check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index eeb8539..ff4fd93 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -147,6 +147,84 @@
 	)
 '
 
+test_expect_success POSIXPERM 'GIT_SHELL_PATH points to a valid executable' '
+	shellpath=$(git var GIT_SHELL_PATH) &&
+	test_path_is_executable "$shellpath"
+'
+
+# We know in this environment that our shell will be one of a few fixed values
+# that all end in "sh".
+test_expect_success MINGW 'GIT_SHELL_PATH points to a suitable shell' '
+	shellpath=$(git var GIT_SHELL_PATH) &&
+	case "$shellpath" in
+	*sh) ;;
+	*) return 1;;
+	esac
+'
+
+test_expect_success 'GIT_ATTR_SYSTEM produces expected output' '
+	test_must_fail env GIT_ATTR_NOSYSTEM=1 git var GIT_ATTR_SYSTEM &&
+	(
+		sane_unset GIT_ATTR_NOSYSTEM &&
+		systempath=$(git var GIT_ATTR_SYSTEM) &&
+		test "$systempath" != ""
+	)
+'
+
+test_expect_success 'GIT_ATTR_GLOBAL points to the correct location' '
+	TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+	globalpath=$(XDG_CONFIG_HOME="$TRASHDIR/.config" git var GIT_ATTR_GLOBAL) &&
+	test "$globalpath" = "$TRASHDIR/.config/git/attributes" &&
+	(
+		sane_unset XDG_CONFIG_HOME &&
+		globalpath=$(HOME="$TRASHDIR" git var GIT_ATTR_GLOBAL) &&
+		test "$globalpath" = "$TRASHDIR/.config/git/attributes"
+	)
+'
+
+test_expect_success 'GIT_CONFIG_SYSTEM points to the correct location' '
+	TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+	test_must_fail env GIT_CONFIG_NOSYSTEM=1 git var GIT_CONFIG_SYSTEM &&
+	(
+		sane_unset GIT_CONFIG_NOSYSTEM &&
+		systempath=$(git var GIT_CONFIG_SYSTEM) &&
+		test "$systempath" != "" &&
+		systempath=$(GIT_CONFIG_SYSTEM=/dev/null git var GIT_CONFIG_SYSTEM) &&
+		if test_have_prereq MINGW
+		then
+			test "$systempath" = "nul"
+		else
+			test "$systempath" = "/dev/null"
+		fi &&
+		systempath=$(GIT_CONFIG_SYSTEM="$TRASHDIR/gitconfig" git var GIT_CONFIG_SYSTEM) &&
+		test "$systempath" = "$TRASHDIR/gitconfig"
+	)
+'
+
+test_expect_success 'GIT_CONFIG_GLOBAL points to the correct location' '
+	TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+	HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var GIT_CONFIG_GLOBAL >actual &&
+	echo "$TRASHDIR/foo/git/config" >expected &&
+	echo "$TRASHDIR/.gitconfig" >>expected &&
+	test_cmp expected actual &&
+	(
+		sane_unset XDG_CONFIG_HOME &&
+		HOME="$TRASHDIR" git var GIT_CONFIG_GLOBAL >actual &&
+		echo "$TRASHDIR/.config/git/config" >expected &&
+		echo "$TRASHDIR/.gitconfig" >>expected &&
+		test_cmp expected actual &&
+		globalpath=$(GIT_CONFIG_GLOBAL=/dev/null git var GIT_CONFIG_GLOBAL) &&
+		if test_have_prereq MINGW
+		then
+			test "$globalpath" = "nul"
+		else
+			test "$globalpath" = "/dev/null"
+		fi &&
+		globalpath=$(GIT_CONFIG_GLOBAL="$TRASHDIR/gitconfig" git var GIT_CONFIG_GLOBAL) &&
+		test "$globalpath" = "$TRASHDIR/gitconfig"
+	)
+'
+
 # For git var -l, we check only a representative variable;
 # testing the whole output would make our test too brittle with
 # respect to unrelated changes in the test suite's environment.
@@ -164,8 +242,39 @@
 	test_cmp expect actual.bare
 '
 
+test_expect_success 'git var -l lists multiple global configs' '
+	TRASHDIR="$(test-tool path-utils normalize_path_copy "$(pwd)")" &&
+	HOME="$TRASHDIR" XDG_CONFIG_HOME="$TRASHDIR/foo" git var -l >actual &&
+	grep "^GIT_CONFIG_GLOBAL=" actual >filtered &&
+	echo "GIT_CONFIG_GLOBAL=$TRASHDIR/foo/git/config" >expected &&
+	echo "GIT_CONFIG_GLOBAL=$TRASHDIR/.gitconfig" >>expected &&
+	test_cmp expected filtered
+'
+
+test_expect_success 'git var -l does not split multiline editors' '
+	(
+		GIT_EDITOR="!f() {
+			echo Hello!
+		}; f" &&
+		export GIT_EDITOR &&
+		echo "GIT_EDITOR=$GIT_EDITOR" >expected &&
+		git var -l >var &&
+		sed -n -e "/^GIT_EDITOR/,\$p" var | head -n 3 >actual &&
+		test_cmp expected actual
+	)
+'
+
 test_expect_success 'listing and asking for variables are exclusive' '
 	test_must_fail git var -l GIT_COMMITTER_IDENT
 '
 
+test_expect_success '`git var -l` works even without HOME' '
+	(
+		XDG_CONFIG_HOME= &&
+		export XDG_CONFIG_HOME &&
+		unset HOME &&
+		git var -l
+	)
+'
+
 test_done
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index c70d11b..361446b 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -49,7 +49,7 @@
 
 stderr_contains () {
 	regexp="$1"
-	if test_i18ngrep "$regexp" "$HOME/stderr"
+	if test_grep "$regexp" "$HOME/stderr"
 	then
 		return 0
 	else
@@ -942,7 +942,7 @@
 	ln -s ignore subdir/.gitignore &&
 	test_must_fail git check-ignore subdir/file >actual 2>err &&
 	test_must_be_empty actual &&
-	test_i18ngrep "unable to access.*gitignore" err
+	test_grep "unable to access.*gitignore" err
 '
 
 test_done
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
deleted file mode 100755
index eea9910..0000000
--- a/t/t0009-prio-queue.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh
-
-test_description='basic tests for priority queue implementation'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-cat >expect <<'EOF'
-1
-2
-3
-4
-5
-5
-6
-7
-8
-9
-10
-EOF
-test_expect_success 'basic ordering' '
-	test-tool prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
-	test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-2
-3
-4
-1
-5
-6
-EOF
-test_expect_success 'mixed put and get' '
-	test-tool prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
-	test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-1
-2
-NULL
-1
-2
-NULL
-EOF
-test_expect_success 'notice empty queue' '
-	test-tool prio-queue 1 2 get get get 1 2 get get get >actual &&
-	test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-3
-2
-6
-4
-5
-1
-8
-EOF
-test_expect_success 'stack order' '
-	test-tool prio-queue stack 8 1 5 4 6 2 3 dump >actual &&
-	test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index 837c8b7..84172a3 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -10,25 +10,24 @@
 
 for trial in 0 1 2 3 4
 do
-	rm -f .git/index
-	echo frotz >infocom
-	git update-index --add infocom
-	echo xyzzy >infocom
+	test_expect_success "Racy git trial #$trial part A" '
+		rm -f .git/index &&
+		echo frotz >infocom &&
+		git update-index --add infocom &&
+		echo xyzzy >infocom &&
 
-	files=$(git diff-files -p)
-	test_expect_success \
-	"Racy GIT trial #$trial part A" \
-	'test "" != "$files"'
-
+		git diff-files -p >out &&
+		test_file_not_empty out
+	'
 	sleep 1
-	echo xyzzy >cornerstone
-	git update-index --add cornerstone
 
-	files=$(git diff-files -p)
-	test_expect_success \
-	"Racy GIT trial #$trial part B" \
-	'test "" != "$files"'
+	test_expect_success "Racy git trial #$trial part B" '
+		echo xyzzy >cornerstone &&
+		git update-index --add cornerstone &&
 
+		git diff-files -p >out &&
+		test_file_not_empty out
+	'
 done
 
 test_done
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 1cb6aa6..46e74ad 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -239,7 +239,7 @@
 	echo value40 >> expect &&
 	echo size >> in &&
 	echo 64 39 >> expect &&
-	cat in | test-tool hashmap > out &&
+	test-tool hashmap <in >out &&
 	test_cmp expect out
 
 '
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index dbfc5c8..1d273d9 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -100,17 +100,17 @@
 
 test_expect_success 'git help' '
 	git help >help.output &&
-	test_i18ngrep "^   clone  " help.output &&
-	test_i18ngrep "^   add    " help.output &&
-	test_i18ngrep "^   log    " help.output &&
-	test_i18ngrep "^   commit " help.output &&
-	test_i18ngrep "^   fetch  " help.output
+	test_grep "^   clone  " help.output &&
+	test_grep "^   add    " help.output &&
+	test_grep "^   log    " help.output &&
+	test_grep "^   commit " help.output &&
+	test_grep "^   fetch  " help.output
 '
 
 test_expect_success 'git help -g' '
 	git help -g >help.output &&
-	test_i18ngrep "^   everyday   " help.output &&
-	test_i18ngrep "^   tutorial   " help.output
+	test_grep "^   everyday   " help.output &&
+	test_grep "^   tutorial   " help.output
 '
 
 test_expect_success 'git help fails for non-existing html pages' '
@@ -257,7 +257,7 @@
 			export GIT_CEILING_DIRECTORIES &&
 			test_expect_code 129 git -C sub $builtin -h >output 2>&1
 		) &&
-		test_i18ngrep usage output
+		test_grep usage output
 	'
 done <builtins
 
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh
index 5324047..0881417 100755
--- a/t/t0013-sha1dc.sh
+++ b/t/t0013-sha1dc.sh
@@ -16,7 +16,7 @@
 
 test_expect_success 'test-sha1 detects shattered pdf' '
 	test_must_fail test-tool sha1 <"$TEST_DATA/shattered-1.pdf" 2>err &&
-	test_i18ngrep collision err &&
+	test_grep collision err &&
 	grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err
 '
 
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
index 8d3d914..9556834 100755
--- a/t/t0014-alias.sh
+++ b/t/t0014-alias.sh
@@ -8,7 +8,7 @@
 	git config alias.nested-internal-1 nested-internal-2 &&
 	git config alias.nested-internal-2 status &&
 	git nested-internal-1 >output &&
-	test_i18ngrep "^On branch " output
+	test_grep "^On branch " output
 '
 
 test_expect_success 'nested aliases - mixed execution' '
@@ -16,7 +16,7 @@
 	git config alias.nested-external-2 "!git nested-external-3" &&
 	git config alias.nested-external-3 status &&
 	git nested-external-1 >output &&
-	test_i18ngrep "^On branch " output
+	test_grep "^On branch " output
 '
 
 test_expect_success 'looping aliases - internal execution' '
@@ -24,7 +24,7 @@
 	git config alias.loop-internal-2 loop-internal-3 &&
 	git config alias.loop-internal-3 loop-internal-2 &&
 	test_must_fail git loop-internal-1 2>output &&
-	test_i18ngrep "^fatal: alias loop detected: expansion of" output
+	test_grep "^fatal: alias loop detected: expansion of" output
 '
 
 # This test is disabled until external loops are fixed, because would block
@@ -34,7 +34,7 @@
 #	git config alias.loop-mixed-1 loop-mixed-2 &&
 #	git config alias.loop-mixed-2 "!git loop-mixed-1" &&
 #	test_must_fail git loop-mixed-1 2>output &&
-#	test_i18ngrep "^fatal: alias loop detected: expansion of" output
+#	test_grep "^fatal: alias loop detected: expansion of" output
 #'
 
 test_expect_success 'run-command formats empty args properly' '
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index c13057a..0dcfb76 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -17,7 +17,6 @@
 test_expect_success 'advice should be printed when config variable is set to true' '
 	cat >expect <<-\EOF &&
 	hint: This is a piece of advice
-	hint: Disable this message with "git config advice.nestedTag false"
 	EOF
 	test_config advice.nestedTag true &&
 	test-tool advise "This is a piece of advice" 2>actual &&
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 35cc8c3..81946e8 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -125,7 +125,7 @@
 	munge_cr append dir/two &&
 	git update-index -- one dir/two &&
 	differs=$(git diff-index --cached HEAD) &&
-	verbose test -z "$differs"
+	test -z "$differs"
 
 '
 
@@ -138,7 +138,7 @@
 	munge_cr append dir/two &&
 	git update-index -- one dir/two &&
 	differs=$(git diff-index --cached HEAD) &&
-	verbose test -z "$differs"
+	test -z "$differs"
 
 '
 
@@ -153,7 +153,7 @@
 	test "$one" = $(git hash-object --stdin <one) &&
 	test "$two" = $(git hash-object --stdin <dir/two) &&
 	differs=$(git diff-index --cached HEAD) &&
-	verbose test -z "$differs"
+	test -z "$differs"
 '
 
 test_expect_success 'checkout with autocrlf=input' '
@@ -167,7 +167,7 @@
 	test "$one" = $(git hash-object --stdin <one) &&
 	test "$two" = $(git hash-object --stdin <dir/two) &&
 	differs=$(git diff-index --cached HEAD) &&
-	verbose test -z "$differs"
+	test -z "$differs"
 '
 
 test_expect_success 'apply patch (autocrlf=input)' '
@@ -177,7 +177,7 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply patch.file &&
-	verbose test "$patched" = "$(git hash-object --stdin <one)"
+	test "$patched" = "$(git hash-object --stdin <one)"
 '
 
 test_expect_success 'apply patch --cached (autocrlf=input)' '
@@ -187,7 +187,7 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply --cached patch.file &&
-	verbose test "$patched" = $(git rev-parse :one)
+	test "$patched" = $(git rev-parse :one)
 '
 
 test_expect_success 'apply patch --index (autocrlf=input)' '
@@ -197,8 +197,8 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply --index patch.file &&
-	verbose test "$patched" = $(git rev-parse :one) &&
-	verbose test "$patched" = $(git hash-object --stdin <one)
+	test "$patched" = $(git rev-parse :one) &&
+	test "$patched" = $(git hash-object --stdin <one)
 '
 
 test_expect_success 'apply patch (autocrlf=true)' '
@@ -208,7 +208,7 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply patch.file &&
-	verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+	test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
 '
 
 test_expect_success 'apply patch --cached (autocrlf=true)' '
@@ -218,7 +218,7 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply --cached patch.file &&
-	verbose test "$patched" = $(git rev-parse :one)
+	test "$patched" = $(git rev-parse :one)
 '
 
 test_expect_success 'apply patch --index (autocrlf=true)' '
@@ -228,8 +228,8 @@
 	git read-tree --reset -u HEAD &&
 
 	git apply --index patch.file &&
-	verbose test "$patched" = $(git rev-parse :one) &&
-	verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+	test "$patched" = $(git rev-parse :one) &&
+	test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
 '
 
 test_expect_success '.gitattributes says two is binary' '
@@ -240,7 +240,7 @@
 	git read-tree --reset -u HEAD &&
 
 	! has_cr dir/two &&
-	verbose has_cr one &&
+	has_cr one &&
 	! has_cr three
 '
 
@@ -259,8 +259,8 @@
 	echo "t* crlf" >.gitattributes &&
 	git read-tree --reset -u HEAD &&
 
-	verbose has_cr dir/two &&
-	verbose has_cr three
+	has_cr dir/two &&
+	has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (1)' '
@@ -273,7 +273,7 @@
 	git read-tree --reset -u HEAD &&
 
 	! has_cr one &&
-	verbose has_cr three
+	has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (2)' '
@@ -283,7 +283,7 @@
 	git checkout-index -f -q -u -a &&
 
 	! has_cr one &&
-	verbose has_cr three
+	has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (3)' '
@@ -294,7 +294,7 @@
 	git checkout-index -u one dir/two three &&
 
 	! has_cr one &&
-	verbose has_cr three
+	has_cr three
 '
 
 test_expect_success 'in-tree .gitattributes (4)' '
@@ -305,7 +305,7 @@
 	git checkout-index -u .gitattributes &&
 
 	! has_cr one &&
-	verbose has_cr three
+	has_cr three
 '
 
 test_expect_success 'checkout with existing .gitattributes' '
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 46abbee..0b49970 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -263,7 +263,7 @@
 
 	echo test >test.ac &&
 	test_must_fail git add test.ac 2>stderr &&
-	test_i18ngrep "fatal: test.ac: clean filter .absentclean. failed" stderr
+	test_grep "fatal: test.ac: clean filter .absentclean. failed" stderr
 '
 
 test_expect_success 'required filter with absent smudge field' '
@@ -276,7 +276,7 @@
 	git add test.as &&
 	rm -f test.as &&
 	test_must_fail git checkout -- test.as 2>stderr &&
-	test_i18ngrep "fatal: test.as: smudge filter absentsmudge failed" stderr
+	test_grep "fatal: test.as: smudge filter absentsmudge failed" stderr
 '
 
 test_expect_success 'filtering large input to small output should use little memory' '
@@ -733,7 +733,7 @@
 		git checkout --quiet --no-progress . 2>git-stderr.log &&
 
 		grep "smudge write error" git-stderr.log &&
-		test_i18ngrep "error: external filter" git-stderr.log &&
+		test_grep "error: external filter" git-stderr.log &&
 
 		cat >expected.log <<-EOF &&
 			START
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index a34de56..a7f4de4 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -9,7 +9,7 @@
 
 	git config core.autocrlf true &&
 
-	printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+	printf "CRLF line ending\r\nAnd another\r\n" >sample &&
 	git add sample &&
 
 	test_tick &&
@@ -19,8 +19,9 @@
 
 test_expect_success 'tar archive' '
 
-	git archive --format=tar HEAD |
-	( mkdir untarred && cd untarred && "$TAR" -xf - ) &&
+	git archive --format=tar HEAD >test.tar &&
+	mkdir untarred &&
+	"$TAR" xf test.tar -C untarred &&
 
 	test_cmp sample untarred/sample
 
@@ -30,7 +31,11 @@
 
 	git archive --format=zip HEAD >test.zip &&
 
-	( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) &&
+	mkdir unzipped &&
+	(
+		cd unzipped &&
+		"$GIT_UNZIP" ../test.zip
+	) &&
 
 	test_cmp sample unzipped/sample
 
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index a94ac1e..2f57c86 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -70,7 +70,8 @@
 				cp CRLF        ${pfx}_CRLF.txt &&
 				cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
 				cp LF_mix_CR   ${pfx}_LF_mix_CR.txt &&
-				cp CRLF_nul    ${pfx}_CRLF_nul.txt
+				cp CRLF_nul    ${pfx}_CRLF_nul.txt ||
+				return 1
 			done
 		done
 	done
@@ -101,7 +102,8 @@
 	do
 		fname=${pfx}_$f.txt &&
 		cp $f $fname &&
-		git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
+		git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+		return 1
 	done &&
 	git commit -m "core.autocrlf $crlf" &&
 	check_warning "$lfname" ${pfx}_LF.err &&
@@ -121,15 +123,19 @@
 	lfmixcr=$1 ; shift
 	crlfnul=$1 ; shift
 	pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
-	#Commit files on top of existing file
-	create_gitattributes "$attr" $aeol &&
-	for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-	do
-		fname=${pfx}_$f.txt &&
-		cp $f $fname &&
-		printf Z >>"$fname" &&
-		git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
-	done
+
+	test_expect_success 'setup commit NNO files' '
+		#Commit files on top of existing file
+		create_gitattributes "$attr" $aeol &&
+		for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+		do
+			fname=${pfx}_$f.txt &&
+			cp $f $fname &&
+			printf Z >>"$fname" &&
+			git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+			return 1
+		done
+	'
 
 	test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
 		check_warning "$lfwarn" ${pfx}_LF.err
@@ -163,15 +169,19 @@
 	lfmixcr=$1 ; shift
 	crlfnul=$1 ; shift
 	pfx=MIX_attr_${attr}_aeol_${aeol}_${crlf}
-	#Commit file with CLRF_mix_LF on top of existing file
-	create_gitattributes "$attr" $aeol &&
-	for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-	do
-		fname=${pfx}_$f.txt &&
-		cp CRLF_mix_LF $fname &&
-		printf Z >>"$fname" &&
-		git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
-	done
+
+	test_expect_success 'setup commit file with mixed EOL' '
+		#Commit file with CLRF_mix_LF on top of existing file
+		create_gitattributes "$attr" $aeol &&
+		for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+		do
+			fname=${pfx}_$f.txt &&
+			cp CRLF_mix_LF $fname &&
+			printf Z >>"$fname" &&
+			git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" ||
+			return 1
+		done
+	'
 
 	test_expect_success "commit file with mixed EOL onto LF crlf=$crlf attr=$attr" '
 		check_warning "$lfwarn" ${pfx}_LF.err
@@ -289,17 +299,17 @@
 	lfmixcrlf=$1 ; shift
 	lfmixcr=$1 ; shift
 	crlfnul=$1 ; shift
-	create_gitattributes "$attr" $ident $aeol &&
-	git config core.autocrlf $crlf &&
+	test_expect_success "setup config for checkout attr=$attr ident=$ident aeol=$aeol core.autocrlf=$crlf" '
+		create_gitattributes "$attr" $ident $aeol &&
+		git config core.autocrlf $crlf
+	'
 	pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ &&
 	for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
 	do
-		rm crlf_false_attr__$f.txt &&
-		if test -z "$ceol"; then
-			git checkout -- crlf_false_attr__$f.txt
-		else
-			git -c core.eol=$ceol checkout -- crlf_false_attr__$f.txt
-		fi
+		test_expect_success "setup $f checkout ${ceol:+ with -c core.eol=$ceol}"  '
+			rm -f crlf_false_attr__$f.txt &&
+			git ${ceol:+-c core.eol=$ceol} checkout -- crlf_false_attr__$f.txt
+		'
 	done
 
 	test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" '
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index c196fdb..ad151a3 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -92,23 +92,23 @@
 		# In these cases the BOM is prohibited.
 		cp bebom.utf${i}be.raw bebom.utf${i}be &&
 		test_must_fail git add bebom.utf${i}be 2>err.out &&
-		test_i18ngrep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
-		test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+		test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
+		test_grep "use UTF-${i} as working-tree-encoding" err.out &&
 
 		cp lebom.utf${i}le.raw lebom.utf${i}be &&
 		test_must_fail git add lebom.utf${i}be 2>err.out &&
-		test_i18ngrep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
-		test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+		test_grep "fatal: BOM is prohibited .* utf-${i}be" err.out &&
+		test_grep "use UTF-${i} as working-tree-encoding" err.out &&
 
 		cp bebom.utf${i}be.raw bebom.utf${i}le &&
 		test_must_fail git add bebom.utf${i}le 2>err.out &&
-		test_i18ngrep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
-		test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out &&
+		test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
+		test_grep "use UTF-${i} as working-tree-encoding" err.out &&
 
 		cp lebom.utf${i}le.raw lebom.utf${i}le &&
 		test_must_fail git add lebom.utf${i}le 2>err.out &&
-		test_i18ngrep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
-		test_i18ngrep "use UTF-${i} as working-tree-encoding" err.out
+		test_grep "fatal: BOM is prohibited .* utf-${i}LE" err.out &&
+		test_grep "use UTF-${i} as working-tree-encoding" err.out
 	'
 
 	test_expect_success "check required UTF-${i} BOM" '
@@ -118,21 +118,21 @@
 
 		cp nobom.utf${i}be.raw nobom.utf${i} &&
 		test_must_fail git add nobom.utf${i} 2>err.out &&
-		test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
-		test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out &&
+		test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+		test_grep "use UTF-${i}BE or UTF-${i}LE" err.out &&
 
 		cp nobom.utf${i}le.raw nobom.utf${i} &&
 		test_must_fail git add nobom.utf${i} 2>err.out &&
-		test_i18ngrep "fatal: BOM is required .* utf-${i}" err.out &&
-		test_i18ngrep "use UTF-${i}BE or UTF-${i}LE" err.out
+		test_grep "fatal: BOM is required .* utf-${i}" err.out &&
+		test_grep "use UTF-${i}BE or UTF-${i}LE" err.out
 	'
 
 	test_expect_success "eol conversion for UTF-${i} encoded files on checkout" '
 		test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
 		test_when_finished "git reset --hard HEAD^" &&
 
-		cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
-		cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+		write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+		write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
 		cp crlf.utf${i}.raw eol.utf${i} &&
 
 		cat >expectIndexLF <<-EOF &&
@@ -169,7 +169,7 @@
 	echo "*.set text working-tree-encoding" >.gitattributes &&
 	printf "set" >t.set &&
 	test_must_fail git add t.set 2>err.out &&
-	test_i18ngrep "true/false are no valid working-tree-encodings" err.out &&
+	test_grep "true/false are no valid working-tree-encodings" err.out &&
 
 	echo "*.unset text -working-tree-encoding" >.gitattributes &&
 	printf "unset" >t.unset &&
@@ -182,7 +182,7 @@
 	echo "*.garbage text working-tree-encoding=garbage" >.gitattributes &&
 	printf "garbage" >t.garbage &&
 	test_must_fail git add t.garbage 2>err.out &&
-	test_i18ngrep "failed to encode" err.out
+	test_grep "failed to encode" err.out
 '
 
 test_expect_success 'error if encoding round trip is not the same during refresh' '
@@ -201,7 +201,7 @@
 	git update-ref refs/heads/main $COMMIT &&
 
 	test_must_fail git checkout HEAD^ 2>err.out &&
-	test_i18ngrep "error: .* overwritten by checkout:" err.out
+	test_grep "error: .* overwritten by checkout:" err.out
 '
 
 test_expect_success 'error if encoding garbage is already in Git' '
@@ -217,7 +217,7 @@
 	git update-ref refs/heads/main $COMMIT &&
 
 	git diff 2>err.out &&
-	test_i18ngrep "error: BOM is required" err.out
+	test_grep "error: BOM is required" err.out
 '
 
 test_lazy_prereq ICONV_SHIFT_JIS '
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index 0a5713c..f10f42f 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -17,396 +17,378 @@
     printf "$1" | git stripspace
 }
 
-test_expect_success \
-    'long lines without spaces should be unchanged' '
-    echo "$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+test_expect_success 'long lines without spaces should be unchanged' '
+	echo "$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+	echo "$ttt$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$ttt$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+	echo "$ttt$ttt$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$ttt$ttt$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual
+	echo "$ttt$ttt$ttt$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'lines with spaces at the beginning should be unchanged' '
-    echo "$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+test_expect_success 'lines with spaces at the beginning should be unchanged' '
+	echo "$sss$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual &&
 
-    echo "$sss$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+	echo "$sss$sss$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual &&
 
-    echo "$sss$sss$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual
+	echo "$sss$sss$sss$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'lines with intermediate spaces should be unchanged' '
-    echo "$ttt$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual &&
+test_expect_success 'lines with intermediate spaces should be unchanged' '
+	echo "$ttt$sss$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$sss$sss$ttt" >expect &&
-    git stripspace <expect >actual &&
-    test_cmp expect actual
+	echo "$ttt$sss$sss$ttt" >expect &&
+	git stripspace <expect >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'consecutive blank lines should be unified' '
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive blank lines should be unified' '
+	printf "$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$ttt$ttt\n" > expect &&
+	printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+	printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt$ttt$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$ttt\n" > expect &&
+	printf "$ttt\n\t\n \n\n  \t\t\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$ttt$ttt\n" > expect &&
+	printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual
+	printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+	printf "$ttt\n\t\n \n\n  \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'only consecutive blank lines should be completely removed' '
+test_expect_success 'only consecutive blank lines should be completely removed' '
+	printf "\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "\n\n\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "\n\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
-    test_must_be_empty actual &&
-
-    printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
-    test_must_be_empty actual
+	printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
+	test_must_be_empty actual
 '
 
-test_expect_success \
-    'consecutive blank lines at the beginning should be removed' '
-    printf "$ttt\n" > expect &&
-    printf "\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive blank lines at the beginning should be removed' '
+	printf "$ttt\n" > expect &&
+	printf "\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
-    printf "\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n" > expect &&
+	printf "\n\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" > expect &&
-    printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n" > expect &&
+	printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n" > expect &&
-    printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt$ttt\n" > expect &&
+	printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt$ttt\n" > expect &&
-    printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt$ttt$ttt\n" > expect &&
+	printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
+	printf "$ttt\n" > expect &&
 
-    printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual
+	printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'consecutive blank lines at the end should be removed' '
-    printf "$ttt\n" > expect &&
-    printf "$ttt\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive blank lines at the end should be removed' '
+	printf "$ttt\n" > expect &&
+	printf "$ttt\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
-    printf "$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n" > expect &&
+	printf "$ttt\n\n\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" > expect &&
-    printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n" > expect &&
+	printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt$ttt\n" > expect &&
+	printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt$ttt\n" > expect &&
-    printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt$ttt$ttt\n" > expect &&
+	printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n" > expect &&
+	printf "$ttt\n" > expect &&
 
-    printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
-    test_cmp expect actual
+	printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'text without newline at end should end with newline' '
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt"
+test_expect_success 'text without newline at end should end with newline' '
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt"
 '
 
 # text plus spaces at the end:
 
-test_expect_success \
-    'text plus spaces without newline at end should end with newline' '
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" &&
-    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss"
+test_expect_success 'text plus spaces without newline at end should end with newline' '
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" &&
+	test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss"
 '
 
-test_expect_success \
-    'text plus spaces without newline at end should not show spaces' '
-    printf "$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    printf "$ttt$sss$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null
+test_expect_success 'text plus spaces without newline at end should not show spaces' '
+	printf "$ttt$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	printf "$ttt$ttt$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	printf "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	printf "$ttt$sss$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	printf "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	printf "$ttt$sss$sss$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null
 '
 
-test_expect_success \
-    'text plus spaces without newline should show the correct lines' '
-    printf "$ttt\n" >expect &&
-    printf "$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'text plus spaces without newline should show the correct lines' '
+	printf "$ttt\n" >expect &&
+	printf "$ttt$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n" >expect &&
-    printf "$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n" >expect &&
+	printf "$ttt$sss$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n" >expect &&
-    printf "$ttt$sss$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n" >expect &&
+	printf "$ttt$sss$sss$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n" >expect &&
+	printf "$ttt$ttt$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n" >expect &&
+	printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual
+	printf "$ttt$ttt$ttt\n" >expect &&
+	printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success \
-    'text plus spaces at end should not show spaces' '
-    echo "$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$ttt$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null &&
-    echo "$ttt$sss$sss$sss" | git stripspace >tmp &&
-    ! grep "  " tmp >/dev/null
+test_expect_success 'text plus spaces at end should not show spaces' '
+	echo "$ttt$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	echo "$ttt$ttt$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	echo "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	echo "$ttt$sss$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	echo "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null &&
+	echo "$ttt$sss$sss$sss" | git stripspace >tmp &&
+	! grep "  " tmp >/dev/null
 '
 
-test_expect_success \
-    'text plus spaces at end should be cleaned and newline must remain' '
-    echo "$ttt" >expect &&
-    echo "$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'text plus spaces at end should be cleaned and newline must remain' '
+	echo "$ttt" >expect &&
+	echo "$ttt$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt" >expect &&
-    echo "$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	echo "$ttt" >expect &&
+	echo "$ttt$sss$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt" >expect &&
-    echo "$ttt$sss$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	echo "$ttt" >expect &&
+	echo "$ttt$sss$sss$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$ttt" >expect &&
-    echo "$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	echo "$ttt$ttt" >expect &&
+	echo "$ttt$ttt$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$ttt" >expect &&
-    echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
-    test_cmp expect actual &&
+	echo "$ttt$ttt" >expect &&
+	echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    echo "$ttt$ttt$ttt" >expect &&
-    echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
-    test_cmp expect actual
+	echo "$ttt$ttt$ttt" >expect &&
+	echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+	test_cmp expect actual
 '
 
 # spaces only:
 
-test_expect_success \
-    'spaces with newline at end should be replaced with empty string' '
-    echo | git stripspace >actual &&
-    test_must_be_empty actual &&
+test_expect_success 'spaces with newline at end should be replaced with empty string' '
+	echo | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    echo "$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	echo "$sss" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    echo "$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	echo "$sss$sss" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    echo "$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	echo "$sss$sss$sss" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    echo "$sss$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual
+	echo "$sss$sss$sss$sss" | git stripspace >actual &&
+	test_must_be_empty actual
 '
 
-test_expect_success \
-    'spaces without newline at end should not show spaces' '
-    printf "" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss$sss$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null &&
-    printf "$sss$sss$sss$sss" | git stripspace >tmp &&
-    ! grep " " tmp >/dev/null
+test_expect_success 'spaces without newline at end should not show spaces' '
+	printf "" | git stripspace >tmp &&
+	! grep " " tmp >/dev/null &&
+	printf "$sss" | git stripspace >tmp &&
+	! grep " " tmp >/dev/null &&
+	printf "$sss$sss" | git stripspace >tmp &&
+	! grep " " tmp >/dev/null &&
+	printf "$sss$sss$sss" | git stripspace >tmp &&
+	! grep " " tmp >/dev/null &&
+	printf "$sss$sss$sss$sss" | git stripspace >tmp &&
+	! grep " " tmp >/dev/null
 '
 
-test_expect_success \
-    'spaces without newline at end should be replaced with empty string' '
-    printf "" | git stripspace >actual &&
-    test_must_be_empty actual &&
+test_expect_success 'spaces without newline at end should be replaced with empty string' '
+	printf "" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "$sss$sss" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual &&
+	printf "$sss$sss$sss" | git stripspace >actual &&
+	test_must_be_empty actual &&
 
-    printf "$sss$sss$sss$sss" | git stripspace >actual &&
-    test_must_be_empty actual
+	printf "$sss$sss$sss$sss" | git stripspace >actual &&
+	test_must_be_empty actual
 '
 
-test_expect_success \
-    'consecutive text lines should be unchanged' '
-    printf "$ttt$ttt\n$ttt\n" >expect &&
-    printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+test_expect_success 'consecutive text lines should be unchanged' '
+	printf "$ttt$ttt\n$ttt\n" >expect &&
+	printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
-    printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
+	printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
-    printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
+	printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
-    printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
+	printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
-    printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual &&
+	printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
+	printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual &&
 
-    printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
-    printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
-    test_cmp expect actual
+	printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
+	printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'strip comments, too' '
@@ -419,6 +401,21 @@
 	test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)"
 '
 
+test_expect_success 'strip comments with changed comment string' '
+	test ! -z "$(echo "// comment" | git -c core.commentchar=// stripspace)" &&
+	test -z "$(echo "// comment" | git -c core.commentchar="//" stripspace -s)"
+'
+
+test_expect_success 'newline as commentchar is forbidden' '
+	test_must_fail git -c core.commentChar="$LF" stripspace -s 2>err &&
+	grep "core.commentchar cannot contain newline" err
+'
+
+test_expect_success 'empty commentchar is forbidden' '
+	test_must_fail git -c core.commentchar= stripspace -s 2>err &&
+	grep "core.commentchar must have at least one character" err
+'
+
 test_expect_success '-c with single line' '
 	printf "# foo\n" >expect &&
 	printf "foo" | git stripspace -c >actual &&
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index 11c15a4..d3cb2a1 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -7,27 +7,52 @@
 
 pwd="$(pwd)"
 
-expect_accepted () {
-	git "$@" rev-parse --git-dir
+expect_accepted_implicit () {
+	test_when_finished 'rm "$pwd/trace.perf"' &&
+	GIT_TRACE2_PERF="$pwd/trace.perf" git "$@" rev-parse --git-dir &&
+	# Note: we're intentionally only checking that the bare repo has a
+	# directory *prefix* of $pwd
+	grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
+}
+
+expect_accepted_explicit () {
+	test_when_finished 'rm "$pwd/trace.perf"' &&
+	GIT_DIR="$1" GIT_TRACE2_PERF="$pwd/trace.perf" git rev-parse --git-dir &&
+	! grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
 }
 
 expect_rejected () {
-	test_must_fail git "$@" rev-parse --git-dir 2>err &&
-	grep -F "cannot use bare repository" err
+	test_when_finished 'rm "$pwd/trace.perf"' &&
+	test_env GIT_TRACE2_PERF="$pwd/trace.perf" \
+		test_must_fail git "$@" rev-parse --git-dir 2>err &&
+	grep -F "cannot use bare repository" err &&
+	grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
 }
 
-test_expect_success 'setup bare repo in worktree' '
+test_expect_success 'setup an embedded bare repo, secondary worktree and submodule' '
 	git init outer-repo &&
-	git init --bare outer-repo/bare-repo
+	git init --bare --initial-branch=main outer-repo/bare-repo &&
+	git -C outer-repo worktree add ../outer-secondary &&
+	test_path_is_dir outer-secondary &&
+	(
+		cd outer-repo &&
+		test_commit A &&
+		git push bare-repo +HEAD:refs/heads/main &&
+		git -c protocol.file.allow=always \
+			submodule add --name subn -- ./bare-repo subd
+	) &&
+	test_path_is_dir outer-repo/.git/worktrees/outer-secondary &&
+	test_path_is_dir outer-repo/.git/modules/subn
 '
 
 test_expect_success 'safe.bareRepository unset' '
-	expect_accepted -C outer-repo/bare-repo
+	test_unconfig --global safe.bareRepository &&
+	expect_accepted_implicit -C outer-repo/bare-repo
 '
 
 test_expect_success 'safe.bareRepository=all' '
 	test_config_global safe.bareRepository all &&
-	expect_accepted -C outer-repo/bare-repo
+	expect_accepted_implicit -C outer-repo/bare-repo
 '
 
 test_expect_success 'safe.bareRepository=explicit' '
@@ -39,15 +64,14 @@
 	# 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_config -C outer-repo/bare-repo safe.bareRepository all &&
 	test_config_global safe.bareRepository explicit &&
 	expect_rejected -C outer-repo/bare-repo
 '
 
 test_expect_success 'safe.bareRepository on the command line' '
 	test_config_global safe.bareRepository explicit &&
-	expect_accepted -C outer-repo/bare-repo \
+	expect_accepted_implicit -C outer-repo/bare-repo \
 		-c safe.bareRepository=all
 '
 
@@ -60,4 +84,24 @@
 	expect_rejected -C outer-repo/bare-repo
 '
 
+test_expect_success 'no trace when GIT_DIR is explicitly provided' '
+	expect_accepted_explicit "$pwd/outer-repo/bare-repo"
+'
+
+test_expect_success 'no trace when "bare repository" is .git' '
+	expect_accepted_implicit -C outer-repo/.git
+'
+
+test_expect_success 'no trace when "bare repository" is a subdir of .git' '
+	expect_accepted_implicit -C outer-repo/.git/objects
+'
+
+test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
+	expect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary
+'
+
+test_expect_success 'no trace in $GIT_DIR of a submodule' '
+	expect_accepted_implicit -C outer-repo/.git/modules/subn
+'
+
 test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 7d7ecfd..8bb2a8b 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -13,29 +13,36 @@
 
     A helper function for the parse-options API.
 
-    --yes                 get a boolean
+    --[no-]yes            get a boolean
     -D, --no-doubt        begins with 'no-'
+    --doubt               opposite of --no-doubt
     -B, --no-fear         be brave
-    -b, --boolean         increment by one
-    -4, --or4             bitwise-or boolean with ...0100
-    --neg-or4             same as --no-or4
+    -b, --[no-]boolean    increment by one
+    -4, --[no-]or4        bitwise-or boolean with ...0100
+    --[no-]neg-or4        same as --no-or4
 
-    -i, --integer <n>     get a integer
+    -i, --[no-]integer <n>
+                          get a integer
     -j <n>                get a integer, too
     -m, --magnitude <n>   get a magnitude
-    --set23               set integer to 23
+    --[no-]set23          set integer to 23
     --mode1               set integer to 1 (cmdmode option)
     --mode2               set integer to 2 (cmdmode option)
-    -L, --length <str>    get length of <str>
-    -F, --file <file>     set file to <file>
+    --[no-]mode34 (3|4)   set integer to 3 or 4 (cmdmode option)
+    -L, --[no-]length <str>
+                          get length of <str>
+    -F, --[no-]file <file>
+                          set file to <file>
 
 String options
-    -s, --string <string>
+    -s, --[no-]string <string>
                           get a string
-    --string2 <str>       get another string
-    --st <st>             get another string (pervert ordering)
+    --[no-]string2 <str>  get another string
+    --[no-]st <st>        get another string (pervert ordering)
     -o <str>              get another string
-    --list <str>          add str to list
+    --longhelp            help text of this entry
+                          spans multiple lines
+    --[no-]list <str>     add str to list
 
 Magic arguments
     -NUM                  set integer to NUM
@@ -44,16 +51,17 @@
     --no-ambiguous        negative ambiguity
 
 Standard options
-    --abbrev[=<n>]        use <n> digits to display object names
-    -v, --verbose         be verbose
-    -n, --dry-run         dry run
-    -q, --quiet           be quiet
-    --expect <string>     expected output in the variable dump
+    --[no-]abbrev[=<n>]   use <n> digits to display object names
+    -v, --[no-]verbose    be verbose
+    -n, --[no-]dry-run    dry run
+    -q, --[no-]quiet      be quiet
+    --[no-]expect <string>
+                          expected output in the variable dump
 
 Alias
-    -A, --alias-source <string>
+    -A, --[no-]alias-source <string>
                           get a string
-    -Z, --alias-target <string>
+    -Z, --[no-]alias-target <string>
                           alias of --alias-source
 
 EOF
@@ -202,6 +210,22 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'superfluous value provided: boolean, abbreviated' '
+	cat >expect <<-\EOF &&
+	error: option `yes'\'' takes no value
+	EOF
+	test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+	test-tool parse-options --ye=hi 2>actual &&
+	test_cmp expect actual &&
+
+	cat >expect <<-\EOF &&
+	error: option `no-yes'\'' takes no value
+	EOF
+	test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+	test-tool parse-options --no-ye=hi 2>actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'superfluous value provided: cmdmode' '
 	cat >expect <<-\EOF &&
 	error: option `mode1'\'' takes no value
@@ -359,19 +383,41 @@
 '
 
 test_expect_success 'OPT_CMDMODE() works' '
-	test-tool parse-options --expect="integer: 1" --mode1
+	test-tool parse-options --expect="integer: 1" --mode1 &&
+	test-tool parse-options --expect="integer: 3" --mode34=3
 '
 
-test_expect_success 'OPT_CMDMODE() detects incompatibility' '
+test_expect_success 'OPT_CMDMODE() detects incompatibility (1)' '
 	test_must_fail test-tool parse-options --mode1 --mode2 >output 2>output.err &&
 	test_must_be_empty output &&
-	test_i18ngrep "incompatible with --mode" output.err
+	test_grep "mode1" output.err &&
+	test_grep "mode2" output.err &&
+	test_grep "cannot be used together" output.err
 '
 
-test_expect_success 'OPT_CMDMODE() detects incompatibility with something else' '
+test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
 	test_must_fail test-tool parse-options --set23 --mode2 >output 2>output.err &&
 	test_must_be_empty output &&
-	test_i18ngrep "incompatible with something else" output.err
+	test_grep "mode2" output.err &&
+	test_grep "set23" output.err &&
+	test_grep "cannot be used together" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
+	test_must_fail test-tool parse-options --mode2 --set23 >output 2>output.err &&
+	test_must_be_empty output &&
+	test_grep "mode2" output.err &&
+	test_grep "set23" output.err &&
+	test_grep "cannot be used together" output.err
+'
+
+test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
+	test_must_fail test-tool parse-options --mode2 --mode34=3 \
+		>output 2>output.err &&
+	test_must_be_empty output &&
+	test_grep "mode2" output.err &&
+	test_grep "mode34.3" output.err &&
+	test_grep "cannot be used together" output.err
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
diff --git a/t/t0041-usage.sh b/t/t0041-usage.sh
index c4fc34e..1464294 100755
--- a/t/t0041-usage.sh
+++ b/t/t0041-usage.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup ' '
@@ -20,8 +21,8 @@
 test_expect_success 'tag --contains <inexistent_tag>' '
 	test_must_fail git tag --contains "notag" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_grep "error" actual.err &&
+	test_grep ! "usage" actual.err
 '
 
 test_expect_success 'tag --no-contains <existent_tag>' '
@@ -33,27 +34,27 @@
 test_expect_success 'tag --no-contains <inexistent_tag>' '
 	test_must_fail git tag --no-contains "notag" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_grep "error" actual.err &&
+	test_grep ! "usage" actual.err
 '
 
 test_expect_success 'tag usage error' '
 	test_must_fail git tag --noopt >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "usage" actual.err
+	test_grep "usage" actual.err
 '
 
 test_expect_success 'branch --contains <existent_commit>' '
 	git branch --contains "main" >actual 2>actual.err &&
-	test_i18ngrep "main" actual &&
+	test_grep "main" actual &&
 	test_line_count = 0 actual.err
 '
 
 test_expect_success 'branch --contains <inexistent_commit>' '
 	test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_grep "error" actual.err &&
+	test_grep ! "usage" actual.err
 '
 
 test_expect_success 'branch --no-contains <existent_commit>' '
@@ -65,14 +66,14 @@
 test_expect_success 'branch --no-contains <inexistent_commit>' '
 	test_must_fail git branch --no-contains "nocommit" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_grep "error" actual.err &&
+	test_grep ! "usage" actual.err
 '
 
 test_expect_success 'branch usage error' '
 	test_must_fail git branch --noopt >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "usage" actual.err
+	test_grep "usage" actual.err
 '
 
 test_expect_success 'for-each-ref --contains <existent_object>' '
@@ -84,8 +85,8 @@
 test_expect_success 'for-each-ref --contains <inexistent_object>' '
 	test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_grep "error" actual.err &&
+	test_grep ! "usage" actual.err
 '
 
 test_expect_success 'for-each-ref --no-contains <existent_object>' '
@@ -97,14 +98,14 @@
 test_expect_success 'for-each-ref --no-contains <inexistent_object>' '
 	test_must_fail git for-each-ref --no-contains "noobject" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "error" actual.err &&
-	test_i18ngrep ! "usage" actual.err
+	test_grep "error" actual.err &&
+	test_grep ! "usage" actual.err
 '
 
 test_expect_success 'for-each-ref usage error' '
 	test_must_fail git for-each-ref --noopt >actual 2>actual.err &&
 	test_line_count = 0 actual &&
-	test_i18ngrep "usage" actual.err
+	test_grep "usage" actual.err
 '
 
 test_done
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
index 6bada37..c3eb115 100755
--- a/t/t0055-beyond-symlinks.sh
+++ b/t/t0055-beyond-symlinks.sh
@@ -15,12 +15,22 @@
 
 test_expect_success SYMLINKS 'update-index --add beyond symlinks' '
 	test_must_fail git update-index --add c/d &&
-	! ( git ls-files | grep c/d )
+	cat >expect <<-\EOF &&
+	a
+	b/d
+	EOF
+	git ls-files >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'add beyond symlinks' '
 	test_must_fail git add c/d &&
-	! ( git ls-files | grep c/d )
+	cat >expect <<-\EOF &&
+	a
+	b/d
+	EOF
+	git ls-files >actual &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 68e29c9..0afa3d0 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -10,20 +10,27 @@
 
 norm_path() {
 	expected=$(test-tool path-utils print_path "$2")
-	test_expect_success $3 "normalize path: $1 => $2" \
-	"test \"\$(test-tool path-utils normalize_path_copy '$1')\" = '$expected'"
+	test_expect_success $3 "normalize path: $1 => $2" "
+		echo '$expected' >expect &&
+		test-tool path-utils normalize_path_copy '$1' >actual &&
+		test_cmp expect actual
+	"
 }
 
 relative_path() {
 	expected=$(test-tool path-utils print_path "$3")
-	test_expect_success $4 "relative path: $1 $2 => $3" \
-	"test \"\$(test-tool path-utils relative_path '$1' '$2')\" = '$expected'"
+	test_expect_success $4 "relative path: $1 $2 => $3" "
+		echo '$expected' >expect &&
+		test-tool path-utils relative_path '$1' '$2' >actual &&
+		test_cmp expect actual
+	"
 }
 
 test_submodule_relative_url() {
 	test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
-		actual=\$(test-tool submodule resolve-relative-url '$1' '$2' '$3') &&
-		test \"\$actual\" = '$4'
+		echo '$4' >expect &&
+		test-tool submodule resolve-relative-url '$1' '$2' '$3' >actual &&
+		test_cmp expect actual
 	"
 }
 
@@ -64,9 +71,11 @@
 		expected=$(($expected-$rootslash+$rootoff))
 		;;
 	esac
-	test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
-	"actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
-	 test \"\$actual\" = '$expected'"
+	test_expect_success $4 "longest ancestor: $1 $2 => $expected" "
+		echo '$expected' >expect &&
+		test-tool path-utils longest_ancestor_length '$1' '$2' >actual &&
+		test_cmp expect actual
+	"
 }
 
 # Some absolute path tests should be skipped on Windows due to path mangling
@@ -166,8 +175,10 @@
 ancestor //server/share/my-directory //server/share/ 14 MINGW
 
 test_expect_success 'strip_path_suffix' '
-	test c:/msysgit = $(test-tool path-utils strip_path_suffix \
-		c:/msysgit/libexec//git-core libexec/git-core)
+	echo c:/msysgit >expect &&
+	test-tool path-utils strip_path_suffix \
+		c:/msysgit/libexec//git-core libexec/git-core >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'absolute path rejects the empty string' '
@@ -188,35 +199,61 @@
 '
 
 test_expect_success POSIX 'real path works on absolute paths 1' '
+	echo / >expect &&
+	test-tool path-utils real_path "/" >actual &&
+	test_cmp expect actual &&
+
 	nopath="hopefully-absent-path" &&
-	test "/" = "$(test-tool path-utils real_path "/")" &&
-	test "/$nopath" = "$(test-tool path-utils real_path "/$nopath")"
+	echo "/$nopath" >expect &&
+	test-tool path-utils real_path "/$nopath" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'real path works on absolute paths 2' '
-	nopath="hopefully-absent-path" &&
 	# Find an existing top-level directory for the remaining tests:
 	d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
-	test "$d" = "$(test-tool path-utils real_path "$d")" &&
-	test "$d/$nopath" = "$(test-tool path-utils real_path "$d/$nopath")"
+	echo "$d" >expect &&
+	test-tool path-utils real_path "$d" >actual &&
+	test_cmp expect actual &&
+
+	nopath="hopefully-absent-path" &&
+	echo "$d/$nopath" >expect &&
+	test-tool path-utils real_path "$d/$nopath" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success POSIX 'real path removes extra leading slashes' '
+	echo "/" >expect &&
+	test-tool path-utils real_path "///" >actual &&
+	test_cmp expect actual &&
+
 	nopath="hopefully-absent-path" &&
-	test "/" = "$(test-tool path-utils real_path "///")" &&
-	test "/$nopath" = "$(test-tool path-utils real_path "///$nopath")" &&
+	echo "/$nopath" >expect &&
+	test-tool path-utils real_path "///$nopath" >actual &&
+	test_cmp expect actual &&
+
 	# Find an existing top-level directory for the remaining tests:
 	d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
-	test "$d" = "$(test-tool path-utils real_path "//$d")" &&
-	test "$d/$nopath" = "$(test-tool path-utils real_path "//$d/$nopath")"
+	echo "$d" >expect &&
+	test-tool path-utils real_path "//$d" >actual &&
+	test_cmp expect actual &&
+
+	echo "$d/$nopath" >expect &&
+	test-tool path-utils real_path "//$d/$nopath" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'real path removes other extra slashes' '
-	nopath="hopefully-absent-path" &&
 	# Find an existing top-level directory for the remaining tests:
 	d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") &&
-	test "$d" = "$(test-tool path-utils real_path "$d///")" &&
-	test "$d/$nopath" = "$(test-tool path-utils real_path "$d///$nopath")"
+	echo "$d" >expect &&
+	test-tool path-utils real_path "$d///" >actual &&
+	test_cmp expect actual &&
+
+	nopath="hopefully-absent-path" &&
+	echo "$d/$nopath" >expect &&
+	test-tool path-utils real_path "$d///$nopath" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'real path works on symlinks' '
@@ -227,19 +264,29 @@
 	mkdir third &&
 	dir="$(cd .git && pwd -P)" &&
 	dir2=third/../second/other/.git &&
-	test "$dir" = "$(test-tool path-utils real_path $dir2)" &&
+	echo "$dir" >expect &&
+	test-tool path-utils real_path $dir2 >actual &&
+	test_cmp expect actual &&
 	file="$dir"/index &&
-	test "$file" = "$(test-tool path-utils real_path $dir2/index)" &&
+	echo "$file" >expect &&
+	test-tool path-utils real_path $dir2/index >actual &&
+	test_cmp expect actual &&
 	basename=blub &&
-	test "$dir/$basename" = "$(cd .git && test-tool path-utils real_path "$basename")" &&
+	echo "$dir/$basename" >expect &&
+	test-tool -C .git path-utils real_path "$basename" >actual &&
+	test_cmp expect actual &&
 	ln -s ../first/file .git/syml &&
 	sym="$(cd first && pwd -P)"/file &&
-	test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
+	echo "$sym" >expect &&
+	test-tool path-utils real_path "$dir2/syml" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
 	ln -s target symlink &&
-	test "$(test-tool path-utils prefix_path prefix "$(pwd)/symlink")" = "symlink"
+	echo "symlink" >expect &&
+	test-tool path-utils prefix_path prefix "$(pwd)/symlink" >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'prefix_path works with only absolute path to work tree' '
@@ -255,7 +302,10 @@
 test_expect_success SYMLINKS 'prefix_path works with absolute path to a symlink to work tree having  same beginning as work tree' '
 	git init repo &&
 	ln -s repo repolink &&
-	test "a" = "$(cd repo && test-tool path-utils prefix_path prefix "$(pwd)/../repolink/a")"
+	echo "a" >expect &&
+	repo_path="$(cd repo && pwd)" &&
+	test-tool -C repo path-utils prefix_path prefix "$repo_path/../repolink/a" >actual &&
+	test_cmp expect actual
 '
 
 relative_path /foo/a/b/c/	/foo/a/b/	c/
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index e2411f6..20986b6 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -19,12 +19,12 @@
 
 test_expect_success 'start_command reports ENOENT (slash)' '
 	test-tool run-command start-command-ENOENT ./does-not-exist 2>err &&
-	test_i18ngrep "\./does-not-exist" err
+	test_grep "\./does-not-exist" err
 '
 
 test_expect_success 'start_command reports ENOENT (no slash)' '
 	test-tool run-command start-command-ENOENT does-not-exist 2>err &&
-	test_i18ngrep "does-not-exist" err
+	test_grep "does-not-exist" err
 '
 
 test_expect_success 'run_command can run a command' '
@@ -49,7 +49,7 @@
 	echo yikes
 	EOF
 	test_must_fail test-tool run-command run-command should-not-run 2>err &&
-	test_i18ngrep "should-not-run" err
+	test_grep "should-not-run" err
 '
 
 test_expect_success !MINGW 'run_command can run a script without a #! line' '
diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh
index 46d4839..1fee6d9 100755
--- a/t/t0063-string-list.sh
+++ b/t/t0063-string-list.sh
@@ -18,6 +18,14 @@
 	"
 }
 
+test_split_in_place() {
+	cat >expected &&
+	test_expect_success "split (in place) $1 at $2, max $3" "
+		test-tool string-list split_in_place '$1' '$2' '$3' >actual &&
+		test_cmp expected actual
+	"
+}
+
 test_split "foo:bar:baz" ":" "-1" <<EOF
 3
 [0]: "foo"
@@ -61,6 +69,49 @@
 [1]: ""
 EOF
 
+test_split_in_place "foo:;:bar:;:baz:;:" ":;" "-1" <<EOF
+10
+[0]: "foo"
+[1]: ""
+[2]: ""
+[3]: "bar"
+[4]: ""
+[5]: ""
+[6]: "baz"
+[7]: ""
+[8]: ""
+[9]: ""
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "0" <<EOF
+1
+[0]: "foo:;:bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "1" <<EOF
+2
+[0]: "foo"
+[1]: ";:bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:baz" ":;" "2" <<EOF
+3
+[0]: "foo"
+[1]: ""
+[2]: ":bar:;:baz"
+EOF
+
+test_split_in_place "foo:;:bar:;:" ":;" "-1" <<EOF
+7
+[0]: "foo"
+[1]: ""
+[2]: ""
+[3]: "bar"
+[4]: ""
+[5]: ""
+[6]: ""
+EOF
+
 test_expect_success "test filter_string_list" '
 	test "x-" = "x$(test-tool string-list filter - y)" &&
 	test "x-" = "x$(test-tool string-list filter no y)" &&
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh
index 3648d43..4b90b74 100755
--- a/t/t0068-for-each-repo.sh
+++ b/t/t0068-for-each-repo.sh
@@ -40,4 +40,23 @@
 	git for-each-repo --config=bogus.config -- help --no-such-option
 '
 
+test_expect_success 'error on bad config keys' '
+	test_expect_code 129 git for-each-repo --config=a &&
+	test_expect_code 129 git for-each-repo --config=a.b. &&
+	test_expect_code 129 git for-each-repo --config="'\''.b"
+'
+
+test_expect_success 'error on NULL value for config keys' '
+	cat >>.git/config <<-\EOF &&
+	[empty]
+		key
+	EOF
+	cat >expect <<-\EOF &&
+	error: missing value for '\''empty.key'\''
+	EOF
+	test_expect_code 129 git for-each-repo --config=empty.key 2>actual.raw &&
+	grep ^error actual.raw >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 574de34..0ecec2b 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -9,10 +9,6 @@
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-test_expect_success 'character classes (isspace, isalpha etc.)' '
-	test-tool ctype
-'
-
 test_expect_success 'mktemp to nonexistent directory prints filename' '
 	test_must_fail test-tool mktemp doesnotexist/testXXXXXX 2>err &&
 	grep "doesnotexist/test" err
@@ -44,13 +40,71 @@
 test_expect_success 'eof on sideband message is reported' '
 	printf 1234 >input &&
 	test-tool pkt-line receive-sideband <input 2>err &&
-	test_i18ngrep "unexpected disconnect" err
+	test_grep "unexpected disconnect" err
 '
 
 test_expect_success 'missing sideband designator is reported' '
 	printf 0004 >input &&
 	test-tool pkt-line receive-sideband <input 2>err &&
-	test_i18ngrep "missing sideband" err
+	test_grep "missing sideband" err
+'
+
+test_expect_success 'unpack-sideband: --no-chomp-newline' '
+	test_when_finished "rm -f expect-out expect-err" &&
+	test-tool pkt-line send-split-sideband >split-sideband &&
+	test-tool pkt-line unpack-sideband \
+		--no-chomp-newline <split-sideband >out 2>err &&
+	cat >expect-out <<-EOF &&
+		primary: regular output
+	EOF
+	cat >expect-err <<-EOF &&
+		Foo.
+		Bar.
+		Hello, world!
+	EOF
+	test_cmp expect-out out &&
+	test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: --chomp-newline (default)' '
+	test_when_finished "rm -f expect-out expect-err" &&
+	test-tool pkt-line send-split-sideband >split-sideband &&
+	test-tool pkt-line unpack-sideband \
+		--chomp-newline <split-sideband >out 2>err &&
+	printf "primary: regular output" >expect-out &&
+	printf "Foo.Bar.Hello, world!" >expect-err &&
+	test_cmp expect-out out &&
+	test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, no chomp payload' '
+	test_when_finished "rm -f expect-out expect-err" &&
+	test-tool pkt-line send-split-sideband >split-sideband &&
+	test-tool pkt-line unpack-sideband \
+		--reader-use-sideband \
+		--no-chomp-newline <split-sideband >out 2>err &&
+	cat >expect-out <<-EOF &&
+		primary: regular output
+	EOF
+	printf "remote: Foo.        \n"           >expect-err &&
+	printf "remote: Bar.        \n"          >>expect-err &&
+	printf "remote: Hello, world!        \n" >>expect-err &&
+	test_cmp expect-out out &&
+	test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, chomp payload' '
+	test_when_finished "rm -f expect-out expect-err" &&
+	test-tool pkt-line send-split-sideband >split-sideband &&
+	test-tool pkt-line unpack-sideband \
+		--reader-use-sideband \
+		--chomp-newline <split-sideband >out 2>err &&
+	printf "primary: regular output" >expect-out &&
+	printf "remote: Foo.        \n"           >expect-err &&
+	printf "remote: Bar.        \n"          >>expect-err &&
+	printf "remote: Hello, world!        \n" >>expect-err &&
+	test_cmp expect-out out &&
+	test_cmp expect-err err
 '
 
 test_done
diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh
new file mode 100755
index 0000000..6657c11
--- /dev/null
+++ b/t/t0080-unit-test-output.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='Test the output of the unit test framework'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'TAP output from unit tests' '
+	cat >expect <<-EOF &&
+	ok 1 - passing test
+	ok 2 - passing test and assertion return 1
+	# check "1 == 2" failed at t/unit-tests/t-basic.c:76
+	#    left: 1
+	#   right: 2
+	not ok 3 - failing test
+	ok 4 - failing test and assertion return 0
+	not ok 5 - passing TEST_TODO() # TODO
+	ok 6 - passing TEST_TODO() returns 1
+	# todo check ${SQ}check(x)${SQ} succeeded at t/unit-tests/t-basic.c:25
+	not ok 7 - failing TEST_TODO()
+	ok 8 - failing TEST_TODO() returns 0
+	# check "0" failed at t/unit-tests/t-basic.c:30
+	# skipping test - missing prerequisite
+	# skipping check ${SQ}1${SQ} at t/unit-tests/t-basic.c:32
+	ok 9 - test_skip() # SKIP
+	ok 10 - skipped test returns 1
+	# skipping test - missing prerequisite
+	ok 11 - test_skip() inside TEST_TODO() # SKIP
+	ok 12 - test_skip() inside TEST_TODO() returns 1
+	# check "0" failed at t/unit-tests/t-basic.c:48
+	not ok 13 - TEST_TODO() after failing check
+	ok 14 - TEST_TODO() after failing check returns 0
+	# check "0" failed at t/unit-tests/t-basic.c:56
+	not ok 15 - failing check after TEST_TODO()
+	ok 16 - failing check after TEST_TODO() returns 0
+	# check "!strcmp("\thello\\\\", "there\"\n")" failed at t/unit-tests/t-basic.c:61
+	#    left: "\011hello\\\\"
+	#   right: "there\"\012"
+	# check "!strcmp("NULL", NULL)" failed at t/unit-tests/t-basic.c:62
+	#    left: "NULL"
+	#   right: NULL
+	# check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/unit-tests/t-basic.c:63
+	#    left: ${SQ}a${SQ}
+	#   right: ${SQ}\012${SQ}
+	# check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/unit-tests/t-basic.c:64
+	#    left: ${SQ}\\\\${SQ}
+	#   right: ${SQ}\\${SQ}${SQ}
+	not ok 17 - messages from failing string and char comparison
+	# BUG: test has no checks at t/unit-tests/t-basic.c:91
+	not ok 18 - test with no checks
+	ok 19 - test with no checks returns 0
+	1..19
+	EOF
+
+	! "$GIT_BUILD_DIR"/t/unit-tests/bin/t-basic >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh
new file mode 100755
index 0000000..67b1121
--- /dev/null
+++ b/t/t0081-find-pack.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+test_description='test `test-tool find-pack`'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit one &&
+	test_commit two &&
+	test_commit three &&
+	test_commit four &&
+	test_commit five
+'
+
+test_expect_success 'repack everything into a single packfile' '
+	git repack -a -d --no-write-bitmap-index &&
+
+	head_commit_pack=$(test-tool find-pack HEAD) &&
+	head_tree_pack=$(test-tool find-pack HEAD^{tree}) &&
+	one_pack=$(test-tool find-pack HEAD:one.t) &&
+	three_pack=$(test-tool find-pack HEAD:three.t) &&
+	old_commit_pack=$(test-tool find-pack HEAD~4) &&
+
+	test-tool find-pack --check-count 1 HEAD &&
+	test-tool find-pack --check-count=1 HEAD^{tree} &&
+	! test-tool find-pack --check-count=0 HEAD:one.t &&
+	! test-tool find-pack -c 2 HEAD:one.t &&
+	test-tool find-pack -c 1 HEAD:three.t &&
+
+	# Packfile exists at the right path
+	case "$head_commit_pack" in
+		".git/objects/pack/pack-"*".pack") true ;;
+		*) false ;;
+	esac &&
+	test -f "$head_commit_pack" &&
+
+	# Everything is in the same pack
+	test "$head_commit_pack" = "$head_tree_pack" &&
+	test "$head_commit_pack" = "$one_pack" &&
+	test "$head_commit_pack" = "$three_pack" &&
+	test "$head_commit_pack" = "$old_commit_pack"
+'
+
+test_expect_success 'add more packfiles' '
+	git rev-parse HEAD^{tree} HEAD:two.t HEAD:four.t >objects &&
+	git pack-objects .git/objects/pack/mypackname1 >packhash1 <objects &&
+
+	git rev-parse HEAD~ HEAD~^{tree} HEAD:five.t >objects &&
+	git pack-objects .git/objects/pack/mypackname2 >packhash2 <objects &&
+
+	head_commit_pack=$(test-tool find-pack HEAD) &&
+
+	# HEAD^{tree} is in 2 packfiles
+	test-tool find-pack HEAD^{tree} >head_tree_packs &&
+	grep "$head_commit_pack" head_tree_packs &&
+	grep mypackname1 head_tree_packs &&
+	! grep mypackname2 head_tree_packs &&
+	test-tool find-pack --check-count 2 HEAD^{tree} &&
+	! test-tool find-pack --check-count 1 HEAD^{tree} &&
+
+	# HEAD:five.t is also in 2 packfiles
+	test-tool find-pack HEAD:five.t >five_packs &&
+	grep "$head_commit_pack" five_packs &&
+	! grep mypackname1 five_packs &&
+	grep mypackname2 five_packs &&
+	test-tool find-pack -c 2 HEAD:five.t &&
+	! test-tool find-pack --check-count=0 HEAD:five.t
+'
+
+test_expect_success 'add more commits (as loose objects)' '
+	test_commit six &&
+	test_commit seven &&
+
+	test -z "$(test-tool find-pack HEAD)" &&
+	test -z "$(test-tool find-pack HEAD:six.t)" &&
+	test-tool find-pack --check-count 0 HEAD &&
+	test-tool find-pack -c 0 HEAD:six.t &&
+	! test-tool find-pack -c 1 HEAD:seven.t
+'
+
+test_done
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index b6d2f59..fca3904 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -5,29 +5,50 @@
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-# Headers "[System Info]" will be followed by a non-empty line if we put some
-# information there; we can make sure all our headers were followed by some
-# information to check if the command was successful.
-HEADER_PATTERN="^\[.*\]$"
+test_expect_success 'create a report' '
+	git bugreport -s format &&
+	test_file_not_empty git-bugreport-format.txt
+'
 
-check_all_headers_populated () {
-	while read -r line
-	do
-		if test "$(grep "$HEADER_PATTERN" "$line")"
-		then
-			echo "$line"
-			read -r nextline
-			if test -z "$nextline"; then
-				return 1;
-			fi
-		fi
-	done
-}
+test_expect_success 'report contains wanted template (before first section)' '
+	sed -ne "/^\[/q;p" git-bugreport-format.txt >actual &&
+	cat >expect <<-\EOF &&
+	Thank you for filling out a Git bug report!
+	Please answer the following questions to help us understand your issue.
 
-test_expect_success 'creates a report with content in the right places' '
-	test_when_finished rm git-bugreport-check-headers.txt &&
-	git bugreport -s check-headers &&
-	check_all_headers_populated <git-bugreport-check-headers.txt
+	What did you do before the bug happened? (Steps to reproduce your issue)
+
+	What did you expect to happen? (Expected behavior)
+
+	What happened instead? (Actual behavior)
+
+	What'\''s different between what you expected and what actually happened?
+
+	Anything else you want to add:
+
+	Please review the rest of the bug report below.
+	You can delete any lines you don'\''t wish to share.
+
+
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'sanity check "System Info" section' '
+	test_when_finished rm -f git-bugreport-format.txt &&
+
+	sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
+
+	# The beginning should match "git version --build-options" verbatim,
+	# but rather than checking bit-for-bit equality, just test some basics.
+	grep "git version " system &&
+	grep "shell-path: ." system &&
+
+	# After the version, there should be some more info.
+	# This is bound to differ from environment to environment,
+	# so we just do some rather high-level checks.
+	grep "uname: ." system &&
+	grep "compiler info: ." system
 '
 
 test_expect_success 'dies if file with same name as report already exists' '
@@ -44,7 +65,14 @@
 
 test_expect_success 'incorrect arguments abort with usage' '
 	test_must_fail git bugreport --false 2>output &&
-	test_i18ngrep usage output &&
+	test_grep usage output &&
+	test_path_is_missing git-bugreport-*
+'
+
+test_expect_success 'incorrect positional arguments abort with usage and hint' '
+	test_must_fail git bugreport false 2>output &&
+	test_grep usage output &&
+	test_grep false output &&
 	test_path_is_missing git-bugreport-*
 '
 
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index a16cc3d..70a3223 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -12,7 +12,9 @@
 	test_commit A &&
 	git checkout -b junk &&
 	git checkout - &&
-	test "$(git symbolic-ref HEAD)" = refs/heads/main &&
+	echo refs/heads/main >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
 	git branch -d @{-1} &&
 	test_must_fail git rev-parse --verify refs/heads/junk
 '
@@ -21,7 +23,9 @@
 	git reflog expire --expire=now &&
 	git checkout -b junk2 &&
 	git checkout - &&
-	test "$(git symbolic-ref HEAD)" = refs/heads/main &&
+	echo refs/heads/main >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
 	test_must_fail git branch -d @{-12} &&
 	git rev-parse --verify refs/heads/main
 '
diff --git a/t/t0202/test.pl b/t/t0202/test.pl
index 2cbf7b9..47d96a2 100755
--- a/t/t0202/test.pl
+++ b/t/t0202/test.pl
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-use 5.008;
+use 5.008001;
 use lib (split(/:/, $ENV{GITPERLLIB}));
 use strict;
 use warnings;
diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh
index 4f2e0dc..310a450 100755
--- a/t/t0204-gettext-reencode-sanity.sh
+++ b/t/t0204-gettext-reencode-sanity.sh
@@ -82,7 +82,7 @@
     printf "Bjó til tóma Git lind" >expect &&
     LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
     test_when_finished "rm -rf repo" &&
-    grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+    grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual
 '
 
 test_done
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 80e76a4..c312657 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -2,7 +2,7 @@
 
 test_description='test trace2 facility (normal target)'
 
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
@@ -283,4 +283,22 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'unsafe URLs are redacted by default' '
+	test_when_finished \
+		"rm -r trace.normal unredacted.normal clone clone2" &&
+
+	test_config_global \
+		"url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+	test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+	GIT_TRACE2="$(pwd)/trace.normal" \
+		git clone https://user:pwd@example.com/ clone &&
+	! grep user:pwd trace.normal &&
+
+	GIT_TRACE2_REDACT=0 GIT_TRACE2="$(pwd)/unredacted.normal" \
+		git clone https://user:pwd@example.com/ clone2 &&
+	grep "start .* clone https://user:pwd@example.com" unredacted.normal &&
+	grep "remote.origin.url=https://user:pwd@example.com" unredacted.normal
+'
+
 test_done
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index b4e9135..13ef69b 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -2,7 +2,7 @@
 
 test_description='test trace2 facility (perf target)'
 
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
@@ -203,7 +203,7 @@
 	have_timer_event "main" "timer" "test" "test1" 5 actual
 '
 
-test_expect_success PTHREAD 'stopwatch timer test/test2' '
+test_expect_success PTHREADS 'stopwatch timer test/test2' '
 	test_when_finished "rm trace.perf actual" &&
 	test_config_global trace2.perfBrief 1 &&
 	test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
@@ -249,7 +249,7 @@
 	have_counter_event "main" "counter" "test" "test1" 15 actual
 '
 
-test_expect_success PTHREAD 'global counter test/test2' '
+test_expect_success PTHREADS 'global counter test/test2' '
 	test_when_finished "rm trace.perf actual" &&
 	test_config_global trace2.perfBrief 1 &&
 	test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
@@ -268,4 +268,254 @@
 	have_counter_event "main" "counter" "test" "test2" 60 actual
 '
 
+test_expect_success 'unsafe URLs are redacted by default' '
+	test_when_finished \
+		"rm -r actual trace.perf unredacted.perf clone clone2" &&
+
+	test_config_global \
+		"url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+	test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+	GIT_TRACE2_PERF="$(pwd)/trace.perf" \
+		git clone https://user:pwd@example.com/ clone &&
+	! grep user:pwd trace.perf &&
+
+	GIT_TRACE2_REDACT=0 GIT_TRACE2_PERF="$(pwd)/unredacted.perf" \
+		git clone https://user:pwd@example.com/ clone2 &&
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <unredacted.perf >actual &&
+	grep "d0|main|start|.* clone https://user:pwd@example.com" actual &&
+	grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual
+'
+
+# Confirm that the requested command produces a "cmd_name" and a
+# set of "def_param" events.
+#
+try_simple () {
+	test_when_finished "rm prop.perf actual" &&
+
+	cmd=$1 &&
+	cmd_name=$2 &&
+
+	test_config_global "trace2.configParams" "cfg.prop.*" &&
+	test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+	test_config_global "cfg.prop.foo" "red" &&
+
+	ENV_PROP_FOO=blue \
+		GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+			$cmd &&
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+	grep "d0|main|cmd_name|.*|$cmd_name" actual &&
+	grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual
+}
+
+# Representative mainstream builtin Git command dispatched
+# in run_builtin() in git.c
+#
+test_expect_success 'expect def_params for normal builtin command' '
+	try_simple "git version" "version"
+'
+
+# Representative query command dispatched in handle_options()
+# in git.c
+#
+test_expect_success 'expect def_params for query command' '
+	try_simple "git --man-path" "_query_"
+'
+
+# remote-curl.c does not use the builtin setup in git.c, so confirm
+# that executables built from remote-curl.c emit def_params.
+#
+# Also tests the dashed-command handling where "git foo" silently
+# spawns "git-foo".  Make sure that both commands should emit
+# def_params.
+#
+# Pass bogus arguments to remote-https and allow the command to fail
+# because we don't actually have a remote to fetch from.  We just want
+# to see the run-dashed code run an executable built from
+# remote-curl.c rather than git.c.  Confirm that we get def_param
+# events from both layers.
+#
+test_expect_success 'expect def_params for remote-curl and _run_dashed_' '
+	test_when_finished "rm prop.perf actual" &&
+
+	test_config_global "trace2.configParams" "cfg.prop.*" &&
+	test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+	test_config_global "cfg.prop.foo" "red" &&
+
+	test_might_fail env \
+		ENV_PROP_FOO=blue \
+		GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+		git remote-http x y &&
+
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+	grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+	grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+	grep "d1|main|cmd_name|.*|remote-curl" actual &&
+	grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Similarly, `git-http-fetch` is not built from git.c so do a
+# trivial fetch so that the main git.c run-dashed code spawns
+# an executable built from http-fetch.c.  Confirm that we get
+# def_param events from both layers.
+#
+test_expect_success 'expect def_params for http-fetch and _run_dashed_' '
+	test_when_finished "rm prop.perf actual" &&
+
+	test_config_global "trace2.configParams" "cfg.prop.*" &&
+	test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+	test_config_global "cfg.prop.foo" "red" &&
+
+	test_might_fail env \
+		ENV_PROP_FOO=blue \
+		GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+		git http-fetch --stdin file:/// <<-EOF &&
+	EOF
+
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+	grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+	grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+	grep "d1|main|cmd_name|.*|http-fetch" actual &&
+	grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Historically, alias expansion explicitly emitted the def_param
+# events (independent of whether the command was a builtin, a Git
+# command or arbitrary shell command) so that it wasn't dependent
+# upon the unpeeling of the alias. Let's make sure that we preserve
+# the net effect.
+#
+test_expect_success 'expect def_params during git alias expansion' '
+	test_when_finished "rm prop.perf actual" &&
+
+	test_config_global "trace2.configParams" "cfg.prop.*" &&
+	test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+	test_config_global "cfg.prop.foo" "red" &&
+
+	test_config_global "alias.xxx" "version" &&
+
+	ENV_PROP_FOO=blue \
+		GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+			git xxx &&
+
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+	# "git xxx" is first mapped to "git-xxx" and the child will fail.
+	grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+	# We unpeel that and substitute "version" into "xxx" (giving
+	# "git version") and update the cmd_name event.
+	grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual &&
+
+	# These def_param events could be associated with either of the
+	# above cmd_name events.  It does not matter.
+	grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+	# The "git version" child sees a different cmd_name hierarchy.
+	# Also test the def_param (only for completeness).
+	grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual &&
+	grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during shell alias expansion' '
+	test_when_finished "rm prop.perf actual" &&
+
+	test_config_global "trace2.configParams" "cfg.prop.*" &&
+	test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+	test_config_global "cfg.prop.foo" "red" &&
+
+	test_config_global "alias.xxx" "!git version" &&
+
+	ENV_PROP_FOO=blue \
+		GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+			git xxx &&
+
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+	# "git xxx" is first mapped to "git-xxx" and the child will fail.
+	grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+	# We unpeel that and substitute "git version" for "git xxx" (as a
+	# shell command.  Another cmd_name event is emitted as we unpeel.
+	grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual &&
+
+	# These def_param events could be associated with either of the
+	# above cmd_name events.  It does not matter.
+	grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+	# We get the following only because we used a git command for the
+	# shell command. In general, it could have been a shell script and
+	# we would see nothing.
+	#
+	# The child knows the cmd_name hierarchy so it includes it.
+	grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual &&
+	grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during nested git alias expansion' '
+	test_when_finished "rm prop.perf actual" &&
+
+	test_config_global "trace2.configParams" "cfg.prop.*" &&
+	test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+	test_config_global "cfg.prop.foo" "red" &&
+
+	test_config_global "alias.xxx" "yyy" &&
+	test_config_global "alias.yyy" "version" &&
+
+	ENV_PROP_FOO=blue \
+		GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+			git xxx &&
+
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+	# "git xxx" is first mapped to "git-xxx" and try to spawn "git-xxx"
+	# and the child will fail.
+	grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+	grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual &&
+
+	# We unpeel that and substitute "yyy" into "xxx" (giving "git yyy")
+	# and spawn "git-yyy" and the child will fail.
+	grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual &&
+	grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual &&
+	grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual &&
+
+	# We unpeel that and substitute "version" into "xxx" (giving
+	# "git version") and update the cmd_name event.
+	grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual &&
+	grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual &&
+
+	# These def_param events could be associated with any of the
+	# above cmd_name events.  It does not matter.
+	grep "d0|main|def_param|.*|cfg.prop.foo:red" actual >actual.matches &&
+	grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+	# However, we do not want them repeated each time we unpeel.
+	test_line_count = 1 actual.matches &&
+
+	# The "git version" child sees a different cmd_name hierarchy.
+	# Also test the def_param (only for completeness).
+	grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual &&
+	grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+	grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
 test_done
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
index 6d3374f..147643d 100755
--- a/t/t0212-trace2-event.sh
+++ b/t/t0212-trace2-event.sh
@@ -323,4 +323,44 @@
 	head -n2 trace_target_dir/git-trace2-discard | tail -n1 | grep \"event\":\"too_many_files\"
 '
 
+# In the following "...redact..." tests, skip testing the GIT_TRACE2_REDACT=0
+# case because we would need to exactly model the full JSON event stream like
+# we did in the basic tests above and I do not think it is worth it.
+
+test_expect_success 'unsafe URLs are redacted by default in cmd_start events' '
+	test_when_finished \
+		"rm -r trace.event" &&
+
+	GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+		test-tool trace2 300redact_start git clone https://user:pwd@example.com/ clone2 &&
+	! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in child_start events' '
+	test_when_finished \
+		"rm -r trace.event" &&
+
+	GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+		test-tool trace2 301redact_child_start git clone https://user:pwd@example.com/ clone2 &&
+	! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in exec events' '
+	test_when_finished \
+		"rm -r trace.event" &&
+
+	GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+		test-tool trace2 302redact_exec git clone https://user:pwd@example.com/ clone2 &&
+	! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in def_param events' '
+	test_when_finished \
+		"rm -r trace.event" &&
+
+	GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+		test-tool trace2 303redact_def_param url https://user:pwd@example.com/ &&
+	! grep user:pwd trace.event
+'
+
 test_done
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index c66d91e..400f6bd 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -214,6 +214,24 @@
 	EOF
 '
 
+test_expect_success 'credential_approve stores oauth refresh token' '
+	check approve useless <<-\EOF
+	protocol=http
+	host=example.com
+	username=foo
+	password=bar
+	oauth_refresh_token=xyzzy
+	--
+	--
+	useless: store
+	useless: protocol=http
+	useless: host=example.com
+	useless: username=foo
+	useless: password=bar
+	useless: oauth_refresh_token=xyzzy
+	EOF
+'
+
 test_expect_success 'do not bother storing password-less credential' '
 	check approve useless <<-\EOF
 	protocol=http
@@ -808,8 +826,8 @@
 
 	git -c credential.$partial.helper=yep \
 		-c credential.with%0anewline.username=uh-oh \
-		credential fill <stdin >stdout 2>stderr &&
-	test_i18ngrep "skipping credential lookup for key" stderr
+		credential fill <stdin 2>stderr &&
+	test_grep "skipping credential lookup for key" stderr
 '
 
 test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index 698b715..f2c146f 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -8,6 +8,14 @@
 	skip_all='skipping credential-cache tests, unix sockets not available'
 	test_done
 }
+if test_have_prereq MINGW
+then
+	service_running=$(sc query afunix | grep "4  RUNNING")
+	test -z "$service_running" || {
+		skip_all='skipping credential-cache tests, unix sockets not available'
+		test_done
+	}
+fi
 
 uname_s=$(uname -s)
 case $uname_s in
@@ -29,6 +37,8 @@
 
 # test that the daemon works with no special setup
 helper_test cache
+helper_test_password_expiry_utc cache
+helper_test_oauth_refresh_token cache
 
 test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
 	test_when_finished "
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
index f028fd1..72ae405 100755
--- a/t/t0303-credential-external.sh
+++ b/t/t0303-credential-external.sh
@@ -32,9 +32,24 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-credential.sh
 
+# If we're not given a specific external helper to run against,
+# there isn't much to test. But we can still run through our
+# battery of tests with a fake helper and check that the
+# test themselves are self-consistent and clean up after
+# themselves.
+#
+# We'll use the "store" helper, since we can easily inspect
+# its state by looking at the on-disk file. But since it doesn't
+# implement any caching or expiry logic, we'll cheat and override
+# the "check" function to just report all results as OK.
 if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
-	skip_all="used to test external credential helpers"
-	test_done
+	GIT_TEST_CREDENTIAL_HELPER=store
+	GIT_TEST_CREDENTIAL_HELPER_TIMEOUT=store
+	check () {
+		test "$1" = "approve" || return 0
+		git -c credential.helper=store credential approve
+	}
+	check_cleanup=t
 fi
 
 test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
@@ -45,6 +60,8 @@
 helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
 
 helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_password_expiry_utc "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_oauth_refresh_token "$GIT_TEST_CREDENTIAL_HELPER"
 
 if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
 	say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
@@ -57,4 +74,11 @@
 # might be long-term system storage
 helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
 
+if test "$check_cleanup" = "t"
+then
+	test_expect_success 'test cleanup removes everything' '
+		test_must_be_empty "$HOME/.git-credentials"
+	'
+fi
+
 test_done
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 5b7bee8..88a66f0 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -49,7 +49,7 @@
 	test_cmp_config -C client 1 core.repositoryformatversion
 '
 
-test_expect_success SHA1 'convert to partial clone with noop extension' '
+test_expect_success DEFAULT_REPO_FORMAT 'convert to partial clone with noop extension' '
 	rm -fr server client &&
 	test_create_repo server &&
 	test_commit -C server my_commit 1 &&
@@ -60,7 +60,7 @@
 	git -C client fetch --unshallow --filter="blob:none"
 '
 
-test_expect_success SHA1 'converting to partial clone fails with unrecognized extension' '
+test_expect_success DEFAULT_REPO_FORMAT 'converting to partial clone fails with unrecognized extension' '
 	rm -fr server client &&
 	test_create_repo server &&
 	test_commit -C server my_commit 1 &&
@@ -665,6 +665,21 @@
 	git -C partial.git rev-list --objects --missing=print HEAD >out &&
 	grep "[?]$FILE_HASH" out &&
 
+	# The no-lazy-fetch mechanism prevents Git from fetching
+	test_must_fail env GIT_NO_LAZY_FETCH=1 \
+		git -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# The same with command line option to "git"
+	test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+	# The same, forcing a subprocess via an alias
+	test_must_fail git --no-lazy-fetch -C partial.git \
+		-c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
+
+	# Sanity check that the file is still missing
+	git -C partial.git rev-list --objects --missing=print HEAD >out &&
+	grep "[?]$FILE_HASH" out &&
+
 	git -C full cat-file -s "$FILE_HASH" >expect &&
 	test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
 	test_cmp expect actual &&
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
index cd3969e..69917d7 100755
--- a/t/t0450-txt-doc-vs-help.sh
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -59,7 +59,9 @@
 		-e '/^\[verse\]$/,/^$/ {
 			/^$/d;
 			/^\[verse\]$/d;
-
+			s/_//g;
+			s/++//g;
+			s/`//g;
 			s/{litdd}/--/g;
 			s/'\''\(git[ a-z-]*\)'\''/\1/g;
 
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
new file mode 100755
index 0000000..6421434
--- /dev/null
+++ b/t/t0600-reffiles-backend.sh
@@ -0,0 +1,475 @@
+#!/bin/sh
+
+test_description='Test reffiles backend'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq REFFILES
+then
+	skip_all='skipping reffiles specific tests'
+	test_done
+fi
+
+test_expect_success 'setup' '
+	git commit --allow-empty -m Initial &&
+	C=$(git rev-parse HEAD) &&
+	git commit --allow-empty -m Second &&
+	D=$(git rev-parse HEAD) &&
+	git commit --allow-empty -m Third &&
+	E=$(git rev-parse HEAD)
+'
+
+test_expect_success 'empty directory should not fool rev-parse' '
+	prefix=refs/e-rev-parse &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	echo "$C" >expected &&
+	git rev-parse $prefix/foo >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'empty directory should not fool for-each-ref' '
+	prefix=refs/e-for-each-ref &&
+	git update-ref $prefix/foo $C &&
+	git for-each-ref $prefix >expected &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	git for-each-ref $prefix >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'empty directory should not fool create' '
+	prefix=refs/e-create &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	printf "create %s $C\n" $prefix/foo |
+	git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool verify' '
+	prefix=refs/e-verify &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	printf "verify %s $C\n" $prefix/foo |
+	git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 1-arg update' '
+	prefix=refs/e-update-1 &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	printf "update %s $D\n" $prefix/foo |
+	git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 2-arg update' '
+	prefix=refs/e-update-2 &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	printf "update %s $D $C\n" $prefix/foo |
+	git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 0-arg delete' '
+	prefix=refs/e-delete-0 &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	printf "delete %s\n" $prefix/foo |
+	git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 1-arg delete' '
+	prefix=refs/e-delete-1 &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	mkdir -p .git/$prefix/foo/bar/baz &&
+	printf "delete %s $C\n" $prefix/foo |
+	git update-ref --stdin
+'
+
+test_expect_success 'non-empty directory blocks create' '
+	prefix=refs/ne-create &&
+	mkdir -p .git/$prefix/foo/bar &&
+	: >.git/$prefix/foo/bar/baz.lock &&
+	test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+	EOF
+	printf "%s\n" "update $prefix/foo $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+	EOF
+	printf "%s\n" "update $prefix/foo $D $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err
+'
+
+test_expect_success 'broken reference blocks create' '
+	prefix=refs/broken-create &&
+	mkdir -p .git/$prefix &&
+	echo "gobbledigook" >.git/$prefix/foo &&
+	test_when_finished "rm -f .git/$prefix/foo" &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+	EOF
+	printf "%s\n" "update $prefix/foo $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+	EOF
+	printf "%s\n" "update $prefix/foo $D $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err
+'
+
+test_expect_success 'non-empty directory blocks indirect create' '
+	prefix=refs/ne-indirect-create &&
+	git symbolic-ref $prefix/symref $prefix/foo &&
+	mkdir -p .git/$prefix/foo/bar &&
+	: >.git/$prefix/foo/bar/baz.lock &&
+	test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+	EOF
+	printf "%s\n" "update $prefix/symref $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+	EOF
+	printf "%s\n" "update $prefix/symref $D $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err
+'
+
+test_expect_success 'broken reference blocks indirect create' '
+	prefix=refs/broken-indirect-create &&
+	git symbolic-ref $prefix/symref $prefix/foo &&
+	echo "gobbledigook" >.git/$prefix/foo &&
+	test_when_finished "rm -f .git/$prefix/foo" &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+	EOF
+	printf "%s\n" "update $prefix/symref $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err &&
+	cat >expected <<-EOF &&
+	fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+	EOF
+	printf "%s\n" "update $prefix/symref $D $C" |
+	test_must_fail git update-ref --stdin 2>output.err &&
+	test_cmp expected output.err
+'
+
+test_expect_success 'no bogus intermediate values during delete' '
+	prefix=refs/slow-transaction &&
+	# Set up a reference with differing loose and packed versions:
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	git update-ref $prefix/foo $D &&
+	# Now try to update the reference, but hold the `packed-refs` lock
+	# for a while to see what happens while the process is blocked:
+	: >.git/packed-refs.lock &&
+	test_when_finished "rm -f .git/packed-refs.lock" &&
+	{
+		# Note: the following command is intentionally run in the
+		# background. We increase the timeout so that `update-ref`
+		# attempts to acquire the `packed-refs` lock for much longer
+		# than it takes for us to do the check then delete it:
+		git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
+	} &&
+	pid2=$! &&
+	# Give update-ref plenty of time to get to the point where it tries
+	# to lock packed-refs:
+	sleep 1 &&
+	# Make sure that update-ref did not complete despite the lock:
+	kill -0 $pid2 &&
+	# Verify that the reference still has its old value:
+	sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
+	case "$sha1" in
+	$D)
+		# This is what we hope for; it means that nothing
+		# user-visible has changed yet.
+		: ;;
+	undefined)
+		# This is not correct; it means the deletion has happened
+		# already even though update-ref should not have been
+		# able to acquire the lock yet.
+		echo "$prefix/foo deleted prematurely" &&
+		break
+		;;
+	$C)
+		# This value should never be seen. Probably the loose
+		# reference has been deleted but the packed reference
+		# is still there:
+		echo "$prefix/foo incorrectly observed to be C" &&
+		break
+		;;
+	*)
+		# WTF?
+		echo "unexpected value observed for $prefix/foo: $sha1" &&
+		break
+		;;
+	esac >out &&
+	rm -f .git/packed-refs.lock &&
+	wait $pid2 &&
+	test_must_be_empty out &&
+	test_must_fail git rev-parse --verify --quiet $prefix/foo
+'
+
+test_expect_success 'delete fails cleanly if packed-refs file is locked' '
+	prefix=refs/locked-packed-refs &&
+	# Set up a reference with differing loose and packed versions:
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	git update-ref $prefix/foo $D &&
+	git for-each-ref $prefix >unchanged &&
+	# Now try to delete it while the `packed-refs` lock is held:
+	: >.git/packed-refs.lock &&
+	test_when_finished "rm -f .git/packed-refs.lock" &&
+	test_must_fail git update-ref -d $prefix/foo >out 2>err &&
+	git for-each-ref $prefix >actual &&
+	test_grep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
+	test_cmp unchanged actual
+'
+
+test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
+	# Setup and expectations are similar to the test above.
+	prefix=refs/failed-packed-refs &&
+	git update-ref $prefix/foo $C &&
+	git pack-refs --all &&
+	git update-ref $prefix/foo $D &&
+	git for-each-ref $prefix >unchanged &&
+	# This should not happen in practice, but it is an easy way to get a
+	# reliable error (we open with create_tempfile(), which uses O_EXCL).
+	: >.git/packed-refs.new &&
+	test_when_finished "rm -f .git/packed-refs.new" &&
+	test_must_fail git update-ref -d $prefix/foo &&
+	git for-each-ref $prefix >actual &&
+	test_cmp unchanged actual
+'
+
+RWT="test-tool ref-store worktree:wt"
+RMAIN="test-tool ref-store worktree:main"
+
+test_expect_success 'setup worktree' '
+	test_commit first &&
+	git worktree add -b wt-main wt &&
+	(
+		cd wt &&
+		test_commit second
+	)
+'
+
+# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
+# only appear in the for-each-reflog output if it is called from the correct
+# worktree, which is exercised in this test. This test is poorly written for
+# mulitple reasons: 1) it creates invalidly formatted log entres. 2) it uses
+# direct FS access for creating the reflogs. 3) PSEUDO-WT and refs/bisect/random
+# do not create reflogs by default, so it is not testing a realistic scenario.
+test_expect_success 'for_each_reflog()' '
+	echo $ZERO_OID >.git/logs/PSEUDO_MAIN_HEAD &&
+	mkdir -p     .git/logs/refs/bisect &&
+	echo $ZERO_OID >.git/logs/refs/bisect/random &&
+
+	echo $ZERO_OID >.git/worktrees/wt/logs/PSEUDO_WT_HEAD &&
+	mkdir -p     .git/worktrees/wt/logs/refs/bisect &&
+	echo $ZERO_OID >.git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+	$RWT for-each-reflog >actual &&
+	cat >expected <<-\EOF &&
+	HEAD
+	PSEUDO_WT_HEAD
+	refs/bisect/wt-random
+	refs/heads/main
+	refs/heads/wt-main
+	EOF
+	test_cmp expected actual &&
+
+	$RMAIN for-each-reflog >actual &&
+	cat >expected <<-\EOF &&
+	HEAD
+	PSEUDO_MAIN_HEAD
+	refs/bisect/random
+	refs/heads/main
+	refs/heads/wt-main
+	EOF
+	test_cmp expected actual
+'
+
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
+	git checkout -b reflogskip &&
+	zf=$(test_oid zero_2) &&
+	ident="abc <xyz> 0000000001 +0000" &&
+	for i in $(test_seq 1 75); do
+		printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
+		if test $i = 75; then
+			for j in $(test_seq 1 89); do
+				printf X || return 1
+			done
+		else
+			printf X
+		fi &&
+		printf "\n" || return 1
+	done >.git/logs/refs/heads/reflogskip &&
+	git rev-parse reflogskip@{73} >actual &&
+	echo ${zf}03 >expect &&
+	test_cmp expect actual
+'
+
+# This test takes a lock on an individual ref; this is not supported in
+# reftable.
+test_expect_success 'reflog expire operates on symref not referrent' '
+	git branch --create-reflog the_symref &&
+	git branch --create-reflog referrent &&
+	git update-ref referrent HEAD &&
+	git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
+	test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
+	touch .git/refs/heads/referrent.lock &&
+	git reflog expire --expire=all the_symref
+'
+
+test_expect_success 'empty reflog' '
+	test_when_finished "rm -rf empty" &&
+	git init empty &&
+	test_commit -C empty A &&
+	>empty/.git/logs/refs/heads/foo &&
+	git -C empty reflog expire --all 2>err &&
+	test_must_be_empty err
+'
+
+test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
+       ln -s does-not-exist .git/refs/heads/broken &&
+       test_must_fail git rev-parse --verify broken
+'
+
+test_expect_success 'log diagnoses bogus HEAD hash' '
+	git init empty &&
+	test_when_finished "rm -rf empty" &&
+	echo 1234abcd >empty/.git/refs/heads/main &&
+	test_must_fail git -C empty log 2>stderr &&
+	test_grep broken stderr
+'
+
+test_expect_success 'log diagnoses bogus HEAD symref' '
+	git init empty &&
+	test-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&
+	test_must_fail git -C empty log 2>stderr &&
+	test_grep broken stderr &&
+	test_must_fail git -C empty log --default totally-bogus 2>stderr &&
+	test_grep broken stderr
+'
+
+test_expect_success 'empty directory removal' '
+	git branch d1/d2/r1 HEAD &&
+	git branch d1/r2 HEAD &&
+	test_path_is_file .git/refs/heads/d1/d2/r1 &&
+	test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
+	git branch -d d1/d2/r1 &&
+	test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
+	test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
+	test_path_is_file .git/refs/heads/d1/r2 &&
+	test_path_is_file .git/logs/refs/heads/d1/r2
+'
+
+test_expect_success 'symref empty directory removal' '
+	git branch e1/e2/r1 HEAD &&
+	git branch e1/r2 HEAD &&
+	git checkout e1/e2/r1 &&
+	test_when_finished "git checkout main" &&
+	test_path_is_file .git/refs/heads/e1/e2/r1 &&
+	test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
+	git update-ref -d HEAD &&
+	test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
+	test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
+	test_path_is_file .git/refs/heads/e1/r2 &&
+	test_path_is_file .git/logs/refs/heads/e1/r2 &&
+	test_path_is_file .git/logs/HEAD
+'
+
+test_expect_success 'directory not created deleting packed ref' '
+	git branch d1/d2/r1 HEAD &&
+	git pack-refs --all &&
+	test_path_is_missing .git/refs/heads/d1/d2 &&
+	git update-ref -d refs/heads/d1/d2/r1 &&
+	test_path_is_missing .git/refs/heads/d1/d2 &&
+	test_path_is_missing .git/refs/heads/d1
+'
+
+test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+	git branch --create-reflog u &&
+	mv .git/logs/refs/heads/u real-u &&
+	ln -s real-u .git/logs/refs/heads/u &&
+	test_must_fail git branch -m u v
+'
+
+test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
+	test_when_finished "rm -rf subdir" &&
+	git init --bare subdir &&
+
+	rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
+	ln -s ../.git/refs subdir/refs &&
+	ln -s ../.git/objects subdir/objects &&
+	ln -s ../.git/packed-refs subdir/packed-refs &&
+
+	git -C subdir rev-parse --absolute-git-dir >subdir.dir &&
+	git rev-parse --absolute-git-dir >our.dir &&
+	! test_cmp subdir.dir our.dir &&
+
+	git -C subdir log &&
+	git -C subdir branch rename-src &&
+	git rev-parse rename-src >expect &&
+	git -C subdir branch -m rename-src rename-dest &&
+	git rev-parse rename-dest >actual &&
+	test_cmp expect actual &&
+	git branch -D rename-dest
+'
+
+test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
+	git checkout main &&
+	mv .git/logs actual_logs &&
+	cmd //c "mklink /D .git\logs ..\actual_logs" &&
+	git rebase -f HEAD^ &&
+	test -L .git/logs &&
+	rm .git/logs &&
+	mv actual_logs .git/logs
+'
+
+test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
+	umask 077 &&
+	git config core.sharedRepository group &&
+	git reflog expire --all &&
+	actual="$(ls -l .git/logs/refs/heads/main)" &&
+	case "$actual" in
+	-rw-rw-*)
+		: happy
+		;;
+	*)
+		echo Ooops, .git/logs/refs/heads/main is not 066x [$actual]
+		false
+		;;
+	esac
+'
+
+test_done
diff --git a/t/t0601-reffiles-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
new file mode 100755
index 0000000..7d4ab0b
--- /dev/null
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -0,0 +1,383 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+# Copyright (c) 2006 Christian Couder
+#
+
+test_description='git pack-refs should not change the branch semantic
+
+This test runs git pack-refs and git show-ref and checks that the branch
+semantic is still the same.
+'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq REFFILES
+then
+	skip_all='skipping reffiles specific tests'
+	test_done
+fi
+
+test_expect_success 'enable reflogs' '
+	git config core.logallrefupdates true
+'
+
+test_expect_success 'prepare a trivial repository' '
+	echo Hello > A &&
+	git update-index --add A &&
+	git commit -m "Initial commit." &&
+	HEAD=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'pack-refs --prune --all' '
+	test_path_is_missing .git/packed-refs &&
+	git pack-refs --no-prune --all &&
+	test_path_is_file .git/packed-refs &&
+	N=$(find .git/refs -type f | wc -l) &&
+	test "$N" != 0 &&
+
+	git pack-refs --prune --all &&
+	test_path_is_file .git/packed-refs &&
+	N=$(find .git/refs -type f) &&
+	test -z "$N"
+'
+
+SHA1=
+
+test_expect_success 'see if git show-ref works as expected' '
+	git branch a &&
+	SHA1=$(cat .git/refs/heads/a) &&
+	echo "$SHA1 refs/heads/a" >expect &&
+	git show-ref a >result &&
+	test_cmp expect result
+'
+
+test_expect_success 'see if a branch still exists when packed' '
+	git branch b &&
+	git pack-refs --all &&
+	rm -f .git/refs/heads/b &&
+	echo "$SHA1 refs/heads/b" >expect &&
+	git show-ref b >result &&
+	test_cmp expect result
+'
+
+test_expect_success 'git branch c/d should barf if branch c exists' '
+	git branch c &&
+	git pack-refs --all &&
+	rm -f .git/refs/heads/c &&
+	test_must_fail git branch c/d
+'
+
+test_expect_success 'see if a branch still exists after git pack-refs --prune' '
+	git branch e &&
+	git pack-refs --all --prune &&
+	echo "$SHA1 refs/heads/e" >expect &&
+	git show-ref e >result &&
+	test_cmp expect result
+'
+
+test_expect_success 'see if git pack-refs --prune remove ref files' '
+	git branch f &&
+	git pack-refs --all --prune &&
+	! test -f .git/refs/heads/f
+'
+
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+	git branch r/s/t &&
+	git pack-refs --all --prune &&
+	! test -e .git/refs/heads/r
+'
+
+test_expect_success 'git branch g should work when git branch g/h has been deleted' '
+	git branch g/h &&
+	git pack-refs --all --prune &&
+	git branch -d g/h &&
+	git branch g &&
+	git pack-refs --all &&
+	git branch -d g
+'
+
+test_expect_success 'git branch i/j/k should barf if branch i exists' '
+	git branch i &&
+	git pack-refs --all --prune &&
+	test_must_fail git branch i/j/k
+'
+
+test_expect_success 'test git branch k after branch k/l/m and k/lm have been deleted' '
+	git branch k/l &&
+	git branch k/lm &&
+	git branch -d k/l &&
+	git branch k/l/m &&
+	git branch -d k/l/m &&
+	git branch -d k/lm &&
+	git branch k
+'
+
+test_expect_success 'test git branch n after some branch deletion and pruning' '
+	git branch n/o &&
+	git branch n/op &&
+	git branch -d n/o &&
+	git branch n/o/p &&
+	git branch -d n/op &&
+	git pack-refs --all --prune &&
+	git branch -d n/o/p &&
+	git branch n
+'
+
+test_expect_success 'test excluded refs are not packed' '
+	git branch dont_pack1 &&
+	git branch dont_pack2 &&
+	git branch pack_this &&
+	git pack-refs --all --exclude "refs/heads/dont_pack*" &&
+	test -f .git/refs/heads/dont_pack1 &&
+	test -f .git/refs/heads/dont_pack2 &&
+	! test -f .git/refs/heads/pack_this'
+
+test_expect_success 'test --no-exclude refs clears excluded refs' '
+	git branch dont_pack3 &&
+	git branch dont_pack4 &&
+	git pack-refs --all --exclude "refs/heads/dont_pack*" --no-exclude &&
+	! test -f .git/refs/heads/dont_pack3 &&
+	! test -f .git/refs/heads/dont_pack4'
+
+test_expect_success 'test only included refs are packed' '
+	git branch pack_this1 &&
+	git branch pack_this2 &&
+	git tag dont_pack5 &&
+	git pack-refs --include "refs/heads/pack_this*" &&
+	test -f .git/refs/tags/dont_pack5 &&
+	! test -f .git/refs/heads/pack_this1 &&
+	! test -f .git/refs/heads/pack_this2'
+
+test_expect_success 'test --no-include refs clears included refs' '
+	git branch pack1 &&
+	git branch pack2 &&
+	git pack-refs --include "refs/heads/pack*" --no-include &&
+	test -f .git/refs/heads/pack1 &&
+	test -f .git/refs/heads/pack2'
+
+test_expect_success 'test --exclude takes precedence over --include' '
+	git branch dont_pack5 &&
+	git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
+	test -f .git/refs/heads/dont_pack5'
+
+test_expect_success '--auto packs and prunes refs as usual' '
+	git branch auto &&
+	test_path_is_file .git/refs/heads/auto &&
+	git pack-refs --auto --all &&
+	test_path_is_missing .git/refs/heads/auto
+'
+
+test_expect_success 'see if up-to-date packed refs are preserved' '
+	git branch q &&
+	git pack-refs --all --prune &&
+	git update-ref refs/heads/q refs/heads/q &&
+	! test -f .git/refs/heads/q
+'
+
+test_expect_success 'pack, prune and repack' '
+	git tag foo &&
+	git pack-refs --all --prune &&
+	git show-ref >all-of-them &&
+	git pack-refs &&
+	git show-ref >again &&
+	test_cmp all-of-them again
+'
+
+test_expect_success 'explicit pack-refs with dangling packed reference' '
+	git commit --allow-empty -m "soon to be garbage-collected" &&
+	git pack-refs --all &&
+	git reset --hard HEAD^ &&
+	git reflog expire --expire=all --all &&
+	git prune --expire=all &&
+	git pack-refs --all 2>result &&
+	test_must_be_empty result
+'
+
+test_expect_success 'delete ref with dangling packed version' '
+	git checkout -b lamb &&
+	git commit --allow-empty -m "future garbage" &&
+	git pack-refs --all &&
+	git reset --hard HEAD^ &&
+	git checkout main &&
+	git reflog expire --expire=all --all &&
+	git prune --expire=all &&
+	git branch -d lamb 2>result &&
+	test_must_be_empty result
+'
+
+test_expect_success 'delete ref while another dangling packed ref' '
+	git branch lamb &&
+	git commit --allow-empty -m "future garbage" &&
+	git pack-refs --all &&
+	git reset --hard HEAD^ &&
+	git reflog expire --expire=all --all &&
+	git prune --expire=all &&
+	git branch -d lamb 2>result &&
+	test_must_be_empty result
+'
+
+test_expect_success 'pack ref directly below refs/' '
+	git update-ref refs/top HEAD &&
+	git pack-refs --all --prune &&
+	grep refs/top .git/packed-refs &&
+	test_path_is_missing .git/refs/top
+'
+
+test_expect_success 'do not pack ref in refs/bisect' '
+	git update-ref refs/bisect/local HEAD &&
+	git pack-refs --all --prune &&
+	! grep refs/bisect/local .git/packed-refs >/dev/null &&
+	test_path_is_file .git/refs/bisect/local
+'
+
+test_expect_success 'disable reflogs' '
+	git config core.logallrefupdates false &&
+	rm -rf .git/logs
+'
+
+test_expect_success 'create packed foo/bar/baz branch' '
+	git branch foo/bar/baz &&
+	git pack-refs --all --prune &&
+	test_path_is_missing .git/refs/heads/foo/bar/baz &&
+	test_must_fail git reflog exists refs/heads/foo/bar/baz
+'
+
+test_expect_success 'notice d/f conflict with existing directory' '
+	test_must_fail git branch foo &&
+	test_must_fail git branch foo/bar
+'
+
+test_expect_success 'existing directory reports concrete ref' '
+	test_must_fail git branch foo 2>stderr &&
+	test_grep refs/heads/foo/bar/baz stderr
+'
+
+test_expect_success 'notice d/f conflict with existing ref' '
+	test_must_fail git branch foo/bar/baz/extra &&
+	test_must_fail git branch foo/bar/baz/lots/of/extra/components
+'
+
+test_expect_success 'reject packed-refs with unterminated line' '
+	cp .git/packed-refs .git/packed-refs.bak &&
+	test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+	printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
+	echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
+	test_must_fail git for-each-ref >out 2>err &&
+	test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs containing junk' '
+	cp .git/packed-refs .git/packed-refs.bak &&
+	test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+	printf "%s\n" "bogus content" >>.git/packed-refs &&
+	echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
+	test_must_fail git for-each-ref >out 2>err &&
+	test_cmp expected_err err
+'
+
+test_expect_success 'reject packed-refs with a short SHA-1' '
+	cp .git/packed-refs .git/packed-refs.bak &&
+	test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
+	printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
+	printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
+	test_must_fail git for-each-ref >out 2>err &&
+	test_cmp expected_err err
+'
+
+test_expect_success 'timeout if packed-refs.lock exists' '
+	LOCK=.git/packed-refs.lock &&
+	>"$LOCK" &&
+	test_when_finished "rm -f $LOCK" &&
+	test_must_fail git pack-refs --all --prune
+'
+
+test_expect_success 'retry acquiring packed-refs.lock' '
+	LOCK=.git/packed-refs.lock &&
+	>"$LOCK" &&
+	test_when_finished "wait && rm -f $LOCK" &&
+	{
+		( sleep 1 && rm -f $LOCK ) &
+	} &&
+	git -c core.packedrefstimeout=3000 pack-refs --all --prune
+'
+
+test_expect_success SYMLINKS 'pack symlinked packed-refs' '
+	# First make sure that symlinking works when reading:
+	git update-ref refs/heads/lossy refs/heads/main &&
+	git for-each-ref >all-refs-before &&
+	mv .git/packed-refs .git/my-deviant-packed-refs &&
+	ln -s my-deviant-packed-refs .git/packed-refs &&
+	git for-each-ref >all-refs-linked &&
+	test_cmp all-refs-before all-refs-linked &&
+	git pack-refs --all --prune &&
+	git for-each-ref >all-refs-packed &&
+	test_cmp all-refs-before all-refs-packed &&
+	test -h .git/packed-refs &&
+	test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
+'
+
+# The 'packed-refs' file is stored directly in .git/. This means it is global
+# to the repository, and can only contain refs that are shared across all
+# worktrees.
+test_expect_success 'refs/worktree must not be packed' '
+	test_commit initial &&
+	test_commit wt1 &&
+	test_commit wt2 &&
+	git worktree add wt1 wt1 &&
+	git worktree add wt2 wt2 &&
+	git checkout initial &&
+	git update-ref refs/worktree/foo HEAD &&
+	git -C wt1 update-ref refs/worktree/foo HEAD &&
+	git -C wt2 update-ref refs/worktree/foo HEAD &&
+	git pack-refs --all &&
+	test_path_is_missing .git/refs/tags/wt1 &&
+	test_path_is_file .git/refs/worktree/foo &&
+	test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
+	test_path_is_file .git/worktrees/wt2/refs/worktree/foo
+'
+
+# we do not want to count on running pack-refs to
+# actually pack it, as it is perfectly reasonable to
+# skip processing a broken ref
+test_expect_success 'create packed-refs file with broken ref' '
+	test_tick && git commit --allow-empty -m one &&
+	recoverable=$(git rev-parse HEAD) &&
+	test_tick && git commit --allow-empty -m two &&
+	missing=$(git rev-parse HEAD) &&
+	rm -f .git/refs/heads/main &&
+	cat >.git/packed-refs <<-EOF &&
+	$missing refs/heads/main
+	$recoverable refs/heads/other
+	EOF
+	echo $missing >expect &&
+	git rev-parse refs/heads/main >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not silently delete broken packed ref' '
+	git pack-refs --all --prune &&
+	git rev-parse refs/heads/main >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not drop broken refs during deletion' '
+	git update-ref -d refs/heads/other &&
+	git rev-parse refs/heads/main >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'maintenance --auto unconditionally packs loose refs' '
+	git update-ref refs/heads/something HEAD &&
+	test_path_is_file .git/refs/heads/something &&
+	git rev-parse refs/heads/something >expect &&
+	git maintenance run --task=pack-refs --auto &&
+	test_path_is_missing .git/refs/heads/something &&
+	git rev-parse refs/heads/something >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
new file mode 100755
index 0000000..38a535b
--- /dev/null
+++ b/t/t0610-reftable-basics.sh
@@ -0,0 +1,989 @@
+#!/bin/sh
+#
+# Copyright (c) 2020 Google LLC
+#
+
+test_description='reftable basics'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+if ! test_have_prereq REFTABLE
+then
+	skip_all='skipping reftable tests; set GIT_TEST_DEFAULT_REF_FORMAT=reftable'
+	test_done
+fi
+
+INVALID_OID=$(test_oid 001)
+
+test_expect_success 'init: creates basic reftable structures' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_path_is_dir repo/.git/reftable &&
+	test_path_is_file repo/.git/reftable/tables.list &&
+	echo reftable >expect &&
+	git -C repo rev-parse --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'init: sha256 object format via environment variable' '
+	test_when_finished "rm -rf repo" &&
+	GIT_DEFAULT_HASH=sha256 git init repo &&
+	cat >expect <<-EOF &&
+	sha256
+	reftable
+	EOF
+	git -C repo rev-parse --show-object-format --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'init: sha256 object format via option' '
+	test_when_finished "rm -rf repo" &&
+	git init --object-format=sha256 repo &&
+	cat >expect <<-EOF &&
+	sha256
+	reftable
+	EOF
+	git -C repo rev-parse --show-object-format --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'init: reinitializing reftable backend succeeds' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo for-each-ref >expect &&
+	git init --ref-format=reftable repo &&
+	git -C repo for-each-ref >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'init: reinitializing files with reftable backend fails' '
+	test_when_finished "rm -rf repo" &&
+	git init --ref-format=files repo &&
+	test_commit -C repo file &&
+
+	cp repo/.git/HEAD expect &&
+	test_must_fail git init --ref-format=reftable repo &&
+	test_cmp expect repo/.git/HEAD
+'
+
+test_expect_success 'init: reinitializing reftable with files backend fails' '
+	test_when_finished "rm -rf repo" &&
+	git init --ref-format=reftable repo &&
+	test_commit -C repo file &&
+
+	cp repo/.git/HEAD expect &&
+	test_must_fail git init --ref-format=files repo &&
+	test_cmp expect repo/.git/HEAD
+'
+
+test_expect_perms () {
+	local perms="$1"
+	local file="$2"
+	local actual=$(ls -l "$file") &&
+
+	case "$actual" in
+	$perms*)
+		: happy
+		;;
+	*)
+		echo "$(basename $2) is not $perms but $actual"
+		false
+		;;
+	esac
+}
+
+test_expect_reftable_perms () {
+	local umask="$1"
+	local shared="$2"
+	local expect="$3"
+
+	test_expect_success POSIXPERM "init: honors --shared=$shared with umask $umask" '
+		test_when_finished "rm -rf repo" &&
+		(
+			umask $umask &&
+			git init --shared=$shared repo
+		) &&
+		test_expect_perms "$expect" repo/.git/reftable/tables.list &&
+		for table in repo/.git/reftable/*.ref
+		do
+			test_expect_perms "$expect" "$table" ||
+			return 1
+		done
+	'
+
+	test_expect_success POSIXPERM "pack-refs: honors --shared=$shared with umask $umask" '
+		test_when_finished "rm -rf repo" &&
+		(
+			umask $umask &&
+			git init --shared=$shared repo &&
+			test_commit -C repo A &&
+			test_line_count = 3 repo/.git/reftable/tables.list &&
+			git -C repo pack-refs
+		) &&
+		test_expect_perms "$expect" repo/.git/reftable/tables.list &&
+		for table in repo/.git/reftable/*.ref
+		do
+			test_expect_perms "$expect" "$table" ||
+			return 1
+		done
+	'
+}
+
+test_expect_reftable_perms 002 umask "-rw-rw-r--"
+test_expect_reftable_perms 022 umask "-rw-r--r--"
+test_expect_reftable_perms 027 umask "-rw-r-----"
+
+test_expect_reftable_perms 002 group "-rw-rw-r--"
+test_expect_reftable_perms 022 group "-rw-rw-r--"
+test_expect_reftable_perms 027 group "-rw-rw----"
+
+test_expect_reftable_perms 002 world "-rw-rw-r--"
+test_expect_reftable_perms 022 world "-rw-rw-r--"
+test_expect_reftable_perms 027 world "-rw-rw-r--"
+
+test_expect_success 'clone: can clone reftable repository' '
+	test_when_finished "rm -rf repo clone" &&
+	git init repo &&
+	test_commit -C repo message1 file1 &&
+
+	git clone repo cloned &&
+	echo reftable >expect &&
+	git -C cloned rev-parse --show-ref-format >actual &&
+	test_cmp expect actual &&
+	test_path_is_file cloned/file1
+'
+
+test_expect_success 'clone: can clone reffiles into reftable repository' '
+	test_when_finished "rm -rf reffiles reftable" &&
+	git init --ref-format=files reffiles &&
+	test_commit -C reffiles A &&
+	git clone --ref-format=reftable ./reffiles reftable &&
+
+	git -C reffiles rev-parse HEAD >expect &&
+	git -C reftable rev-parse HEAD >actual &&
+	test_cmp expect actual &&
+
+	git -C reftable rev-parse --show-ref-format >actual &&
+	echo reftable >expect &&
+	test_cmp expect actual &&
+
+	git -C reffiles rev-parse --show-ref-format >actual &&
+	echo files >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'clone: can clone reftable into reffiles repository' '
+	test_when_finished "rm -rf reffiles reftable" &&
+	git init --ref-format=reftable reftable &&
+	test_commit -C reftable A &&
+	git clone --ref-format=files ./reftable reffiles &&
+
+	git -C reftable rev-parse HEAD >expect &&
+	git -C reffiles rev-parse HEAD >actual &&
+	test_cmp expect actual &&
+
+	git -C reftable rev-parse --show-ref-format >actual &&
+	echo reftable >expect &&
+	test_cmp expect actual &&
+
+	git -C reffiles rev-parse --show-ref-format >actual &&
+	echo files >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'ref transaction: corrupted tables cause failure' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file1 &&
+		for f in .git/reftable/*.ref
+		do
+			: >"$f" || return 1
+		done &&
+		test_must_fail git update-ref refs/heads/main HEAD
+	)
+'
+
+test_expect_success 'ref transaction: corrupted tables.list cause failure' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file1 &&
+		echo garbage >.git/reftable/tables.list &&
+		test_must_fail git update-ref refs/heads/main HEAD
+	)
+'
+
+test_expect_success 'ref transaction: refuses to write ref causing F/D conflict' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo file &&
+	test_must_fail git -C repo update-ref refs/heads/main/forbidden
+'
+
+test_expect_success 'ref transaction: deleting ref with invalid name fails' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo file &&
+	test_must_fail git -C repo update-ref -d ../../my-private-file
+'
+
+test_expect_success 'ref transaction: can skip object ID verification' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_must_fail test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID 0 &&
+	test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION
+'
+
+test_expect_success 'ref transaction: updating same ref multiple times fails' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo A &&
+	cat >updates <<-EOF &&
+	update refs/heads/main $A
+	update refs/heads/main $A
+	EOF
+	cat >expect <<-EOF &&
+	fatal: multiple updates for ref ${SQ}refs/heads/main${SQ} not allowed
+	EOF
+	test_must_fail git -C repo update-ref --stdin <updates 2>err &&
+	test_cmp expect err
+'
+
+test_expect_success 'ref transaction: can delete symbolic self-reference with git-symbolic-ref(1)' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+	git -C repo symbolic-ref -d refs/heads/self
+'
+
+test_expect_success 'ref transaction: deleting symbolic self-reference without --no-deref fails' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+	cat >expect <<-EOF &&
+	error: multiple updates for ${SQ}refs/heads/self${SQ} (including one via symref ${SQ}refs/heads/self${SQ}) are not allowed
+	EOF
+	test_must_fail git -C repo update-ref -d refs/heads/self 2>err &&
+	test_cmp expect err
+'
+
+test_expect_success 'ref transaction: deleting symbolic self-reference with --no-deref succeeds' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+	git -C repo update-ref -d --no-deref refs/heads/self
+'
+
+test_expect_success 'ref transaction: creating symbolic ref fails with F/D conflict' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo A &&
+	cat >expect <<-EOF &&
+	error: unable to write symref for refs/heads: file/directory conflict
+	EOF
+	test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
+	test_cmp expect err
+'
+
+test_expect_success 'ref transaction: ref deletion' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file &&
+		HEAD_OID=$(git show-ref -s --verify HEAD) &&
+		cat >expect <<-EOF &&
+		$HEAD_OID refs/heads/main
+		$HEAD_OID refs/tags/file
+		EOF
+		git show-ref >actual &&
+		test_cmp expect actual &&
+
+		test_must_fail git update-ref -d refs/tags/file $INVALID_OID &&
+		git show-ref >actual &&
+		test_cmp expect actual &&
+
+		git update-ref -d refs/tags/file $HEAD_OID &&
+		echo "$HEAD_OID refs/heads/main" >expect &&
+		git show-ref >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'ref transaction: writes cause auto-compaction' '
+	test_when_finished "rm -rf repo" &&
+
+	git init repo &&
+	test_line_count = 1 repo/.git/reftable/tables.list &&
+
+	test_commit -C repo --no-tag A &&
+	test_line_count = 2 repo/.git/reftable/tables.list &&
+
+	test_commit -C repo --no-tag B &&
+	test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+check_fsync_events () {
+	local trace="$1" &&
+	shift &&
+
+	cat >expect &&
+	sed -n \
+		-e '/^{"event":"counter",.*"category":"fsync",/ {
+			s/.*"category":"fsync",//;
+			s/}$//;
+			p;
+		}' \
+		<"$trace" >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'ref transaction: writes are synced' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo initial &&
+
+	GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+	GIT_TEST_FSYNC=true \
+		git -C repo -c core.fsync=reference \
+		-c core.fsyncMethod=fsync update-ref refs/heads/branch HEAD &&
+	check_fsync_events trace2.txt <<-EOF
+	"name":"hardware-flush","count":2
+	EOF
+'
+
+test_expect_success 'ref transaction: empty transaction in empty repo' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo --no-tag A &&
+	git -C repo update-ref -d refs/heads/main &&
+	test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD &&
+	git -C repo update-ref --stdin <<-EOF
+	prepare
+	commit
+	EOF
+'
+
+test_expect_success 'ref transaction: fails gracefully when auto compaction fails' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit A &&
+		for i in $(test_seq 10)
+		do
+			git branch branch-$i &&
+			for table in .git/reftable/*.ref
+			do
+				touch "$table.lock" || exit 1
+			done ||
+			exit 1
+		done &&
+		test_line_count = 13 .git/reftable/tables.list
+	)
+'
+
+test_expect_success 'pack-refs: compacts tables' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+
+	test_commit -C repo A &&
+	ls -1 repo/.git/reftable >table-files &&
+	test_line_count = 4 table-files &&
+	test_line_count = 3 repo/.git/reftable/tables.list &&
+
+	git -C repo pack-refs &&
+	ls -1 repo/.git/reftable >table-files &&
+	test_line_count = 2 table-files &&
+	test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'pack-refs: compaction raises locking errors' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo A &&
+	touch repo/.git/reftable/tables.list.lock &&
+	cat >expect <<-EOF &&
+	error: unable to compact stack: data is locked
+	EOF
+	test_must_fail git -C repo pack-refs 2>err &&
+	test_cmp expect err
+'
+
+for command in pack-refs gc "maintenance run --task=pack-refs"
+do
+test_expect_success "$command: auto compaction" '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit A &&
+
+		# We need a bit of setup to ensure that git-gc(1) actually
+		# triggers, and that it does not write anything to the refdb.
+		git config gc.auto 1 &&
+		git config gc.autoDetach 0 &&
+		git config gc.reflogExpire never &&
+		git config gc.reflogExpireUnreachable never &&
+		test_oid blob17_1 | git hash-object -w --stdin &&
+
+		# The tables should have been auto-compacted, and thus auto
+		# compaction should not have to do anything.
+		ls -1 .git/reftable >tables-expect &&
+		test_line_count = 4 tables-expect &&
+		git $command --auto &&
+		ls -1 .git/reftable >tables-actual &&
+		test_cmp tables-expect tables-actual &&
+
+		test_oid blob17_2 | git hash-object -w --stdin &&
+
+		# Lock all tables write some refs. Auto-compaction will be
+		# unable to compact tables and thus fails gracefully, leaving
+		# the stack in a sub-optimal state.
+		ls .git/reftable/*.ref |
+		while read table
+		do
+			touch "$table.lock" || exit 1
+		done &&
+		git branch B &&
+		git branch C &&
+		rm .git/reftable/*.lock &&
+		test_line_count = 5 .git/reftable/tables.list &&
+
+		git $command --auto &&
+		test_line_count = 1 .git/reftable/tables.list
+	)
+'
+done
+
+test_expect_success 'pack-refs: prunes stale tables' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	touch repo/.git/reftable/stale-table.ref &&
+	git -C repo pack-refs &&
+	test_path_is_missing repo/.git/reftable/stable-ref.ref
+'
+
+test_expect_success 'pack-refs: does not prune non-table files' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	touch repo/.git/reftable/garbage &&
+	git -C repo pack-refs &&
+	test_path_is_file repo/.git/reftable/garbage
+'
+
+test_expect_success 'packed-refs: writes are synced' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo initial &&
+	test_line_count = 2 table-files &&
+
+	: >trace2.txt &&
+	GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+	GIT_TEST_FSYNC=true \
+		git -C repo -c core.fsync=reference \
+		-c core.fsyncMethod=fsync pack-refs &&
+	check_fsync_events trace2.txt <<-EOF
+	"name":"hardware-flush","count":2
+	EOF
+'
+
+test_expect_success 'ref iterator: bogus names are flagged' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit --no-tag file &&
+		test-tool ref-store main update-ref msg "refs/heads/bogus..name" $(git rev-parse HEAD) $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+		cat >expect <<-EOF &&
+		$ZERO_OID refs/heads/bogus..name 0xc
+		$(git rev-parse HEAD) refs/heads/main 0x0
+		EOF
+		test-tool ref-store main for-each-ref "" >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'ref iterator: missing object IDs are not flagged' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test-tool ref-store main update-ref msg "refs/heads/broken-hash" $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+
+		cat >expect <<-EOF &&
+		$INVALID_OID refs/heads/broken-hash 0x0
+		EOF
+		test-tool ref-store main for-each-ref "" >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'basic: commit and list refs' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo file &&
+	test_write_lines refs/heads/main refs/tags/file >expect &&
+	git -C repo for-each-ref --format="%(refname)" >actual &&
+	test_cmp actual expect
+'
+
+test_expect_success 'basic: can write large commit message' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	perl -e "
+		print \"this is a long commit message\" x 50000
+	" >commit-msg &&
+	git -C repo commit --allow-empty --file=../commit-msg
+'
+
+test_expect_success 'basic: show-ref fails with empty repository' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_must_fail git -C repo show-ref >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'basic: can check out unborn branch' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	git -C repo checkout -b main
+'
+
+test_expect_success 'basic: peeled tags are stored' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	test_commit -C repo file &&
+	git -C repo tag -m "annotated tag" test_tag HEAD &&
+	for ref in refs/heads/main refs/tags/file refs/tags/test_tag refs/tags/test_tag^{}
+	do
+		echo "$(git -C repo rev-parse "$ref") $ref" || return 1
+	done >expect &&
+	git -C repo show-ref -d >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'basic: for-each-ref can print symrefs' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file &&
+		git branch &&
+		git symbolic-ref refs/heads/sym refs/heads/main &&
+		cat >expected <<-EOF &&
+		refs/heads/main
+		EOF
+		git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
+		test_cmp expected actual
+	)
+'
+
+test_expect_success 'basic: notes' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		write_script fake_editor <<-\EOF &&
+		echo "$MSG" >"$1"
+		echo "$MSG" >&2
+		EOF
+
+		test_commit 1st &&
+		test_commit 2nd &&
+		GIT_EDITOR=./fake_editor MSG=b4 git notes add &&
+		GIT_EDITOR=./fake_editor MSG=b3 git notes edit &&
+		echo b4 >expect &&
+		git notes --ref commits@{1} show >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'basic: stash' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file &&
+		git stash list >expect &&
+		test_line_count = 0 expect &&
+
+		echo hoi >>file.t &&
+		git stash push -m stashed &&
+		git stash list >expect &&
+		test_line_count = 1 expect &&
+
+		git stash clear &&
+		git stash list >expect &&
+		test_line_count = 0 expect
+	)
+'
+
+test_expect_success 'basic: cherry-pick' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit message1 file1 &&
+		test_commit message2 file2 &&
+		git branch source &&
+		git checkout HEAD^ &&
+		test_commit message3 file3 &&
+		git cherry-pick source &&
+		test_path_is_file file2
+	)
+'
+
+test_expect_success 'basic: rebase' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit message1 file1 &&
+		test_commit message2 file2 &&
+		git branch source &&
+		git checkout HEAD^ &&
+		test_commit message3 file3 &&
+		git rebase source &&
+		test_path_is_file file2
+	)
+'
+
+test_expect_success 'reflog: can delete separate reflog entries' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit file &&
+		test_commit file2 &&
+		test_commit file3 &&
+		test_commit file4 &&
+		git reflog >actual &&
+		grep file3 actual &&
+
+		git reflog delete HEAD@{1} &&
+		git reflog >actual &&
+		! grep file3 actual
+	)
+'
+
+test_expect_success 'reflog: can switch to previous branch' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file1 &&
+		git checkout -b branch1 &&
+		test_commit file2 &&
+		git checkout -b branch2 &&
+		git switch - &&
+		git rev-parse --symbolic-full-name HEAD >actual &&
+		echo refs/heads/branch1 >expect &&
+		test_cmp actual expect
+	)
+'
+
+test_expect_success 'reflog: copying branch writes reflog entry' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit file1 &&
+		test_commit file2 &&
+		oid=$(git rev-parse --short HEAD) &&
+		git branch src &&
+		cat >expect <<-EOF &&
+		${oid} dst@{0}: Branch: copied refs/heads/src to refs/heads/dst
+		${oid} dst@{1}: branch: Created from main
+		EOF
+		git branch -c src dst &&
+		git reflog dst >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'reflog: renaming branch writes reflog entry' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git symbolic-ref HEAD refs/heads/before &&
+		test_commit file &&
+		git show-ref >expected.refs &&
+		sed s/before/after/g <expected.refs >expected &&
+		git branch -M after &&
+		git show-ref >actual &&
+		test_cmp expected actual &&
+		echo refs/heads/after >expected &&
+		git symbolic-ref HEAD >actual &&
+		test_cmp expected actual
+	)
+'
+
+test_expect_success 'reflog: can store empty logs' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_must_fail test-tool ref-store main reflog-exists refs/heads/branch &&
+		test-tool ref-store main create-reflog refs/heads/branch &&
+		test-tool ref-store main reflog-exists refs/heads/branch &&
+		test-tool ref-store main for-each-reflog-ent-reverse refs/heads/branch >actual &&
+		test_must_be_empty actual
+	)
+'
+
+test_expect_success 'reflog: expiry empties reflog' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit initial &&
+		git checkout -b branch &&
+		test_commit fileA &&
+		test_commit fileB &&
+
+		cat >expect <<-EOF &&
+		commit: fileB
+		commit: fileA
+		branch: Created from HEAD
+		EOF
+		git reflog show --format="%gs" refs/heads/branch >actual &&
+		test_cmp expect actual &&
+
+		git reflog expire branch --expire=all &&
+		git reflog show --format="%gs" refs/heads/branch >actual &&
+		test_must_be_empty actual &&
+		test-tool ref-store main reflog-exists refs/heads/branch
+	)
+'
+
+test_expect_success 'reflog: can be deleted' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit initial &&
+		test-tool ref-store main reflog-exists refs/heads/main &&
+		test-tool ref-store main delete-reflog refs/heads/main &&
+		test_must_fail test-tool ref-store main reflog-exists refs/heads/main
+	)
+'
+
+test_expect_success 'reflog: garbage collection deletes reflog entries' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		for count in $(test_seq 1 10)
+		do
+			test_commit "number $count" file.t $count number-$count ||
+			return 1
+		done &&
+		git reflog refs/heads/main >actual &&
+		test_line_count = 10 actual &&
+		grep "commit (initial): number 1" actual &&
+		grep "commit: number 10" actual &&
+
+		git gc &&
+		git reflog refs/heads/main >actual &&
+		test_line_count = 0 actual
+	)
+'
+
+test_expect_success 'reflog: updates via HEAD update HEAD reflog' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit main-one &&
+		git checkout -b new-branch &&
+		test_commit new-one &&
+		test_commit new-two &&
+
+		echo new-one >expect &&
+		git log -1 --format=%s HEAD@{1} >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'worktree: adding worktree creates separate stack' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo worktree add ../worktree &&
+	test_path_is_file repo/.git/worktrees/worktree/refs/heads &&
+	echo "ref: refs/heads/.invalid" >expect &&
+	test_cmp expect repo/.git/worktrees/worktree/HEAD &&
+	test_path_is_dir repo/.git/worktrees/worktree/reftable &&
+	test_path_is_file repo/.git/worktrees/worktree/reftable/tables.list
+'
+
+test_expect_success 'worktree: pack-refs in main repo packs main refs' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+	git -C repo worktree add ../worktree &&
+
+	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 4 repo/.git/reftable/tables.list &&
+	git -C repo pack-refs &&
+	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+	git -C repo worktree add ../worktree &&
+
+	test_line_count = 3 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 4 repo/.git/reftable/tables.list &&
+	git -C worktree pack-refs &&
+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 4 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating shared ref updates main stack' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo worktree add ../worktree &&
+	git -C repo pack-refs &&
+	git -C worktree pack-refs &&
+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list &&
+
+	git -C worktree update-ref refs/heads/shared HEAD &&
+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref updates worktree stack' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo worktree add ../worktree &&
+	git -C repo pack-refs &&
+	git -C worktree pack-refs &&
+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list &&
+
+	git -C worktree update-ref refs/bisect/per-worktree HEAD &&
+	test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref from main repo' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo worktree add ../worktree &&
+	git -C repo pack-refs &&
+	git -C worktree pack-refs &&
+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list &&
+
+	git -C repo update-ref worktrees/worktree/refs/bisect/per-worktree HEAD &&
+	test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref from second worktree' '
+	test_when_finished "rm -rf repo wt1 wt2" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo worktree add ../wt1 &&
+	git -C repo worktree add ../wt2 &&
+	git -C repo pack-refs &&
+	git -C wt1 pack-refs &&
+	git -C wt2 pack-refs &&
+	test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
+	test_line_count = 1 repo/.git/worktrees/wt2/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list &&
+
+	git -C wt1 update-ref worktrees/wt2/refs/bisect/per-worktree HEAD &&
+	test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
+	test_line_count = 2 repo/.git/worktrees/wt2/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: can create shared and per-worktree ref in one transaction' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo A &&
+
+	git -C repo worktree add ../worktree &&
+	git -C repo pack-refs &&
+	git -C worktree pack-refs &&
+	test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 1 repo/.git/reftable/tables.list &&
+
+	cat >stdin <<-EOF &&
+	create worktrees/worktree/refs/bisect/per-worktree HEAD
+	create refs/branches/shared HEAD
+	EOF
+	git -C repo update-ref --stdin <stdin &&
+	test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+	test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: can access common refs' '
+	test_when_finished "rm -rf repo worktree" &&
+	git init repo &&
+	test_commit -C repo file1 &&
+	git -C repo branch branch1 &&
+	git -C repo worktree add ../worktree &&
+
+	echo refs/heads/worktree >expect &&
+	git -C worktree symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
+	git -C worktree checkout branch1
+'
+
+test_expect_success 'worktree: adds worktree with detached HEAD' '
+	test_when_finished "rm -rf repo worktree" &&
+
+	git init repo &&
+	test_commit -C repo A &&
+	git -C repo rev-parse main >expect &&
+
+	git -C repo worktree add --detach ../worktree main &&
+	git -C worktree rev-parse HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
+	test_when_finished "rm -rf repo sub" &&
+
+	git init sub &&
+	test_commit -C sub two &&
+	git -C sub rev-parse HEAD >expect &&
+
+	git init repo &&
+	test_commit -C repo one &&
+	git -C repo fetch ../sub &&
+	git -C repo rev-parse FETCH_HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0611-reftable-httpd.sh b/t/t0611-reftable-httpd.sh
new file mode 100755
index 0000000..5e05b9c
--- /dev/null
+++ b/t/t0611-reftable-httpd.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='reftable HTTPD tests'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+
+test_expect_success 'serving ls-remote' '
+	git init --ref-format=reftable -b main "$REPO" &&
+	cd "$REPO" &&
+	test_commit m1 &&
+	>.git/git-daemon-export-ok &&
+	git ls-remote "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" | cut -f 2-2 -d "	" >actual &&
+	cat >expect <<-EOF &&
+	HEAD
+	refs/heads/main
+	refs/tags/m1
+	EOF
+	test_cmp actual expect
+'
+
+test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 3fb1b0c..88c524f 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -26,7 +26,7 @@
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
 read_tree_twoway () {
-    git read-tree -m "$1" "$2" && git ls-files --stage
+	git read-tree -m "$1" "$2" && git ls-files --stage
 }
 
 compare_change () {
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index cdc077c..a7c2ed0 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -37,315 +37,312 @@
 	esac
 }
 
-test_expect_success \
-    setup \
-    'echo frotz >frotz &&
-     echo nitfol >nitfol &&
-     echo bozbar >bozbar &&
-     echo rezrov >rezrov &&
-     git update-index --add nitfol bozbar rezrov &&
-     treeH=$(git write-tree) &&
-     echo treeH $treeH &&
-     git ls-tree $treeH &&
+test_expect_success setup '
+	echo frotz >frotz &&
+	echo nitfol >nitfol &&
+	echo bozbar >bozbar &&
+	echo rezrov >rezrov &&
+	git update-index --add nitfol bozbar rezrov &&
+	treeH=$(git write-tree) &&
+	echo treeH $treeH &&
+	git ls-tree $treeH &&
 
-     echo gnusto >bozbar &&
-     git update-index --add frotz bozbar --force-remove rezrov &&
-     git ls-files --stage >M.out &&
-     treeM=$(git write-tree) &&
-     echo treeM $treeM &&
-     git ls-tree $treeM &&
-     cp bozbar bozbar.M &&
-     cp frotz frotz.M &&
-     cp nitfol nitfol.M &&
-     git diff-tree $treeH $treeM'
-
-test_expect_success \
-    '1, 2, 3 - no carry forward' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >1-3.out &&
-     cmp M.out 1-3.out &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     check_cache_at bozbar clean &&
-     check_cache_at frotz clean &&
-     check_cache_at nitfol clean'
-
-test_expect_success \
-    '4 - carry forward local addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo "+100644 X 0	yomin" >expected &&
-     echo yomin >yomin &&
-     git update-index --add yomin &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >4.out &&
-     test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
-     compare_change 4diff.out expected &&
-     check_cache_at yomin clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     echo yomin >yomin1 &&
-     diff yomin yomin1 &&
-     rm -f yomin1'
-
-test_expect_success \
-    '5 - carry forward local addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     read_tree_u_must_succeed -m -u $treeH &&
-     echo yomin >yomin &&
-     git update-index --add yomin &&
-     echo yomin yomin >yomin &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >5.out &&
-     test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
-     compare_change 5diff.out expected &&
-     check_cache_at yomin dirty &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     : dirty index should have prevented -u from checking it out. &&
-     echo yomin yomin >yomin1 &&
-     diff yomin yomin1 &&
-     rm -f yomin1'
-
-test_expect_success \
-    '6 - local addition already has the same.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz >frotz &&
-     git update-index --add frotz &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >6.out &&
-     test_cmp M.out 6.out &&
-     check_cache_at frotz clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     echo frotz >frotz1 &&
-     diff frotz frotz1 &&
-     rm -f frotz1'
-
-test_expect_success \
-    '7 - local addition already has the same.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz >frotz &&
-     git update-index --add frotz &&
-     echo frotz frotz >frotz &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >7.out &&
-     test_cmp M.out 7.out &&
-     check_cache_at frotz dirty &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp nitfol.M nitfol &&
-     : dirty index should have prevented -u from checking it out. &&
-     echo frotz frotz >frotz1 &&
-     diff frotz frotz1 &&
-     rm -f frotz1'
-
-test_expect_success \
-    '8 - conflicting addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz frotz >frotz &&
-     git update-index --add frotz &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
-
-test_expect_success \
-    '9 - conflicting addition.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo frotz frotz >frotz &&
-     git update-index --add frotz &&
-     echo frotz >frotz &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
-
-test_expect_success \
-    '10 - path removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov >rezrov &&
-     git update-index --add rezrov &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >10.out &&
-     cmp M.out 10.out &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol
+	echo gnusto >bozbar &&
+	git update-index --add frotz bozbar --force-remove rezrov &&
+	git ls-files --stage >M.out &&
+	treeM=$(git write-tree) &&
+	echo treeM $treeM &&
+	git ls-tree $treeM &&
+	cp bozbar bozbar.M &&
+	cp frotz frotz.M &&
+	cp nitfol nitfol.M &&
+	git diff-tree $treeH $treeM
 '
 
-test_expect_success \
-    '11 - dirty path removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov >rezrov &&
-     git update-index --add rezrov &&
-     echo rezrov rezrov >rezrov &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
+test_expect_success '1, 2, 3 - no carry forward' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >1-3.out &&
+	cmp M.out 1-3.out &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol &&
+	check_cache_at bozbar clean &&
+	check_cache_at frotz clean &&
+	check_cache_at nitfol clean
+'
 
-test_expect_success \
-    '12 - unmatching local changes being removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov rezrov >rezrov &&
-     git update-index --add rezrov &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
+test_expect_success '4 - carry forward local addition.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo "+100644 X 0	yomin" >expected &&
+	echo yomin >yomin &&
+	git update-index --add yomin &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >4.out &&
+	test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
+	compare_change 4diff.out expected &&
+	check_cache_at yomin clean &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol &&
+	echo yomin >yomin1 &&
+	diff yomin yomin1 &&
+	rm -f yomin1
+'
 
-test_expect_success \
-    '13 - unmatching local changes being removed.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo rezrov rezrov >rezrov &&
-     git update-index --add rezrov &&
-     echo rezrov >rezrov &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
+test_expect_success '5 - carry forward local addition.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	read_tree_u_must_succeed -m -u $treeH &&
+	echo yomin >yomin &&
+	git update-index --add yomin &&
+	echo yomin yomin >yomin &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >5.out &&
+	test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
+	compare_change 5diff.out expected &&
+	check_cache_at yomin dirty &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol &&
+	: dirty index should have prevented -u from checking it out. &&
+	echo yomin yomin >yomin1 &&
+	diff yomin yomin1 &&
+	rm -f yomin1
+'
+
+test_expect_success '6 - local addition already has the same.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo frotz >frotz &&
+	git update-index --add frotz &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >6.out &&
+	test_cmp M.out 6.out &&
+	check_cache_at frotz clean &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol &&
+	echo frotz >frotz1 &&
+	diff frotz frotz1 &&
+	rm -f frotz1
+'
+
+test_expect_success '7 - local addition already has the same.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo frotz >frotz &&
+	git update-index --add frotz &&
+	echo frotz frotz >frotz &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >7.out &&
+	test_cmp M.out 7.out &&
+	check_cache_at frotz dirty &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp nitfol.M nitfol &&
+	: dirty index should have prevented -u from checking it out. &&
+	echo frotz frotz >frotz1 &&
+	diff frotz frotz1 &&
+	rm -f frotz1
+'
+
+test_expect_success '8 - conflicting addition.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo frotz frotz >frotz &&
+	git update-index --add frotz &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '9 - conflicting addition.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo frotz frotz >frotz &&
+	git update-index --add frotz &&
+	echo frotz >frotz &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '10 - path removed.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo rezrov >rezrov &&
+	git update-index --add rezrov &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >10.out &&
+	cmp M.out 10.out &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol
+'
+
+test_expect_success '11 - dirty path removed.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo rezrov >rezrov &&
+	git update-index --add rezrov &&
+	echo rezrov rezrov >rezrov &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '12 - unmatching local changes being removed.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo rezrov rezrov >rezrov &&
+	git update-index --add rezrov &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '13 - unmatching local changes being removed.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo rezrov rezrov >rezrov &&
+	git update-index --add rezrov &&
+	echo rezrov >rezrov &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
 
 cat >expected <<EOF
 -100644 X 0	nitfol
 +100644 X 0	nitfol
 EOF
 
-test_expect_success \
-    '14 - unchanged in two heads.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo nitfol nitfol >nitfol &&
-     git update-index --add nitfol &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >14.out &&
-     test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
-     compare_change 14diff.out expected &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     check_cache_at nitfol clean &&
-     echo nitfol nitfol >nitfol1 &&
-     diff nitfol nitfol1 &&
-     rm -f nitfol1'
-
-test_expect_success \
-    '15 - unchanged in two heads.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo nitfol nitfol >nitfol &&
-     git update-index --add nitfol &&
-     echo nitfol nitfol nitfol >nitfol &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >15.out &&
-     test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
-     compare_change 15diff.out expected &&
-     check_cache_at nitfol dirty &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     echo nitfol nitfol nitfol >nitfol1 &&
-     diff nitfol nitfol1 &&
-     rm -f nitfol1'
-
-test_expect_success \
-    '16 - conflicting local change.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar bozbar >bozbar &&
-     git update-index --add bozbar &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
-
-test_expect_success \
-    '17 - conflicting local change.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar bozbar >bozbar &&
-     git update-index --add bozbar &&
-     echo bozbar bozbar bozbar >bozbar &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
-
-test_expect_success \
-    '18 - local change already having a good result.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo gnusto >bozbar &&
-     git update-index --add bozbar &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >18.out &&
-     test_cmp M.out 18.out &&
-     check_cache_at bozbar clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol
+test_expect_success '14 - unchanged in two heads.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo nitfol nitfol >nitfol &&
+	git update-index --add nitfol &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >14.out &&
+	test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
+	compare_change 14diff.out expected &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	check_cache_at nitfol clean &&
+	echo nitfol nitfol >nitfol1 &&
+	diff nitfol nitfol1 &&
+	rm -f nitfol1
 '
 
-test_expect_success \
-    '19 - local change already having a good result, further modified.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo gnusto >bozbar &&
-     git update-index --add bozbar &&
-     echo gnusto gnusto >bozbar &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >19.out &&
-     test_cmp M.out 19.out &&
-     check_cache_at bozbar dirty &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol &&
-     echo gnusto gnusto >bozbar1 &&
-     diff bozbar bozbar1 &&
-     rm -f bozbar1'
-
-test_expect_success \
-    '20 - no local change, use new tree.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar >bozbar &&
-     git update-index --add bozbar &&
-     read_tree_u_must_succeed -m -u $treeH $treeM &&
-     git ls-files --stage >20.out &&
-     test_cmp M.out 20.out &&
-     check_cache_at bozbar clean &&
-     test_cmp bozbar.M bozbar &&
-     test_cmp frotz.M frotz &&
-     test_cmp nitfol.M nitfol
+test_expect_success '15 - unchanged in two heads.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo nitfol nitfol >nitfol &&
+	git update-index --add nitfol &&
+	echo nitfol nitfol nitfol >nitfol &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >15.out &&
+	test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
+	compare_change 15diff.out expected &&
+	check_cache_at nitfol dirty &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	echo nitfol nitfol nitfol >nitfol1 &&
+	diff nitfol nitfol1 &&
+	rm -f nitfol1
 '
 
-test_expect_success \
-    '21 - no local change, dirty cache.' \
-    'rm -f .git/index nitfol bozbar rezrov frotz &&
-     read_tree_u_must_succeed --reset -u $treeH &&
-     echo bozbar >bozbar &&
-     git update-index --add bozbar &&
-     echo gnusto gnusto >bozbar &&
-     ! read_tree_u_must_succeed -m -u $treeH $treeM'
+test_expect_success '16 - conflicting local change.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo bozbar bozbar >bozbar &&
+	git update-index --add bozbar &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '17 - conflicting local change.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo bozbar bozbar >bozbar &&
+	git update-index --add bozbar &&
+	echo bozbar bozbar bozbar >bozbar &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
+
+test_expect_success '18 - local change already having a good result.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo gnusto >bozbar &&
+	git update-index --add bozbar &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >18.out &&
+	test_cmp M.out 18.out &&
+	check_cache_at bozbar clean &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol
+'
+
+test_expect_success '19 - local change already having a good result, further modified.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo gnusto >bozbar &&
+	git update-index --add bozbar &&
+	echo gnusto gnusto >bozbar &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >19.out &&
+	test_cmp M.out 19.out &&
+	check_cache_at bozbar dirty &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol &&
+	echo gnusto gnusto >bozbar1 &&
+	diff bozbar bozbar1 &&
+	rm -f bozbar1
+'
+
+test_expect_success '20 - no local change, use new tree.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo bozbar >bozbar &&
+	git update-index --add bozbar &&
+	read_tree_u_must_succeed -m -u $treeH $treeM &&
+	git ls-files --stage >20.out &&
+	test_cmp M.out 20.out &&
+	check_cache_at bozbar clean &&
+	test_cmp bozbar.M bozbar &&
+	test_cmp frotz.M frotz &&
+	test_cmp nitfol.M nitfol
+'
+
+test_expect_success '21 - no local change, dirty cache.' '
+	rm -f .git/index nitfol bozbar rezrov frotz &&
+	read_tree_u_must_succeed --reset -u $treeH &&
+	echo bozbar >bozbar &&
+	git update-index --add bozbar &&
+	echo gnusto gnusto >bozbar &&
+	! read_tree_u_must_succeed -m -u $treeH $treeM
+'
 
 # Also make sure we did not break DF vs DF/DF case.
-test_expect_success \
-    'DF vs DF/DF case setup.' \
-    'rm -f .git/index &&
-     echo DF >DF &&
-     git update-index --add DF &&
-     treeDF=$(git write-tree) &&
-     echo treeDF $treeDF &&
-     git ls-tree $treeDF &&
+test_expect_success 'DF vs DF/DF case setup.' '
+	rm -f .git/index &&
+	echo DF >DF &&
+	git update-index --add DF &&
+	treeDF=$(git write-tree) &&
+	echo treeDF $treeDF &&
+	git ls-tree $treeDF &&
 
-     rm -f DF &&
-     mkdir DF &&
-     echo DF/DF >DF/DF &&
-     git update-index --add --remove DF DF/DF &&
-     treeDFDF=$(git write-tree) &&
-     echo treeDFDF $treeDFDF &&
-     git ls-tree $treeDFDF &&
-     git ls-files --stage >DFDF.out'
+	rm -f DF &&
+	mkdir DF &&
+	echo DF/DF >DF/DF &&
+	git update-index --add --remove DF DF/DF &&
+	treeDFDF=$(git write-tree) &&
+	echo treeDFDF $treeDFDF &&
+	git ls-tree $treeDFDF &&
+	git ls-files --stage >DFDF.out
+'
 
-test_expect_success \
-    'DF vs DF/DF case test.' \
-    'rm -f .git/index &&
-     rm -fr DF &&
-     echo DF >DF &&
-     git update-index --add DF &&
-     read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
-     git ls-files --stage >DFDFcheck.out &&
-     test_cmp DFDF.out DFDFcheck.out &&
-     check_cache_at DF/DF clean'
+test_expect_success 'DF vs DF/DF case test.' '
+	rm -f .git/index &&
+	rm -fr DF &&
+	echo DF >DF &&
+	git update-index --add DF &&
+	read_tree_u_must_succeed -m -u $treeDF $treeDFDF &&
+	git ls-files --stage >DFDFcheck.out &&
+	test_cmp DFDF.out DFDFcheck.out &&
+	check_cache_at DF/DF clean
+'
 
 test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 12e30d7..26be4a2 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -41,7 +41,8 @@
 	git ls-files -s &&
 	read_tree_u_must_succeed --reset -u HEAD &&
 	git ls-files -s >actual &&
-	! test -f old
+	! test -f old &&
+	test_cmp expect actual
 '
 
 test_expect_success 'two-way reset should remove remnants too' '
@@ -56,7 +57,8 @@
 	git ls-files -s &&
 	read_tree_u_must_succeed --reset -u HEAD HEAD &&
 	git ls-files -s >actual &&
-	! test -f old
+	! test -f old &&
+	test_cmp expect actual
 '
 
 test_expect_success 'Porcelain reset should remove remnants too' '
@@ -71,7 +73,8 @@
 	git ls-files -s &&
 	git reset --hard &&
 	git ls-files -s >actual &&
-	! test -f old
+	! test -f old &&
+	test_cmp expect actual
 '
 
 test_expect_success 'Porcelain checkout -f should remove remnants too' '
@@ -86,7 +89,8 @@
 	git ls-files -s &&
 	git checkout -f &&
 	git ls-files -s >actual &&
-	! test -f old
+	! test -f old &&
+	test_cmp expect actual
 '
 
 test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
@@ -101,7 +105,8 @@
 	git ls-files -s &&
 	git checkout -f HEAD &&
 	git ls-files -s >actual &&
-	! test -f old
+	! test -f old &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 2d875b1..e12b221 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -6,7 +6,7 @@
 
 test_cmdmode_usage () {
 	test_expect_code 129 "$@" 2>err &&
-	grep "^error:.*is incompatible with" err
+	grep "^error: .* cannot be used together" err
 }
 
 for switches in \
@@ -89,7 +89,8 @@
 for opt in --buffer \
 	--follow-symlinks \
 	--batch-all-objects \
-	-z
+	-z \
+	-Z
 do
 	test_expect_success "usage: bad option combination: $opt without batch mode" '
 		test_incompatible_usage git cat-file $opt &&
@@ -109,81 +110,67 @@
     echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
 }
 
-maybe_remove_timestamp () {
-	if test -z "$2"; then
-		echo_without_newline "$1"
-	else
-		echo_without_newline "$(printf '%s\n' "$1" | remove_timestamp)"
-	fi
-}
-
-remove_timestamp () {
-	sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//'
-}
-
-
 run_tests () {
     type=$1
-    sha1=$2
+    oid=$2
     size=$3
     content=$4
     pretty_content=$5
-    no_ts=$6
 
-    batch_output="$sha1 $type $size
+    batch_output="$oid $type $size
 $content"
 
     test_expect_success "$type exists" '
-	git cat-file -e $sha1
+	git cat-file -e $oid
     '
 
     test_expect_success "Type of $type is correct" '
 	echo $type >expect &&
-	git cat-file -t $sha1 >actual &&
+	git cat-file -t $oid >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct" '
 	echo $size >expect &&
-	git cat-file -s $sha1 >actual &&
+	git cat-file -s $oid >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success "Type of $type is correct using --allow-unknown-type" '
 	echo $type >expect &&
-	git cat-file -t --allow-unknown-type $sha1 >actual &&
+	git cat-file -t --allow-unknown-type $oid >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct using --allow-unknown-type" '
 	echo $size >expect &&
-	git cat-file -s --allow-unknown-type $sha1 >actual &&
+	git cat-file -s --allow-unknown-type $oid >actual &&
 	test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
-	maybe_remove_timestamp "$content" $no_ts >expect &&
-	maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual &&
+	echo_without_newline "$content" >expect &&
+	git cat-file $type $oid >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success "Pretty content of $type is correct" '
-	maybe_remove_timestamp "$pretty_content" $no_ts >expect &&
-	maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual &&
+	echo_without_newline "$pretty_content" >expect &&
+	git cat-file -p $oid >actual &&
 	test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "--batch output of $type is correct" '
-	maybe_remove_timestamp "$batch_output" $no_ts >expect &&
-	maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual &&
+	echo "$batch_output" >expect &&
+	echo $oid | git cat-file --batch >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success "--batch-check output of $type is correct" '
-	echo "$sha1 $type $size" >expect &&
-	echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+	echo "$oid $type $size" >expect &&
+	echo_without_newline $oid | git cat-file --batch-check >actual &&
 	test_cmp expect actual
     '
 
@@ -191,35 +178,34 @@
     do
 	test -z "$content" ||
 		test_expect_success "--batch-command $opt output of $type content is correct" '
-		maybe_remove_timestamp "$batch_output" $no_ts >expect &&
-		maybe_remove_timestamp "$(test_write_lines "contents $sha1" |
-		git cat-file --batch-command $opt)" $no_ts >actual &&
+		echo "$batch_output" >expect &&
+		test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual &&
 		test_cmp expect actual
 	'
 
 	test_expect_success "--batch-command $opt output of $type info is correct" '
-		echo "$sha1 $type $size" >expect &&
-		test_write_lines "info $sha1" |
+		echo "$oid $type $size" >expect &&
+		test_write_lines "info $oid" |
 		git cat-file --batch-command $opt >actual &&
 		test_cmp expect actual
 	'
     done
 
     test_expect_success "custom --batch-check format" '
-	echo "$type $sha1" >expect &&
-	echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+	echo "$type $oid" >expect &&
+	echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success "custom --batch-command format" '
-	echo "$type $sha1" >expect &&
-	echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+	echo "$type $oid" >expect &&
+	echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
 	test_cmp expect actual
     '
 
     test_expect_success '--batch-check with %(rest)' '
 	echo "$type this is some extra content" >expect &&
-	echo "$sha1    this is some extra content" |
+	echo "$oid    this is some extra content" |
 		git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
 	test_cmp expect actual
     '
@@ -228,10 +214,9 @@
     test_expect_success "--batch without type ($type)" '
 	{
 		echo "$size" &&
-		maybe_remove_timestamp "$content" $no_ts
+		echo "$content"
 	} >expect &&
-	echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
-	maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+	echo $oid | git cat-file --batch="%(objectsize)" >actual &&
 	test_cmp expect actual
     '
 
@@ -239,142 +224,200 @@
     test_expect_success "--batch without size ($type)" '
 	{
 		echo "$type" &&
-		maybe_remove_timestamp "$content" $no_ts
+		echo "$content"
 	} >expect &&
-	echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
-	maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+	echo $oid | git cat-file --batch="%(objecttype)" >actual &&
 	test_cmp expect actual
     '
 }
 
 hello_content="Hello World"
 hello_size=$(strlen "$hello_content")
-hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
 
 test_expect_success "setup" '
+	git config core.repositoryformatversion 1 &&
+	git config extensions.objectformat $test_hash_algo &&
+	git config extensions.compatobjectformat $test_compat_hash_algo &&
 	echo_without_newline "$hello_content" > hello &&
 	git update-index --add hello
 '
 
-run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+run_blob_tests () {
+    oid=$1
 
-test_expect_success '--batch-command --buffer with flush for blob info' '
-	echo "$hello_sha1 blob $hello_size" >expect &&
-	test_write_lines "info $hello_sha1" "flush" |
+    run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
+
+    test_expect_success '--batch-command --buffer with flush for blob info' '
+	echo "$oid blob $hello_size" >expect &&
+	test_write_lines "info $oid" "flush" |
 	GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
 	git cat-file --batch-command --buffer >actual &&
 	test_cmp expect actual
-'
+    '
 
-test_expect_success '--batch-command --buffer without flush for blob info' '
+    test_expect_success '--batch-command --buffer without flush for blob info' '
 	touch output &&
-	test_write_lines "info $hello_sha1" |
+	test_write_lines "info $oid" |
 	GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
 	git cat-file --batch-command --buffer >>output &&
 	test_must_be_empty output
-'
+    '
+}
+
+hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
+run_blob_tests $hello_oid
+run_blob_tests $hello_compat_oid
 
 test_expect_success '--batch-check without %(rest) considers whole line' '
-	echo "$hello_sha1 blob $hello_size" >expect &&
-	git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+	echo "$hello_oid blob $hello_size" >expect &&
+	git update-index --add --cacheinfo 100644 $hello_oid "white space" &&
 	test_when_finished "git update-index --remove \"white space\"" &&
 	echo ":white space" | git cat-file --batch-check >actual &&
 	test_cmp expect actual
 '
 
-tree_sha1=$(git write-tree)
+tree_oid=$(git write-tree)
+tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
 tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1	hello"
+tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))
+tree_pretty_content="100644 blob $hello_oid	hello${LF}"
+tree_compat_pretty_content="100644 blob $hello_compat_oid	hello${LF}"
 
-run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
 
 commit_message="Initial commit"
-commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
+commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
 commit_size=$(($(test_oid hexsz) + 137))
-commit_content="tree $tree_sha1
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0 +0000
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0 +0000
+commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
+commit_content="tree $tree_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
 $commit_message"
 
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1
+commit_compat_content="tree $tree_compat_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
-tag_header_without_timestamp="object $hello_sha1
-type blob
+$commit_message"
+
+run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
+
+tag_header_without_oid="type blob
 tag hellotag
 tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_header_without_timestamp="object $hello_oid
+$tag_header_without_oid"
+tag_compat_header_without_timestamp="object $hello_compat_oid
+$tag_header_without_oid"
 tag_description="This is a tag"
 tag_content="$tag_header_without_timestamp 0 +0000
 
 $tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
 
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+$tag_description"
+
+tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
 tag_size=$(strlen "$tag_content")
 
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
 
-test_expect_success \
-    "Reach a blob from a tag pointing to it" \
-    "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\""
+run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
+run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
 
-for batch in batch batch-check batch-command
+test_expect_success "Reach a blob from a tag pointing to it" '
+	echo_without_newline "$hello_content" >expect &&
+	git cat-file blob $tag_oid >actual &&
+	test_cmp expect actual
+'
+
+for oid in $hello_oid $hello_compat_oid
 do
-    for opt in t s e p
+    for batch in batch batch-check batch-command
     do
+	for opt in t s e p
+	do
 	test_expect_success "Passing -$opt with --$batch fails" '
-	    test_must_fail git cat-file --$batch -$opt $hello_sha1
+	    test_must_fail git cat-file --$batch -$opt $oid
 	'
 
 	test_expect_success "Passing --$batch with -$opt fails" '
-	    test_must_fail git cat-file -$opt --$batch $hello_sha1
+	    test_must_fail git cat-file -$opt --$batch $oid
+	'
+	done
+
+	test_expect_success "Passing <type> with --$batch fails" '
+	test_must_fail git cat-file --$batch blob $oid
+	'
+
+	test_expect_success "Passing --$batch with <type> fails" '
+	test_must_fail git cat-file blob --$batch $oid
+	'
+
+	test_expect_success "Passing oid with --$batch fails" '
+	test_must_fail git cat-file --$batch $oid
 	'
     done
-
-    test_expect_success "Passing <type> with --$batch fails" '
-	test_must_fail git cat-file --$batch blob $hello_sha1
-    '
-
-    test_expect_success "Passing --$batch with <type> fails" '
-	test_must_fail git cat-file blob --$batch $hello_sha1
-    '
-
-    test_expect_success "Passing sha1 with --$batch fails" '
-	test_must_fail git cat-file --$batch $hello_sha1
-    '
 done
 
-for opt in t s e p
+for oid in $hello_oid $hello_compat_oid
 do
-    test_expect_success "Passing -$opt with --follow-symlinks fails" '
-	    test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+    for opt in t s e p
+    do
+	test_expect_success "Passing -$opt with --follow-symlinks fails" '
+	    test_must_fail git cat-file --follow-symlinks -$opt $oid
 	'
+    done
 done
 
 test_expect_success "--batch-check for a non-existent named object" '
-    test "foobar42 missing
-foobar84 missing" = \
-    "$( ( echo foobar42 && echo_without_newline foobar84 ) | git cat-file --batch-check)"
+	cat >expect <<-EOF &&
+	foobar42 missing
+	foobar84 missing
+	EOF
+
+	printf "foobar42\nfoobar84" >in &&
+	git cat-file --batch-check <in >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success "--batch-check for a non-existent hash" '
-    test "0000000000000000000000000000000000000042 missing
-0000000000000000000000000000000000000084 missing" = \
-    "$( ( echo 0000000000000000000000000000000000000042 &&
-	 echo_without_newline 0000000000000000000000000000000000000084 ) |
-       git cat-file --batch-check)"
+	cat >expect <<-EOF &&
+	0000000000000000000000000000000000000042 missing
+	0000000000000000000000000000000000000084 missing
+	EOF
+
+	printf "0000000000000000000000000000000000000042\n0000000000000000000000000000000000000084" >in &&
+	git cat-file --batch-check <in >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success "--batch for an existent and a non-existent hash" '
-    test "$tag_sha1 tag $tag_size
-$tag_content
-0000000000000000000000000000000000000000 missing" = \
-    "$( ( echo $tag_sha1 &&
-	 echo_without_newline 0000000000000000000000000000000000000000 ) |
-       git cat-file --batch)"
+	cat >expect <<-EOF &&
+	$tag_oid tag $tag_size
+	$tag_content
+	0000000000000000000000000000000000000000 missing
+	EOF
+
+	printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
+	git cat-file --batch <in >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success "--batch-check for an empty line" '
-    test " missing" = "$(echo | git cat-file --batch-check)"
+	cat >expect <<-EOF &&
+	 missing
+	EOF
+
+	echo >in &&
+	git cat-file --batch-check <in >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'empty --batch-check notices missing object' '
@@ -383,82 +426,102 @@
 	test_cmp expect actual
 '
 
-batch_input="$hello_sha1
-$commit_sha1
-$tag_sha1
+batch_tests () {
+    boid=$1
+    loid=$2
+    lsize=$3
+    coid=$4
+    csize=$5
+    ccontent=$6
+    toid=$7
+    tsize=$8
+    tcontent=$9
+
+    batch_input="$boid
+$coid
+$toid
 deadbeef
 
 "
 
-batch_output="$hello_sha1 blob $hello_size
-$hello_content
-$commit_sha1 commit $commit_size
-$commit_content
-$tag_sha1 tag $tag_size
-$tag_content
-deadbeef missing
- missing"
+    printf "%s\0" \
+	"$boid blob $hello_size" \
+	"$hello_content" \
+	"$coid commit $csize" \
+	"$ccontent" \
+	"$toid tag $tsize" \
+	"$tcontent" \
+	"deadbeef missing" \
+	" missing" >batch_output
 
-test_expect_success '--batch with multiple sha1s gives correct format' '
-	test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)"
-'
-
-test_expect_success '--batch, -z with multiple sha1s gives correct format' '
-	echo_without_newline_nul "$batch_input" >in &&
-	test "$(maybe_remove_timestamp "$batch_output" 1)" = \
-	"$(maybe_remove_timestamp "$(git cat-file --batch -z <in)" 1)"
-'
-
-batch_check_input="$hello_sha1
-$tree_sha1
-$commit_sha1
-$tag_sha1
-deadbeef
-
-"
-
-batch_check_output="$hello_sha1 blob $hello_size
-$tree_sha1 tree $tree_size
-$commit_sha1 commit $commit_size
-$tag_sha1 tag $tag_size
-deadbeef missing
- missing"
-
-test_expect_success "--batch-check with multiple sha1s gives correct format" '
-    test "$batch_check_output" = \
-    "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
-'
-
-test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
-    echo_without_newline_nul "$batch_check_input" >in &&
-    test "$batch_check_output" = "$(git cat-file --batch-check -z <in)"
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
-	touch -- "newline${LF}embedded" &&
-	git add -- "newline${LF}embedded" &&
-	git commit -m "file with newline embedded" &&
-	test_tick &&
-
-	printf "HEAD:newline${LF}embedded" >in &&
-	git cat-file --batch-check -z <in >actual &&
-
-	echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+    test_expect_success '--batch with multiple oids gives correct format' '
+	tr "\0" "\n" <batch_output >expect &&
+	echo_without_newline "$batch_input" >in &&
+	git cat-file --batch <in >actual &&
 	test_cmp expect actual
-'
+    '
 
-batch_command_multiple_info="info $hello_sha1
-info $tree_sha1
-info $commit_sha1
-info $tag_sha1
+    test_expect_success '--batch, -z with multiple oids gives correct format' '
+	echo_without_newline_nul "$batch_input" >in &&
+	tr "\0" "\n" <batch_output >expect &&
+	git cat-file --batch -z <in >actual &&
+	test_cmp expect actual
+    '
+
+    test_expect_success '--batch, -Z with multiple oids gives correct format' '
+	echo_without_newline_nul "$batch_input" >in &&
+	git cat-file --batch -Z <in >actual &&
+	test_cmp batch_output actual
+    '
+
+batch_check_input="$boid
+$loid
+$coid
+$toid
+deadbeef
+
+"
+
+    printf "%s\0" \
+	"$boid blob $hello_size" \
+	"$loid tree $lsize" \
+	"$coid commit $csize" \
+	"$toid tag $tsize" \
+	"deadbeef missing" \
+	" missing" >batch_check_output
+
+    test_expect_success "--batch-check with multiple oids gives correct format" '
+	tr "\0" "\n" <batch_check_output >expect &&
+	echo_without_newline "$batch_check_input" >in &&
+	git cat-file --batch-check <in >actual &&
+	test_cmp expect actual
+    '
+
+    test_expect_success "--batch-check, -z with multiple oids gives correct format" '
+	tr "\0" "\n" <batch_check_output >expect &&
+	echo_without_newline_nul "$batch_check_input" >in &&
+	git cat-file --batch-check -z <in >actual &&
+	test_cmp expect actual
+    '
+
+    test_expect_success "--batch-check, -Z with multiple oids gives correct format" '
+	echo_without_newline_nul "$batch_check_input" >in &&
+	git cat-file --batch-check -Z <in >actual &&
+	test_cmp batch_check_output actual
+    '
+
+batch_command_multiple_info="info $boid
+info $loid
+info $coid
+info $toid
 info deadbeef"
 
-test_expect_success '--batch-command with multiple info calls gives correct format' '
+    test_expect_success '--batch-command with multiple info calls gives correct format' '
 	cat >expect <<-EOF &&
-	$hello_sha1 blob $hello_size
-	$tree_sha1 tree $tree_size
-	$commit_sha1 commit $commit_size
-	$tag_sha1 tag $tag_size
+	$boid blob $hello_size
+	$loid tree $lsize
+	$coid commit $csize
+	$toid tag $tsize
 	deadbeef missing
 	EOF
 
@@ -470,36 +533,72 @@
 	echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
 	git cat-file --batch-command --buffer -z <in >actual &&
 
-	test_cmp expect actual
-'
+	test_cmp expect actual &&
 
-batch_command_multiple_contents="contents $hello_sha1
-contents $commit_sha1
-contents $tag_sha1
+	echo "$batch_command_multiple_info" | tr "\n" "\0" >in &&
+	tr "\n" "\0" <expect >expect_nul &&
+	git cat-file --batch-command --buffer -Z <in >actual &&
+
+	test_cmp expect_nul actual
+    '
+
+batch_command_multiple_contents="contents $boid
+contents $coid
+contents $toid
 contents deadbeef
 flush"
 
-test_expect_success '--batch-command with multiple command calls gives correct format' '
-	remove_timestamp >expect <<-EOF &&
-	$hello_sha1 blob $hello_size
-	$hello_content
-	$commit_sha1 commit $commit_size
-	$commit_content
-	$tag_sha1 tag $tag_size
-	$tag_content
-	deadbeef missing
-	EOF
+    test_expect_success '--batch-command with multiple command calls gives correct format' '
+	printf "%s\0" \
+		"$boid blob $hello_size" \
+		"$hello_content" \
+		"$coid commit $csize" \
+		"$ccontent" \
+		"$toid tag $tsize" \
+		"$tcontent" \
+		"deadbeef missing" >expect_nul &&
+	tr "\0" "\n" <expect_nul >expect &&
 
 	echo "$batch_command_multiple_contents" >in &&
-	git cat-file --batch-command --buffer <in >actual_raw &&
+	git cat-file --batch-command --buffer <in >actual &&
 
-	remove_timestamp <actual_raw >actual &&
 	test_cmp expect actual &&
 
 	echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
-	git cat-file --batch-command --buffer -z <in >actual_raw &&
+	git cat-file --batch-command --buffer -z <in >actual &&
 
-	remove_timestamp <actual_raw >actual &&
+	test_cmp expect actual &&
+
+	echo "$batch_command_multiple_contents" | tr "\n" "\0" >in &&
+	git cat-file --batch-command --buffer -Z <in >actual &&
+
+	test_cmp expect_nul actual
+    '
+
+}
+
+batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
+batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
+
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
+	touch -- "newline${LF}embedded" &&
+	git add -- "newline${LF}embedded" &&
+	git commit -m "file with newline embedded" &&
+	test_tick &&
+
+	printf "HEAD:newline${LF}embedded" >in
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+	git cat-file --batch-check -z <in >actual &&
+	echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+	git cat-file --batch-check -Z <in >actual &&
+	printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
 	test_cmp expect actual
 '
 
@@ -527,7 +626,7 @@
 # we will check only that one of the two objects is a delta
 # against the other, but not the order. We can do so by just
 # asking for the base of both, and checking whether either
-# sha1 appears in the output.
+# oid appears in the output.
 test_expect_success '%(deltabase) reports packed delta bases' '
 	git repack -ad &&
 	git cat-file --batch-check="%(deltabase)" <blobs >actual &&
@@ -541,12 +640,12 @@
 	bogus_short_type="bogus" &&
 	bogus_short_content="bogus" &&
 	bogus_short_size=$(strlen "$bogus_short_content") &&
-	bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+	bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
 
 	bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
 	bogus_long_content="bogus" &&
 	bogus_long_size=$(strlen "$bogus_long_content") &&
-	bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+	bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
 '
 
 for arg1 in '' --allow-unknown-type
@@ -566,9 +665,9 @@
 
 			if test "$arg1" = "--allow-unknown-type"
 			then
-				git cat-file $arg1 $arg2 $bogus_short_sha1
+				git cat-file $arg1 $arg2 $bogus_short_oid
 			else
-				test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+				test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
 				test_must_be_empty out &&
 				test_cmp expect actual
 			fi
@@ -578,21 +677,21 @@
 			if test "$arg2" = "-p"
 			then
 				cat >expect <<-EOF
-				error: header for $bogus_long_sha1 too long, exceeds 32 bytes
-				fatal: Not a valid object name $bogus_long_sha1
+				error: header for $bogus_long_oid too long, exceeds 32 bytes
+				fatal: Not a valid object name $bogus_long_oid
 				EOF
 			else
 				cat >expect <<-EOF
-				error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+				error: header for $bogus_long_oid too long, exceeds 32 bytes
 				fatal: git cat-file: could not get object info
 				EOF
 			fi &&
 
 			if test "$arg1" = "--allow-unknown-type"
 			then
-				git cat-file $arg1 $arg2 $bogus_short_sha1
+				git cat-file $arg1 $arg2 $bogus_short_oid
 			else
-				test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+				test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
 				test_must_be_empty out &&
 				test_cmp expect actual
 			fi
@@ -603,7 +702,8 @@
 			fatal: Not a valid object name $(test_oid deadbeef_short)
 			EOF
 			test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
-			test_must_be_empty out
+			test_must_be_empty out &&
+			test_cmp expect.err err.actual
 		'
 
 		test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
@@ -625,28 +725,28 @@
 done
 
 test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
-	git cat-file -e $bogus_short_sha1
+	git cat-file -e $bogus_short_oid
 '
 
 test_expect_success '-e can not be combined with --allow-unknown-type' '
-	test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
+	test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid
 '
 
 test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
-	test_must_fail git cat-file -p $bogus_short_sha1 &&
-	test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+	test_must_fail git cat-file -p $bogus_short_oid &&
+	test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
 '
 
 test_expect_success '<type> <hash> does not work with objects of broken types' '
 	cat >err.expect <<-\EOF &&
 	fatal: invalid object type "bogus"
 	EOF
-	test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
+	test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual &&
 	test_cmp err.expect err.actual
 '
 
 test_expect_success 'broken types combined with --batch and --batch-check' '
-	echo $bogus_short_sha1 >bogus-oid &&
+	echo $bogus_short_oid >bogus-oid &&
 
 	cat >err.expect <<-\EOF &&
 	fatal: invalid object type
@@ -668,52 +768,52 @@
 	cat >expect <<-EOF &&
 	$bogus_short_type
 	EOF
-	git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+	git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
 	test_cmp expect actual &&
 
 	# Create it manually, as "git replace" will die on bogus
 	# types.
 	head=$(git rev-parse --verify HEAD) &&
-	test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
-	test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+	test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
+	test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 
 	cat >expect <<-EOF &&
 	commit
 	EOF
-	git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+	git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success "Type of broken object is correct" '
 	echo $bogus_short_type >expect &&
-	git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+	git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success "Size of broken object is correct" '
 	echo $bogus_short_size >expect &&
-	git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
+	git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-	rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+	rm .git/objects/$(test_oid_to_path $bogus_short_oid)
 '
 
 test_expect_success "Type of broken object is correct when type is large" '
 	echo $bogus_long_type >expect &&
-	git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
+	git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success "Size of large broken object is correct when type is large" '
 	echo $bogus_long_size >expect &&
-	git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
+	git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-	rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+	rm .git/objects/$(test_oid_to_path $bogus_long_oid)
 '
 
 test_expect_success 'cat-file -t and -s on corrupt loose object' '
@@ -810,7 +910,7 @@
 	test_ln_s_add loop2 loop1 &&
 	git add morx dir/subdir/ind2 dir/ind1 &&
 	git commit -am "test" &&
-	echo $hello_sha1 blob $hello_size >found
+	echo $hello_oid blob $hello_size >found
 '
 
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
@@ -839,6 +939,13 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for broken in-repo, same-dir links' '
+	printf "HEAD:broken-same-dir-link\0" >in &&
+	printf "dangling 25\0HEAD:broken-same-dir-link\0" >expect &&
+	git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
 	echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
 	test_cmp found actual
@@ -853,6 +960,15 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-check --follow-symlinks -Z works for parent-dir links' '
+	echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	printf "notdir 29\0HEAD:dir/parent-dir-link/nope\0" >expect &&
+	printf "HEAD:dir/parent-dir-link/nope\0" >in &&
+	git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
 	echo dangling 22 >expect &&
 	echo HEAD:dir/link-dir/nope >>expect &&
@@ -882,7 +998,7 @@
 	echo HEAD:dirlink/morx >>expect &&
 	echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
 	test_cmp expect actual &&
-	echo $hello_sha1 blob $hello_size >expect &&
+	echo $hello_oid blob $hello_size >expect &&
 	echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
 	test_cmp expect actual
 '
@@ -967,6 +1083,13 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git cat-file --batch-check --follow-symlink -Z breaks loops' '
+	printf "loop 10\0HEAD:loop1\0" >expect &&
+	printf "HEAD:loop1\0" >in &&
+	git cat-file --batch-check --follow-symlinks -Z <in >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
 	echo HEAD:morx | git cat-file --batch >expect &&
 	echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
@@ -1034,6 +1157,42 @@
 	cmp expect actual
 '
 
+test_expect_success 'cat-file %(objectsize:disk) with --batch-all-objects' '
+	# our state has both loose and packed objects,
+	# so find both for our expected output
+	{
+		find .git/objects/?? -type f |
+		awk -F/ "{ print \$0, \$3\$4 }" |
+		while read path oid
+		do
+			size=$(test_file_size "$path") &&
+			echo "$oid $size" ||
+			return 1
+		done &&
+		rawsz=$(test_oid rawsz) &&
+		find .git/objects/pack -name "*.idx" |
+		while read idx
+		do
+			git show-index <"$idx" >idx.raw &&
+			sort -nr <idx.raw >idx.sorted &&
+			packsz=$(test_file_size "${idx%.idx}.pack") &&
+			end=$((packsz - rawsz)) &&
+			while read start oid rest
+			do
+				size=$((end - start)) &&
+				end=$start &&
+				echo "$oid $size" ||
+				return 1
+			done <idx.sorted ||
+			return 1
+		done
+	} >expect.raw &&
+	sort <expect.raw >expect &&
+	git cat-file --batch-all-objects \
+		--batch-check="%(objectname) %(objectsize:disk)" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'set up replacement object' '
 	orig=$(git rev-parse HEAD) &&
 	git cat-file commit $orig >orig &&
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index ac3d173..64aea38 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -124,8 +124,8 @@
 	path0_sha=$(git hash-object --path=file0 file1) &&
 	test "$file0_sha" = "$path0_sha" &&
 	test "$file1_sha" = "$path1_sha" &&
-	path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
-	path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+	path1_sha=$(git hash-object --path=file1 --stdin <file0) &&
+	path0_sha=$(git hash-object --path=file0 --stdin <file1) &&
 	test "$file0_sha" = "$path0_sha" &&
 	test "$file1_sha" = "$path1_sha"
 '
@@ -154,7 +154,7 @@
 test_expect_success 'check that --no-filters option works' '
 	nofilters_file1=$(git hash-object --no-filters file1) &&
 	test "$file0_sha" = "$nofilters_file1" &&
-	nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+	nofilters_file1=$(git hash-object --stdin <file1) &&
 	test "$file0_sha" = "$nofilters_file1"
 '
 
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index 3c08194..22875ba 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -60,11 +60,11 @@
 '
 
 test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
-	test_must_fail git mktree <all >actual
+	test_must_fail git mktree <all
 '
 
 test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
-	test_must_fail git mktree <all.withsub >actual
+	test_must_fail git mktree <all.withsub
 '
 
 test_done
diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh
new file mode 100755
index 0000000..8132cd3
--- /dev/null
+++ b/t/t1016-compatObjectFormat.sh
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Eric Biederman
+#
+
+test_description='Test how well compatObjectFormat works'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+# All of the follow variables must be defined in the environment:
+# GIT_AUTHOR_NAME
+# GIT_AUTHOR_EMAIL
+# GIT_AUTHOR_DATE
+# GIT_COMMITTER_NAME
+# GIT_COMMITTER_EMAIL
+# GIT_COMMITTER_DATE
+#
+# The test relies on these variables being set so that the two
+# different commits in two different repositories encoded with two
+# different hash functions result in the same content in the commits.
+# This means that when the commit is translated between hash functions
+# the commit is identical to the commit in the other repository.
+
+compat_hash () {
+    case "$1" in
+    "sha1")
+	echo "sha256"
+	;;
+    "sha256")
+	echo "sha1"
+	;;
+    esac
+}
+
+hello_oid () {
+    case "$1" in
+    "sha1")
+	echo "$hello_sha1_oid"
+	;;
+    "sha256")
+	echo "$hello_sha256_oid"
+	;;
+    esac
+}
+
+tree_oid () {
+    case "$1" in
+    "sha1")
+	echo "$tree_sha1_oid"
+	;;
+    "sha256")
+	echo "$tree_sha256_oid"
+	;;
+    esac
+}
+
+commit_oid () {
+    case "$1" in
+    "sha1")
+	echo "$commit_sha1_oid"
+	;;
+    "sha256")
+	echo "$commit_sha256_oid"
+	;;
+    esac
+}
+
+commit2_oid () {
+    case "$1" in
+    "sha1")
+	echo "$commit2_sha1_oid"
+	;;
+    "sha256")
+	echo "$commit2_sha256_oid"
+	;;
+    esac
+}
+
+del_sigcommit () {
+    local delete=$1
+
+    if test "$delete" = "sha256" ; then
+	local pattern="gpgsig-sha256"
+    else
+	local pattern="gpgsig"
+    fi
+    test-tool delete-gpgsig "$pattern"
+}
+
+
+del_sigtag () {
+    local storage=$1
+    local delete=$2
+
+    if test "$storage" = "$delete" ; then
+	local pattern="trailer"
+    elif test "$storage" = "sha256" ; then
+	local pattern="gpgsig"
+    else
+	local pattern="gpgsig-sha256"
+    fi
+    test-tool delete-gpgsig "$pattern"
+}
+
+base=$(pwd)
+for hash in sha1 sha256
+do
+	cd "$base"
+	mkdir -p repo-$hash
+	cd repo-$hash
+
+	test_expect_success "setup $hash repository" '
+		git init --object-format=$hash &&
+		git config core.repositoryformatversion 1 &&
+		git config extensions.objectformat $hash &&
+		git config extensions.compatobjectformat $(compat_hash $hash) &&
+		git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+		echo "Hellow World!" > hello &&
+		eval hello_${hash}_oid=$(git hash-object hello) &&
+		git update-index --add hello &&
+		git commit -m "Initial commit" &&
+		eval commit_${hash}_oid=$(git rev-parse HEAD) &&
+		eval tree_${hash}_oid=$(git rev-parse HEAD^{tree})
+	'
+	test_expect_success "create a $hash  tagged blob" '
+		git tag --no-sign -m "This is a tag" hellotag $(hello_oid $hash) &&
+		eval hellotag_${hash}_oid=$(git rev-parse hellotag)
+	'
+	test_expect_success "create a $hash tagged tree" '
+		git tag --no-sign -m "This is a tag" treetag $(tree_oid $hash) &&
+		eval treetag_${hash}_oid=$(git rev-parse treetag)
+	'
+	test_expect_success "create a $hash tagged commit" '
+		git tag --no-sign -m "This is a tag" committag $(commit_oid $hash) &&
+		eval committag_${hash}_oid=$(git rev-parse committag)
+	'
+	test_expect_success GPG2 "create a $hash signed commit" '
+		git commit --gpg-sign --allow-empty -m "This is a signed commit" &&
+		eval signedcommit_${hash}_oid=$(git rev-parse HEAD)
+	'
+	test_expect_success GPG2 "create a $hash signed tag" '
+		git tag -s -m "This is a signed tag" signedtag HEAD &&
+		eval signedtag_${hash}_oid=$(git rev-parse signedtag)
+	'
+	test_expect_success "create a $hash branch" '
+		git checkout -b branch $(commit_oid $hash) &&
+		echo "More more more give me more!" > more &&
+		eval more_${hash}_oid=$(git hash-object more) &&
+		echo "Another and another and another" > another &&
+		eval another_${hash}_oid=$(git hash-object another) &&
+		git update-index --add more another &&
+		git commit -m "Add more files!" &&
+		eval commit2_${hash}_oid=$(git rev-parse HEAD) &&
+		eval tree2_${hash}_oid=$(git rev-parse HEAD^{tree})
+	'
+	test_expect_success GPG2 "create another $hash signed tag" '
+		git tag -s -m "This is another signed tag" signedtag2 $(commit2_oid $hash) &&
+		eval signedtag2_${hash}_oid=$(git rev-parse signedtag2)
+	'
+	test_expect_success GPG2 "merge the $hash branches together" '
+		git merge -S -m "merge some signed tags together" signedtag signedtag2 &&
+		eval signedcommit2_${hash}_oid=$(git rev-parse HEAD)
+	'
+	test_expect_success GPG2 "create additional $hash signed commits" '
+		git commit --gpg-sign --allow-empty -m "This is an additional signed commit" &&
+		git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" &&
+		git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" &&
+		eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) &&
+		eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4)
+	'
+	test_expect_success GPG2 "create additional $hash signed tags" '
+		git tag -s -m "This is an additional signed tag" signedtag34 HEAD &&
+		git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 &&
+		git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 &&
+		eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
+		eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
+	'
+done
+cd "$base"
+
+compare_oids () {
+    test "$#" = 5 && { local PREREQ=$1; shift; } || PREREQ=
+    local type="$1"
+    local name="$2"
+    local sha1_oid="$3"
+    local sha256_oid="$4"
+
+    echo ${sha1_oid} > ${name}_sha1_expected
+    echo ${sha256_oid} > ${name}_sha256_expected
+    echo ${type} > ${name}_type_expected
+
+    git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found
+    git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found
+    local sha1_sha256_oid=$(cat ${name}_sha1_sha256_found)
+    local sha256_sha1_oid=$(cat ${name}_sha256_sha1_found)
+
+    test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" '
+	git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 &&
+	test_cmp ${name}_sha1 ${name}_sha1_expected
+'
+
+    test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" '
+	git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 &&
+	test_cmp ${name}_sha256 ${name}_sha256_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 type" '
+	git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 &&
+	git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 &&
+	test_cmp ${name}_type1 ${name}_type2 &&
+	test_cmp ${name}_type1 ${name}_type_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 type" '
+	git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 &&
+	git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 &&
+	test_cmp ${name}_type3 ${name}_type4 &&
+	test_cmp ${name}_type3 ${name}_type_expected
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 size" '
+	git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 &&
+	git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 &&
+	test_cmp ${name}_size1 ${name}_size2
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 size" '
+	git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 &&
+	git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 &&
+	test_cmp ${name}_size3 ${name}_size4
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" '
+	git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 &&
+	git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 &&
+	test_cmp ${name}_content1 ${name}_content2
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" '
+	git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 &&
+	git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 &&
+	test_cmp ${name}_content3 ${name}_content4
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha1 content" '
+	git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 &&
+	git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 &&
+	test_cmp ${name}_content5 ${name}_content6
+'
+
+    test_expect_success $PREREQ "Verify ${name}'s sha256 content" '
+	git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 &&
+	git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 &&
+	test_cmp ${name}_content7 ${name}_content8
+'
+
+}
+
+compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid"
+compare_oids 'tree' tree "$tree_sha1_oid" "$tree_sha256_oid"
+compare_oids 'commit' commit "$commit_sha1_oid" "$commit_sha256_oid"
+compare_oids GPG2 'commit' signedcommit "$signedcommit_sha1_oid" "$signedcommit_sha256_oid"
+compare_oids 'tag' hellotag "$hellotag_sha1_oid" "$hellotag_sha256_oid"
+compare_oids 'tag' treetag "$treetag_sha1_oid" "$treetag_sha256_oid"
+compare_oids 'tag' committag "$committag_sha1_oid" "$committag_sha256_oid"
+compare_oids GPG2 'tag' signedtag "$signedtag_sha1_oid" "$signedtag_sha256_oid"
+
+compare_oids 'blob' more "$more_sha1_oid" "$more_sha256_oid"
+compare_oids 'blob' another "$another_sha1_oid" "$another_sha256_oid"
+compare_oids 'tree' tree2 "$tree2_sha1_oid" "$tree2_sha256_oid"
+compare_oids 'commit' commit2 "$commit2_sha1_oid" "$commit2_sha256_oid"
+compare_oids GPG2 'tag' signedtag2 "$signedtag2_sha1_oid" "$signedtag2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit2 "$signedcommit2_sha1_oid" "$signedcommit2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit3 "$signedcommit3_sha1_oid" "$signedcommit3_sha256_oid"
+compare_oids GPG2 'commit' signedcommit4 "$signedcommit4_sha1_oid" "$signedcommit4_sha256_oid"
+compare_oids GPG2 'tag' signedtag3 "$signedtag3_sha1_oid" "$signedtag3_sha256_oid"
+compare_oids GPG2 'tag' signedtag4 "$signedtag4_sha1_oid" "$signedtag4_sha256_oid"
+
+test_done
diff --git a/t/t1016/gpg b/t/t1016/gpg
new file mode 100755
index 0000000..2601cb1
--- /dev/null
+++ b/t/t1016/gpg
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec gpg --faked-system-time "20230918T154812" "$@"
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index 35261af..5e0f0a3 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -125,7 +125,7 @@
 		cd bit-error-cp &&
 		test_must_fail git -c transfer.unpackLimit=1 \
 			fetch ../no-bit-error 2>stderr &&
-		test_i18ngrep ! -i collision stderr
+		test_grep ! -i collision stderr
 	)
 '
 
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 627267b..ab3a105 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -47,7 +47,7 @@
 test_expect_success 'git sparse-checkout list (not sparse)' '
 	test_must_fail git -C repo sparse-checkout list >list 2>err &&
 	test_must_be_empty list &&
-	test_i18ngrep "this worktree is not sparse" err
+	test_grep "this worktree is not sparse" err
 '
 
 test_expect_success 'git sparse-checkout list (not sparse)' '
@@ -55,7 +55,7 @@
 	rm repo/.git/info/sparse-checkout &&
 	git -C repo sparse-checkout list >list 2>err &&
 	test_must_be_empty list &&
-	test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
+	test_grep "this worktree is not sparse (sparse-checkout file may not exist)" err
 '
 
 test_expect_success 'git sparse-checkout list (populated)' '
@@ -230,7 +230,7 @@
 	git -C repo config --worktree core.sparseCheckoutCone true &&
 	rm -rf repo/a repo/folder1 repo/folder2 &&
 	git -C repo read-tree -mu HEAD 2>err &&
-	test_i18ngrep ! "disabling cone patterns" err &&
+	test_grep ! "disabling cone patterns" err &&
 	git -C repo reset --hard &&
 	check_files repo a folder1 folder2
 '
@@ -240,7 +240,7 @@
 	cp repo/.git/info/sparse-checkout . &&
 	echo "!/deep/deeper/*/" >>repo/.git/info/sparse-checkout &&
 	git -C repo read-tree -mu HEAD 2>err &&
-	test_i18ngrep "unrecognized negative pattern" err
+	test_grep "unrecognized negative pattern" err
 '
 
 test_expect_success 'sparse-checkout disable' '
@@ -283,7 +283,7 @@
 test_expect_success 'cone mode: init and set' '
 	git -C repo sparse-checkout init --cone &&
 	git -C repo config --list >config &&
-	test_i18ngrep "core.sparsecheckoutcone=true" config &&
+	test_grep "core.sparsecheckoutcone=true" config &&
 	list_files repo >dir  &&
 	echo a >expect &&
 	test_cmp expect dir &&
@@ -334,7 +334,7 @@
 
 test_expect_success 'cone mode: add independent path' '
 	git -C repo sparse-checkout set deep/deeper1 &&
-	git -C repo sparse-checkout add folder1 &&
+	git -C repo sparse-checkout add --end-of-options folder1 &&
 	cat >expect <<-\EOF &&
 	/*
 	!/*/
@@ -386,7 +386,7 @@
 
 	git -C repo sparse-checkout set deep/deeper1 2>err &&
 
-	test_i18ngrep "The following paths are not up to date" err &&
+	test_grep "The following paths are not up to date" err &&
 	test_cmp expect repo/.git/info/sparse-checkout &&
 	check_files repo/deep a deeper1 deeper2 &&
 	check_files repo/deep/deeper1 a deepest &&
@@ -401,8 +401,8 @@
 		git add file &&
 		git commit -m "test" &&
 		git sparse-checkout set nothing 2>err &&
-		test_i18ngrep ! "Sparse checkout leaves no entry on working directory" err &&
-		test_i18ngrep ! ".git/index.lock" err &&
+		test_grep ! "Sparse checkout leaves no entry on working directory" err &&
+		test_grep ! ".git/index.lock" err &&
 		git sparse-checkout set --no-cone file
 	)
 '
@@ -411,14 +411,14 @@
 	test_when_finished rm -rf repo/.git/info/sparse-checkout.lock &&
 	touch repo/.git/info/sparse-checkout.lock &&
 	test_must_fail git -C repo sparse-checkout set deep 2>err &&
-	test_i18ngrep "Unable to create .*\.lock" err
+	test_grep "Unable to create .*\.lock" err
 '
 
 test_expect_success '.gitignore should not warn about cone mode' '
 	git -C repo config --worktree core.sparseCheckoutCone true &&
 	echo "**/bin/*" >repo/.gitignore &&
 	git -C repo reset --hard 2>err &&
-	test_i18ngrep ! "disabling cone patterns" err
+	test_grep ! "disabling cone patterns" err
 '
 
 test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '
@@ -426,10 +426,10 @@
 	echo dirty >dirty/folder1/a &&
 
 	git -C dirty sparse-checkout init --no-cone 2>err &&
-	test_i18ngrep "warning.*The following paths are not up to date" err &&
+	test_grep "warning.*The following paths are not up to date" err &&
 
 	git -C dirty sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
-	test_i18ngrep "warning.*The following paths are not up to date" err &&
+	test_grep "warning.*The following paths are not up to date" err &&
 	test_path_is_file dirty/folder1/a &&
 
 	git -C dirty sparse-checkout disable 2>err &&
@@ -453,14 +453,14 @@
 	git -C unmerged update-index --index-info <input &&
 
 	git -C unmerged sparse-checkout init --no-cone 2>err &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 
 	git -C unmerged sparse-checkout set /folder2/* /deep/deeper1/* 2>err &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 	test_path_is_file dirty/folder1/a &&
 
 	git -C unmerged sparse-checkout disable 2>err &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 
 	git -C unmerged reset --hard &&
 	git -C unmerged sparse-checkout init --no-cone &&
@@ -480,24 +480,24 @@
 	git -C tweak update-index --index-info <input &&
 
 	git -C tweak sparse-checkout init --cone 2>err &&
-	test_i18ngrep "warning.*The following paths are not up to date" err &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are not up to date" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 
 	git -C tweak sparse-checkout set folder2 deep/deeper1 2>err &&
-	test_i18ngrep "warning.*The following paths are not up to date" err &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are not up to date" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 
 	git -C tweak sparse-checkout reapply 2>err &&
-	test_i18ngrep "warning.*The following paths are not up to date" err &&
+	test_grep "warning.*The following paths are not up to date" err &&
 	test_path_is_file tweak/deep/deeper2/a &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 	test_path_is_file tweak/folder1/a &&
 
 	git -C tweak checkout HEAD deep/deeper2/a &&
 	git -C tweak sparse-checkout reapply 2>err &&
-	test_i18ngrep ! "warning.*The following paths are not up to date" err &&
+	test_grep ! "warning.*The following paths are not up to date" err &&
 	test_path_is_missing tweak/deep/deeper2/a &&
-	test_i18ngrep "warning.*The following paths are unmerged" err &&
+	test_grep "warning.*The following paths are unmerged" err &&
 	test_path_is_file tweak/folder1/a &&
 
 	# NEEDSWORK: We are asking to update a file outside of the
@@ -555,7 +555,7 @@
 	check_files repo a folder1
 '
 
-test_expect_success 'interaction with submodules' '
+test_expect_success 'setup submodules' '
 	git clone repo super &&
 	(
 		cd super &&
@@ -566,11 +566,22 @@
 		git commit -m "add submodule" &&
 		git sparse-checkout init --cone &&
 		git sparse-checkout set folder1
-	) &&
+	)
+'
+
+test_expect_success 'interaction with submodules' '
 	check_files super a folder1 modules &&
 	check_files super/modules/child a deep folder1 folder2
 '
 
+test_expect_success 'check-rules interaction with submodules' '
+	git -C super ls-tree --name-only -r HEAD >all-files &&
+	git -C super sparse-checkout check-rules >check-rules-matches <all-files &&
+
+	test_grep ! "modules/" check-rules-matches &&
+	test_grep "folder1/" check-rules-matches
+'
+
 test_expect_success 'different sparse-checkouts with worktrees' '
 	git -C repo sparse-checkout set --cone deep folder1 &&
 	git -C repo worktree add --detach ../worktree &&
@@ -605,7 +616,7 @@
 	then
 		test_must_be_empty err
 	else
-		test_i18ngrep "$ERRORS" err
+		test_grep "$ERRORS" err
 	fi &&
 	check_files $REPO $FILES
 }
@@ -875,6 +886,12 @@
 	grep ".gitignore.*is not a directory" error
 '
 
+test_expect_success 'error on mistyped command line options' '
+	test_must_fail git -C repo sparse-checkout add --sikp-checks .gitignore 2>error &&
+
+	grep "unknown option.*sikp-checks" error
+'
+
 test_expect_success 'by default, non-cone mode will warn on individual files' '
 	git -C repo sparse-checkout reapply --no-cone &&
 	git -C repo sparse-checkout add .gitignore 2>warning &&
@@ -882,4 +899,156 @@
 	grep "pass a leading slash before paths.*if you want a single file" warning
 '
 
+test_expect_success 'setup bare repo' '
+	git clone --bare "file://$(pwd)/repo" bare
+'
+test_expect_success 'list fails outside work tree' '
+	test_must_fail git -C bare sparse-checkout list 2>err &&
+	test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'add fails outside work tree' '
+	test_must_fail git -C bare sparse-checkout add deeper 2>err &&
+	test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'set fails outside work tree' '
+	test_must_fail git -C bare sparse-checkout set deeper 2>err &&
+	test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'init fails outside work tree' '
+	test_must_fail git -C bare sparse-checkout init 2>err &&
+	test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'reapply fails outside work tree' '
+	test_must_fail git -C bare sparse-checkout reapply 2>err &&
+	test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'disable fails outside work tree' '
+	test_must_fail git -C bare sparse-checkout disable 2>err &&
+	test_grep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'setup clean' '
+	git -C repo clean -fdx
+'
+
+test_expect_success 'check-rules cone mode' '
+	cat >rules <<-\EOF &&
+	folder1
+	deep/deeper1/deepest
+	EOF
+
+	git -C bare ls-tree -r --name-only HEAD >all-files &&
+	git -C bare sparse-checkout check-rules --cone \
+		--rules-file ../rules >check-rules-file <all-files &&
+
+	git -C repo sparse-checkout set --cone --stdin <rules&&
+	git -C repo ls-files -t >out &&
+	sed -n "/^S /!s/^. //p" out >ls-files &&
+
+	git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+	test_grep "deep/deeper1/deepest/a" check-rules-file &&
+	test_grep ! "deep/deeper2" check-rules-file &&
+
+	test_cmp check-rules-file ls-files &&
+	test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules non-cone mode' '
+	cat >rules <<-\EOF &&
+	deep/deeper1/deepest/a
+	EOF
+
+	git -C bare ls-tree -r --name-only HEAD >all-files &&
+	git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
+		>check-rules-file <all-files &&
+
+	git -C repo sparse-checkout set --no-cone --stdin <rules &&
+	git -C repo ls-files -t >out &&
+	sed -n "/^S /!s/^. //p" out >ls-files &&
+
+	git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+	cat >expect <<-\EOF &&
+	deep/deeper1/deepest/a
+	EOF
+
+	test_cmp expect check-rules-file &&
+	test_cmp check-rules-file ls-files &&
+	test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules cone mode is default' '
+	cat >rules <<-\EOF &&
+	folder1
+	EOF
+
+	cat >all-files <<-\EOF &&
+	toplevel
+	folder2/file
+	folder1/file
+	EOF
+
+	cat >expect <<-\EOF &&
+	toplevel
+	folder1/file
+	EOF
+
+	git -C repo sparse-checkout set --no-cone &&
+	git -C repo sparse-checkout check-rules \
+		--rules-file ../rules >actual <all-files &&
+
+	git -C bare sparse-checkout check-rules \
+		--rules-file ../rules >actual-bare <all-files &&
+
+	test_cmp expect actual &&
+	test_cmp expect actual-bare
+'
+
+test_expect_success 'check-rules quoting' '
+	cat >rules <<-EOF &&
+	"folder\" a"
+	EOF
+	cat >files <<-EOF &&
+	"folder\" a/file"
+	"folder\" b/file"
+	EOF
+	cat >expect <<-EOF &&
+	"folder\" a/file"
+	EOF
+	git sparse-checkout check-rules --cone \
+		--rules-file rules >actual <files &&
+
+	test_cmp expect actual
+'
+
+test_expect_success 'check-rules null termination' '
+	cat >rules <<-EOF &&
+	"folder\" a"
+	EOF
+
+	lf_to_nul >files <<-EOF &&
+	folder" a/a
+	folder" a/b
+	folder" b/fileQ
+	EOF
+
+	cat >expect <<-EOF &&
+	folder" a/aQfolder" a/bQ
+	EOF
+
+	git sparse-checkout check-rules --cone -z \
+		--rules-file rules >actual.nul <files &&
+	nul_to_q <actual.nul >actual &&
+	echo >>actual &&
+
+	test_cmp expect actual
+'
+
+
 test_done
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 8019190..2f1ae5f 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -337,8 +337,8 @@
 	init_repos &&
 	git -C sparse-checkout status >full &&
 	git -C sparse-index status >sparse &&
-	test_i18ngrep "You are in a sparse checkout with " full &&
-	test_i18ngrep "You are in a sparse checkout." sparse
+	test_grep "You are in a sparse checkout with " full &&
+	test_grep "You are in a sparse checkout." sparse
 '
 
 test_expect_success 'add, commit, checkout' '
@@ -1182,7 +1182,7 @@
 	# Without --ignore-skip-worktree-bits, outside-of-cone files will trigger
 	# an error
 	test_sparse_match test_must_fail git checkout-index -- folder1/a &&
-	test_i18ngrep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
+	test_grep "folder1/a has skip-worktree enabled" sparse-checkout-err &&
 	test_path_is_missing folder1/a &&
 
 	# With --ignore-skip-worktree-bits, outside-of-cone files are checked out
@@ -1377,7 +1377,7 @@
 	! test_region index ensure_full_index trace2.txt
 '
 
-ensure_not_expanded () {
+run_sparse_index_trace2 () {
 	rm -f trace2.txt &&
 	if test -z "$WITHOUT_UNTRACKED_TXT"
 	then
@@ -1397,7 +1397,16 @@
 			git -C sparse-index "$@" \
 			>sparse-index-out \
 			2>sparse-index-error || return 1
-	fi &&
+	fi
+}
+
+ensure_expanded () {
+	run_sparse_index_trace2 "$@" &&
+	test_region index ensure_full_index trace2.txt
+}
+
+ensure_not_expanded () {
+	run_sparse_index_trace2 "$@" &&
 	test_region ! index ensure_full_index trace2.txt
 }
 
@@ -1514,6 +1523,31 @@
 	ensure_not_expanded stash pop
 '
 
+test_expect_success 'describe tested on all' '
+	init_repos &&
+
+	# Add tag to be read by describe
+
+	run_on_all git tag -a v1.0 -m "Version 1" &&
+	test_all_match git describe --dirty &&
+	run_on_all rm g &&
+	test_all_match git describe --dirty
+'
+
+
+test_expect_success 'sparse-index is not expanded: describe' '
+	init_repos &&
+
+	# Add tag to be read by describe
+
+	git -C sparse-index tag -a v1.0 -m "Version 1" &&
+
+	ensure_not_expanded describe --dirty &&
+	echo "test" >>sparse-index/g &&
+	ensure_not_expanded describe --dirty &&
+	ensure_not_expanded describe
+'
+
 test_expect_success 'sparse index is not expanded: diff' '
 	init_repos &&
 
@@ -2055,4 +2089,246 @@
 	test_cmp actual expect
 '
 
+test_expect_success 'write-tree' '
+	init_repos &&
+
+	test_all_match git write-tree &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>"$1"
+	EOF
+
+	# make a change inside the sparse cone
+	run_on_all ../edit-contents deep/a &&
+	test_all_match git update-index deep/a &&
+	test_all_match git write-tree &&
+	test_all_match git status --porcelain=v2 &&
+
+	# make a change outside the sparse cone
+	run_on_all mkdir -p folder1 &&
+	run_on_all cp a folder1/a &&
+	run_on_all ../edit-contents folder1/a &&
+	test_all_match git update-index folder1/a &&
+	test_all_match git write-tree &&
+	test_all_match git status --porcelain=v2 &&
+
+	# check that SKIP_WORKTREE files are not materialized
+	test_path_is_missing sparse-checkout/folder2/a &&
+	test_path_is_missing sparse-index/folder2/a
+'
+
+test_expect_success 'sparse-index is not expanded: write-tree' '
+	init_repos &&
+
+	ensure_not_expanded write-tree &&
+
+	echo "test1" >>sparse-index/a &&
+	git -C sparse-index update-index a &&
+	ensure_not_expanded write-tree
+'
+
+test_expect_success 'diff-files with pathspec inside sparse definition' '
+	init_repos &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>"$1"
+	EOF
+
+	run_on_all ../edit-contents deep/a &&
+
+	test_all_match git diff-files &&
+
+	test_all_match git diff-files -- deep/a &&
+
+	# test wildcard
+	test_all_match git diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-files with pathspec outside sparse definition' '
+	init_repos &&
+
+	test_sparse_match git diff-files -- folder2/a &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>"$1"
+	EOF
+
+	# The directory "folder1" is outside the cone of interest
+	# and will not exist in the sparse checkout repositories.
+	# Create it as needed, add file "folder1/a" there with
+	# contents that is different from the staged version.
+	run_on_all mkdir -p folder1 &&
+	run_on_all cp a folder1/a &&
+
+	run_on_all ../edit-contents folder1/a &&
+	test_all_match git diff-files &&
+	test_all_match git diff-files -- folder1/a &&
+	test_all_match git diff-files -- "folder*/a"
+'
+
+test_expect_success 'sparse index is not expanded: diff-files' '
+	init_repos &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>"$1"
+	EOF
+
+	run_on_all ../edit-contents deep/a &&
+
+	ensure_not_expanded diff-files &&
+	ensure_not_expanded diff-files -- deep/a &&
+	ensure_not_expanded diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-tree' '
+	init_repos &&
+
+	# Test change inside sparse cone
+	tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
+	tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
+	test_all_match git diff-tree $tree1 $tree2 &&
+	test_all_match git diff-tree $tree1 $tree2 -- deep/a &&
+	test_all_match git diff-tree HEAD update-deep &&
+	test_all_match git diff-tree HEAD update-deep -- deep/a &&
+
+	# Test change outside sparse cone
+	tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
+	test_all_match git diff-tree $tree1 $tree3 &&
+	test_all_match git diff-tree $tree1 $tree3 -- folder1/a &&
+	test_all_match git diff-tree HEAD update-folder1 &&
+	test_all_match git diff-tree HEAD update-folder1 -- folder1/a &&
+
+	# Check that SKIP_WORKTREE files are not materialized
+	test_path_is_missing sparse-checkout/folder1/a &&
+	test_path_is_missing sparse-index/folder1/a &&
+	test_path_is_missing sparse-checkout/folder2/a &&
+	test_path_is_missing sparse-index/folder2/a
+'
+
+test_expect_success 'sparse-index is not expanded: diff-tree' '
+	init_repos &&
+
+	tree1=$(git -C sparse-index rev-parse HEAD^{tree}) &&
+	tree2=$(git -C sparse-index rev-parse update-deep^{tree}) &&
+	tree3=$(git -C sparse-index rev-parse update-folder1^{tree}) &&
+
+	ensure_not_expanded diff-tree $tree1 $tree2 &&
+	ensure_not_expanded diff-tree $tree1 $tree2 -- deep/a &&
+	ensure_not_expanded diff-tree HEAD update-deep &&
+	ensure_not_expanded diff-tree HEAD update-deep -- deep/a &&
+	ensure_not_expanded diff-tree $tree1 $tree3 &&
+	ensure_not_expanded diff-tree $tree1 $tree3 -- folder1/a &&
+	ensure_not_expanded diff-tree HEAD update-folder1 &&
+	ensure_not_expanded diff-tree HEAD update-folder1 -- folder1/a
+'
+
+test_expect_success 'worktree' '
+	init_repos &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>"$1"
+	EOF
+
+	for repo in full-checkout sparse-checkout sparse-index
+	do
+		worktree=${repo}-wt &&
+		git -C $repo worktree add ../$worktree &&
+
+		# Compare worktree content with "ls"
+		(cd $repo && ls) >worktree_contents &&
+		(cd $worktree && ls) >new_worktree_contents &&
+		test_cmp worktree_contents new_worktree_contents &&
+
+		# Compare index content with "ls-files --sparse"
+		git -C $repo ls-files --sparse >index_contents &&
+		git -C $worktree ls-files --sparse >new_index_contents &&
+		test_cmp index_contents new_index_contents &&
+
+		git -C $repo worktree remove ../$worktree || return 1
+	done &&
+
+	test_all_match git worktree add .worktrees/hotfix &&
+	run_on_all ../edit-contents .worktrees/hotfix/deep/a &&
+	test_all_match test_must_fail git worktree remove .worktrees/hotfix
+'
+
+test_expect_success 'worktree is not expanded' '
+	init_repos &&
+
+	ensure_not_expanded worktree add .worktrees/hotfix &&
+	ensure_not_expanded worktree remove .worktrees/hotfix
+'
+
+test_expect_success 'check-attr with pathspec inside sparse definition' '
+	init_repos &&
+
+	echo "a -crlf myAttr" >>.gitattributes &&
+	run_on_all cp ../.gitattributes ./deep &&
+
+	test_all_match git check-attr -a -- deep/a &&
+
+	test_all_match git add deep/.gitattributes &&
+	test_all_match git check-attr -a --cached -- deep/a
+'
+
+test_expect_success 'check-attr with pathspec outside sparse definition' '
+	init_repos &&
+
+	echo "a -crlf myAttr" >>.gitattributes &&
+	run_on_sparse mkdir folder1 &&
+	run_on_all cp ../.gitattributes ./folder1 &&
+	run_on_all cp a folder1/a &&
+
+	test_all_match git check-attr -a -- folder1/a &&
+
+	git -C full-checkout add folder1/.gitattributes &&
+	test_sparse_match git add --sparse folder1/.gitattributes &&
+	test_all_match git commit -m "add .gitattributes" &&
+	test_sparse_match git sparse-checkout reapply &&
+	test_all_match git check-attr -a --cached -- folder1/a
+'
+
+# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due
+# to an underlying issue in oneway_diff() within diff-lib.c.
+# 'do_oneway_diff()' is not called as expected for paths that could match
+# inside of a sparse directory. Specifically, the 'ce_path_match()' function
+# fails to recognize files inside a sparse directory (e.g., when 'folder1/'
+# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to
+# proceed with 'do_oneway_diff()' if the pathspec could match inside of a
+# sparse directory.
+test_expect_failure 'diff --check with pathspec outside sparse definition' '
+	init_repos &&
+
+	write_script edit-contents <<-\EOF &&
+	echo "a " >"$1"
+	EOF
+
+	test_all_match git config core.whitespace -trailing-space,-space-before-tab &&
+
+	echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes &&
+	run_on_all mkdir -p folder1 &&
+	run_on_all cp ../.gitattributes ./folder1 &&
+	test_all_match git add --sparse folder1/.gitattributes &&
+	run_on_all ../edit-contents folder1/a &&
+	test_all_match git add --sparse folder1/a &&
+
+	test_sparse_match git sparse-checkout reapply &&
+	test_all_match test_must_fail git diff --check --cached -- folder1/a
+'
+
+test_expect_success 'sparse-index is not expanded: check-attr' '
+	init_repos &&
+
+	echo "a -crlf myAttr" >>.gitattributes &&
+	mkdir ./sparse-index/folder1 &&
+	cp ./sparse-index/a ./sparse-index/folder1/a &&
+	cp .gitattributes ./sparse-index/deep &&
+	cp .gitattributes ./sparse-index/folder1 &&
+
+	git -C sparse-index add deep/.gitattributes &&
+	git -C sparse-index add --sparse folder1/.gitattributes &&
+	ensure_not_expanded check-attr -a --cached -- deep/a &&
+	ensure_not_expanded check-attr -a --cached -- folder1/a
+'
+
 test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index f1d42b6..9b65d9e 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,98 @@
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
+test_expect_success 'setup whitespace config' '
+	sed -e "s/^|//" \
+	    -e "s/[$]$//" \
+	    -e "s/X/	/g" >.git/config <<-\EOF
+	[section]
+	|	solid = rock
+	|	sparse = big XX blue
+	|	sparseAndTail = big XX blue $
+	|	sparseAndTailQuoted = "big XX blue "
+	|	sparseAndBiggerTail = big XX blue X X
+	|	sparseAndBiggerTailQuoted = "big XX blue X X"
+	|	sparseAndBiggerTailQuotedPlus = "big XX blue X X"X $
+	|	headAndTail = Xbig blue $
+	|	headAndTailQuoted = "Xbig blue "
+	|	headAndTailQuotedPlus = "Xbig blue " $
+	|	annotated = big blueX# to be discarded
+	|	annotatedQuoted = "big blue"X# to be discarded
+	EOF
+'
+
+test_expect_success 'no internal whitespace' '
+	echo "rock" >expect &&
+	git config --get section.solid >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'internal whitespace' '
+	echo "big QQ blue" | q_to_tab >expect &&
+	git config --get section.sparse >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace' '
+	echo "big QQ blue" | q_to_tab >expect &&
+	git config --get section.sparseAndTail >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace, all quoted' '
+	echo "big QQ blue " | q_to_tab >expect &&
+	git config --get section.sparseAndTailQuoted >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace' '
+	echo "big QQ blue" | q_to_tab >expect &&
+	git config --get section.sparseAndBiggerTail >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, all quoted' '
+	echo "big QQ blue Q Q" | q_to_tab >expect &&
+	git config --get section.sparseAndBiggerTailQuoted >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, not all quoted' '
+	echo "big QQ blue Q Q" | q_to_tab >expect &&
+	git config --get section.sparseAndBiggerTailQuotedPlus >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace' '
+	echo "big blue" >expect &&
+	git config --get section.headAndTail >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, all quoted' '
+	echo "Qbig blue " | q_to_tab >expect &&
+	git config --get section.headAndTailQuoted >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, not all quoted' '
+	echo "Qbig blue " | q_to_tab >expect &&
+	git config --get section.headAndTailQuotedPlus >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'inline comment' '
+	echo "big blue" >expect &&
+	git config --get section.annotated >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'inline comment, quoted' '
+	echo "big blue" >expect &&
+	git config --get section.annotatedQuoted >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'clear default config' '
 	rm -f .git/config
 '
@@ -69,14 +161,32 @@
 
 cat > expect << EOF
 [section]
-	penguin = very blue
 	Movie = BadPhysics
 	UPPERCASE = true
-	penguin = kingpin
+	penguin = gentoo # Pygoscelis papua
+	disposition = peckish # find fish
+	foo = bar #abc
+	spsp = value # and comment
+	htsp = value	# and comment
 [Sections]
 	WhatEver = Second
 EOF
 
+test_expect_success 'append comments' '
+	git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
+	git config --comment="find fish" section.disposition peckish &&
+	git config --comment="#abc" section.foo bar &&
+
+	git config --comment="and comment" section.spsp value &&
+	git config --comment="	# and comment" section.htsp value &&
+
+	test_cmp expect .git/config
+'
+
+test_expect_success 'Prohibited LF in comment' '
+	test_must_fail git config --comment="a${LF}b" section.k v
+'
+
 test_expect_success 'non-match result' 'test_cmp expect .git/config'
 
 test_expect_success 'find mixed-case key by canonical name' '
@@ -98,6 +208,23 @@
 	test_cmp_config two section.SubSection.key
 '
 
+test_missing_key () {
+	local key="$1" &&
+	local title="$2" &&
+	test_expect_success "value for $title is not printed" '
+		test_must_fail git config "$key" >out 2>err &&
+		test_must_be_empty out &&
+		test_must_be_empty err
+	'
+}
+
+test_missing_key 'missingsection.missingkey' 'missing section and missing key'
+test_missing_key 'missingsection.penguin' 'missing section and existing key'
+test_missing_key 'section.missingkey' 'existing section and missing key'
+test_missing_key 'section.MissingSubSection.missingkey' 'missing subsection and missing key'
+test_missing_key 'section.SubSection.missingkey' 'existing subsection and missing key'
+test_missing_key 'section.MissingSubSection.key' 'missing subsection and existing key'
+
 cat > .git/config <<\EOF
 [alpha]
 bar = foo
@@ -436,7 +563,7 @@
 
 test_expect_success 'no arguments, but no crash' '
 	test_must_fail git config >output 2>&1 &&
-	test_i18ngrep usage output
+	test_grep usage output
 '
 
 cat > .git/config << EOF
@@ -703,25 +830,25 @@
 	git config aninvalid.unit "1auto" &&
 	test_cmp_config 1auto aninvalid.unit &&
 	test_must_fail git config --int --get aninvalid.unit 2>actual &&
-	test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
+	test_grep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
 '
 
 test_expect_success 'invalid unit boolean' '
 	git config commit.gpgsign "1true" &&
 	test_cmp_config 1true commit.gpgsign &&
 	test_must_fail git config --bool --get commit.gpgsign 2>actual &&
-	test_i18ngrep "bad boolean config value .1true. for .commit.gpgsign." actual
+	test_grep "bad boolean config value .1true. for .commit.gpgsign." actual
 '
 
 test_expect_success 'line number is reported correctly' '
 	printf "[bool]\n\tvar\n" >invalid &&
 	test_must_fail git config -f invalid --path bool.var 2>actual &&
-	test_i18ngrep "line 2" actual
+	test_grep "line 2" actual
 '
 
 test_expect_success 'invalid stdin config' '
 	echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
-	test_i18ngrep "bad config line 1 in standard input" output
+	test_grep "bad config line 1 in standard input" output
 '
 
 cat > expect << EOF
@@ -902,7 +1029,7 @@
 		git config --get --path path.normal >>result &&
 		git config --get --path path.trailingtilde >>result
 	) &&
-	test_i18ngrep "[Ff]ailed to expand.*~/" msg &&
+	test_grep "[Ff]ailed to expand.*~/" msg &&
 	test_cmp expect result
 '
 
@@ -969,7 +1096,7 @@
 
 test_expect_success 'set --type=color barfs on non-color' '
 	test_must_fail git config --type=color foo.color "not-a-color" 2>error &&
-	test_i18ngrep "cannot parse color" error
+	test_grep "cannot parse color" error
 '
 
 cat > expect << EOF
@@ -1049,9 +1176,25 @@
 	test_cmp expect result
 '
 
-test_expect_success 'inner whitespace kept verbatim' '
-	git config section.val "foo 	  bar" &&
-	test_cmp_config "foo 	  bar" section.val
+test_expect_success 'inner whitespace kept verbatim, spaces only' '
+	echo "foo   bar" >expect &&
+	git config section.val "foo   bar" &&
+	git config --get section.val >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
+	echo "fooQQbar" | q_to_tab >expect &&
+	git config section.val "$(cat expect)" &&
+	git config --get section.val >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
+	echo "foo Q  bar" | q_to_tab >expect &&
+	git config section.val "$(cat expect)" &&
+	git config --get section.val >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success SYMLINKS 'symlinked configuration' '
@@ -1081,15 +1224,20 @@
 	test_must_fail git config --file=linktolinktonada --list
 '
 
-test_expect_success 'check split_cmdline return' "
-	git config alias.split-cmdline-fix 'echo \"' &&
-	test_must_fail git split-cmdline-fix &&
-	echo foo > foo &&
-	git add foo &&
-	git commit -m 'initial commit' &&
-	git config branch.main.mergeoptions 'echo \"' &&
-	test_must_fail git merge main
-"
+test_expect_success 'check split_cmdline return' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git config alias.split-cmdline-fix "echo \"" &&
+		test_must_fail git split-cmdline-fix &&
+		echo foo >foo &&
+		git add foo &&
+		git commit -m "initial commit" &&
+		git config branch.main.mergeoptions "echo \"" &&
+		test_must_fail git merge main
+	)
+'
 
 test_expect_success 'git -c "key=value" support' '
 	cat >expect <<-\EOF &&
@@ -1140,10 +1288,16 @@
 '
 
 test_expect_success 'aliases can be CamelCased' '
-	test_config alias.CamelCased "rev-parse HEAD" &&
-	git CamelCased >out &&
-	git rev-parse HEAD >expect &&
-	test_cmp expect out
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		git config alias.CamelCased "rev-parse HEAD" &&
+		git CamelCased >out &&
+		git rev-parse HEAD >expect &&
+		test_cmp expect out
+	)
 '
 
 test_expect_success 'git -c does not split values on equals' '
@@ -1430,12 +1584,12 @@
 
 test_expect_success 'git --config-env fails with invalid parameters' '
 	test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
-	test_i18ngrep "invalid config format: foo.flag" error &&
+	test_grep "invalid config format: foo.flag" error &&
 	test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
-	test_i18ngrep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
+	test_grep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
 	sane_unset NONEXISTENT &&
 	test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
-	test_i18ngrep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
+	test_grep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
 '
 
 test_expect_success 'git -c and --config-env work together' '
@@ -1488,55 +1642,49 @@
 	test_must_be_empty error
 '
 
-test_expect_success 'git config ignores pairs with zero count' '
-	test_must_fail env \
-		GIT_CONFIG_COUNT=0 \
-		GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
-		git config pair.one
-'
-
 test_expect_success 'git config ignores pairs exceeding count' '
 	GIT_CONFIG_COUNT=1 \
 		GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
 		GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
-		git config --get-regexp "pair.*" >actual &&
+		git config --get-regexp "pair.*" >actual 2>error &&
 	cat >expect <<-EOF &&
 	pair.one value
 	EOF
-	test_cmp expect actual
+	test_cmp expect actual &&
+	test_must_be_empty error
 '
 
 test_expect_success 'git config ignores pairs with zero count' '
 	test_must_fail env \
 		GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
-		git config pair.one >error &&
+		git config pair.one 2>error &&
 	test_must_be_empty error
 '
 
 test_expect_success 'git config ignores pairs with empty count' '
 	test_must_fail env \
 		GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
-		git config pair.one >error &&
+		git config pair.one 2>error &&
 	test_must_be_empty error
 '
 
 test_expect_success 'git config fails with invalid count' '
 	test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
-	test_i18ngrep "bogus count" error &&
+	test_grep "bogus count" error &&
 	test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
-	test_i18ngrep "too many entries" error
+	test_grep "too many entries" error
 '
 
 test_expect_success 'git config fails with missing config key' '
 	test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
 		git config --list 2>error &&
-	test_i18ngrep "missing config key" error
+	test_grep "missing config key" error
 '
 
 test_expect_success 'git config fails with missing config value' '
 	test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
 		git config --list 2>error &&
-	test_i18ngrep "missing config value" error
+	test_grep "missing config value" error
 '
 
 test_expect_success 'git config fails with invalid config pair key' '
@@ -1601,12 +1749,12 @@
 # malformed configuration files
 test_expect_success 'barf on syntax error' '
 	cat >.git/config <<-\EOF &&
-	# broken section line
+	# broken key=value
 	[section]
 	key garbage
 	EOF
-	test_must_fail git config --get section.key >actual 2>error &&
-	test_i18ngrep " line 3 " error
+	test_must_fail git config --get section.key 2>error &&
+	test_grep " line 3 " error
 '
 
 test_expect_success 'barf on incomplete section header' '
@@ -1615,18 +1763,18 @@
 	[section
 	key = value
 	EOF
-	test_must_fail git config --get section.key >actual 2>error &&
-	test_i18ngrep " line 2 " error
+	test_must_fail git config --get section.key 2>error &&
+	test_grep " line 2 " error
 '
 
 test_expect_success 'barf on incomplete string' '
 	cat >.git/config <<-\EOF &&
-	# broken section line
+	# broken value string
 	[section]
 	key = "value string
 	EOF
-	test_must_fail git config --get section.key >actual 2>error &&
-	test_i18ngrep " line 3 " error
+	test_must_fail git config --get section.key 2>error &&
+	test_grep " line 3 " error
 '
 
 test_expect_success 'urlmatch' '
@@ -1657,6 +1805,21 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'urlmatch with --show-scope' '
+	cat >.git/config <<-\EOF &&
+	[http "https://weak.example.com"]
+		sslVerify = false
+		cookieFile = /tmp/cookie.txt
+	EOF
+
+	cat >expect <<-EOF &&
+	local	http.cookiefile /tmp/cookie.txt
+	local	http.sslverify false
+	EOF
+	git config --get-urlmatch --show-scope HTTP https://weak.example.com >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'urlmatch favors more specific URLs' '
 	cat >.git/config <<-\EOF &&
 	[http "https://example.com/"]
@@ -1983,11 +2146,11 @@
 '
 
 test_expect_success 'set up custom config file' '
-	CUSTOM_CONFIG_FILE="custom.conf" &&
-	cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
+	cat >"custom.conf" <<-\EOF &&
 	[user]
 		custom = true
 	EOF
+	CUSTOM_CONFIG_FILE="$(test-tool path-utils real_path custom.conf)"
 '
 
 test_expect_success !MINGW 'set up custom config file with special name characters' '
@@ -2026,22 +2189,39 @@
 '
 
 test_expect_success '--show-origin blob' '
-	blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
-	cat >expect <<-EOF &&
-	blob:$blob	user.custom=true
-	EOF
-	git config --blob=$blob --show-origin --list >output &&
-	test_cmp expect output
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+		cat >expect <<-EOF &&
+		blob:$blob	user.custom=true
+		EOF
+		git config --blob=$blob --show-origin --list >output &&
+		test_cmp expect output
+	)
 '
 
 test_expect_success '--show-origin blob ref' '
-	cat >expect <<-\EOF &&
-	blob:main:custom.conf	user.custom=true
-	EOF
-	git add "$CUSTOM_CONFIG_FILE" &&
-	git commit -m "new config file" &&
-	git config --blob=main:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
-	test_cmp expect output
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		cat >expect <<-\EOF &&
+		blob:main:custom.conf	user.custom=true
+		EOF
+		cp "$CUSTOM_CONFIG_FILE" custom.conf &&
+		git add custom.conf &&
+		git commit -m "new config file" &&
+		git config --blob=main:custom.conf --show-origin --list >output &&
+		test_cmp expect output
+	)
+'
+
+test_expect_success '--show-origin with --default' '
+	git config --show-origin --default foo some.key >actual &&
+	echo "command line:	foo" >expect &&
+	test_cmp expect actual
 '
 
 test_expect_success '--show-scope with --list' '
@@ -2112,6 +2292,12 @@
 	test_cmp expect output
 '
 
+test_expect_success '--show-scope with --default' '
+	git config --show-scope --default foo some.key >actual &&
+	echo "command	foo" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'override global and system config' '
 	test_when_finished rm -f \"\$HOME\"/.gitconfig &&
 	cat >"$HOME"/.gitconfig <<-EOF &&
@@ -2228,17 +2414,17 @@
 
 test_expect_success 'non-identical modern --type specifiers are not allowed' '
 	test_must_fail git config --type=int --type=bool section.big 2>error &&
-	test_i18ngrep "only one type at a time" error
+	test_grep "only one type at a time" error
 '
 
 test_expect_success 'non-identical legacy --type specifiers are not allowed' '
 	test_must_fail git config --int --bool section.big 2>error &&
-	test_i18ngrep "only one type at a time" error
+	test_grep "only one type at a time" error
 '
 
 test_expect_success 'non-identical mixed --type specifiers are not allowed' '
 	test_must_fail git config --type=int --bool section.big 2>error &&
-	test_i18ngrep "only one type at a time" error
+	test_grep "only one type at a time" error
 '
 
 test_expect_success '--type allows valid type specifiers' '
@@ -2255,7 +2441,7 @@
 
 test_expect_success '--type rejects unknown specifiers' '
 	test_must_fail git config --type=nonsense section.foo 2>error &&
-	test_i18ngrep "unrecognized --type argument" error
+	test_grep "unrecognized --type argument" error
 '
 
 test_expect_success '--type=int requires at least one digit' '
@@ -2301,7 +2487,7 @@
 
 	# multiple matches => failure
 	test_must_fail git config --file=config abc.key three o+ 2>err &&
-	test_i18ngrep "has multiple values" err &&
+	test_grep "has multiple values" err &&
 
 	# multiple values, no match => add
 	git config --file=config abc.key three a+ &&
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 1b6437e..29cf8a9 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -52,6 +52,17 @@
 	test 2 = $(git config core.sharedrepository)
 '
 
+test_expect_success 'template cannot set core.bare' '
+	test_when_finished "rm -rf subdir" &&
+	test_when_finished "rm -rf templates" &&
+	test_config core.bare true &&
+	umask 0022 &&
+	mkdir -p templates/ &&
+	cp .git/config templates/config &&
+	git init --template=templates subdir &&
+	test_path_is_missing subdir/HEAD
+'
+
 test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
 	: > a1 &&
 	git add a1 &&
@@ -89,7 +100,7 @@
 		rm -f .git/info/refs &&
 		git update-server-info &&
 		actual="$(test_modebits .git/info/refs)" &&
-		verbose test "x$actual" = "x-$y"
+		test "x$actual" = "x-$y"
 
 	'
 
@@ -99,7 +110,7 @@
 		rm -f .git/info/refs &&
 		git update-server-info &&
 		actual="$(test_modebits .git/info/refs)" &&
-		verbose test "x$actual" = "x-$x"
+		test "x$actual" = "x-$x"
 
 	'
 
@@ -115,22 +126,6 @@
 	test_cmp expect actual
 '
 
-test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
-	umask 077 &&
-	git config core.sharedRepository group &&
-	git reflog expire --all &&
-	actual="$(ls -l .git/logs/refs/heads/main)" &&
-	case "$actual" in
-	-rw-rw-*)
-		: happy
-		;;
-	*)
-		echo Ooops, .git/logs/refs/heads/main is not 066x [$actual]
-		false
-		;;
-	esac
-'
-
 test_expect_success POSIXPERM 'forced modes' '
 	test_when_finished "rm -rf new" &&
 	mkdir -p templates/hooks &&
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 70389fa..42caa0d 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -9,10 +9,6 @@
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-	test_oid_cache <<-\EOF &&
-	version sha1:0
-	version sha256:1
-	EOF
 	cat >test.patch <<-\EOF &&
 	diff --git a/test.txt b/test.txt
 	new file mode 100644
@@ -28,7 +24,12 @@
 '
 
 test_expect_success 'gitdir selection on normal repos' '
-	test_oid version >expect &&
+	if test_have_prereq DEFAULT_REPO_FORMAT
+	then
+		echo 0
+	else
+		echo 1
+	fi >expect &&
 	git config core.repositoryformatversion >actual &&
 	git -C test config core.repositoryformatversion >actual2 &&
 	test_cmp expect actual &&
@@ -37,7 +38,7 @@
 
 test_expect_success 'gitdir selection on unsupported repo' '
 	# Make sure it would stop at test2, not trash
-	test_expect_code 1 git -C test2 config core.repositoryformatversion >actual
+	test_expect_code 1 git -C test2 config core.repositoryformatversion
 '
 
 test_expect_success 'gitdir not required mode' '
@@ -79,8 +80,13 @@
 
 while read outcome version extensions; do
 	test_expect_success "$outcome version=$version $extensions" "
-		mkconfig $version $extensions >.git/config &&
-		check_${outcome}
+		test_when_finished 'rm -rf extensions' &&
+		git init extensions &&
+		(
+			cd extensions &&
+			mkconfig $version $extensions >.git/config &&
+			check_${outcome}
+		)
 	"
 done <<\EOF
 allow 0
@@ -94,7 +100,8 @@
 EOF
 
 test_expect_success 'precious-objects allowed' '
-	mkconfig 1 preciousObjects >.git/config &&
+	git config core.repositoryFormatVersion 1 &&
+	git config extensions.preciousObjects 1 &&
 	check_allow
 '
 
diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh
index 0a7099d..b9852fe 100755
--- a/t/t1307-config-blob.sh
+++ b/t/t1307-config-blob.sh
@@ -63,7 +63,7 @@
 	git commit -m broken &&
 
 	test_must_fail git config --blob=HEAD:config some.value 2>err &&
-	test_i18ngrep "HEAD:config" err
+	test_grep "HEAD:config" err
 '
 
 test_expect_success 'can parse blob ending with CR' '
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
index b38e158..3bfec07 100755
--- a/t/t1308-config-set.sh
+++ b/t/t1308-config-set.sh
@@ -58,6 +58,8 @@
 		skin = false
 		nose = 1
 		horns
+	[value]
+		less
 	EOF
 '
 
@@ -116,10 +118,53 @@
 	check_config get_value case.baz "hask"
 '
 
+test_expect_success 'return value for an existing key' '
+	test-tool config get lamb.chop >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err
+'
+
+test_expect_success 'return value for value-less key' '
+	test-tool config get value.less >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err
+'
+
+test_expect_success 'return value for a missing key' '
+	cat >expect <<-\EOF &&
+	Value not found for "missing.key"
+	EOF
+	test_expect_code 1 test-tool config get missing.key >actual 2>err &&
+	test_cmp actual expect &&
+	test_must_be_empty err
+'
+
+test_expect_success 'return value for a bad key: CONFIG_INVALID_KEY' '
+	cat >expect <<-\EOF &&
+	Key "fails.iskeychar.-" is invalid
+	EOF
+	test_expect_code 1 test-tool config get fails.iskeychar.- >actual 2>err &&
+	test_cmp actual expect &&
+	test_must_be_empty out
+'
+
+test_expect_success 'return value for a bad key: CONFIG_NO_SECTION_OR_NAME' '
+	cat >expect <<-\EOF &&
+	Key "keynosection" has no section
+	EOF
+	test_expect_code 1 test-tool config get keynosection >actual 2>err &&
+	test_cmp actual expect &&
+	test_must_be_empty out
+'
+
 test_expect_success 'find integer value for a key' '
 	check_config get_int lamb.chop 65
 '
 
+test_expect_success 'parse integer value during iteration' '
+	check_config git_config_int lamb.chop 65
+'
+
 test_expect_success 'find string value for a key' '
 	check_config get_string case.baz hask &&
 	check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\""
@@ -127,13 +172,18 @@
 
 test_expect_success 'check line error when NULL string is queried' '
 	test_expect_code 128 test-tool config get_string case.foo 2>result &&
-	test_i18ngrep "fatal: .*case\.foo.*\.git/config.*line 7" result
+	test_grep "fatal: .*case\.foo.*\.git/config.*line 7" result
 '
 
 test_expect_success 'find integer if value is non parse-able' '
 	check_config expect_code 128 get_int lamb.head
 '
 
+test_expect_success 'non parse-able integer value during iteration' '
+	check_config expect_code 128 git_config_int lamb.head 2>result &&
+	grep "fatal: bad numeric config value .* in file \.git/config" result
+'
+
 test_expect_success 'find bool value for the entered key' '
 	check_config get_bool goat.head 1 &&
 	check_config get_bool goat.skin 0 &&
@@ -146,6 +196,71 @@
 	check_config get_value_multi case.baz sam bat hask
 '
 
+test_NULL_in_multi () {
+	local op="$1" &&
+	local file="$2" &&
+
+	test_expect_success "$op: NULL value in config${file:+ in $file}" '
+		config="$file" &&
+		if test -z "$config"
+		then
+			config=.git/config &&
+			test_when_finished "mv $config.old $config" &&
+			mv "$config" "$config".old
+		fi &&
+
+		# Value-less in the middle of a list
+		cat >"$config" <<-\EOF &&
+		[a]key=x
+		[a]key
+		[a]key=y
+		EOF
+		case "$op" in
+		*_multi)
+			cat >expect <<-\EOF
+			x
+			(NULL)
+			y
+			EOF
+			;;
+		*)
+			cat >expect <<-\EOF
+			y
+			EOF
+			;;
+		esac &&
+		test-tool config "$op" a.key $file >actual &&
+		test_cmp expect actual &&
+
+		# Value-less at the end of a least
+		cat >"$config" <<-\EOF &&
+		[a]key=x
+		[a]key=y
+		[a]key
+		EOF
+		case "$op" in
+		*_multi)
+			cat >expect <<-\EOF
+			x
+			y
+			(NULL)
+			EOF
+			;;
+		*)
+			cat >expect <<-\EOF
+			(NULL)
+			EOF
+			;;
+		esac &&
+		test-tool config "$op" a.key $file >actual &&
+		test_cmp expect actual
+	'
+}
+
+test_NULL_in_multi "get_value_multi"
+test_NULL_in_multi "configset_get_value" "my.config"
+test_NULL_in_multi "configset_get_value_multi" "my.config"
+
 test_expect_success 'find value from a configset' '
 	cat >config2 <<-\EOF &&
 	[case]
@@ -207,7 +322,7 @@
 	cp .git/config .git/config.old &&
 	test_when_finished "mv .git/config.old .git/config" &&
 	echo "[" >>.git/config &&
-	echo "fatal: bad config line 34 in file .git/config" >expect &&
+	echo "fatal: bad config line 36 in file .git/config" >expect &&
 	test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
 	test_cmp expect actual
 '
@@ -227,14 +342,14 @@
 		br
 	EOF
 	test_expect_code 128 git br 2>result &&
-	test_i18ngrep "missing value for .alias\.br" result &&
-	test_i18ngrep "fatal: .*\.git/config" result &&
-	test_i18ngrep "fatal: .*line 2" result
+	test_grep "missing value for .alias\.br" result &&
+	test_grep "fatal: .*\.git/config" result &&
+	test_grep "fatal: .*line 2" result
 '
 
 test_expect_success 'error on modifying repo config without repo' '
 	nongit test_must_fail git config a.b c 2>err &&
-	test_i18ngrep "not in a git directory" err
+	test_grep "not in a git directory" err
 '
 
 cmdline_config="'foo.bar=from-cmdline'"
diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh
index 537435b..523aa99 100755
--- a/t/t1309-early-config.sh
+++ b/t/t1309-early-config.sh
@@ -78,7 +78,7 @@
 
 test_expect_success 'ignore .git/ with incompatible repository version' '
 	test_with_config "[core]repositoryformatversion = 999999" 2>err &&
-	test_i18ngrep "warning:.* Expected git repo version <= [1-9]" err
+	test_grep "warning:.* Expected git repo version <= [1-9]" err
 '
 
 test_expect_failure 'ignore .git/ with invalid repository version' '
diff --git a/t/t1310-config-default.sh b/t/t1310-config-default.sh
index 09b10c1..1a90d31 100755
--- a/t/t1310-config-default.sh
+++ b/t/t1310-config-default.sh
@@ -26,12 +26,12 @@
 test_expect_success 'dies when --default cannot be parsed' '
 	test_must_fail git config -f config --type=expiry-date --default=x --get \
 		not.a.section 2>error &&
-	test_i18ngrep "failed to format default config value" error
+	test_grep "failed to format default config value" error
 '
 
 test_expect_success 'does not allow --default without --get' '
 	test_must_fail git config --default=quux --unset a.section >output 2>&1 &&
-	test_i18ngrep "\-\-default is only applicable to" output
+	test_grep "\-\-default is only applicable to" output
 '
 
 test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index cf58cf0..ec3443c 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -9,8 +9,6 @@
 Z=$ZERO_OID
 
 m=refs/heads/main
-n_dir=refs/heads/gu
-n=$n_dir/fixes
 outside=refs/foo
 bare=bare-repo
 
@@ -62,10 +60,10 @@
 	test_must_fail git show-ref --verify -q $m
 '
 
-test_expect_success "fail to create $n" '
-	test_when_finished "rm -f .git/$n_dir" &&
-	touch .git/$n_dir &&
-	test_must_fail git update-ref $n $A
+test_expect_success "fail to create $n due to file/directory conflict" '
+	test_when_finished "git update-ref -d refs/heads/gu" &&
+	git update-ref refs/heads/gu $A &&
+	test_must_fail git update-ref refs/heads/gu/fixes $A
 '
 
 test_expect_success "create $m (by HEAD)" '
@@ -92,7 +90,8 @@
 	git symbolic-ref HEAD $m &&
 	git update-ref -m delete-$m -d $m &&
 	test_must_fail git show-ref --verify -q $m &&
-	grep "delete-$m$" .git/logs/HEAD
+	test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+	grep "delete-$m$" actual
 '
 
 test_expect_success "deleting by HEAD adds message to HEAD's log" '
@@ -101,7 +100,8 @@
 	git symbolic-ref HEAD $m &&
 	git update-ref -m delete-by-head -d HEAD &&
 	test_must_fail git show-ref --verify -q $m &&
-	grep "delete-by-head$" .git/logs/HEAD
+	test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+	grep "delete-by-head$" actual
 '
 
 test_expect_success 'update-ref does not create reflogs by default' '
@@ -132,7 +132,7 @@
 
 test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '
 	test_when_finished "git -C $bare config --unset core.logAllRefUpdates && \
-		rm $bare/logs/$m" &&
+		test-tool ref-store main delete-reflog $m" &&
 	git -C $bare config core.logAllRefUpdates true &&
 	git -C $bare update-ref $m $bareB &&
 	git -C $bare rev-parse $bareB >expect &&
@@ -221,27 +221,27 @@
 '
 
 test_expect_success 'update-ref -d is not confused by self-reference' '
+	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
 	git symbolic-ref refs/heads/self refs/heads/self &&
-	test_when_finished "rm -f .git/refs/heads/self" &&
-	test_path_is_file .git/refs/heads/self &&
+	git symbolic-ref --no-recurse refs/heads/self &&
 	test_must_fail git update-ref -d refs/heads/self &&
-	test_path_is_file .git/refs/heads/self
+	git symbolic-ref --no-recurse refs/heads/self
 '
 
 test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
 	git symbolic-ref refs/heads/self refs/heads/self &&
-	test_when_finished "rm -f .git/refs/heads/self" &&
-	test_path_is_file .git/refs/heads/self &&
+	git symbolic-ref --no-recurse refs/heads/self &&
 	git update-ref --no-deref -d refs/heads/self &&
 	test_must_fail git show-ref --verify -q refs/heads/self
 '
 
-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '
 	>.git/refs/heads/bad &&
 	test_when_finished "rm -f .git/refs/heads/bad" &&
 	git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
 	test_when_finished "git update-ref -d refs/heads/ref-to-bad" &&
-	test_path_is_file .git/refs/heads/ref-to-bad &&
+	git symbolic-ref --no-recurse refs/heads/ref-to-bad &&
 	git update-ref --no-deref -d refs/heads/ref-to-bad &&
 	test_must_fail git show-ref --verify -q refs/heads/ref-to-bad
 '
@@ -265,7 +265,10 @@
 	! test $B = $(git show-ref -s --verify $m)
 '
 
-rm -f .git/logs/refs/heads/main
+test_expect_success "clean up reflog" '
+	test-tool ref-store main delete-reflog $m
+'
+
 test_expect_success "create $m (logged by touch)" '
 	test_config core.logAllRefUpdates false &&
 	GIT_COMMITTER_DATE="2005-05-26 23:30" \
@@ -285,40 +288,13 @@
 	test $A = $(git show-ref -s --verify $m)
 '
 
-test_expect_success 'empty directory removal' '
-	git branch d1/d2/r1 HEAD &&
-	git branch d1/r2 HEAD &&
-	test_path_is_file .git/refs/heads/d1/d2/r1 &&
-	test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
-	git branch -d d1/d2/r1 &&
-	test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
-	test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
-	test_path_is_file .git/refs/heads/d1/r2 &&
-	test_path_is_file .git/logs/refs/heads/d1/r2
-'
-
-test_expect_success 'symref empty directory removal' '
-	git branch e1/e2/r1 HEAD &&
-	git branch e1/r2 HEAD &&
-	git checkout e1/e2/r1 &&
-	test_when_finished "git checkout main" &&
-	test_path_is_file .git/refs/heads/e1/e2/r1 &&
-	test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
-	git update-ref -d HEAD &&
-	test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
-	test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
-	test_path_is_file .git/refs/heads/e1/r2 &&
-	test_path_is_file .git/logs/refs/heads/e1/r2 &&
-	test_path_is_file .git/logs/HEAD
-'
-
 cat >expect <<EOF
 $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	Initial Creation
 $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000	Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
 test_expect_success "verifying $m's log (logged by touch)" '
-	test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+	test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
 	test-tool ref-store main for-each-reflog-ent $m >actual &&
 	test_cmp actual expect
 '
@@ -348,20 +324,34 @@
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
 EOF
 test_expect_success "verifying $m's log (logged by config)" '
-	test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+	test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
 	test-tool ref-store main for-each-reflog-ent $m >actual &&
 	test_cmp actual expect
 '
 
 test_expect_success 'set up for querying the reflog' '
+	git update-ref -d $m &&
+	test-tool ref-store main delete-reflog $m &&
+
+	GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $C &&
+	GIT_COMMITTER_DATE="1117150350 -0500" git update-ref $m $A &&
+	GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+	GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+	GIT_COMMITTER_DATE="1117150980 -0500" git update-ref $m $E &&
 	git update-ref $m $D &&
-	cat >.git/logs/$m <<-EOF
+	# Delete the last reflog entry so that the tip of m and the reflog for
+	# it disagree.
+	git reflog delete $m@{0} &&
+
+	cat >expect <<-EOF &&
 	$Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
 	$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
 	$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
-	$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
-	$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+	$B $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+	$F $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
 	EOF
+	test-tool ref-store main for-each-reflog-ent $m >actual &&
+	test_cmp expect actual
 '
 
 ed="Thu, 26 May 2005 18:32:00 -0500"
@@ -409,13 +399,12 @@
 	test_when_finished "rm -f o e" &&
 	git rev-parse --verify "main@{2005-05-26 23:33:01}" >o 2>e &&
 	echo "$B" >expect &&
-	test_cmp expect o &&
-	test_i18ngrep -F "warning: log for ref $m has gap after $gd" e
+	test_cmp expect o
 '
 test_expect_success 'Query "main@{2005-05-26 23:38:00}" (middle of history)' '
 	test_when_finished "rm -f o e" &&
 	git rev-parse --verify "main@{2005-05-26 23:38:00}" >o 2>e &&
-	echo "$Z" >expect &&
+	echo "$F" >expect &&
 	test_cmp expect o &&
 	test_must_be_empty e
 '
@@ -431,10 +420,27 @@
 	git rev-parse --verify "main@{2005-05-28}" >o 2>e &&
 	echo "$D" >expect &&
 	test_cmp expect o &&
-	test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e
+	test_grep -F "warning: log for ref $m unexpectedly ended on $ld" e
 '
 
-rm -f .git/$m .git/logs/$m expect
+rm -f expect
+git update-ref -d $m
+
+test_expect_success 'query reflog with gap' '
+	test_when_finished "git update-ref -d $m" &&
+
+	GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $A &&
+	GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+	GIT_COMMITTER_DATE="1117150480 -0500" git update-ref $m $C &&
+	GIT_COMMITTER_DATE="1117150580 -0500" git update-ref $m $D &&
+	GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+	git reflog delete $m@{2} &&
+
+	git rev-parse --verify "main@{2005-05-26 23:33:01}" >actual 2>stderr &&
+	echo "$B" >expect &&
+	test_cmp expect actual &&
+	test_grep -F "warning: log for ref $m has gap after $gd" stderr
+'
 
 test_expect_success 'creating initial files' '
 	test_when_finished rm -f M &&
@@ -486,57 +492,57 @@
 test_expect_success 'given old value for missing pseudoref, do not create' '
 	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
 	test_must_fail git rev-parse PSEUDOREF &&
-	test_i18ngrep "unable to resolve reference" err
+	test_grep "unable to resolve reference" err
 '
 
 test_expect_success 'create pseudoref' '
 	git update-ref PSEUDOREF $A &&
-	test $A = $(git rev-parse PSEUDOREF)
+	test $A = $(git show-ref -s --verify PSEUDOREF)
 '
 
 test_expect_success 'overwrite pseudoref with no old value given' '
 	git update-ref PSEUDOREF $B &&
-	test $B = $(git rev-parse PSEUDOREF)
+	test $B = $(git show-ref -s --verify PSEUDOREF)
 '
 
 test_expect_success 'overwrite pseudoref with correct old value' '
 	git update-ref PSEUDOREF $C $B &&
-	test $C = $(git rev-parse PSEUDOREF)
+	test $C = $(git show-ref -s --verify PSEUDOREF)
 '
 
 test_expect_success 'do not overwrite pseudoref with wrong old value' '
 	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
-	test $C = $(git rev-parse PSEUDOREF) &&
-	test_i18ngrep "cannot lock ref.*expected" err
+	test $C = $(git show-ref -s --verify PSEUDOREF) &&
+	test_grep "cannot lock ref.*expected" err
 '
 
 test_expect_success 'delete pseudoref' '
 	git update-ref -d PSEUDOREF &&
-	test_must_fail git rev-parse PSEUDOREF
+	test_must_fail git show-ref -s --verify PSEUDOREF
 '
 
 test_expect_success 'do not delete pseudoref with wrong old value' '
 	git update-ref PSEUDOREF $A &&
 	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
-	test $A = $(git rev-parse PSEUDOREF) &&
-	test_i18ngrep "cannot lock ref.*expected" err
+	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+	test_grep "cannot lock ref.*expected" err
 '
 
 test_expect_success 'delete pseudoref with correct old value' '
 	git update-ref -d PSEUDOREF $A &&
-	test_must_fail git rev-parse PSEUDOREF
+	test_must_fail git show-ref -s --verify PSEUDOREF
 '
 
 test_expect_success 'create pseudoref with old OID zero' '
 	git update-ref PSEUDOREF $A $Z &&
-	test $A = $(git rev-parse PSEUDOREF)
+	test $A = $(git show-ref -s --verify PSEUDOREF)
 '
 
 test_expect_success 'do not overwrite pseudoref with old OID zero' '
 	test_when_finished git update-ref -d PSEUDOREF &&
 	test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
-	test $A = $(git rev-parse PSEUDOREF) &&
-	test_i18ngrep "already exists" err
+	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+	test_grep "already exists" err
 '
 
 # Test --stdin
@@ -556,7 +562,7 @@
 
 test_expect_success '-z fails without --stdin' '
 	test_must_fail git update-ref -z $m $m $m 2>err &&
-	test_i18ngrep "usage: git update-ref" err
+	test_grep "usage: git update-ref" err
 '
 
 test_expect_success 'stdin works with no input' '
@@ -616,7 +622,7 @@
 test_expect_success 'stdin fails create with no new value' '
 	echo "create $a" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $a: missing <newvalue>" err
+	grep "fatal: create $a: missing <new-oid>" err
 '
 
 test_expect_success 'stdin fails create with too many arguments' '
@@ -634,7 +640,7 @@
 test_expect_success 'stdin fails update with no new value' '
 	echo "update $a" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: update $a: missing <newvalue>" err
+	grep "fatal: update $a: missing <new-oid>" err
 '
 
 test_expect_success 'stdin fails update with too many arguments' '
@@ -674,7 +680,7 @@
 	create $a $m
 	EOF
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
+	test_grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
 '
 
 test_expect_success 'stdin create ref works' '
@@ -759,21 +765,21 @@
 test_expect_success 'stdin update ref fails with bad old value' '
 	echo "update $c $m does-not-exist" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+	grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with bad new value' '
 	echo "create $c does-not-exist" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+	grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with zero new value' '
 	echo "create $c " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $c: zero <newvalue>" err &&
+	grep "fatal: create $c: zero <new-oid>" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -797,7 +803,7 @@
 test_expect_success 'stdin delete ref fails with zero old value' '
 	echo "delete $a " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: delete $a: zero <oldvalue>" err &&
+	grep "fatal: delete $a: zero <old-oid>" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
@@ -1021,7 +1027,7 @@
 test_expect_success 'stdin -z fails create with no new value' '
 	printf $F "create $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
+	grep "fatal: create $a: unexpected end of input when reading <new-oid>" err
 '
 
 test_expect_success 'stdin -z fails create with too many arguments' '
@@ -1039,27 +1045,27 @@
 test_expect_success 'stdin -z fails update with too few args' '
 	printf $F "update $a" "$m" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+	grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z emits warning with empty new value' '
 	git update-ref $a $m &&
 	printf $F "update $a" "" "" >stdin &&
 	git update-ref -z --stdin <stdin 2>err &&
-	grep "warning: update $a: missing <newvalue>, treating as zero" err &&
+	grep "warning: update $a: missing <new-oid>, treating as zero" err &&
 	test_must_fail git rev-parse --verify -q $a
 '
 
 test_expect_success 'stdin -z fails update with no new value' '
 	printf $F "update $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
+	grep "fatal: update $a: unexpected end of input when reading <new-oid>" err
 '
 
 test_expect_success 'stdin -z fails update with no old value' '
 	printf $F "update $a" "$m" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+	grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z fails update with too many arguments' '
@@ -1077,7 +1083,7 @@
 test_expect_success 'stdin -z fails delete with no old value' '
 	printf $F "delete $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
+	grep "fatal: delete $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z fails delete with too many arguments' '
@@ -1095,7 +1101,7 @@
 test_expect_success 'stdin -z fails verify with no old value' '
 	printf $F "verify $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
+	grep "fatal: verify $a: unexpected end of input when reading <old-oid>" err
 '
 
 test_expect_success 'stdin -z fails option with unknown name' '
@@ -1107,7 +1113,7 @@
 test_expect_success 'stdin -z fails with duplicate refs' '
 	printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
+	test_grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
 '
 
 test_expect_success 'stdin -z create ref works' '
@@ -1154,7 +1160,7 @@
 test_expect_success 'stdin -z update ref fails with bad old value' '
 	printf $F "update $c" "$m" "does-not-exist" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+	grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -1172,14 +1178,14 @@
 	git update-ref -d "$c" &&
 	printf $F "create $c" "does-not-exist" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+	grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin -z create ref fails with empty new value' '
 	printf $F "create $c" "" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $c: missing <newvalue>" err &&
+	grep "fatal: create $c: missing <new-oid>" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -1203,7 +1209,7 @@
 test_expect_success 'stdin -z delete ref fails with zero old value' '
 	printf $F "delete $a" "$Z" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete $a: zero <oldvalue>" err &&
+	grep "fatal: delete $a: zero <old-oid>" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
@@ -1338,7 +1344,7 @@
 	update HEAD $B
 	EOF
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	test_i18ngrep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
+	test_grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
 	echo "refs/heads/target1" >expect &&
 	git symbolic-ref HEAD >actual &&
 	test_cmp expect actual &&
@@ -1355,7 +1361,7 @@
 	update refs/heads/symref2 $B
 	EOF
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	test_i18ngrep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
+	test_grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
 	echo "refs/heads/target2" >expect &&
 	git symbolic-ref refs/heads/symref2 >actual &&
 	test_cmp expect actual &&
@@ -1568,6 +1574,7 @@
 	EOF
 	git update-ref --stdin <stdin >actual &&
 	printf "%s: ok\n" start commit start commit >expect &&
+	test_cmp expect actual &&
 	test_must_fail git show-ref --verify refs/heads/create-and-delete
 '
 
@@ -1595,6 +1602,8 @@
 	commit
 	EOF
 	test_must_fail git update-ref --stdin <stdin >actual &&
+	printf "%s: ok\n" start >expect &&
+	test_cmp expect actual &&
 	test_must_fail git show-ref --verify refs/heads/restart
 '
 
@@ -1632,13 +1641,4 @@
 	test_cmp expected actual
 '
 
-test_expect_success 'directory not created deleting packed ref' '
-	git branch d1/d2/r1 HEAD &&
-	git pack-refs --all &&
-	test_path_is_missing .git/refs/heads/d1/d2 &&
-	git update-ref -d refs/heads/d1/d2/r1 &&
-	test_path_is_missing .git/refs/heads/d1/d2 &&
-	test_path_is_missing .git/refs/heads/d1
-'
-
 test_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index be23be3..5c60d6f 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -33,7 +33,8 @@
 reset_to_sane
 
 test_expect_success 'symbolic-ref refuses bare sha1' '
-	test_must_fail git symbolic-ref HEAD $(git rev-parse HEAD)
+	rev=$(git rev-parse HEAD) &&
+	test_must_fail git symbolic-ref HEAD "$rev"
 '
 
 reset_to_sane
@@ -105,9 +106,8 @@
 '
 
 test_expect_success 'symbolic-ref reports failure in exit code' '
-	test_when_finished "rm -f .git/HEAD.lock" &&
-	>.git/HEAD.lock &&
-	test_must_fail git symbolic-ref HEAD refs/heads/whatever
+	# Create d/f conflict to simulate failure.
+	test_must_fail git symbolic-ref refs/heads refs/heads/foo
 '
 
 test_expect_success 'symbolic-ref writes reflog entry' '
@@ -170,8 +170,8 @@
 '
 
 test_expect_success 'symbolic-ref allows top-level target for non-HEAD' '
-	git symbolic-ref refs/heads/top-level FETCH_HEAD &&
-	git update-ref FETCH_HEAD HEAD &&
+	git symbolic-ref refs/heads/top-level ORIG_HEAD &&
+	git update-ref ORIG_HEAD HEAD &&
 	test_cmp_rev top-level HEAD
 '
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 9252a58..33fb7a3 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -174,6 +174,14 @@
 	test_must_be_empty actual
 '
 
+test_expect_success 'show-ref --verify pseudorefs' '
+	git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID &&
+	test_when_finished "git update-ref -d CHERRY_PICK_HEAD" &&
+	git show-ref -s --verify HEAD >actual &&
+	git show-ref -s --verify CHERRY_PICK_HEAD >expect &&
+	test_cmp actual expect
+'
+
 test_expect_success 'show-ref --verify with dangling ref' '
 	sha1_file() {
 		echo "$*" | sed "s#..#.git/objects/&/#"
@@ -196,4 +204,86 @@
 	)
 '
 
+test_expect_success 'show-ref sub-modes are mutually exclusive' '
+	test_must_fail git show-ref --verify --exclude-existing 2>err &&
+	grep "verify" err &&
+	grep "exclude-existing" err &&
+	grep "cannot be used together" err &&
+
+	test_must_fail git show-ref --verify --exists 2>err &&
+	grep "verify" err &&
+	grep "exists" err &&
+	grep "cannot be used together" err &&
+
+	test_must_fail git show-ref --exclude-existing --exists 2>err &&
+	grep "exclude-existing" err &&
+	grep "exists" err &&
+	grep "cannot be used together" err
+'
+
+test_expect_success '--exists with existing reference' '
+	git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+'
+
+test_expect_success '--exists with missing reference' '
+	test_expect_code 2 git show-ref --exists refs/heads/does-not-exist
+'
+
+test_expect_success '--exists does not use DWIM' '
+	test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
+	grep "reference does not exist" err
+'
+
+test_expect_success '--exists with HEAD' '
+	git show-ref --exists HEAD
+'
+
+test_expect_success '--exists with bad reference name' '
+	test_when_finished "git update-ref -d refs/heads/bad...name" &&
+	new_oid=$(git rev-parse HEAD) &&
+	test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+	git show-ref --exists refs/heads/bad...name
+'
+
+test_expect_success '--exists with arbitrary symref' '
+	test_when_finished "git symbolic-ref -d refs/symref" &&
+	git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
+	git show-ref --exists refs/symref
+'
+
+test_expect_success '--exists with dangling symref' '
+	test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
+	git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+	git show-ref --exists refs/heads/dangling
+'
+
+test_expect_success '--exists with nonexistent object ID' '
+	test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+	git show-ref --exists refs/heads/missing-oid
+'
+
+test_expect_success '--exists with non-commit object' '
+	tree_oid=$(git rev-parse HEAD^{tree}) &&
+	test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+	git show-ref --exists refs/heads/tree
+'
+
+test_expect_success '--exists with directory fails with generic error' '
+	cat >expect <<-EOF &&
+	error: reference does not exist
+	EOF
+	test_expect_code 2 git show-ref --exists refs/heads 2>err &&
+	test_cmp expect err
+'
+
+test_expect_success '--exists with non-existent special ref' '
+	test_expect_code 2 git show-ref --exists FETCH_HEAD
+'
+
+test_expect_success '--exists with existing special ref' '
+	test_when_finished "rm .git/FETCH_HEAD" &&
+	git rev-parse HEAD >.git/FETCH_HEAD &&
+	git show-ref --exists FETCH_HEAD
+'
+
 test_done
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index b5606d9..98e9158 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -29,7 +29,7 @@
 	fi &&
 	printf "create $prefix/%s $C\n" $create >input &&
 	test_must_fail git update-ref --stdin <input 2>output.err &&
-	test_i18ngrep -F "$error" output.err &&
+	test_grep -F "$error" output.err &&
 	git for-each-ref $prefix >actual &&
 	test_cmp unchanged actual
 }
@@ -92,9 +92,6 @@
 	else
 		delname="$delref"
 	fi &&
-	cat >expected-err <<-EOF &&
-	fatal: cannot lock ref $SQ$addname$SQ: $SQ$delref$SQ exists; cannot create $SQ$addref$SQ
-	EOF
 	$pack &&
 	if $add_del
 	then
@@ -103,7 +100,7 @@
 		printf "%s\n" "delete $delname" "create $addname $D"
 	fi >commands &&
 	test_must_fail git update-ref --stdin <commands 2>output.err &&
-	test_cmp expected-err output.err &&
+	grep "fatal:\( cannot lock ref $SQ$addname$SQ:\)\? $SQ$delref$SQ exists; cannot create $SQ$addref$SQ" output.err &&
 	printf "%s\n" "$C $delref" >expected-refs &&
 	git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
 	test_cmp expected-refs actual-refs
@@ -191,141 +188,69 @@
 
 '
 
-test_expect_success REFFILES 'empty directory should not fool rev-parse' '
-	prefix=refs/e-rev-parse &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	echo "$C" >expected &&
-	git rev-parse $prefix/foo >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success REFFILES 'empty directory should not fool for-each-ref' '
-	prefix=refs/e-for-each-ref &&
-	git update-ref $prefix/foo $C &&
-	git for-each-ref $prefix >expected &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	git for-each-ref $prefix >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success REFFILES 'empty directory should not fool create' '
-	prefix=refs/e-create &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	printf "create %s $C\n" $prefix/foo |
-	git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool verify' '
-	prefix=refs/e-verify &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	printf "verify %s $C\n" $prefix/foo |
-	git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 1-arg update' '
-	prefix=refs/e-update-1 &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	printf "update %s $D\n" $prefix/foo |
-	git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 2-arg update' '
-	prefix=refs/e-update-2 &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	printf "update %s $D $C\n" $prefix/foo |
-	git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 0-arg delete' '
-	prefix=refs/e-delete-0 &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	printf "delete %s\n" $prefix/foo |
-	git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 1-arg delete' '
-	prefix=refs/e-delete-1 &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	mkdir -p .git/$prefix/foo/bar/baz &&
-	printf "delete %s $C\n" $prefix/foo |
-	git update-ref --stdin
-'
-
-test_expect_success REFFILES 'D/F conflict prevents add long + delete short' '
+test_expect_success 'D/F conflict prevents add long + delete short' '
 	df_test refs/df-al-ds --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents add short + delete long' '
+test_expect_success 'D/F conflict prevents add short + delete long' '
 	df_test refs/df-as-dl --add-del foo foo/bar
 '
 
-test_expect_success REFFILES 'D/F conflict prevents delete long + add short' '
+test_expect_success 'D/F conflict prevents delete long + add short' '
 	df_test refs/df-dl-as --del-add foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents delete short + add long' '
+test_expect_success 'D/F conflict prevents delete short + add long' '
 	df_test refs/df-ds-al --del-add foo foo/bar
 '
 
-test_expect_success REFFILES 'D/F conflict prevents add long + delete short packed' '
+test_expect_success 'D/F conflict prevents add long + delete short packed' '
 	df_test refs/df-al-dsp --pack --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents add short + delete long packed' '
+test_expect_success 'D/F conflict prevents add short + delete long packed' '
 	df_test refs/df-as-dlp --pack --add-del foo foo/bar
 '
 
-test_expect_success REFFILES 'D/F conflict prevents delete long packed + add short' '
+test_expect_success 'D/F conflict prevents delete long packed + add short' '
 	df_test refs/df-dlp-as --pack --del-add foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents delete short packed + add long' '
+test_expect_success 'D/F conflict prevents delete short packed + add long' '
 	df_test refs/df-dsp-al --pack --del-add foo foo/bar
 '
 
 # Try some combinations involving symbolic refs...
 
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short' '
 	df_test refs/df-ial-ds --sym-add --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' '
 	df_test refs/df-ial-ids --sym-add --sym-del --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents indirect add short + indirect delete long' '
+test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' '
 	df_test refs/df-ias-idl --sym-add --sym-del --add-del foo foo/bar
 '
 
-test_expect_success REFFILES 'D/F conflict prevents indirect delete long + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' '
 	df_test refs/df-idl-ias --sym-add --sym-del --del-add foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short packed' '
 	df_test refs/df-ial-dsp --sym-add --pack --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' '
 	df_test refs/df-ial-idsp --sym-add --sym-del --pack --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents add long + indirect delete short packed' '
 	df_test refs/df-al-idsp --sym-del --pack --add-del foo/bar foo
 '
 
-test_expect_success REFFILES 'D/F conflict prevents indirect delete long packed + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' '
 	df_test refs/df-idlp-ias --sym-add --sym-del --pack --del-add foo/bar foo
 '
 
@@ -468,170 +393,4 @@
 	test_cmp expected output.err
 '
 
-test_expect_success REFFILES 'non-empty directory blocks create' '
-	prefix=refs/ne-create &&
-	mkdir -p .git/$prefix/foo/bar &&
-	: >.git/$prefix/foo/bar/baz.lock &&
-	test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
-	EOF
-	printf "%s\n" "update $prefix/foo $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
-	EOF
-	printf "%s\n" "update $prefix/foo $D $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'broken reference blocks create' '
-	prefix=refs/broken-create &&
-	mkdir -p .git/$prefix &&
-	echo "gobbledigook" >.git/$prefix/foo &&
-	test_when_finished "rm -f .git/$prefix/foo" &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
-	EOF
-	printf "%s\n" "update $prefix/foo $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
-	EOF
-	printf "%s\n" "update $prefix/foo $D $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'non-empty directory blocks indirect create' '
-	prefix=refs/ne-indirect-create &&
-	git symbolic-ref $prefix/symref $prefix/foo &&
-	mkdir -p .git/$prefix/foo/bar &&
-	: >.git/$prefix/foo/bar/baz.lock &&
-	test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
-	EOF
-	printf "%s\n" "update $prefix/symref $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
-	EOF
-	printf "%s\n" "update $prefix/symref $D $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'broken reference blocks indirect create' '
-	prefix=refs/broken-indirect-create &&
-	git symbolic-ref $prefix/symref $prefix/foo &&
-	echo "gobbledigook" >.git/$prefix/foo &&
-	test_when_finished "rm -f .git/$prefix/foo" &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
-	EOF
-	printf "%s\n" "update $prefix/symref $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err &&
-	cat >expected <<-EOF &&
-	fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
-	EOF
-	printf "%s\n" "update $prefix/symref $D $C" |
-	test_must_fail git update-ref --stdin 2>output.err &&
-	test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'no bogus intermediate values during delete' '
-	prefix=refs/slow-transaction &&
-	# Set up a reference with differing loose and packed versions:
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	git update-ref $prefix/foo $D &&
-	git for-each-ref $prefix >unchanged &&
-	# Now try to update the reference, but hold the `packed-refs` lock
-	# for a while to see what happens while the process is blocked:
-	: >.git/packed-refs.lock &&
-	test_when_finished "rm -f .git/packed-refs.lock" &&
-	{
-		# Note: the following command is intentionally run in the
-		# background. We increase the timeout so that `update-ref`
-		# attempts to acquire the `packed-refs` lock for much longer
-		# than it takes for us to do the check then delete it:
-		git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
-	} &&
-	pid2=$! &&
-	# Give update-ref plenty of time to get to the point where it tries
-	# to lock packed-refs:
-	sleep 1 &&
-	# Make sure that update-ref did not complete despite the lock:
-	kill -0 $pid2 &&
-	# Verify that the reference still has its old value:
-	sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
-	case "$sha1" in
-	$D)
-		# This is what we hope for; it means that nothing
-		# user-visible has changed yet.
-		: ;;
-	undefined)
-		# This is not correct; it means the deletion has happened
-		# already even though update-ref should not have been
-		# able to acquire the lock yet.
-		echo "$prefix/foo deleted prematurely" &&
-		break
-		;;
-	$C)
-		# This value should never be seen. Probably the loose
-		# reference has been deleted but the packed reference
-		# is still there:
-		echo "$prefix/foo incorrectly observed to be C" &&
-		break
-		;;
-	*)
-		# WTF?
-		echo "unexpected value observed for $prefix/foo: $sha1" &&
-		break
-		;;
-	esac >out &&
-	rm -f .git/packed-refs.lock &&
-	wait $pid2 &&
-	test_must_be_empty out &&
-	test_must_fail git rev-parse --verify --quiet $prefix/foo
-'
-
-test_expect_success REFFILES 'delete fails cleanly if packed-refs file is locked' '
-	prefix=refs/locked-packed-refs &&
-	# Set up a reference with differing loose and packed versions:
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	git update-ref $prefix/foo $D &&
-	git for-each-ref $prefix >unchanged &&
-	# Now try to delete it while the `packed-refs` lock is held:
-	: >.git/packed-refs.lock &&
-	test_when_finished "rm -f .git/packed-refs.lock" &&
-	test_must_fail git update-ref -d $prefix/foo >out 2>err &&
-	git for-each-ref $prefix >actual &&
-	test_i18ngrep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
-	test_cmp unchanged actual
-'
-
-test_expect_success REFFILES 'delete fails cleanly if packed-refs.new write fails' '
-	# Setup and expectations are similar to the test above.
-	prefix=refs/failed-packed-refs &&
-	git update-ref $prefix/foo $C &&
-	git pack-refs --all &&
-	git update-ref $prefix/foo $D &&
-	git for-each-ref $prefix >unchanged &&
-	# This should not happen in practice, but it is an easy way to get a
-	# reliable error (we open with create_tempfile(), which uses O_EXCL).
-	: >.git/packed-refs.new &&
-	test_when_finished "rm -f .git/packed-refs.new" &&
-	test_must_fail git update-ref -d $prefix/foo &&
-	git for-each-ref $prefix >actual &&
-	test_cmp unchanged actual
-'
-
 test_done
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index e4627cf..a6bcd62 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -15,14 +15,6 @@
 	test_commit one
 '
 
-test_expect_success REFFILES 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
-	N=`find .git/refs -type f | wc -l` &&
-	test "$N" != 0 &&
-	$RUN pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
-	N=`find .git/refs -type f` &&
-	test -z "$N"
-'
-
 test_expect_success 'create_symref(FOO, refs/heads/main)' '
 	$RUN create-symref FOO refs/heads/main nothing &&
 	echo refs/heads/main >expected &&
@@ -41,12 +33,6 @@
 	test_must_fail git rev-parse refs/tags/new-tag --
 '
 
-# In reftable, we keep the reflogs around for deleted refs.
-test_expect_success !REFFILES 'delete-reflog(FOO, refs/tags/new-tag)' '
-	$RUN delete-reflog FOO &&
-	$RUN delete-reflog refs/tags/new-tag
-'
-
 test_expect_success 'rename_refs(main, new-main)' '
 	git rev-parse main >expected &&
 	$RUN rename-ref refs/heads/main refs/heads/new-main &&
@@ -82,11 +68,11 @@
 '
 
 test_expect_success 'for_each_reflog()' '
-	$RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual &&
+	$RUN for-each-reflog >actual &&
 	cat >expected <<-\EOF &&
-	HEAD 0x1
-	refs/heads/main 0x0
-	refs/heads/new-main 0x0
+	HEAD
+	refs/heads/main
+	refs/heads/new-main
 	EOF
 	test_cmp expected actual
 '
@@ -112,7 +98,7 @@
 	test_must_fail git reflog exists HEAD
 '
 
-test_expect_success REFFILES 'create-reflog(HEAD)' '
+test_expect_success 'create-reflog(HEAD)' '
 	$RUN create-reflog HEAD &&
 	git reflog exists HEAD
 '
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
index e6a7f73..c01f0f1 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -63,11 +63,11 @@
 '
 
 test_expect_success 'for_each_reflog()' '
-	$RUN for-each-reflog | sort | cut -d" " -f 2- >actual &&
+	$RUN for-each-reflog >actual &&
 	cat >expected <<-\EOF &&
-	HEAD 0x1
-	refs/heads/main 0x0
-	refs/heads/new-main 0x0
+	HEAD
+	refs/heads/main
+	refs/heads/new-main
 	EOF
 	test_cmp expected actual
 '
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index 05b1881..48b1c92 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -53,41 +53,4 @@
 	test_cmp expected actual
 '
 
-# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
-# only appear in the for-each-reflog output if it is called from the correct
-# worktree, which is exercised in this test. This test is poorly written (and
-# therefore marked REFFILES) for mulitple reasons: 1) it creates invalidly
-# formatted log entres. 2) it uses direct FS access for creating the reflogs. 3)
-# PSEUDO-WT and refs/bisect/random do not create reflogs by default, so it is
-# not testing a realistic scenario.
-test_expect_success REFFILES 'for_each_reflog()' '
-	echo $ZERO_OID > .git/logs/PSEUDO-MAIN &&
-	mkdir -p     .git/logs/refs/bisect &&
-	echo $ZERO_OID > .git/logs/refs/bisect/random &&
-
-	echo $ZERO_OID > .git/worktrees/wt/logs/PSEUDO-WT &&
-	mkdir -p     .git/worktrees/wt/logs/refs/bisect &&
-	echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random &&
-
-	$RWT for-each-reflog | cut -d" " -f 2- | sort >actual &&
-	cat >expected <<-\EOF &&
-	HEAD 0x1
-	PSEUDO-WT 0x0
-	refs/bisect/wt-random 0x0
-	refs/heads/main 0x0
-	refs/heads/wt-main 0x0
-	EOF
-	test_cmp expected actual &&
-
-	$RMAIN for-each-reflog | cut -d" " -f 2- | sort >actual &&
-	cat >expected <<-\EOF &&
-	HEAD 0x1
-	PSEUDO-MAIN 0x0
-	refs/bisect/random 0x0
-	refs/heads/main 0x0
-	refs/heads/wt-main 0x0
-	EOF
-	test_cmp expected actual
-'
-
 test_done
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
index f23c015..7748973 100755
--- a/t/t1409-avoid-packing-refs.sh
+++ b/t/t1409-avoid-packing-refs.sh
@@ -5,6 +5,12 @@
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
+if test_have_prereq !REFFILES
+then
+  skip_all='skipping files-backend specific pack-refs tests'
+  test_done
+fi
+
 # Add an identifying mark to the packed-refs file header line. This
 # shouldn't upset readers, and it should be omitted if the file is
 # ever rewritten.
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 6c45965..5bf883f 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -29,7 +29,7 @@
 	'')
 		test_must_be_empty fsck.output ;;
 	*)
-		test_i18ngrep "$1" fsck.output ;;
+		test_grep "$1" fsck.output ;;
 	esac
 }
 
@@ -308,9 +308,9 @@
 	test_config gc.reflogexpireunreachable never &&
 
 	test_must_fail git reflog expire main@{123} 2>stderr &&
-	test_i18ngrep "points nowhere" stderr &&
+	test_grep "points nowhere" stderr &&
 	test_must_fail git reflog expire does-not-exist 2>stderr &&
-	test_i18ngrep "points nowhere" stderr
+	test_grep "points nowhere" stderr
 '
 
 test_expect_success 'checkout should not delete log for packed ref' '
@@ -354,36 +354,6 @@
 	test_must_be_empty actual
 '
 
-# Triggering the bug detected by this test requires a newline to fall
-# exactly BUFSIZ-1 bytes from the end of the file. We don't know
-# what that value is, since it's platform dependent. However, if
-# we choose some value N, we also catch any D which divides N evenly
-# (since we will read backwards in chunks of D). So we choose 8K,
-# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
-#
-# Each line is 114 characters, so we need 75 to still have a few before the
-# last 8K. The 89-character padding on the final entry lines up our
-# newline exactly.
-test_expect_success REFFILES,SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
-	git checkout -b reflogskip &&
-	zf=$(test_oid zero_2) &&
-	ident="abc <xyz> 0000000001 +0000" &&
-	for i in $(test_seq 1 75); do
-		printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
-		if test $i = 75; then
-			for j in $(test_seq 1 89); do
-				printf X || return 1
-			done
-		else
-			printf X
-		fi &&
-		printf "\n" || return 1
-	done >.git/logs/refs/heads/reflogskip &&
-	git rev-parse reflogskip@{73} >actual &&
-	echo ${zf}03 >expect &&
-	test_cmp expect actual
-'
-
 test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
 	git update-ref --create-reflog -m "Creating ref" \
 		refs/tests/tree-in-reflog HEAD &&
@@ -397,18 +367,6 @@
 	test_line_count = 3 actual
 '
 
-# This test takes a lock on an individual ref; this is not supported in
-# reftable.
-test_expect_success REFFILES 'reflog expire operates on symref not referrent' '
-	git branch --create-reflog the_symref &&
-	git branch --create-reflog referrent &&
-	git update-ref referrent HEAD &&
-	git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
-	test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
-	touch .git/refs/heads/referrent.lock &&
-	git reflog expire --expire=all the_symref
-'
-
 test_expect_success 'continue walking past root commits' '
 	git init orphanage &&
 	(
@@ -446,13 +404,144 @@
 	)
 '
 
-test_expect_success REFFILES 'empty reflog' '
+test_expect_success 'expire one of multiple worktrees' '
+	git init main-wt2 &&
+	(
+		cd main-wt2 &&
+		test_tick &&
+		test_commit foo &&
+		git worktree add link-wt &&
+		test_tick &&
+		test_commit -C link-wt foobar &&
+		test_tick &&
+		test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \
+			>expect-link-wt &&
+		git reflog expire --verbose --all --expire=$test_tick \
+			--single-worktree &&
+		test-tool ref-store worktree:main for-each-reflog-ent HEAD \
+			>actual-main &&
+		test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \
+			>actual-link-wt &&
+		test_must_be_empty actual-main &&
+		test_cmp expect-link-wt actual-link-wt
+	)
+'
+
+test_expect_success 'empty reflog' '
 	test_when_finished "rm -rf empty" &&
 	git init empty &&
 	test_commit -C empty A &&
-	>empty/.git/logs/refs/heads/foo &&
+	test-tool ref-store main create-reflog refs/heads/foo &&
 	git -C empty reflog expire --all 2>err &&
 	test_must_be_empty err
 '
 
+test_expect_success 'list reflogs' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git reflog list >actual &&
+		test_must_be_empty actual &&
+
+		test_commit A &&
+		cat >expect <<-EOF &&
+		HEAD
+		refs/heads/main
+		EOF
+		git reflog list >actual &&
+		test_cmp expect actual &&
+
+		git branch b &&
+		cat >expect <<-EOF &&
+		HEAD
+		refs/heads/b
+		refs/heads/main
+		EOF
+		git reflog list >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'list reflogs with worktree' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit A &&
+		git worktree add wt &&
+		git -c core.logAllRefUpdates=always \
+			update-ref refs/worktree/main HEAD &&
+		git -c core.logAllRefUpdates=always \
+			update-ref refs/worktree/per-worktree HEAD &&
+		git -c core.logAllRefUpdates=always -C wt \
+			update-ref refs/worktree/per-worktree HEAD &&
+		git -c core.logAllRefUpdates=always -C wt \
+			update-ref refs/worktree/worktree HEAD &&
+
+		cat >expect <<-EOF &&
+		HEAD
+		refs/heads/main
+		refs/heads/wt
+		refs/worktree/main
+		refs/worktree/per-worktree
+		EOF
+		git reflog list >actual &&
+		test_cmp expect actual &&
+
+		cat >expect <<-EOF &&
+		HEAD
+		refs/heads/main
+		refs/heads/wt
+		refs/worktree/per-worktree
+		refs/worktree/worktree
+		EOF
+		git -C wt reflog list >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'reflog list returns error with additional args' '
+	cat >expect <<-EOF &&
+	error: list does not accept arguments: ${SQ}bogus${SQ}
+	EOF
+	test_must_fail git reflog list bogus 2>err &&
+	test_cmp expect err
+'
+
+test_expect_success 'reflog for symref with unborn target can be listed' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		git symbolic-ref HEAD refs/heads/unborn &&
+		cat >expect <<-EOF &&
+		HEAD
+		refs/heads/main
+		EOF
+		git reflog list >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'reflog with invalid object ID can be listed' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		test-tool ref-store main update-ref msg refs/heads/missing \
+			$(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+		cat >expect <<-EOF &&
+		HEAD
+		refs/heads/main
+		refs/heads/missing
+		EOF
+		git reflog list >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
index ea64cec..be6c3f4 100755
--- a/t/t1414-reflog-walk.sh
+++ b/t/t1414-reflog-walk.sh
@@ -121,13 +121,12 @@
 
 # Create a situation where the reflog and ref database disagree about the latest
 # state of HEAD.
-test_expect_success REFFILES 'walk prefers reflog to ref tip' '
+test_expect_success 'walk prefers reflog to ref tip' '
+	test_commit A &&
+	test_commit B &&
+	git reflog delete HEAD@{0} &&
 	head=$(git rev-parse HEAD) &&
-	one=$(git rev-parse one) &&
-	ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
-	echo "$head $one $ident	broken reflog entry" >>.git/logs/HEAD &&
-
-	echo $one >expect &&
+	git rev-parse A >expect &&
 	git log -g --format=%H -1 >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index 3b53184..eb4eec8 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -17,17 +17,6 @@
 	git -C wt2 update-ref refs/worktree/foo HEAD
 '
 
-# The 'packed-refs' file is stored directly in .git/. This means it is global
-# to the repository, and can only contain refs that are shared across all
-# worktrees.
-test_expect_success REFFILES 'refs/worktree must not be packed' '
-	git pack-refs --all &&
-	test_path_is_missing .git/refs/tags/wt1 &&
-	test_path_is_file .git/refs/worktree/foo &&
-	test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
-	test_path_is_file .git/worktrees/wt2/refs/worktree/foo
-'
-
 test_expect_success 'refs/worktree are per-worktree' '
 	test_cmp_rev worktree/foo initial &&
 	( cd wt1 && test_cmp_rev worktree/foo wt1 ) &&
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index b32ca79..2092488 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -37,7 +37,7 @@
 		fi
 	EOF
 	test_must_fail git update-ref HEAD POST 2>err &&
-	test_i18ngrep "ref updates aborted by hook" err
+	test_grep "ref updates aborted by hook" err
 '
 
 test_expect_success 'hook gets all queued updates in prepared state' '
diff --git a/t/t1417-reflog-updateref.sh b/t/t1417-reflog-updateref.sh
index 14f13b5..0eb5e67 100755
--- a/t/t1417-reflog-updateref.sh
+++ b/t/t1417-reflog-updateref.sh
@@ -14,9 +14,13 @@
 		test_commit B &&
 		test_commit C &&
 
-		cp .git/logs/HEAD HEAD.old &&
+		git reflog HEAD >expect &&
 		git reset --hard HEAD~ &&
-		cp HEAD.old .git/logs/HEAD
+		# Make sure that the reflog does not point to the same commit
+		# as HEAD.
+		git reflog delete HEAD@{0} &&
+		git reflog HEAD >actual &&
+		test_cmp expect actual
 	)
 '
 
@@ -25,7 +29,7 @@
 	shift
 	args="$@"
 
-	test_expect_success REFFILES "get '$exp' with '$args'"  '
+	test_expect_success "get '$exp' with '$args'"  '
 		test_when_finished "rm -rf copy" &&
 		cp -R repo copy &&
 
diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh
new file mode 100755
index 0000000..1359574
--- /dev/null
+++ b/t/t1419-exclude-refs.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+test_description='test exclude_patterns functionality in main ref store'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if test_have_prereq !REFFILES
+then
+	skip_all='skipping `git for-each-ref --exclude` tests; need files backend'
+	test_done
+fi
+
+for_each_ref__exclude () {
+	GIT_TRACE2_PERF=1 test-tool ref-store main \
+		for-each-ref--exclude "$@" >actual.raw
+	cut -d ' ' -f 2 actual.raw
+}
+
+for_each_ref () {
+	git for-each-ref --format='%(refname)' "$@"
+}
+
+assert_jumps () {
+	local nr="$1"
+	local trace="$2"
+
+	grep -q "name:jumps_made value:$nr$" $trace
+}
+
+assert_no_jumps () {
+	! assert_jumps ".*" "$1"
+}
+
+test_expect_success 'setup' '
+	test_commit --no-tag base &&
+	base="$(git rev-parse HEAD)" &&
+
+	for name in foo bar baz quux
+	do
+		for i in 1 2 3
+		do
+			echo "create refs/heads/$name/$i $base" || return 1
+		done || return 1
+	done >in &&
+	echo "delete refs/heads/main" >>in &&
+
+	git update-ref --stdin <in &&
+	git pack-refs --all
+'
+
+test_expect_success 'excluded region in middle' '
+	for_each_ref__exclude refs/heads refs/heads/foo >actual 2>perf &&
+	for_each_ref refs/heads/bar refs/heads/baz refs/heads/quux >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at beginning' '
+	for_each_ref__exclude refs/heads refs/heads/bar >actual 2>perf &&
+	for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at end' '
+	for_each_ref__exclude refs/heads refs/heads/quux >actual 2>perf &&
+	for_each_ref refs/heads/foo refs/heads/bar refs/heads/baz >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 1 perf
+'
+
+test_expect_success 'disjoint excluded regions' '
+	for_each_ref__exclude refs/heads refs/heads/bar refs/heads/quux >actual 2>perf &&
+	for_each_ref refs/heads/baz refs/heads/foo >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 2 perf
+'
+
+test_expect_success 'adjacent, non-overlapping excluded regions' '
+	for_each_ref__exclude refs/heads refs/heads/bar refs/heads/baz >actual 2>perf &&
+	for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 1 perf
+'
+
+test_expect_success 'overlapping excluded regions' '
+	for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf &&
+	for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 1 perf
+'
+
+test_expect_success 'several overlapping excluded regions' '
+	for_each_ref__exclude refs/heads \
+		refs/heads/bar refs/heads/baz refs/heads/foo >actual 2>perf &&
+	for_each_ref refs/heads/quux >expect &&
+
+	test_cmp expect actual &&
+	assert_jumps 1 perf
+'
+
+test_expect_success 'non-matching excluded section' '
+	for_each_ref__exclude refs/heads refs/heads/does/not/exist >actual 2>perf &&
+	for_each_ref >expect &&
+
+	test_cmp expect actual &&
+	assert_no_jumps perf
+'
+
+test_expect_success 'meta-characters are discarded' '
+	for_each_ref__exclude refs/heads "refs/heads/ba*" >actual 2>perf &&
+	for_each_ref >expect &&
+
+	test_cmp expect actual &&
+	assert_no_jumps perf
+'
+
+test_done
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index ff1c967..0c00118 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -47,7 +47,7 @@
 	test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
 	git branch >output 2>error &&
-	test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+	test_grep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
 	! grep -e "broken\.\.\.ref" output
 '
 
@@ -158,23 +158,23 @@
 	git rev-parse --verify one >expect &&
 	git rev-parse --verify shadow >actual 2>err &&
 	test_cmp expect actual &&
-	test_i18ngrep "ignoring dangling symref refs/tags/shadow" err
+	test_grep "ignoring dangling symref refs/tags/shadow" err
 '
 
 test_expect_success 'for-each-ref emits warnings for broken names' '
 	test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
-	printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+	test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
-	printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+	test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
 	git for-each-ref >output 2>error &&
 	! grep -e "broken\.\.\.ref" output &&
 	! grep -e "badname" output &&
 	! grep -e "broken\.\.\.symref" output &&
-	test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
-	test_i18ngrep ! "ignoring broken ref refs/heads/badname" error &&
-	test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
+	test_grep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
+	test_grep ! "ignoring broken ref refs/heads/badname" error &&
+	test_grep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
 '
 
 test_expect_success 'update-ref -d can delete broken name' '
@@ -192,7 +192,7 @@
 	test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
 	git branch -d broken...ref >output 2>error &&
-	test_i18ngrep "Deleted branch broken...ref (was broken)" output &&
+	test_grep "Deleted branch broken...ref (was broken)" output &&
 	test_must_be_empty error &&
 	git branch >output 2>error &&
 	! grep -e "broken\.\.\.ref" error &&
@@ -205,8 +205,9 @@
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
 	test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+	test_ref_exists refs/heads/badname &&
 	git update-ref --no-deref -d refs/heads/badname >output 2>error &&
-	test_path_is_missing .git/refs/heads/badname &&
+	test_ref_missing refs/heads/badname &&
 	test_must_be_empty output &&
 	test_must_be_empty error
 '
@@ -216,17 +217,19 @@
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
 	test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+	test_ref_exists refs/heads/badname &&
 	git branch -d badname >output 2>error &&
-	test_path_is_missing .git/refs/heads/badname &&
-	test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+	test_ref_missing refs/heads/badname &&
+	test_grep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
 	test_must_be_empty error
 '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '
 	test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+	test_ref_exists refs/heads/badname &&
 	git update-ref --no-deref -d refs/heads/badname >output 2>error &&
-	test_path_is_missing .git/refs/heads/badname &&
+	test_ref_missing refs/heads/badname &&
 	test_must_be_empty output &&
 	test_must_be_empty error
 '
@@ -234,9 +237,10 @@
 test_expect_success 'branch -d can delete dangling symref to broken name' '
 	test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+	test_ref_exists refs/heads/badname &&
 	git branch -d badname >output 2>error &&
-	test_path_is_missing .git/refs/heads/badname &&
-	test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
+	test_ref_missing refs/heads/badname &&
+	test_grep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
 	test_must_be_empty error
 '
 
@@ -245,45 +249,50 @@
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
 	test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
+	test_ref_exists refs/heads/broken...ref &&
 	git update-ref -d refs/heads/badname >output 2>error &&
-	test_path_is_missing .git/refs/heads/broken...ref &&
+	test_ref_missing refs/heads/broken...ref &&
 	test_must_be_empty output &&
 	test_must_be_empty error
 '
 
 test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
-	printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+	test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+	test_ref_exists refs/heads/broken...symref &&
 	git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
-	test_path_is_missing .git/refs/heads/broken...symref &&
+	test_ref_missing refs/heads/broken...symref &&
 	test_must_be_empty output &&
 	test_must_be_empty error
 '
 
 test_expect_success 'branch -d can delete symref with broken name' '
-	printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+	test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+	test_ref_exists refs/heads/broken...symref &&
 	git branch -d broken...symref >output 2>error &&
-	test_path_is_missing .git/refs/heads/broken...symref &&
-	test_i18ngrep "Deleted branch broken...symref (was refs/heads/main)" output &&
+	test_ref_missing refs/heads/broken...symref &&
+	test_grep "Deleted branch broken...symref (was refs/heads/main)" output &&
 	test_must_be_empty error
 '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
-	printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+	test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+	test_ref_exists refs/heads/broken...symref &&
 	git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
-	test_path_is_missing .git/refs/heads/broken...symref &&
+	test_ref_missing refs/heads/broken...symref &&
 	test_must_be_empty output &&
 	test_must_be_empty error
 '
 
 test_expect_success 'branch -d can delete dangling symref with broken name' '
-	printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+	test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
 	test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
+	test_ref_exists refs/heads/broken...symref &&
 	git branch -d broken...symref >output 2>error &&
-	test_path_is_missing .git/refs/heads/broken...symref &&
-	test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
+	test_ref_missing refs/heads/broken...symref &&
+	test_grep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
 	test_must_be_empty error
 '
 
@@ -292,7 +301,7 @@
 	echo precious >expect &&
 	test_must_fail git update-ref -d my-private-file >output 2>error &&
 	test_must_be_empty output &&
-	test_i18ngrep -e "refusing to update ref with bad name" error &&
+	test_grep -e "refusing to update ref with bad name" error &&
 	test_cmp expect .git/my-private-file
 '
 
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index fdb886d..8a456b1 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -15,6 +15,7 @@
 	git config --unset i18n.commitencoding &&
 	git checkout HEAD^0 &&
 	test_commit B fileB two &&
+	orig_head=$(git rev-parse HEAD) &&
 	git tag -d A B &&
 	git reflog expire --expire=now --all
 '
@@ -115,63 +116,62 @@
 '
 
 test_expect_success 'branch pointing to non-commit' '
-	git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+	tree_oid=$(git rev-parse --verify HEAD^{tree}) &&
 	test_when_finished "git update-ref -d refs/heads/invalid" &&
+	test-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "not a commit" out
+	test_grep "not a commit" out
 '
 
-test_expect_success 'HEAD link pointing at a funny object' '
-	test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-	mv .git/HEAD .git/SAVED_HEAD &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object' '
+	test_when_finished "git update-ref HEAD $orig_head" &&
 	echo $ZERO_OID >.git/HEAD &&
 	# avoid corrupt/broken HEAD from interfering with repo discovery
 	test_must_fail env GIT_DIR=.git git fsck 2>out &&
-	test_i18ngrep "detached HEAD points" out
+	test_grep "detached HEAD points" out
 '
 
 test_expect_success 'HEAD link pointing at a funny place' '
-	test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-	mv .git/HEAD .git/SAVED_HEAD &&
-	echo "ref: refs/funny/place" >.git/HEAD &&
+	test_when_finished "git update-ref --no-deref HEAD $orig_head" &&
+	test-tool ref-store main create-symref HEAD refs/funny/place &&
 	# avoid corrupt/broken HEAD from interfering with repo discovery
 	test_must_fail env GIT_DIR=.git git fsck 2>out &&
-	test_i18ngrep "HEAD points to something strange" out
+	test_grep "HEAD points to something strange" out
 '
 
-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
-	test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-	test_when_finished "rm -rf .git/worktrees wt" &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '
+	test_when_finished "git update-ref HEAD $orig_head" &&
+	test_when_finished "git worktree remove -f wt" &&
 	git worktree add wt &&
-	mv .git/HEAD .git/SAVED_HEAD &&
 	echo $ZERO_OID >.git/HEAD &&
 	# avoid corrupt/broken HEAD from interfering with repo discovery
 	test_must_fail git -C wt fsck 2>out &&
-	test_i18ngrep "main-worktree/HEAD: detached HEAD points" out
+	test_grep "main-worktree/HEAD: detached HEAD points" out
 '
 
-test_expect_success 'other worktree HEAD link pointing at a funny object' '
-	test_when_finished "rm -rf .git/worktrees other" &&
+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '
+	test_when_finished "git worktree remove -f other" &&
 	git worktree add other &&
 	echo $ZERO_OID >.git/worktrees/other/HEAD &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "worktrees/other/HEAD: detached HEAD points" out
+	test_grep "worktrees/other/HEAD: detached HEAD points" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at missing object' '
-	test_when_finished "rm -rf .git/worktrees other" &&
+	test_when_finished "git worktree remove -f other" &&
 	git worktree add other &&
-	echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
+	object_id=$(echo "Contents missing from repo" | git hash-object --stdin) &&
+	test-tool -C other ref-store main update-ref msg HEAD $object_id "" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "worktrees/other/HEAD: invalid sha1 pointer" out
+	test_grep "worktrees/other/HEAD: invalid sha1 pointer" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at a funny place' '
-	test_when_finished "rm -rf .git/worktrees other" &&
+	test_when_finished "git worktree remove -f other" &&
 	git worktree add other &&
-	echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
+	git -C other symbolic-ref HEAD refs/funny/place &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "worktrees/other/HEAD points to something strange" out
+	test_grep "worktrees/other/HEAD points to something strange" out
 '
 
 test_expect_success 'commit with multiple signatures is okay' '
@@ -217,7 +217,7 @@
 	git update-ref refs/heads/bogus "$new" &&
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $new" out
+	test_grep "error in commit $new" out
 '
 
 test_expect_success 'missing < email delimiter is reported nicely' '
@@ -228,7 +228,7 @@
 	git update-ref refs/heads/bogus "$new" &&
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $new.* - bad name" out
+	test_grep "error in commit $new.* - bad name" out
 '
 
 test_expect_success 'missing email is reported nicely' '
@@ -239,7 +239,7 @@
 	git update-ref refs/heads/bogus "$new" &&
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $new.* - missing email" out
+	test_grep "error in commit $new.* - missing email" out
 '
 
 test_expect_success '> in name is reported' '
@@ -250,7 +250,7 @@
 	git update-ref refs/heads/bogus "$new" &&
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $new" out
+	test_grep "error in commit $new" out
 '
 
 # date is 2^64 + 1
@@ -263,7 +263,7 @@
 	git update-ref refs/heads/bogus "$new" &&
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $new.*integer overflow" out
+	test_grep "error in commit $new.*integer overflow" out
 '
 
 test_expect_success 'commit with NUL in header' '
@@ -274,7 +274,7 @@
 	git update-ref refs/heads/bogus "$new" &&
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $new.*unterminated header: NUL at offset" out
+	test_grep "error in commit $new.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'tree object with duplicate entries' '
@@ -295,7 +295,7 @@
 		git hash-object --literally -w -t tree --stdin
 	) &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in tree .*contains duplicate file entries" out
+	test_grep "error in tree .*contains duplicate file entries" out
 '
 
 check_duplicate_names () {
@@ -318,8 +318,8 @@
 		done >badtree &&
 		badtree=$(git mktree <badtree) &&
 		test_must_fail git fsck 2>out &&
-		test_i18ngrep "$badtree" out &&
-		test_i18ngrep "error in tree .*contains duplicate file entries" out
+		test_grep "$badtree" out &&
+		test_grep "error in tree .*contains duplicate file entries" out
 	'
 }
 
@@ -341,9 +341,9 @@
 	commit_sha1=$(git commit-tree $tree_sha1) &&
 	git update-ref refs/heads/wrong $commit_sha1 &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error: empty filename in tree entry" out &&
-	test_i18ngrep "$tree_sha1" out &&
-	test_i18ngrep ! "fatal: empty filename in tree entry" out
+	test_grep "error: empty filename in tree entry" out &&
+	test_grep "$tree_sha1" out &&
+	test_grep ! "fatal: empty filename in tree entry" out
 '
 
 test_expect_success 'tree entry with type mismatch' '
@@ -360,8 +360,8 @@
 	commit=$(git commit-tree $tree) &&
 	git update-ref refs/heads/type_mismatch $commit &&
 	test_must_fail git fsck >out 2>&1 &&
-	test_i18ngrep "is a blob, not a tree" out &&
-	test_i18ngrep ! "dangling blob" out
+	test_grep "is a blob, not a tree" out &&
+	test_grep ! "dangling blob" out
 '
 
 test_expect_success 'tree entry with bogus mode' '
@@ -391,10 +391,10 @@
 
 	tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
 	test_when_finished "remove_object $tag" &&
-	echo $tag >.git/refs/tags/invalid &&
+	git update-ref refs/tags/invalid $tag &&
 	test_when_finished "git update-ref -d refs/tags/invalid" &&
 	test_must_fail git fsck --tags >out &&
-	test_i18ngrep "broken link" out
+	test_grep "broken link" out
 '
 
 test_expect_success 'tag pointing to something else than its type' '
@@ -411,7 +411,7 @@
 
 	tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
 	test_when_finished "remove_object $tag" &&
-	echo $tag >.git/refs/tags/wrong &&
+	git update-ref refs/tags/wrong $tag &&
 	test_when_finished "git update-ref -d refs/tags/wrong" &&
 	test_must_fail git fsck --tags
 '
@@ -428,7 +428,7 @@
 
 	tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
 	test_when_finished "remove_object $tag" &&
-	echo $tag >.git/refs/tags/wrong &&
+	git update-ref refs/tags/wrong $tag &&
 	test_when_finished "git update-ref -d refs/tags/wrong" &&
 	git fsck --tags 2>out &&
 
@@ -452,10 +452,10 @@
 
 	tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
 	test_when_finished "remove_object $tag" &&
-	echo $tag >.git/refs/tags/wrong &&
+	git update-ref refs/tags/wrong $tag &&
 	test_when_finished "git update-ref -d refs/tags/wrong" &&
 	test_must_fail git fsck --tags 2>out &&
-	test_i18ngrep "error in tag .*: invalid author/committer" out
+	test_grep "error in tag .*: invalid author/committer" out
 '
 
 test_expect_success 'tag with NUL in header' '
@@ -471,10 +471,10 @@
 
 	tag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&
 	test_when_finished "remove_object $tag" &&
-	echo $tag >.git/refs/tags/wrong &&
+	git update-ref refs/tags/wrong $tag &&
 	test_when_finished "git update-ref -d refs/tags/wrong" &&
 	test_must_fail git fsck --tags 2>out &&
-	test_i18ngrep "error in tag $tag.*unterminated header: NUL at offset" out
+	test_grep "error in tag $tag.*unterminated header: NUL at offset" out
 '
 
 test_expect_success 'cleaned up' '
@@ -504,7 +504,7 @@
 	test_when_finished "git update-ref -d refs/heads/bogus" &&
 
 	test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
-	test_i18ngrep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
+	test_grep -q "error: hash mismatch $(dirname $new)$(test_oid ff_2)" out
 '
 
 # An actual bit corruption is more likely than swapped commits, but
@@ -575,7 +575,7 @@
 	 sha=$(printf "100644 file$_bz$_bzoid" |
 	       git hash-object --literally -w --stdin -t tree) &&
 	  git fsck 2>out &&
-	  test_i18ngrep "warning.*null sha1" out
+	  test_grep "warning.*null sha1" out
 	)
 '
 
@@ -585,7 +585,17 @@
 	 sha=$(printf "160000 submodule$_bz$_bzoid" |
 	       git hash-object --literally -w --stdin -t tree) &&
 	  git fsck 2>out &&
-	  test_i18ngrep "warning.*null sha1" out
+	  test_grep "warning.*null sha1" out
+	)
+'
+
+test_expect_success 'fsck notices excessively large tree entry name' '
+	git init large-name &&
+	(
+		cd large-name &&
+		test_commit a-long-name &&
+		git -c fsck.largePathname=warn:10 fsck 2>out &&
+		grep "warning.*large pathname" out
 	)
 '
 
@@ -606,7 +616,7 @@
 			printf "$mode $type %s\t%s" "$value" "$path" >bad &&
 			bad_tree=$(git mktree <bad) &&
 			git fsck 2>out &&
-			test_i18ngrep "warning.*tree $bad_tree" out
+			test_grep "warning.*tree $bad_tree" out
 		)'
 	done <<-\EOF
 	100644 blob
@@ -652,9 +662,9 @@
 		git branch bad $(cat name) &&
 
 		test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
-		test_i18ngrep nulInCommit warn.1 &&
+		test_grep nulInCommit warn.1 &&
 		git fsck 2>warn.2 &&
-		test_i18ngrep nulInCommit warn.2
+		test_grep nulInCommit warn.2
 	)
 '
 
@@ -774,7 +784,7 @@
 		tree=$(git rev-parse --verify julius:) &&
 		git tag -d julius &&
 		test_must_fail git fsck --name-objects >out &&
-		test_i18ngrep "$tree (refs/tags/augustus44\\^:" out
+		test_grep "$tree (refs/tags/augustus44\\^:" out
 	)
 '
 
@@ -787,7 +797,7 @@
 	mkdir alt.git/objects/$(dirname $path) &&
 	>alt.git/objects/$(dirname $path)/$(basename $path) &&
 	test_must_fail git fsck >out 2>&1 &&
-	test_i18ngrep alt.git out
+	test_grep alt.git out
 '
 
 test_expect_success 'fsck errors in packed objects' '
@@ -806,8 +816,8 @@
 	remove_object $one &&
 	remove_object $two &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "error in commit $one.* - bad name" out &&
-	test_i18ngrep "error in commit $two.* - bad name" out &&
+	test_grep "error in commit $one.* - bad name" out &&
+	test_grep "error in commit $two.* - bad name" out &&
 	! grep corrupt out
 '
 
@@ -824,7 +834,7 @@
 	test_when_finished "rm -f .git/objects/pack/pack-$pack.*" &&
 	remove_object $hsh &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "checksum mismatch" out
+	test_grep "checksum mismatch" out
 '
 
 test_expect_success 'fsck finds problems in duplicate loose objects' '
@@ -861,7 +871,7 @@
 	chmod +w "$file" &&
 	echo garbage >>"$file" &&
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep "garbage.*$commit" out
+	test_grep "garbage.*$commit" out
 '
 
 test_expect_success 'fsck detects trailing loose garbage (large blob)' '
@@ -871,7 +881,7 @@
 	chmod +w "$file" &&
 	echo garbage >>"$file" &&
 	test_must_fail git -c core.bigfilethreshold=5 fsck 2>out &&
-	test_i18ngrep "garbage.*$blob" out
+	test_grep "garbage.*$blob" out
 '
 
 test_expect_success 'fsck detects truncated loose object' '
@@ -887,10 +897,10 @@
 
 	# check both regular and streaming code paths
 	test_must_fail git fsck 2>out &&
-	test_i18ngrep corrupt.*$blob out &&
+	test_grep corrupt.*$blob out &&
 
 	test_must_fail git -c core.bigfilethreshold=128 fsck 2>out &&
-	test_i18ngrep corrupt.*$blob out
+	test_grep corrupt.*$blob out
 '
 
 # for each of type, we have one version which is referenced by another object
@@ -979,7 +989,7 @@
 	test_when_finished "mv .git/index.backup .git/index" &&
 	corrupt_index_checksum &&
 	test_must_fail git fsck --cache 2>errors &&
-	test_i18ngrep "bad index file" errors
+	test_grep "bad index file" errors
 '
 
 test_expect_success 'fsck error and recovery on invalid object type' '
@@ -989,10 +999,7 @@
 
 		garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
 
-		cat >err.expect <<-\EOF &&
-		fatal: invalid object type
-		EOF
-		test_must_fail git fsck >out 2>err &&
+		test_must_fail git fsck 2>err &&
 		grep -e "^error" -e "^fatal" err >errors &&
 		test_line_count = 1 errors &&
 		grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
@@ -1023,4 +1030,34 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'fsck detects problems in worktree index' '
+	test_when_finished "git worktree remove -f wt" &&
+	git worktree add wt &&
+
+	echo "this will be removed to break the worktree index" >wt/file &&
+	git -C wt add file &&
+	blob=$(git -C wt rev-parse :file) &&
+	remove_object $blob &&
+
+	test_must_fail git fsck --name-objects >actual 2>&1 &&
+	cat >expect <<-EOF &&
+	missing blob $blob (.git/worktrees/wt/index:file)
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'fsck reports problems in current worktree index without filename' '
+	test_when_finished "rm -f .git/index && git read-tree HEAD" &&
+	echo "this object will be removed to break current worktree index" >file &&
+	git add file &&
+	blob=$(git rev-parse :file) &&
+	remove_object $blob &&
+
+	test_must_fail git fsck --name-objects >actual 2>&1 &&
+	cat >expect <<-EOF &&
+	missing blob $blob (:file)
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 37ee509..a669e59 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -208,6 +208,23 @@
 	grep "unknown mode for --show-object-format: squeamish-ossifrage" err
 '
 
+test_expect_success 'rev-parse --show-ref-format' '
+	test_detect_ref_format >expect &&
+	git rev-parse --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --show-ref-format with invalid storage' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git config extensions.refstorage broken &&
+		test_must_fail git rev-parse --show-ref-format 2>err &&
+		grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err
+	)
+'
+
 test_expect_success '--show-toplevel from subdir of working tree' '
 	pwd >expect &&
 	git -C sub/dir rev-parse --show-toplevel >actual &&
@@ -264,4 +281,27 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --bisect includes bad, excludes good' '
+	test_commit_bulk 6 &&
+
+	git update-ref refs/bisect/bad-1 HEAD~1 &&
+	git update-ref refs/bisect/b HEAD~2 &&
+	git update-ref refs/bisect/bad-3 HEAD~3 &&
+	git update-ref refs/bisect/good-3 HEAD~3 &&
+	git update-ref refs/bisect/bad-4 HEAD~4 &&
+	git update-ref refs/bisect/go HEAD~4 &&
+
+	# Note: refs/bisect/b and refs/bisect/go should be ignored because they
+	# do not match the refs/bisect/bad or refs/bisect/good prefixes.
+	cat >expect <<-EOF &&
+	refs/bisect/bad-1
+	refs/bisect/bad-3
+	refs/bisect/bad-4
+	^refs/bisect/good-3
+	EOF
+
+	git rev-parse --symbolic-full-name --bisect >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index de1d48f..b754b9f 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -3,13 +3,29 @@
 test_description='test git rev-parse --parseopt'
 . ./test-lib.sh
 
+check_invalid_long_option () {
+	spec="$1"
+	opt="$2"
+	test_expect_success "test --parseopt invalid switch $opt help output for $spec" '
+		{
+			cat <<-\EOF &&
+			error: unknown option `'${opt#--}\''
+			EOF
+			sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/$spec.help"
+		} >expect &&
+		test_expect_code 129 git rev-parse --parseopt -- $opt \
+			2>output <"$TEST_DIRECTORY/t1502/$spec" &&
+		test_cmp expect output
+	'
+}
+
 test_expect_success 'setup optionspec' '
 	sed -e "s/^|//" >optionspec <<\EOF
 |some-command [options] <args>...
 |
 |some-command does foo and bar!
 |--
-|h,help    show the help
+|h,help!   show the help
 |
 |foo       some nifty option --foo
 |bar=      some cool option --bar with an argument
@@ -58,44 +74,8 @@
 '
 
 test_expect_success 'test --parseopt help output' '
-	sed -e "s/^|//" >expect <<\END_EXPECT &&
-|cat <<\EOF
-|usage: some-command [options] <args>...
-|
-|    some-command does foo and bar!
-|
-|    -h, --help            show the help
-|    --foo                 some nifty option --foo
-|    --bar ...             some cool option --bar with an argument
-|    -b, --baz             a short and long option
-|
-|An option group Header
-|    -C[...]               option C with an optional argument
-|    -d, --data[=...]      short and long option with an optional argument
-|
-|Argument hints
-|    -B <arg>              short option required argument
-|    --bar2 <arg>          long option required argument
-|    -e, --fuz <with-space>
-|                          short and long option required argument
-|    -s[<some>]            short option optional argument
-|    --long[=<data>]       long option optional argument
-|    -g, --fluf[=<path>]   short and long option optional argument
-|    --longest <very-long-argument-hint>
-|                          a very long argument hint
-|    --pair <key=value>    with an equals sign in the hint
-|    --aswitch             help te=t contains? fl*g characters!`
-|    --bswitch <hint>      hint has trailing tab character
-|    --cswitch             switch has trailing tab character
-|    --short-hint <a>      with a one symbol hint
-|
-|Extras
-|    --extra1              line above used to cause a segfault but no longer does
-|
-|EOF
-END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
-	test_cmp expect output
+	test_cmp "$TEST_DIRECTORY/t1502/optionspec.help" output
 '
 
 test_expect_success 'test --parseopt help output no switches' '
@@ -131,7 +111,7 @@
 |
 |    some-command does foo and bar!
 |
-|    --hidden1             A hidden switch
+|    --[no-]hidden1        A hidden switch
 |
 |EOF
 END_EXPECT
@@ -140,41 +120,12 @@
 '
 
 test_expect_success 'test --parseopt invalid switch help output' '
-	sed -e "s/^|//" >expect <<\END_EXPECT &&
-|error: unknown option `does-not-exist'\''
-|usage: some-command [options] <args>...
-|
-|    some-command does foo and bar!
-|
-|    -h, --help            show the help
-|    --foo                 some nifty option --foo
-|    --bar ...             some cool option --bar with an argument
-|    -b, --baz             a short and long option
-|
-|An option group Header
-|    -C[...]               option C with an optional argument
-|    -d, --data[=...]      short and long option with an optional argument
-|
-|Argument hints
-|    -B <arg>              short option required argument
-|    --bar2 <arg>          long option required argument
-|    -e, --fuz <with-space>
-|                          short and long option required argument
-|    -s[<some>]            short option optional argument
-|    --long[=<data>]       long option optional argument
-|    -g, --fluf[=<path>]   short and long option optional argument
-|    --longest <very-long-argument-hint>
-|                          a very long argument hint
-|    --pair <key=value>    with an equals sign in the hint
-|    --aswitch             help te=t contains? fl*g characters!`
-|    --bswitch <hint>      hint has trailing tab character
-|    --cswitch             switch has trailing tab character
-|    --short-hint <a>      with a one symbol hint
-|
-|Extras
-|    --extra1              line above used to cause a segfault but no longer does
-|
-END_EXPECT
+	{
+		cat <<-\EOF &&
+		error: unknown option `does-not-exist'\''
+		EOF
+		sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/optionspec.help"
+	} >expect &&
 	test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
 	test_cmp expect output
 '
@@ -288,7 +239,7 @@
 	|    [--another-option]
 	|cmd [--yet-another-option]
 	|--
-	|h,help    show the help
+	|h,help!   show the help
 	EOF
 
 	sed -e "s/^|//" >expect <<-\END_EXPECT &&
@@ -302,14 +253,14 @@
 	|EOF
 	END_EXPECT
 
-	test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+	test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'test --parseopt invalid opt-spec' '
 	test_write_lines x -- "=, x" >spec &&
 	echo "fatal: missing opt-spec before option flags" >expect &&
-	test_must_fail git rev-parse --parseopt -- >out <spec 2>err &&
+	test_must_fail git rev-parse --parseopt -- <spec 2>err &&
 	test_cmp expect err
 '
 
@@ -322,7 +273,7 @@
 	|line
 	|blurb
 	|--
-	|h,help    show the help
+	|h,help!   show the help
 	EOF
 
 	sed -e "s/^|//" >expect <<-\END_EXPECT &&
@@ -339,8 +290,47 @@
 	|EOF
 	END_EXPECT
 
-	test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+	test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'test --parseopt help output for optionspec-neg' '
+	test_expect_code 129 git rev-parse --parseopt -- \
+		-h >output <"$TEST_DIRECTORY/t1502/optionspec-neg" &&
+	test_cmp "$TEST_DIRECTORY/t1502/optionspec-neg.help" output
+'
+
+test_expect_success 'test --parseopt valid options for optionspec-neg' '
+	cat >expect <<-\EOF &&
+	set -- --foo --no-foo --no-bar --positive-only --no-negative --
+	EOF
+	git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \
+	       --foo --no-foo --no-bar --positive-only --no-negative &&
+	test_cmp expect output
+'
+
+test_expect_success 'test --parseopt positivated option for optionspec-neg' '
+	cat >expect <<-\EOF &&
+	set -- --no-no-bar --no-no-bar --
+	EOF
+	git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \
+	       --no-no-bar --bar &&
+	test_cmp expect output
+'
+
+check_invalid_long_option optionspec-neg --no-positive-only
+check_invalid_long_option optionspec-neg --negative
+check_invalid_long_option optionspec-neg --no-no-negative
+
+test_expect_success 'ambiguous: --no matches both --noble and --no-noble' '
+	cat >spec <<-\EOF &&
+	some-command [options]
+	--
+	noble The feudal switch.
+	EOF
+	test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+	git rev-parse --parseopt -- <spec 2>err --no &&
+	grep "error: ambiguous option: no (could be --noble or --no-noble)" err
+'
+
 test_done
diff --git a/t/t1502/.gitattributes b/t/t1502/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/t1502/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t1502/optionspec-neg b/t/t1502/optionspec-neg
new file mode 100644
index 0000000..392f43e
--- /dev/null
+++ b/t/t1502/optionspec-neg
@@ -0,0 +1,8 @@
+some-command [options] <args>...
+
+some-command does foo and bar!
+--
+foo		can be negated
+no-bar		can be positivated
+positive-only!	cannot be negated
+no-negative!	cannot be positivated
diff --git a/t/t1502/optionspec-neg.help b/t/t1502/optionspec-neg.help
new file mode 100644
index 0000000..7a29f8c
--- /dev/null
+++ b/t/t1502/optionspec-neg.help
@@ -0,0 +1,12 @@
+cat <<\EOF
+usage: some-command [options] <args>...
+
+    some-command does foo and bar!
+
+    --[no-]foo            can be negated
+    --no-bar              can be positivated
+    --bar                 opposite of --no-bar
+    --positive-only       cannot be negated
+    --no-negative         cannot be positivated
+
+EOF
diff --git a/t/t1502/optionspec.help b/t/t1502/optionspec.help
new file mode 100755
index 0000000..cbdd54d
--- /dev/null
+++ b/t/t1502/optionspec.help
@@ -0,0 +1,36 @@
+cat <<\EOF
+usage: some-command [options] <args>...
+
+    some-command does foo and bar!
+
+    -h, --help            show the help
+    --[no-]foo            some nifty option --foo
+    --[no-]bar ...        some cool option --bar with an argument
+    -b, --[no-]baz        a short and long option
+
+An option group Header
+    -C[...]               option C with an optional argument
+    -d, --[no-]data[=...] short and long option with an optional argument
+
+Argument hints
+    -B <arg>              short option required argument
+    --[no-]bar2 <arg>     long option required argument
+    -e, --[no-]fuz <with-space>
+                          short and long option required argument
+    -s[<some>]            short option optional argument
+    --[no-]long[=<data>]  long option optional argument
+    -g, --[no-]fluf[=<path>]
+                          short and long option optional argument
+    --[no-]longest <very-long-argument-hint>
+                          a very long argument hint
+    --[no-]pair <key=value>
+                          with an equals sign in the hint
+    --[no-]aswitch        help te=t contains? fl*g characters!`
+    --[no-]bswitch <hint> hint has trailing tab character
+    --[no-]cswitch        switch has trailing tab character
+    --[no-]short-hint <a> with a one symbol hint
+
+Extras
+    --[no-]extra1         line above used to cause a segfault but no longer does
+
+EOF
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index bc13683..79df65e 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -144,11 +144,6 @@
 	test_must_fail git rev-parse --verify main@{$Np1}
 '
 
-test_expect_success SYMLINKS,REFFILES 'ref resolution not confused by broken symlinks' '
-	ln -s does-not-exist .git/refs/heads/broken &&
-	test_must_fail git rev-parse --verify broken
-'
-
 test_expect_success 'options can appear after --verify' '
 	git rev-parse --verify HEAD >expect &&
 	git rev-parse --verify -q HEAD >actual &&
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index 0fafcf9..c1679e3 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -6,8 +6,12 @@
 . ./test-lib.sh
 
 test_prefix() {
-	test_expect_success "$1" \
-	"test '$2' = \"\$(git rev-parse --show-prefix)\""
+	local expect="$2" &&
+	test_expect_success "$1: git rev-parse --show-prefix is '$2'" '
+		echo "$expect" >expect &&
+		git rev-parse --show-prefix >actual &&
+		test_cmp expect actual
+	'
 }
 
 test_fail() {
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index 18688ca..ef40511 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -107,16 +107,16 @@
 
 test_expect_success 'incorrect revision id' '
 	test_must_fail git rev-parse foobar:file.txt 2>error &&
-	test_i18ngrep "invalid object name .foobar." error &&
+	test_grep "invalid object name .foobar." error &&
 	test_must_fail git rev-parse foobar 2>error &&
-	test_i18ngrep "unknown revision or path not in the working tree." error
+	test_grep "unknown revision or path not in the working tree." error
 '
 
 test_expect_success 'incorrect file in sha1:path' '
 	test_must_fail git rev-parse HEAD:nothing.txt 2>error &&
-	test_i18ngrep "path .nothing.txt. does not exist in .HEAD." error &&
+	test_grep "path .nothing.txt. does not exist in .HEAD." error &&
 	test_must_fail git rev-parse HEAD:index-only.txt 2>error &&
-	test_i18ngrep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
+	test_grep "path .index-only.txt. exists on disk, but not in .HEAD." error &&
 	(cd subdir &&
 	 test_must_fail git rev-parse HEAD:file2.txt 2>error &&
 	 test_did_you_mean HEAD subdir/ file2.txt exists )
@@ -124,9 +124,9 @@
 
 test_expect_success 'incorrect file in :path and :N:path' '
 	test_must_fail git rev-parse :nothing.txt 2>error &&
-	test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+	test_grep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
 	test_must_fail git rev-parse :1:nothing.txt 2>error &&
-	test_i18ngrep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
+	test_grep "path .nothing.txt. does not exist (neither on disk nor in the index)" error &&
 	test_must_fail git rev-parse :1:file.txt 2>error &&
 	test_did_you_mean ":0" "" file.txt "is in the index" "at stage 1" &&
 	(cd subdir &&
@@ -137,42 +137,42 @@
 	 test_must_fail git rev-parse :2:file2.txt 2>error &&
 	 test_did_you_mean :0 subdir/ file2.txt "is in the index") &&
 	test_must_fail git rev-parse :disk-only.txt 2>error &&
-	test_i18ngrep "path .disk-only.txt. exists on disk, but not in the index" error
+	test_grep "path .disk-only.txt. exists on disk, but not in the index" error
 '
 
 test_expect_success 'invalid @{n} reference' '
 	test_must_fail git rev-parse main@{99999} >output 2>error &&
 	test_must_be_empty output &&
-	test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error  &&
+	test_grep "log for [^ ]* only has [0-9][0-9]* entries" error  &&
 	test_must_fail git rev-parse --verify main@{99999} >output 2>error &&
 	test_must_be_empty output &&
-	test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error
+	test_grep "log for [^ ]* only has [0-9][0-9]* entries" error
 '
 
 test_expect_success 'relative path not found' '
 	(
 		cd subdir &&
 		test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
-		test_i18ngrep subdir/nonexistent.txt error
+		test_grep subdir/nonexistent.txt error
 	)
 '
 
 test_expect_success 'relative path outside worktree' '
 	test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
 	test_must_be_empty output &&
-	test_i18ngrep "outside repository" error
+	test_grep "outside repository" error
 '
 
 test_expect_success 'relative path when cwd is outside worktree' '
 	test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
 	test_must_be_empty output &&
-	test_i18ngrep "relative path syntax can.t be used outside working tree" error
+	test_grep "relative path syntax can.t be used outside working tree" error
 '
 
 test_expect_success '<commit>:file correctly diagnosed after a pathname' '
 	test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&
-	test_i18ngrep ! "exists on disk" error &&
-	test_i18ngrep "no such path in the working tree" error &&
+	test_grep ! "exists on disk" error &&
+	test_grep "no such path in the working tree" error &&
 	cat >expect <<-\EOF &&
 	file.txt
 	HEAD:file.txt
@@ -214,13 +214,13 @@
 
 test_expect_success 'arg before dashdash must be a revision (missing)' '
 	test_must_fail git rev-parse foobar -- 2>stderr &&
-	test_i18ngrep "bad revision" stderr
+	test_grep "bad revision" stderr
 '
 
 test_expect_success 'arg before dashdash must be a revision (file)' '
 	>foobar &&
 	test_must_fail git rev-parse foobar -- 2>stderr &&
-	test_i18ngrep "bad revision" stderr
+	test_grep "bad revision" stderr
 '
 
 test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
@@ -269,7 +269,7 @@
 
 test_expect_success 'arg after end-of-options not interpreted as option' '
 	test_must_fail git rev-parse --end-of-options --not-real -- 2>err &&
-	test_i18ngrep bad.revision.*--not-real err
+	test_grep bad.revision.*--not-real err
 '
 
 test_expect_success 'end-of-options still allows --' '
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index c34714f..b9af6b3 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
@@ -97,7 +98,8 @@
 	commit_subject my-side >actual &&
 	test_cmp expect actual &&
 	echo 5 >expect &&
-	commit_subject my-side@{u} >actual
+	commit_subject my-side@{u} >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'not-tracking@{u} fails' '
@@ -183,6 +185,11 @@
 	test_cmp expect actual
 '
 
+test_expect_success '@{u} silent error when no upstream' '
+	test_must_fail git rev-parse --verify --quiet @{u} 2>actual &&
+	test_must_be_empty actual
+'
+
 test_expect_success 'branch@{u} error message with misspelt branch' '
 	cat >expect <<-EOF &&
 	fatal: no such branch: ${SQ}no-such-branch${SQ}
@@ -258,7 +265,8 @@
 	git add @{yesterday} &&
 	git commit -m "funny reflog file" &&
 	git hash-object @{yesterday} >expect &&
-	git rev-parse HEAD:@{yesterday} >actual
+	git rev-parse HEAD:@{yesterday} >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success '@{upstream}-parsing does not look beyond colon' '
@@ -266,7 +274,8 @@
 	git add @{upstream} &&
 	git commit -m "funny upstream file" &&
 	git hash-object @{upstream} >expect &&
-	git rev-parse HEAD:@{upstream} >actual
+	git rev-parse HEAD:@{upstream} >actual &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
index 87a4286..e841309 100755
--- a/t/t1508-at-combinations.sh
+++ b/t/t1508-at-combinations.sh
@@ -4,6 +4,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check() {
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
index 6d47e2c..dc997e0 100755
--- a/t/t1509/prepare-chroot.sh
+++ b/t/t1509/prepare-chroot.sh
@@ -43,7 +43,7 @@
 # env might slip through, see test-lib.sh, unset.*PERL_PATH
 sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
 for cmd in git $BB;do 
-	ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+	ldd $cmd | sed -n '/\//s,.*\s\(/[^ ]*\).*,\1,p' | while read i; do
 		mkdir -p "$R$(dirname $i)"
 		cp "$i" "$R/$i"
 	done
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 98cefe3..70f1e0a 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -129,7 +129,7 @@
 
 test_expect_success 'warn ambiguity when no candidate matches type hint' '
 	test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
-	test_i18ngrep "short object ID 000000000 is ambiguous" actual
+	test_grep "short object ID 000000000 is ambiguous" actual
 '
 
 test_expect_success 'disambiguate tree-ish' '
@@ -470,10 +470,10 @@
 	echo "0000 ambiguous" >expect &&
 	echo 0000 | git cat-file --batch-check >actual 2>err &&
 	test_cmp expect actual &&
-	test_i18ngrep hint: err &&
+	test_grep hint: err &&
 	echo 0000 | git cat-file --batch >actual 2>err &&
 	test_cmp expect actual &&
-	test_i18ngrep hint: err
+	test_grep hint: err
 '
 
 test_done
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
index d868a08..a835a19 100755
--- a/t/t1514-rev-parse-push.sh
+++ b/t/t1514-rev-parse-push.sh
@@ -4,6 +4,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 resolve () {
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index 9368d82..62e7fd1 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -118,7 +118,7 @@
 		fi &&
 		git add a &&
 		echo $EXPECTED_OUTPUT_VERSION >expect &&
-		test-tool index-version <.git/index >actual &&
+		git update-index --show-index-version >actual &&
 		test_cmp expect actual
 	)
 }
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index b4ab166..a7b7263 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -43,7 +43,7 @@
 	git config splitIndex.maxPercentChange 100 &&
 	git update-index --split-index &&
 	test-tool dump-split-index .git/index >actual &&
-	indexversion=$(test-tool index-version <.git/index) &&
+	indexversion=$(git update-index --show-index-version) &&
 
 	# NEEDSWORK: Stop hard-coding checksums.
 	if test "$indexversion" = "4"
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
index 3506f62..8b0234c 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -156,25 +156,15 @@
 	mkdir bad-hooks &&
 	write_script bad-hooks/test-hook "/bad/path/no/spaces" </dev/null &&
 
-	# TODO: We should emit the same (or at least a more similar)
-	# error on MINGW (essentially Git for Windows) and all other
-	# platforms.. See the OS-specific code in start_command()
-	if test_have_prereq !MINGW
-	then
-		cat >expect <<-\EOF
-		fatal: cannot run bad-hooks/test-hook: ...
-		EOF
-	else
-		cat >expect <<-\EOF
-		error: cannot spawn bad-hooks/test-hook: ...
-		EOF
-	fi &&
 	test_expect_code 1 git \
 		-c core.hooksPath=bad-hooks \
 		hook run test-hook >out 2>err &&
 	test_must_be_empty out &&
-	sed -e "s/test-hook: .*/test-hook: .../" <err >actual &&
-	test_cmp expect actual
+
+	# TODO: We should emit the same (or at least a more similar)
+	# error on MINGW (essentially Git for Windows) and all other
+	# platforms.. See the OS-specific code in start_command()
+	grep -E "^(error|fatal): cannot (exec|spawn) .*bad-hooks/test-hook" err
 '
 
 test_expect_success 'stdin to hooks' '
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index b16d69c..98e818f 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -93,7 +93,7 @@
 	rm -f path* .merge_* actual &&
 	test_must_fail git checkout-index --stage=all --temp \
 		-- does-not-exist 2>stderr &&
-	test_i18ngrep not.in.the.cache stderr
+	test_grep not.in.the.cache stderr
 '
 
 test_expect_success 'checkout all stages/one file to nothing' '
@@ -117,6 +117,26 @@
 	test $(cat $s3) = tree3path1)
 '
 
+test_expect_success '--stage=all implies --temp' '
+	rm -f path* .merge_* actual &&
+	git checkout-index --stage=all -- path1 &&
+	test_path_is_missing path1
+'
+
+test_expect_success 'overriding --stage=all resets implied --temp' '
+	rm -f path* .merge_* actual &&
+	git checkout-index --stage=all --stage=2 -- path1 &&
+	echo tree2path1 >expect &&
+	test_cmp expect path1
+'
+
+test_expect_success '--stage=all --no-temp is rejected' '
+	rm -f path* .merge_* actual &&
+	test_must_fail git checkout-index --stage=all --no-temp -- path1 2>err &&
+	grep -v "already exists" err &&
+	grep "options .--stage=all. and .--no-temp. cannot be used together" err
+'
+
 test_expect_success 'checkout some stages/one file to temporary files' '
 	rm -f path* .merge_* actual &&
 	git checkout-index --stage=all --temp -- path2 >actual &&
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
index 112682a..67d18cf 100755
--- a/t/t2005-checkout-index-symlinks.sh
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -22,8 +22,10 @@
 git checkout-index symlink &&
 test -f symlink'
 
-test_expect_success \
-'the file must be the blob we added during the setup' '
-test "$(git hash-object -t blob symlink)" = $l'
+test_expect_success 'the file must be the blob we added during the setup' '
+	echo "$l" >expect &&
+	git hash-object -t blob symlink >actual &&
+	test_cmp expect actual
+'
 
 test_done
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
index 5d11987..570ba38 100755
--- a/t/t2006-checkout-index-basic.sh
+++ b/t/t2006-checkout-index-basic.sh
@@ -8,7 +8,7 @@
 
 test_expect_success 'checkout-index --gobbledegook' '
 	test_expect_code 129 git checkout-index --gobbledegook 2>err &&
-	test_i18ngrep "[Uu]sage" err
+	test_grep "[Uu]sage" err
 '
 
 test_expect_success 'checkout-index -h in broken repository' '
@@ -19,18 +19,18 @@
 		>.git/index &&
 		test_expect_code 129 git checkout-index -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage" broken/usage
+	test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'checkout-index reports errors (cmdline)' '
 	test_must_fail git checkout-index -- does-not-exist 2>stderr &&
-	test_i18ngrep not.in.the.cache stderr
+	test_grep not.in.the.cache stderr
 '
 
 test_expect_success 'checkout-index reports errors (stdin)' '
 	echo does-not-exist |
 	test_must_fail git checkout-index --stdin 2>stderr &&
-	test_i18ngrep not.in.the.cache stderr
+	test_grep not.in.the.cache stderr
 '
 for mode in 'case' 'utf-8'
 do
@@ -88,8 +88,8 @@
 	git update-index --index-info <objs &&
 
 	test_must_fail git checkout-index --temp symlink file 2>stderr &&
-	test_i18ngrep "unable to read sha1 file of file ($missing_blob)" stderr &&
-	test_i18ngrep "unable to read sha1 file of symlink ($missing_blob)" stderr
+	test_grep "unable to read sha1 file of file ($missing_blob)" stderr &&
+	test_grep "unable to read sha1 file of symlink ($missing_blob)" stderr
 '
 
 test_expect_success 'checkout-index --temp correctly reports error for submodules' '
@@ -98,7 +98,7 @@
 	git submodule add ./sub &&
 	git commit -m sub &&
 	test_must_fail git checkout-index --temp sub 2>stderr &&
-	test_i18ngrep "cannot create temporary submodule sub" stderr
+	test_grep "cannot create temporary submodule sub" stderr
 '
 
 test_done
diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh
index 9d4b375..82c3bfe 100755
--- a/t/t2010-checkout-ambiguous.sh
+++ b/t/t2010-checkout-ambiguous.sh
@@ -62,8 +62,8 @@
 
 test_expect_success 'accurate error message with more than one ref' '
 	test_must_fail git checkout HEAD main -- 2>actual &&
-	test_i18ngrep 2 actual &&
-	test_i18ngrep "one reference expected, 2 given" actual
+	test_grep 2 actual &&
+	test_grep "one reference expected, 2 given" actual
 '
 
 test_done
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index d9997e7..04f53b1 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -18,12 +18,12 @@
 	test_must_fail git checkout -b newbranch main^{tree}
 '
 
-test_expect_success 'checkout main from invalid HEAD' '
+test_expect_success REFFILES 'checkout main from invalid HEAD' '
 	echo $ZERO_OID >.git/HEAD &&
 	git checkout main --
 '
 
-test_expect_success 'checkout notices failure to lock HEAD' '
+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '
 	test_when_finished "rm -f .git/HEAD.lock" &&
 	>.git/HEAD.lock &&
 	test_must_fail git checkout -b other
@@ -31,11 +31,8 @@
 
 test_expect_success 'create ref directory/file conflict scenario' '
 	git update-ref refs/heads/outer/inner main &&
-
-	# do not rely on symbolic-ref to get a known state,
-	# as it may use the same code we are testing
 	reset_to_df () {
-		echo "ref: refs/heads/outer" >.git/HEAD
+		git symbolic-ref HEAD refs/heads/outer
 	}
 '
 
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 747eb55..c4f9bf0 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -38,26 +38,32 @@
 	verify_state dir/foo index index
 '
 
-test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
-	set_and_save_state dir/foo work head &&
-	test_write_lines n y n | git checkout -p HEAD &&
-	verify_saved_state bar &&
-	verify_saved_state dir/foo
-'
+for opt in "HEAD" "@"
+do
+	test_expect_success "git checkout -p $opt with NO staged changes: abort" '
+		set_and_save_state dir/foo work head &&
+		test_write_lines n y n | git checkout -p $opt >output &&
+		verify_saved_state bar &&
+		verify_saved_state dir/foo &&
+		test_grep "Discard" output
+	'
 
-test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
-	test_write_lines n y y | git checkout -p HEAD &&
-	verify_saved_state bar &&
-	verify_state dir/foo head head
-'
+	test_expect_success "git checkout -p $opt with NO staged changes: apply" '
+		test_write_lines n y y | git checkout -p $opt >output &&
+		verify_saved_state bar &&
+		verify_state dir/foo head head &&
+		test_grep "Discard" output
+	'
 
-test_expect_success 'git checkout -p HEAD with change already staged' '
-	set_state dir/foo index index &&
-	# the third n is to get out in case it mistakenly does not apply
-	test_write_lines n y n | git checkout -p HEAD &&
-	verify_saved_state bar &&
-	verify_state dir/foo head head
-'
+	test_expect_success "git checkout -p $opt with change already staged" '
+		set_state dir/foo index index &&
+		# the third n is to get out in case it mistakenly does not apply
+		test_write_lines n y n | git checkout -p $opt >output &&
+		verify_saved_state bar &&
+		verify_state dir/foo head head &&
+		test_grep "Discard" output
+	'
+done
 
 test_expect_success 'git checkout -p HEAD^...' '
 	# the third n is to get out in case it mistakenly does not apply
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
index 947d158..a5c7358 100755
--- a/t/t2017-checkout-orphan.sh
+++ b/t/t2017-checkout-orphan.sh
@@ -86,7 +86,7 @@
 	git rev-parse --verify delta@{0}
 '
 
-test_expect_success REFFILES '--orphan does not make reflog when core.logAllRefUpdates = false' '
+test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
 	git checkout main &&
 	git config core.logAllRefUpdates false &&
 	git checkout --orphan epsilon &&
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index 8581ad3..43551cc 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -278,12 +278,12 @@
 
 test_expect_success 'checkout -b rejects an invalid start point' '
 	test_must_fail git checkout -b branch4 file1 2>err &&
-	test_i18ngrep "is not a commit" err
+	test_grep "is not a commit" err
 '
 
 test_expect_success 'checkout -b rejects an extra path argument' '
 	test_must_fail git checkout -b branch5 branch1 file1 2>err &&
-	test_i18ngrep "Cannot update paths and switch to branch" err
+	test_grep "Cannot update paths and switch to branch" err
 '
 
 test_done
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
index 2c8c926..c67261e 100755
--- a/t/t2019-checkout-ambiguous-ref.sh
+++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -16,7 +16,7 @@
 '
 
 test_expect_success 'checkout ambiguous ref succeeds' '
-	git checkout ambiguity >stdout 2>stderr
+	git checkout ambiguity 2>stderr
 '
 
 test_expect_success 'checkout produces ambiguity warning' '
@@ -32,12 +32,12 @@
 '
 
 test_expect_success 'checkout reports switch to branch' '
-	test_i18ngrep "Switched to branch" stderr &&
-	test_i18ngrep ! "^HEAD is now at" stderr
+	test_grep "Switched to branch" stderr &&
+	test_grep ! "^HEAD is now at" stderr
 '
 
 test_expect_success 'checkout vague ref succeeds' '
-	git checkout vagueness >stdout 2>stderr &&
+	git checkout vagueness 2>stderr &&
 	test_set_prereq VAGUENESS_SUCCESS
 '
 
@@ -54,8 +54,8 @@
 '
 
 test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
-	test_i18ngrep "Switched to branch" stderr &&
-	test_i18ngrep ! "^HEAD is now at" stderr
+	test_grep "Switched to branch" stderr &&
+	test_grep ! "^HEAD is now at" stderr
 '
 
 test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 2eab647..8d90d02 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -17,12 +17,12 @@
 
 PREV_HEAD_DESC='Previous HEAD position was'
 check_orphan_warning() {
-	test_i18ngrep "you are leaving $2 behind" "$1" &&
-	test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
+	test_grep "you are leaving $2 behind" "$1" &&
+	test_grep ! "$PREV_HEAD_DESC" "$1"
 }
 check_no_orphan_warning() {
-	test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
-	test_i18ngrep "$PREV_HEAD_DESC" "$1"
+	test_grep ! "you are leaving .* commit.*behind" "$1" &&
+	test_grep "$PREV_HEAD_DESC" "$1"
 }
 
 reset () {
@@ -45,6 +45,18 @@
 	check_not_detached
 '
 
+for opt in "HEAD" "@"
+do
+	test_expect_success "checkout $opt no-op/don't detach" '
+		reset &&
+		cat .git/HEAD >expect &&
+		git checkout $opt &&
+		cat .git/HEAD >actual &&
+		check_not_detached &&
+		test_cmp expect actual
+	'
+done
+
 test_expect_success 'checkout tag detaches' '
 	reset &&
 	git checkout tag &&
@@ -164,7 +176,10 @@
 	git config branch.child.merge refs/heads/main &&
 	git checkout child^ &&
 	git checkout child >stdout &&
-	test_cmp expect stdout
+	test_cmp expect stdout &&
+
+	git checkout --detach child >stdout &&
+	test_grep ! "can be fast-forwarded\." stdout
 '
 
 test_expect_success 'no advice given for explicit detached head state' '
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
index 713c3fa..ecfacf0 100755
--- a/t/t2021-checkout-overwrite.sh
+++ b/t/t2021-checkout-overwrite.sh
@@ -50,10 +50,13 @@
 
 test_expect_success SYMLINKS 'the symlink remained' '
 
-	test_when_finished "rm a/b" &&
 	test -h a/b
 '
 
+test_expect_success 'cleanup after previous symlink tests' '
+	rm a/b
+'
+
 test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing entries' '
 	git checkout -f start &&
 	mkdir dir &&
@@ -66,4 +69,15 @@
 	test_path_is_file untracked/f
 '
 
+test_expect_success 'checkout --overwrite-ignore should succeed if only ignored files in the way' '
+	git checkout -b df_conflict &&
+	test_commit contents some_dir &&
+	git checkout start &&
+	mkdir some_dir &&
+	echo autogenerated information >some_dir/ignore &&
+	echo ignore >.git/info/exclude &&
+	git checkout --overwrite-ignore df_conflict &&
+	test_path_is_file some_dir
+'
+
 test_done
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index 4a1c901..a3b1449 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -93,7 +93,7 @@
 
 	test_must_fail git checkout ambiguous_branch_and_file 2>err &&
 
-	test_i18ngrep "matched multiple (2) remote tracking branches" err &&
+	test_grep "matched multiple (2) remote tracking branches" err &&
 
 	# file must not be altered
 	test_cmp expect ambiguous_branch_and_file
@@ -105,20 +105,20 @@
 	test_must_fail git checkout foo 2>stderr &&
 	test_branch main &&
 	status_uno_is_clean &&
-	test_i18ngrep "^hint: " stderr &&
+	test_grep "^hint: " stderr &&
 	test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
 		checkout foo 2>stderr &&
 	test_branch main &&
 	status_uno_is_clean &&
-	test_i18ngrep ! "^hint: " stderr
+	test_grep ! "^hint: " stderr
 '
 
-test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
+test_expect_success 'checkout -p with multiple remotes does not print advice' '
 	git checkout -B main &&
 	test_might_fail git branch -D foo &&
 
 	git checkout -p foo 2>stderr &&
-	test_i18ngrep ! "^hint: " stderr &&
+	test_grep ! "^hint: " stderr &&
 	status_uno_is_clean
 '
 
@@ -305,10 +305,13 @@
 	test_config branch.strict.merge refs/heads/main &&
 	test_config branch.loose.merge main &&
 
-	git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+	git checkout strict >expect.raw 2>&1 &&
+	sed -e "s/strict/BRANCHNAME/g" <expect.raw >expect &&
 	status_uno_is_clean &&
-	git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+	git checkout loose >actual.raw 2>&1 &&
+	sed -e "s/loose/BRANCHNAME/g" <actual.raw >actual &&
 	status_uno_is_clean &&
+	grep BRANCHNAME actual &&
 
 	test_cmp expect actual
 '
diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh
index 3832c3d..246609d 100755
--- a/t/t2025-checkout-no-overlay.sh
+++ b/t/t2025-checkout-no-overlay.sh
@@ -26,7 +26,7 @@
 
 test_expect_success 'checkout -p --overlay is disallowed' '
 	test_must_fail git checkout -p --overlay HEAD 2>actual &&
-	test_i18ngrep "fatal: options .-p. and .--overlay. cannot be used together" actual
+	test_grep "fatal: options .-p. and .--overlay. cannot be used together" actual
 '
 
 test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
diff --git a/t/t2026-checkout-pathspec-file.sh b/t/t2026-checkout-pathspec-file.sh
index 9c651ae..acd5521 100755
--- a/t/t2026-checkout-pathspec-file.sh
+++ b/t/t2026-checkout-pathspec-file.sh
@@ -149,16 +149,16 @@
 	echo fileA.t >list &&
 
 	test_must_fail git checkout --pathspec-from-file=list --detach 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--detach. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--detach. cannot be used together" err &&
 
 	test_must_fail git checkout --pathspec-from-file=list --patch 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
 	test_must_fail git checkout --pathspec-from-file=list -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git checkout --pathspec-file-nul 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
 '
 
 test_done
diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
index dca35aa..98f16c7 100755
--- a/t/t2027-checkout-track.sh
+++ b/t/t2027-checkout-track.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -21,7 +22,7 @@
 
 test_expect_success 'checkout --track -b rejects an extra path argument' '
 	test_must_fail git checkout --track -b branch2 main one.t 2>err &&
-	test_i18ngrep "cannot be used with updating paths" err
+	test_grep "cannot be used with updating paths" err
 '
 
 test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
index 2d8c70b..be3fcdd 100755
--- a/t/t2030-unresolve-info.sh
+++ b/t/t2030-unresolve-info.sh
@@ -37,11 +37,17 @@
 	git checkout second^0 &&
 	test_tick &&
 	test_must_fail git merge third^0 &&
-	echo merge does not leave anything &&
 	check_resolve_undo empty &&
-	echo different >fi/le &&
-	git add fi/le &&
-	echo resolving records &&
+
+	# how should the conflict be resolved?
+	case "$1" in
+	remove)
+		rm -f file/le && git rm fi/le
+		;;
+	*) # modify
+		echo different >fi/le && git add fi/le
+		;;
+	esac
 	check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le
 }
 
@@ -122,6 +128,37 @@
 test_expect_success 'unmerge with plumbing' '
 	prime_resolve_undo &&
 	git update-index --unresolve fi/le &&
+	git ls-files --resolve-undo fi/le >actual &&
+	test_must_be_empty actual &&
+	git ls-files -u >actual &&
+	test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge can be done even after committing' '
+	prime_resolve_undo &&
+	git commit -m "record to nuke MERGE_HEAD" &&
+	git update-index --unresolve fi/le &&
+	git ls-files --resolve-undo fi/le >actual &&
+	test_must_be_empty actual &&
+	git ls-files -u >actual &&
+	test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge removal' '
+	prime_resolve_undo remove &&
+	git update-index --unresolve fi/le &&
+	git ls-files --resolve-undo fi/le >actual &&
+	test_must_be_empty actual &&
+	git ls-files -u >actual &&
+	test_line_count = 3 actual
+'
+
+test_expect_success 'unmerge removal after committing' '
+	prime_resolve_undo remove &&
+	git commit -m "record to nuke MERGE_HEAD" &&
+	git update-index --unresolve fi/le &&
+	git ls-files --resolve-undo fi/le >actual &&
+	test_must_be_empty actual &&
 	git ls-files -u >actual &&
 	test_line_count = 3 actual
 '
@@ -191,7 +228,7 @@
 	git commit -m "add differently" &&
 	test_must_fail git merge fifth &&
 	git rerere forget add-differently 2>actual &&
-	test_i18ngrep "no remembered" actual
+	test_grep "no remembered" actual
 '
 
 test_expect_success 'resolve-undo keeps blobs from gc' '
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index 5a7caf9..c91c4db 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -146,4 +146,35 @@
 	test_cmp_config "" --default "" branch.main2.merge
 '
 
+test_expect_success 'switch back when temporarily detached and checked out elsewhere ' '
+	test_when_finished "
+		git worktree remove wt1 ||:
+		git worktree remove wt2 ||:
+		git checkout - ||:
+		git branch -D shared ||:
+	" &&
+	git checkout -b shared &&
+	test_commit shared-first &&
+	HASH1=$(git rev-parse --verify HEAD) &&
+	test_commit shared-second &&
+	test_commit shared-third &&
+	HASH2=$(git rev-parse --verify HEAD) &&
+	git worktree add wt1 -f shared &&
+	git -C wt1 bisect start &&
+	git -C wt1 bisect good $HASH1 &&
+	git -C wt1 bisect bad $HASH2 &&
+	git worktree add wt2 -f shared &&
+	git -C wt2 bisect start &&
+	git -C wt2 bisect good $HASH1 &&
+	git -C wt2 bisect bad $HASH2 &&
+	# we test in both worktrees to ensure that works
+	# as expected with "first" and "next" worktrees
+	test_must_fail git -C wt1 switch shared &&
+	test_must_fail git -C wt1 switch -C shared &&
+	git -C wt1 switch --ignore-other-worktrees shared &&
+	test_must_fail git -C wt2 switch shared &&
+	test_must_fail git -C wt2 switch -C shared &&
+	git -C wt2 switch --ignore-other-worktrees shared
+'
+
 test_done
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index 7c43ddf..ac40494 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -137,4 +138,87 @@
 	test_must_fail git rev-parse HEAD:new1
 '
 
+test_expect_success 'restore --merge to unresolve' '
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	{
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file"
+	} | git update-index --index-info &&
+	echo nothing >file &&
+	git restore --worktree --merge file &&
+	cat >expect <<-\EOF &&
+	<<<<<<< ours
+	ourside
+	=======
+	theirside
+	>>>>>>> theirs
+	EOF
+	test_cmp expect file
+'
+
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	{
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file"
+	} | git update-index --index-info &&
+	echo nothing >file &&
+	git add file &&
+	git restore --worktree --merge file &&
+	cat >expect <<-\EOF &&
+	<<<<<<< ours
+	ourside
+	=======
+	theirside
+	>>>>>>> theirs
+	EOF
+	test_cmp expect file
+'
+
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	{
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file"
+	} | git update-index --index-info &&
+	git rm -f file &&
+	git restore --worktree --merge file &&
+	cat >expect <<-\EOF &&
+	<<<<<<< ours
+	ourside
+	=======
+	theirside
+	>>>>>>> theirs
+	EOF
+	test_cmp expect file
+'
+
+test_expect_success 'restore with merge options are incompatible with certain options' '
+	for opts in \
+		"--staged --ours" \
+		"--staged --theirs" \
+		"--staged --merge" \
+		"--source=HEAD --ours" \
+		"--source=HEAD --theirs" \
+		"--source=HEAD --merge" \
+		"--staged --conflict=diff3" \
+		"--staged --worktree --ours" \
+		"--staged --worktree --theirs" \
+		"--staged --worktree --merge" \
+		"--staged --worktree --conflict=zdiff3"
+	do
+		test_must_fail git restore $opts . 2>err &&
+		grep "cannot be used" err || return
+	done
+'
+
 test_done
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
index b5c5c0f..42d5522 100755
--- a/t/t2071-restore-patch.sh
+++ b/t/t2071-restore-patch.sh
@@ -2,9 +2,10 @@
 
 test_description='git restore --patch'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
 	mkdir dir &&
 	echo parent >dir/foo &&
 	echo dummy >bar &&
@@ -16,43 +17,47 @@
 	save_head
 '
 
-test_expect_success PERL 'restore -p without pathspec is fine' '
+test_expect_success 'restore -p without pathspec is fine' '
 	echo q >cmd &&
 	git restore -p <cmd
 '
 
 # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
 
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
 	set_and_save_state dir/foo work head &&
 	test_write_lines n n | git restore -p &&
 	verify_saved_state bar &&
 	verify_saved_state dir/foo
 '
 
-test_expect_success PERL 'git restore -p' '
+test_expect_success 'git restore -p' '
 	set_and_save_state dir/foo work head &&
 	test_write_lines n y | git restore -p &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
-test_expect_success PERL 'git restore -p with staged changes' '
+test_expect_success 'git restore -p with staged changes' '
 	set_state dir/foo work index &&
 	test_write_lines n y | git restore -p &&
 	verify_saved_state bar &&
 	verify_state dir/foo index index
 '
 
-test_expect_success PERL 'git restore -p --source=HEAD' '
-	set_state dir/foo work index &&
-	# the third n is to get out in case it mistakenly does not apply
-	test_write_lines n y n | git restore -p --source=HEAD &&
-	verify_saved_state bar &&
-	verify_state dir/foo head index
-'
+for opt in "HEAD" "@"
+do
+	test_expect_success "git restore -p --source=$opt" '
+		set_state dir/foo work index &&
+		# the third n is to get out in case it mistakenly does not apply
+		test_write_lines n y n | git restore -p --source=$opt >output &&
+		verify_saved_state bar &&
+		verify_state dir/foo head index &&
+		test_grep "Discard" output
+	'
+done
 
-test_expect_success PERL 'git restore -p --source=HEAD^' '
+test_expect_success 'git restore -p --source=HEAD^' '
 	set_state dir/foo work index &&
 	# the third n is to get out in case it mistakenly does not apply
 	test_write_lines n y n | git restore -p --source=HEAD^ &&
@@ -60,7 +65,7 @@
 	verify_state dir/foo parent index
 '
 
-test_expect_success PERL 'git restore -p --source=HEAD^...' '
+test_expect_success 'git restore -p --source=HEAD^...' '
 	set_state dir/foo work index &&
 	# the third n is to get out in case it mistakenly does not apply
 	test_write_lines n y n | git restore -p --source=HEAD^... &&
@@ -68,7 +73,7 @@
 	verify_state dir/foo parent index
 '
 
-test_expect_success PERL 'git restore -p handles deletion' '
+test_expect_success 'git restore -p handles deletion' '
 	set_state dir/foo work index &&
 	rm dir/foo &&
 	test_write_lines n y | git restore -p &&
@@ -81,21 +86,21 @@
 # dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
 # the failure case (and thus get out of the loop).
 
-test_expect_success PERL 'path limiting works: dir' '
+test_expect_success 'path limiting works: dir' '
 	set_state dir/foo work head &&
 	test_write_lines y n | git restore -p dir &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
-test_expect_success PERL 'path limiting works: -- dir' '
+test_expect_success 'path limiting works: -- dir' '
 	set_state dir/foo work head &&
 	test_write_lines y n | git restore -p -- dir &&
 	verify_saved_state bar &&
 	verify_state dir/foo head head
 '
 
-test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+test_expect_success 'path limiting works: HEAD^ -- dir' '
 	set_state dir/foo work head &&
 	# the third n is to get out in case it mistakenly does not apply
 	test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
@@ -103,7 +108,7 @@
 	verify_state dir/foo parent head
 '
 
-test_expect_success PERL 'path limiting works: foo inside dir' '
+test_expect_success 'path limiting works: foo inside dir' '
 	set_state dir/foo work head &&
 	# the third n is to get out in case it mistakenly does not apply
 	test_write_lines y n n | (cd dir && git restore -p foo) &&
@@ -111,7 +116,7 @@
 	verify_state dir/foo head head
 '
 
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
 	verify_saved_head
 '
 
diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh
index c22669b..86c9c88 100755
--- a/t/t2072-restore-pathspec-file.sh
+++ b/t/t2072-restore-pathspec-file.sh
@@ -2,6 +2,7 @@
 
 test_description='restore --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
@@ -152,16 +153,16 @@
 	>empty_list &&
 
 	test_must_fail git restore --pathspec-from-file=list --patch --source=HEAD^1 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
 	test_must_fail git restore --pathspec-from-file=list --source=HEAD^1 -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git restore --pathspec-file-nul --source=HEAD^1 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
 	test_must_fail git restore --pathspec-from-file=empty_list --source=HEAD^1 2>err &&
-	test_i18ngrep -e "you must specify path(s) to restore" err
+	test_grep -e "you must specify path(s) to restore" err
 '
 
 test_expect_success 'wildcard pathspec matches file in subdirectory' '
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index b8686aa..7ec7f30 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -11,27 +11,27 @@
 sane_unset GIT_TEST_SPLIT_INDEX
 
 test_set_index_version () {
-    GIT_INDEX_VERSION="$1"
-    export GIT_INDEX_VERSION
+	GIT_INDEX_VERSION="$1"
+	export GIT_INDEX_VERSION
 }
 
 test_set_index_version 3
 
-cat >expect.full <<EOF
-H 1
-H 2
-H sub/1
-H sub/2
-EOF
-
-cat >expect.skip <<EOF
-S 1
-H 2
-S sub/1
-H sub/2
-EOF
-
 test_expect_success 'setup' '
+	cat >expect.full <<-\EOF &&
+	H 1
+	H 2
+	H sub/1
+	H sub/2
+	EOF
+
+	cat >expect.skip <<-\EOF &&
+	S 1
+	H 2
+	S sub/1
+	H sub/2
+	EOF
+
 	mkdir sub &&
 	touch ./1 ./2 sub/1 sub/2 &&
 	git add 1 2 sub/1 sub/2 &&
@@ -39,7 +39,7 @@
 '
 
 test_expect_success 'index is at version 2' '
-	test "$(test-tool index-version < .git/index)" = 2
+	test "$(git update-index --show-index-version)" = 2
 '
 
 test_expect_success 'update-index --skip-worktree' '
@@ -48,7 +48,7 @@
 '
 
 test_expect_success 'index is at version 3 after having some skip-worktree entries' '
-	test "$(test-tool index-version < .git/index)" = 3
+	test "$(git update-index --show-index-version)" = 3
 '
 
 test_expect_success 'ls-files -t' '
@@ -61,7 +61,7 @@
 '
 
 test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '
-	test "$(test-tool index-version < .git/index)" = 2
+	test "$(git update-index --show-index-version)" = 2
 '
 
 test_done
diff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh
index d943ddf..95c004d 100755
--- a/t/t2106-update-index-assume-unchanged.sh
+++ b/t/t2106-update-index-assume-unchanged.sh
@@ -22,7 +22,7 @@
 	echo dirt >file &&
 	git update-index --assume-unchanged file &&
 	test_must_fail git checkout - 2>err &&
-	test_i18ngrep overwritten err
+	test_grep overwritten err
 '
 
 test_done
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
index 07e6de8..cc72ead 100755
--- a/t/t2107-update-index-basic.sh
+++ b/t/t2107-update-index-basic.sh
@@ -14,7 +14,7 @@
 
 test_expect_success 'update-index --nonsense dumps usage' '
 	test_expect_code 129 git update-index --nonsense 2>err &&
-	test_i18ngrep "[Uu]sage: git update-index" err
+	test_grep "[Uu]sage: git update-index" err
 '
 
 test_expect_success 'update-index -h with corrupt index' '
@@ -25,7 +25,7 @@
 		>.git/index &&
 		test_expect_code 129 git update-index -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage: git update-index" broken/usage
+	test_grep "[Uu]sage: git update-index" broken/usage
 '
 
 test_expect_success '--cacheinfo complains of missing arguments' '
@@ -83,7 +83,7 @@
 	cd repo &&
 	git config core.worktree ../../worktree &&
 	# --refresh triggers late setup_work_tree,
-	# active_cache_changed is zero, rollback_lock_file fails
+	# the_index.cache_changed is zero, rollback_lock_file fails
 	git update-index --refresh --verbose >out &&
 	test_must_be_empty out &&
 	! test -f .git/index.lock
@@ -111,4 +111,35 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--index-version' '
+	git commit --allow-empty -m snap &&
+	git reset --hard &&
+	git rm -f -r --cached . &&
+
+	# The default index version is 2 --- update this test
+	# when you change it in the code
+	git update-index --show-index-version >actual &&
+	echo 2 >expect &&
+	test_cmp expect actual &&
+
+	# The next test wants us to be using version 2
+	git update-index --index-version 2 &&
+
+	git update-index --index-version 4 --verbose >actual &&
+	echo "index-version: was 2, set to 4" >expect &&
+	test_cmp expect actual &&
+
+	git update-index --index-version 4 --verbose >actual &&
+	echo "index-version: was 4, set to 4" >expect &&
+	test_cmp expect actual &&
+
+	git update-index --index-version 2 --verbose >actual &&
+	echo "index-version: was 4, set to 2" >expect &&
+	test_cmp expect actual &&
+
+	# non-verbose should be silent
+	git update-index --index-version 4 >actual &&
+	test_must_be_empty actual
+'
+
 test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index be394f1..df235ac 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -65,6 +65,16 @@
 	test_must_be_empty out
 '
 
+test_expect_success 'error out when passing untracked path' '
+	git reset --hard &&
+	echo content >>baz &&
+	echo content >>top &&
+	test_must_fail git add -u baz top 2>err &&
+	test_grep -e "error: pathspec .baz. did not match any file(s) known to git" err &&
+	git diff --cached --name-only >actual &&
+	test_must_be_empty actual
+'
+
 test_expect_success 'cache tree has not been corrupted' '
 
 	git ls-files -s |
@@ -197,4 +207,15 @@
 	! grep "non-existent" actual
 '
 
+test_expect_success '"commit -a" implies "add -u" if index becomes empty' '
+	git rm -rf \* &&
+	git commit -m clean-slate &&
+	test_commit file1 &&
+	rm file1.t &&
+	test_tick &&
+	git commit -a -m remove &&
+	git ls-tree HEAD: >out &&
+	test_must_be_empty out
+'
+
 test_done
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index ebf58db..8fa44a9 100755
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
@@ -173,7 +173,7 @@
 		git add -N third &&
 
 		git status | grep -v "^?" >actual.1 &&
-		test_i18ngrep "renamed: *first -> third" actual.1 &&
+		test_grep "renamed: *first -> third" actual.1 &&
 
 		git status --porcelain | grep -v "^?" >actual.2 &&
 		cat >expected.2 <<-\EOF &&
@@ -213,8 +213,8 @@
 		git add -N third &&
 
 		git status | grep -v "^?" >actual.1 &&
-		test_i18ngrep "renamed: *first -> second" actual.1 &&
-		test_i18ngrep "renamed: *second -> third" actual.1 &&
+		test_grep "renamed: *first -> second" actual.1 &&
+		test_grep "renamed: *second -> third" actual.1 &&
 
 		git status --porcelain | grep -v "^?" >actual.2 &&
 		cat >expected.2 <<-\EOF &&
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
index 89424ab..b7cf1e4 100755
--- a/t/t2204-add-ignored.sh
+++ b/t/t2204-add-ignored.sh
@@ -36,7 +36,7 @@
 	'
 
 	test_expect_success "complaints for ignored $i output" '
-		test_i18ngrep -e "Use -f if" err
+		test_grep -e "Use -f if" err
 	'
 
 	test_expect_success "complaints for ignored $i with unignored file" '
@@ -46,7 +46,7 @@
 		test_must_be_empty out
 	'
 	test_expect_success "complaints for ignored $i with unignored file output" '
-		test_i18ngrep -e "Use -f if" err
+		test_grep -e "Use -f if" err
 	'
 done
 
@@ -65,7 +65,7 @@
 	test_expect_success "complaints for ignored $i in dir output" '
 		(
 			cd dir &&
-			test_i18ngrep -e "Use -f if" err
+			test_grep -e "Use -f if" err
 		)
 	'
 done
@@ -85,7 +85,7 @@
 	test_expect_success "complaints for ignored $i in sub output" '
 		(
 			cd sub &&
-			test_i18ngrep -e "Use -f if" err
+			test_grep -e "Use -f if" err
 		)
 	'
 done
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index d587e0b..c28c041 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -121,7 +121,30 @@
 test_expect_success 'die the same branch is already checked out' '
 	(
 		cd here &&
-		test_must_fail git checkout newmain
+		test_must_fail git checkout newmain 2>actual &&
+		grep "already used by worktree at" actual
+	)
+'
+
+test_expect_success 'refuse to reset a branch in use elsewhere' '
+	(
+		cd here &&
+
+		# we know we are on detached HEAD but just in case ...
+		git checkout --detach HEAD &&
+		git rev-parse --verify HEAD >old.head &&
+
+		git rev-parse --verify refs/heads/newmain >old.branch &&
+		test_must_fail git checkout -B newmain 2>error &&
+		git rev-parse --verify refs/heads/newmain >new.branch &&
+		git rev-parse --verify HEAD >new.head &&
+
+		grep "already used by worktree at" error &&
+		test_cmp old.branch new.branch &&
+		test_cmp old.head new.head &&
+
+		# and we must be still on the same detached HEAD state
+		test_must_fail git symbolic-ref HEAD
 	)
 '
 
@@ -298,17 +321,24 @@
 	test_must_fail git -C mish/mash symbolic-ref HEAD
 '
 
-test_expect_success '"add" -b/-B mutually exclusive' '
-	test_must_fail git worktree add -b poodle -B poodle bamboo main
-'
+# Helper function to test mutually exclusive options.
+#
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_excl () {
+	local opts="$*" &&
+	test_expect_success "'worktree add' with '$opts' has mutually exclusive options" '
+		test_must_fail git worktree add $opts 2>actual &&
+		grep -E "fatal:( options)? .* cannot be used together" actual
+	'
+}
 
-test_expect_success '"add" -b/--detach mutually exclusive' '
-	test_must_fail git worktree add -b poodle --detach bamboo main
-'
-
-test_expect_success '"add" -B/--detach mutually exclusive' '
-	test_must_fail git worktree add -B poodle --detach bamboo main
-'
+test_wt_add_excl -b poodle -B poodle bamboo main
+test_wt_add_excl -b poodle --detach bamboo main
+test_wt_add_excl -B poodle --detach bamboo main
+test_wt_add_excl --orphan --detach bamboo
+test_wt_add_excl --orphan --no-checkout bamboo
+test_wt_add_excl --orphan bamboo main
+test_wt_add_excl --orphan -b bamboo wtdir/ main
 
 test_expect_success '"add -B" fails if the branch is checked out' '
 	git rev-parse newmain >before &&
@@ -326,10 +356,111 @@
 '
 
 test_expect_success 'add --quiet' '
+	test_when_finished "git worktree remove -f -f another-worktree" &&
 	git worktree add --quiet another-worktree main 2>actual &&
 	test_must_be_empty actual
 '
 
+test_expect_success 'add --quiet -b' '
+	test_when_finished "git branch -D quietnewbranch" &&
+	test_when_finished "git worktree remove -f -f another-worktree" &&
+	git worktree add --quiet -b quietnewbranch another-worktree 2>actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success '"add --orphan"' '
+	test_when_finished "git worktree remove -f -f orphandir" &&
+	git worktree add --orphan -b neworphan orphandir &&
+	echo refs/heads/neworphan >expected &&
+	git -C orphandir symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '"add --orphan (no -b)"' '
+	test_when_finished "git worktree remove -f -f neworphan" &&
+	git worktree add --orphan neworphan &&
+	echo refs/heads/neworphan >expected &&
+	git -C neworphan symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '"add --orphan --quiet"' '
+	test_when_finished "git worktree remove -f -f orphandir" &&
+	git worktree add --quiet --orphan -b neworphan orphandir 2>log.actual &&
+	test_must_be_empty log.actual &&
+	echo refs/heads/neworphan >expected &&
+	git -C orphandir symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '"add --orphan" fails if the branch already exists' '
+	test_when_finished "git branch -D existingbranch" &&
+	git worktree add -b existingbranch orphandir main &&
+	git worktree remove orphandir &&
+	test_must_fail git worktree add --orphan -b existingbranch orphandir
+'
+
+test_expect_success '"add --orphan" with empty repository' '
+	test_when_finished "rm -rf empty_repo" &&
+	echo refs/heads/newbranch >expected &&
+	GIT_DIR="empty_repo" git init --bare &&
+	git -C empty_repo worktree add --orphan -b newbranch worktreedir &&
+	git -C empty_repo/worktreedir symbolic-ref HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '"add" worktree with orphan branch and lock' '
+	git worktree add --lock --orphan -b orphanbr orphan-with-lock &&
+	test_when_finished "git worktree unlock orphan-with-lock || :" &&
+	test -f .git/worktrees/orphan-with-lock/locked
+'
+
+test_expect_success '"add" worktree with orphan branch, lock, and reason' '
+	lock_reason="why not" &&
+	git worktree add --detach --lock --reason "$lock_reason" orphan-with-lock-reason main &&
+	test_when_finished "git worktree unlock orphan-with-lock-reason || :" &&
+	test -f .git/worktrees/orphan-with-lock-reason/locked &&
+	echo "$lock_reason" >expect &&
+	test_cmp expect .git/worktrees/orphan-with-lock-reason/locked
+'
+
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_orphan_hint () {
+	local context="$1" &&
+	local use_branch=$2 &&
+	shift 2 &&
+	local opts="$*" &&
+	test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
+		test_when_finished "rm -rf repo" &&
+		git init repo &&
+		(cd repo && test_commit commit) &&
+		git -C repo switch --orphan noref &&
+		test_must_fail git -C repo worktree add $opts foobar/ 2>actual &&
+		! grep "error: unknown switch" actual &&
+		grep "hint: If you meant to create a worktree containing a new unborn branch" actual &&
+		if [ $use_branch -eq 1 ]
+		then
+			grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual
+		else
+			grep -E "^hint: +git worktree add --orphan [^ ]+$" actual
+		fi
+
+	'
+}
+
+test_wt_add_orphan_hint 'no opts' 0
+test_wt_add_orphan_hint '-b' 1 -b foobar_branch
+test_wt_add_orphan_hint '-B' 1 -B foobar_branch
+
+test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD w/ --quiet" '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(cd repo && test_commit commit) &&
+	test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&
+	! grep "error: unknown switch" actual &&
+	! grep "hint: If you meant to create a worktree containing a new unborn branch" actual
+'
+
 test_expect_success 'local clone from linked checkout' '
 	git clone --local here here-clone &&
 	( cd here-clone && git fsck )
@@ -359,7 +490,8 @@
 		cd under-rebase &&
 		set_fake_editor &&
 		FAKE_LINES="edit 1" git rebase -i HEAD^ &&
-		git worktree list | grep "under-rebase.*detached HEAD"
+		git worktree list >actual &&
+		grep "under-rebase.*detached HEAD" actual
 	)
 '
 
@@ -400,7 +532,8 @@
 		git bisect start &&
 		git bisect bad &&
 		git bisect good HEAD~2 &&
-		git worktree list | grep "under-bisect.*detached HEAD" &&
+		git worktree list >actual &&
+		grep "under-bisect.*detached HEAD" actual &&
 		test_must_fail git worktree add new-bisect under-bisect &&
 		! test -d new-bisect
 	)
@@ -446,6 +579,14 @@
 	)
 }
 
+test_expect_success '"add" <path> <remote/branch> w/ no HEAD' '
+	test_when_finished rm -rf repo_upstream repo_local foo &&
+	setup_remote_repo repo_upstream repo_local &&
+	git -C repo_local config --bool core.bare true &&
+	git -C repo_local branch -D main &&
+	git -C repo_local worktree add ./foo repo_upstream/foo
+'
+
 test_expect_success '--no-track avoids setting up tracking' '
 	test_when_finished rm -rf repo_upstream repo_local foo &&
 	setup_remote_repo repo_upstream repo_local &&
@@ -528,6 +669,35 @@
 		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
 	)
 '
+test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git worktree add --quiet --guess-remote ../foo 2>actual &&
+		test_must_be_empty actual
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_a foo &&
+		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
+test_expect_success 'git worktree --no-guess-remote (quiet)' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git worktree add --quiet --no-guess-remote ../foo
+	) &&
+	(
+		cd foo &&
+		test_must_fail git config "branch.foo.remote" &&
+		test_must_fail git config "branch.foo.merge" &&
+		test_cmp_rev ! refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
 
 test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
 	test_when_finished rm -rf repo_a repo_b foo &&
@@ -560,6 +730,348 @@
 	)
 '
 
+test_dwim_orphan () {
+	local info_text="No possible source branch, inferring '--orphan'" &&
+	local fetch_error_text="fatal: No local or remote refs exist despite at least one remote" &&
+	local orphan_hint="hint: If you meant to create a worktree containing a new unborn branch" &&
+	local invalid_ref_regex="^fatal: invalid reference: " &&
+	local bad_combo_regex="^fatal: options '[-a-z]*' and '[-a-z]*' cannot be used together" &&
+
+	local git_ns="repo" &&
+	local dashc_args="-C $git_ns" &&
+	local use_cd=0 &&
+
+	local bad_head=0 &&
+	local empty_repo=1 &&
+	local local_ref=0 &&
+	local use_quiet=0 &&
+	local remote=0 &&
+	local remote_ref=0 &&
+	local use_detach=0 &&
+	local use_new_branch=0 &&
+
+	local outcome="$1" &&
+	local outcome_text &&
+	local success &&
+	shift &&
+	local args="" &&
+	local context="" &&
+	case "$outcome" in
+	"infer")
+		success=1 &&
+		outcome_text='"add" DWIM infer --orphan'
+		;;
+	"no_infer")
+		success=1 &&
+		outcome_text='"add" DWIM doesnt infer --orphan'
+		;;
+	"fetch_error")
+		success=0 &&
+		outcome_text='"add" error need fetch'
+		;;
+	"fatal_orphan_bad_combo")
+		success=0 &&
+		outcome_text='"add" error inferred "--orphan" gives illegal opts combo'
+		;;
+	"warn_bad_head")
+		success=0 &&
+		outcome_text='"add" error, warn on bad HEAD, hint use orphan'
+		;;
+	*)
+		echo "test_dwim_orphan(): invalid outcome: '$outcome'" >&2 &&
+		return 1
+		;;
+	esac &&
+	while [ $# -gt 0 ]
+	do
+		case "$1" in
+		# How and from where to create the worktree
+		"-C_repo")
+			use_cd=0 &&
+			git_ns="repo" &&
+			dashc_args="-C $git_ns" &&
+			context="$context, 'git -C repo'"
+			;;
+		"-C_wt")
+			use_cd=0 &&
+			git_ns="wt" &&
+			dashc_args="-C $git_ns" &&
+			context="$context, 'git -C wt'"
+			;;
+		"cd_repo")
+			use_cd=1 &&
+			git_ns="repo" &&
+			dashc_args="" &&
+			context="$context, 'cd repo && git'"
+			;;
+		"cd_wt")
+			use_cd=1 &&
+			git_ns="wt" &&
+			dashc_args="" &&
+			context="$context, 'cd wt && git'"
+			;;
+
+		# Bypass the "pull first" warning
+		"force")
+			args="$args --force" &&
+			context="$context, --force"
+			;;
+
+		# Try to use remote refs when DWIM
+		"guess_remote")
+			args="$args --guess-remote" &&
+			context="$context, --guess-remote"
+			;;
+		"no_guess_remote")
+			args="$args --no-guess-remote" &&
+			context="$context, --no-guess-remote"
+			;;
+
+		# Whether there is at least one local branch present
+		"local_ref")
+			empty_repo=0 &&
+			local_ref=1 &&
+			context="$context, >=1 local branches"
+			;;
+		"no_local_ref")
+			empty_repo=0 &&
+			context="$context, 0 local branches"
+			;;
+
+		# Whether the HEAD points at a valid ref (skip this opt when no refs)
+		"good_head")
+			# requires: local_ref
+			context="$context, valid HEAD"
+			;;
+		"bad_head")
+			bad_head=1 &&
+			context="$context, invalid (or orphan) HEAD"
+			;;
+
+		# Whether the code path is tested with the base add command, -b, or --detach
+		"no_-b")
+			use_new_branch=0 &&
+			context="$context, no --branch"
+			;;
+		"-b")
+			use_new_branch=1 &&
+			context="$context, --branch"
+			;;
+		"detach")
+			use_detach=1 &&
+			context="$context, --detach"
+			;;
+
+		# Whether to check that all output is suppressed (except errors)
+		# or that the output is as expected
+		"quiet")
+			use_quiet=1 &&
+			args="$args --quiet" &&
+			context="$context, --quiet"
+			;;
+		"no_quiet")
+			use_quiet=0 &&
+			context="$context, no --quiet (expect output)"
+			;;
+
+		# Whether there is at least one remote attached to the repo
+		"remote")
+			empty_repo=0 &&
+			remote=1 &&
+			context="$context, >=1 remotes"
+			;;
+		"no_remote")
+			empty_repo=0 &&
+			remote=0 &&
+			context="$context, 0 remotes"
+			;;
+
+		# Whether there is at least one valid remote ref
+		"remote_ref")
+			# requires: remote
+			empty_repo=0 &&
+			remote_ref=1 &&
+			context="$context, >=1 fetched remote branches"
+			;;
+		"no_remote_ref")
+			empty_repo=0 &&
+			remote_ref=0 &&
+			context="$context, 0 fetched remote branches"
+			;;
+
+		# Options or flags that become illegal when --orphan is inferred
+		"no_checkout")
+			args="$args --no-checkout" &&
+			context="$context, --no-checkout"
+			;;
+		"track")
+			args="$args --track" &&
+			context="$context, --track"
+			;;
+
+		# All other options are illegal
+		*)
+			echo "test_dwim_orphan(): invalid arg: '$1'" >&2 &&
+			return 1
+			;;
+		esac &&
+		shift
+	done &&
+	context="${context#', '}" &&
+	if [ $use_new_branch -eq 1 ]
+	then
+		args="$args -b foo"
+	elif [ $use_detach -eq 1 ]
+	then
+		args="$args --detach"
+	else
+		context="DWIM (no --branch), $context"
+	fi &&
+	if [ $empty_repo -eq 1 ]
+	then
+		context="empty repo, $context"
+	fi &&
+	args="$args ../foo" &&
+	context="${context%', '}" &&
+	test_expect_success "$outcome_text w/ $context" '
+		test_when_finished "rm -rf repo" &&
+		git init repo &&
+		if [ $local_ref -eq 1 ] && [ "$git_ns" = "repo" ]
+		then
+			(cd repo && test_commit commit) &&
+			if [ $bad_head -eq 1 ]
+			then
+				git -C repo symbolic-ref HEAD refs/heads/badbranch
+			fi
+		elif [ $local_ref -eq 1 ] && [ "$git_ns" = "wt" ]
+		then
+			test_when_finished "git -C repo worktree remove -f ../wt" &&
+			git -C repo worktree add --orphan -b main ../wt &&
+			(cd wt && test_commit commit) &&
+			if [ $bad_head -eq 1 ]
+			then
+				git -C wt symbolic-ref HEAD refs/heads/badbranch
+			fi
+		elif [ $local_ref -eq 0 ] && [ "$git_ns" = "wt" ]
+		then
+			test_when_finished "git -C repo worktree remove -f ../wt" &&
+			git -C repo worktree add --orphan -b orphanbranch ../wt
+		fi &&
+
+		if [ $remote -eq 1 ]
+		then
+			test_when_finished "rm -rf upstream" &&
+			git init upstream &&
+			(cd upstream && test_commit commit) &&
+			git -C upstream switch -c foo &&
+			git -C repo remote add upstream ../upstream
+		fi &&
+
+		if [ $remote_ref -eq 1 ]
+		then
+			git -C repo fetch
+		fi &&
+		if [ $success -eq 1 ]
+		then
+			test_when_finished git -C repo worktree remove ../foo
+		fi &&
+		(
+			if [ $use_cd -eq 1 ]
+			then
+				cd $git_ns
+			fi &&
+			if [ "$outcome" = "infer" ]
+			then
+				git $dashc_args worktree add $args 2>actual &&
+				if [ $use_quiet -eq 1 ]
+				then
+					test_must_be_empty actual
+				else
+					grep "$info_text" actual
+				fi
+			elif [ "$outcome" = "no_infer" ]
+			then
+				git $dashc_args worktree add $args 2>actual &&
+				if [ $use_quiet -eq 1 ]
+				then
+					test_must_be_empty actual
+				else
+					! grep "$info_text" actual
+				fi
+			elif [ "$outcome" = "fetch_error" ]
+			then
+				test_must_fail git $dashc_args worktree add $args 2>actual &&
+				grep "$fetch_error_text" actual
+			elif [ "$outcome" = "fatal_orphan_bad_combo" ]
+			then
+				test_must_fail git $dashc_args worktree add $args 2>actual &&
+				if [ $use_quiet -eq 1 ]
+				then
+					! grep "$info_text" actual
+				else
+					grep "$info_text" actual
+				fi &&
+				grep "$bad_combo_regex" actual
+			elif [ "$outcome" = "warn_bad_head" ]
+			then
+				test_must_fail git $dashc_args worktree add $args 2>actual &&
+				if [ $use_quiet -eq 1 ]
+				then
+					grep "$invalid_ref_regex" actual &&
+					! grep "$orphan_hint" actual
+				else
+					headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) &&
+					headcontents=$(cat "$headpath") &&
+					grep "HEAD points to an invalid (or orphaned) reference" actual &&
+					grep "HEAD path: .$headpath." actual &&
+					grep "HEAD contents: .$headcontents." actual &&
+					grep "$orphan_hint" actual &&
+					! grep "$info_text" actual
+				fi &&
+				grep "$invalid_ref_regex" actual
+			else
+				# Unreachable
+				false
+			fi
+		) &&
+		if [ $success -ne 1 ]
+		then
+			test_path_is_missing foo
+		fi
+	'
+}
+
+for quiet_mode in "no_quiet" "quiet"
+do
+	for changedir_type in "cd_repo" "cd_wt" "-C_repo" "-C_wt"
+	do
+		dwim_test_args="$quiet_mode $changedir_type"
+		test_dwim_orphan 'infer' $dwim_test_args no_-b
+		test_dwim_orphan 'no_infer' $dwim_test_args no_-b local_ref good_head
+		test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref no_remote no_remote_ref no_guess_remote
+		test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref no_guess_remote
+		test_dwim_orphan 'fetch_error' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote
+		test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote force
+		test_dwim_orphan 'no_infer' $dwim_test_args no_-b no_local_ref remote remote_ref guess_remote
+
+		test_dwim_orphan 'infer' $dwim_test_args -b
+		test_dwim_orphan 'no_infer' $dwim_test_args -b local_ref good_head
+		test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref no_remote no_remote_ref no_guess_remote
+		test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref no_guess_remote
+		test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref guess_remote
+		test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote remote_ref guess_remote
+
+		test_dwim_orphan 'warn_bad_head' $dwim_test_args no_-b local_ref bad_head
+		test_dwim_orphan 'warn_bad_head' $dwim_test_args -b local_ref bad_head
+		test_dwim_orphan 'warn_bad_head' $dwim_test_args detach local_ref bad_head
+	done
+
+	test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b no_checkout
+	test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b track
+	test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b no_checkout
+	test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b track
+done
+
 post_checkout_hook () {
 	test_when_finished "rm -rf .git/hooks" &&
 	mkdir .git/hooks &&
diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh
index 568a47e..71aa9bc 100755
--- a/t/t2401-worktree-prune.sh
+++ b/t/t2401-worktree-prune.sh
@@ -47,7 +47,7 @@
 	: >.git/worktrees/def/gitdir &&
 	chmod u-r .git/worktrees/def/gitdir &&
 	git worktree prune --verbose 2>actual &&
-	test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
+	test_grep "Removing worktrees/def: unable to read gitdir file" actual &&
 	! test -d .git/worktrees/def &&
 	! test -d .git/worktrees
 '
@@ -57,7 +57,7 @@
 	: >.git/worktrees/def/def &&
 	: >.git/worktrees/def/gitdir &&
 	git worktree prune --verbose 2>actual &&
-	test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
+	test_grep "Removing worktrees/def: invalid gitdir file" actual &&
 	! test -d .git/worktrees/def &&
 	! test -d .git/worktrees
 '
@@ -67,7 +67,7 @@
 	: >.git/worktrees/def/def &&
 	echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
 	git worktree prune --verbose 2>actual &&
-	test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
+	test_grep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
 	! test -d .git/worktrees/def &&
 	! test -d .git/worktrees
 '
@@ -103,7 +103,7 @@
 	sed "s/w2/w1/" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new &&
 	mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&
 	git worktree prune --verbose 2>actual &&
-	test_i18ngrep "duplicate entry" actual &&
+	test_grep "duplicate entry" actual &&
 	test -d .git/worktrees/w1 &&
 	! test -d .git/worktrees/w2
 '
@@ -116,7 +116,7 @@
 	rm -fr wt &&
 	mv repo wt &&
 	git -C wt worktree prune --verbose 2>actual &&
-	test_i18ngrep "duplicate entry" actual &&
+	test_grep "duplicate entry" actual &&
 	! test -d .git/worktrees/wt
 '
 
diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh
index 9ad9be0..33ea9cb 100755
--- a/t/t2402-worktree-list.sh
+++ b/t/t2402-worktree-list.sh
@@ -143,7 +143,7 @@
 	rm -rf prunable &&
 	git worktree list --porcelain >out &&
 	sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
-	test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
+	test_grep "^prunable gitdir file points to non-existent location$" only_prunable
 '
 
 test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
@@ -155,8 +155,8 @@
 	grep "/prunable  *[0-9a-f].* prunable$" out &&
 	! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
 	git worktree prune --verbose 2>out &&
-	test_i18ngrep "^Removing worktrees/prunable" out &&
-	test_i18ngrep ! "^Removing worktrees/unprunable" out
+	test_grep "^Removing worktrees/prunable" out &&
+	test_grep ! "^Removing worktrees/unprunable" out
 '
 
 test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index 230a55e..901342e 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -202,7 +202,7 @@
 	for i in noodle noodle/bork
 	do
 		test_must_fail git worktree lock $i 2>err &&
-		test_i18ngrep "not a working tree" err || return 1
+		test_grep "not a working tree" err || return 1
 	done
 '
 
diff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh
index 8970780..edbf502 100755
--- a/t/t2406-worktree-repair.sh
+++ b/t/t2406-worktree-repair.sh
@@ -25,7 +25,7 @@
 	>notdir &&
 	test_must_fail git worktree repair >out 2>err &&
 	test_must_be_empty out &&
-	test_i18ngrep "not a directory" err
+	test_grep "not a directory" err
 '
 
 test_expect_success "don't clobber .git repo" '
@@ -35,7 +35,7 @@
 	test_create_repo repo &&
 	test_must_fail git worktree repair >out 2>err &&
 	test_must_be_empty out &&
-	test_i18ngrep ".git is not a file" err
+	test_grep ".git is not a file" err
 '
 
 test_corrupt_gitfile () {
@@ -47,7 +47,7 @@
 	git -C corrupt rev-parse --absolute-git-dir >expect &&
 	eval "$butcher" &&
 	git -C "$repairdir" worktree repair 2>err &&
-	test_i18ngrep "$problem" err &&
+	test_grep "$problem" err &&
 	git -C corrupt rev-parse --absolute-git-dir >actual &&
 	test_cmp expect actual
 }
@@ -93,7 +93,7 @@
 test_expect_success 'invalid worktree path' '
 	test_must_fail git worktree repair /notvalid >out 2>err &&
 	test_must_be_empty out &&
-	test_i18ngrep "not a valid path" err
+	test_grep "not a valid path" err
 '
 
 test_expect_success 'repo not found; .git not file' '
@@ -101,7 +101,7 @@
 	test_create_repo not-a-worktree &&
 	test_must_fail git worktree repair not-a-worktree >out 2>err &&
 	test_must_be_empty out &&
-	test_i18ngrep ".git is not a file" err
+	test_grep ".git is not a file" err
 '
 
 test_expect_success 'repo not found; .git not referencing repo' '
@@ -111,7 +111,7 @@
 	mv side/.newgit side/.git &&
 	mkdir not-a-repo &&
 	test_must_fail git worktree repair side 2>err &&
-	test_i18ngrep ".git file does not reference a repository" err
+	test_grep ".git file does not reference a repository" err
 '
 
 test_expect_success 'repo not found; .git file broken' '
@@ -121,7 +121,7 @@
 	mv orig moved &&
 	test_must_fail git worktree repair moved >out 2>err &&
 	test_must_be_empty out &&
-	test_i18ngrep ".git file broken" err
+	test_grep ".git file broken" err
 '
 
 test_expect_success 'repair broken gitdir' '
@@ -132,7 +132,7 @@
 	mv orig moved &&
 	git worktree repair moved 2>err &&
 	test_cmp expect .git/worktrees/orig/gitdir &&
-	test_i18ngrep "gitdir unreadable" err
+	test_grep "gitdir unreadable" err
 '
 
 test_expect_success 'repair incorrect gitdir' '
@@ -142,7 +142,7 @@
 	mv orig moved &&
 	git worktree repair moved 2>err &&
 	test_cmp expect .git/worktrees/orig/gitdir &&
-	test_i18ngrep "gitdir incorrect" err
+	test_grep "gitdir incorrect" err
 '
 
 test_expect_success 'repair gitdir (implicit) from linked worktree' '
@@ -152,7 +152,7 @@
 	mv orig moved &&
 	git -C moved worktree repair 2>err &&
 	test_cmp expect .git/worktrees/orig/gitdir &&
-	test_i18ngrep "gitdir incorrect" err
+	test_grep "gitdir incorrect" err
 '
 
 test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
@@ -177,8 +177,8 @@
 	git worktree repair moved1 moved2 2>err &&
 	test_cmp expect1 .git/worktrees/orig1/gitdir &&
 	test_cmp expect2 .git/worktrees/orig2/gitdir &&
-	test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" err &&
-	test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" err
+	test_grep "gitdir incorrect:.*orig1/gitdir$" err &&
+	test_grep "gitdir incorrect:.*orig2/gitdir$" err
 '
 
 test_expect_success 'repair moved main and linked worktrees' '
diff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh
index 019a40d..f6835c9 100755
--- a/t/t2407-worktree-heads.sh
+++ b/t/t2407-worktree-heads.sh
@@ -45,7 +45,7 @@
 		grep "cannot force update the branch" err &&
 
 		test_must_fail git branch -D wt-$i 2>err &&
-		grep "Cannot delete branch" err || return 1
+		grep "cannot delete branch" err || return 1
 	done
 '
 
@@ -58,7 +58,7 @@
 	git -C wt-4 bisect good wt-1 &&
 
 	test_must_fail git branch -f wt-4 HEAD 2>err &&
-	grep "cannot force update the branch '\''wt-4'\'' checked out at.*wt-4" err
+	grep "cannot force update the branch '\''wt-4'\'' used by worktree at.*wt-4" err
 '
 
 test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (apply)' '
@@ -68,7 +68,7 @@
 	test_must_fail git -C wt-2 rebase --apply conflict-2 &&
 
 	test_must_fail git branch -f wt-2 HEAD 2>err &&
-	grep "cannot force update the branch '\''wt-2'\'' checked out at.*wt-2" err
+	grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err
 '
 
 test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (merge)' '
@@ -78,7 +78,7 @@
 	test_must_fail git -C wt-2 rebase conflict-2 &&
 
 	test_must_fail git branch -f wt-2 HEAD 2>err &&
-	grep "cannot force update the branch '\''wt-2'\'' checked out at.*wt-2" err
+	grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err
 '
 
 test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with --update-refs' '
@@ -90,7 +90,7 @@
 	for i in 3 4
 	do
 		test_must_fail git branch -f can-be-updated HEAD 2>err &&
-		grep "cannot force update the branch '\''can-be-updated'\'' checked out at.*wt-3" err ||
+		grep "cannot force update the branch '\''can-be-updated'\'' used by worktree at.*wt-3" err ||
 			return 1
 	done
 '
@@ -150,7 +150,7 @@
 	for i in 1 2
 	do
 		test_must_fail git branch -f fake-$i HEAD 2>err &&
-		grep "cannot force update the branch '\''fake-$i'\'' checked out at" err ||
+		grep "cannot force update the branch '\''fake-$i'\'' used by worktree at" err ||
 			return 1
 	done
 '
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
index a16e25c..12e41a7 100755
--- a/t/t3004-ls-files-basic.sh
+++ b/t/t3004-ls-files-basic.sh
@@ -21,7 +21,7 @@
 
 test_expect_success 'ls-files with nonsense option' '
 	test_expect_code 129 git ls-files --nonsense 2>actual &&
-	test_i18ngrep "[Uu]sage: git ls-files" actual
+	test_grep "[Uu]sage: git ls-files" actual
 '
 
 test_expect_success 'ls-files -h in corrupt repository' '
@@ -32,7 +32,7 @@
 		>.git/index &&
 		test_expect_code 129 git ls-files -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage: git ls-files " broken/usage
+	test_grep "[Uu]sage: git ls-files " broken/usage
 '
 
 test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh
index dd7770e..61771ee 100755
--- a/t/t3007-ls-files-recurse-submodules.sh
+++ b/t/t3007-ls-files-recurse-submodules.sh
@@ -296,13 +296,46 @@
 
 test_expect_success '--recurse-submodules does not support --error-unmatch' '
 	test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual &&
-	test_i18ngrep "does not support --error-unmatch" actual
+	test_grep "does not support --error-unmatch" actual
+'
+
+test_expect_success '--recurse-submodules parses submodule repo config' '
+	test_config -C submodule index.sparse "invalid non-boolean value" &&
+	test_must_fail git ls-files --recurse-submodules 2>err &&
+	grep "bad boolean config value" err
+'
+
+test_expect_success '--recurse-submodules parses submodule worktree config' '
+	test_config -C submodule extensions.worktreeConfig true &&
+	test_config -C submodule --worktree index.sparse "invalid non-boolean value" &&
+
+	test_must_fail git ls-files --recurse-submodules 2>err &&
+	grep "bad boolean config value" err
+'
+
+test_expect_success '--recurse-submodules submodules ignore super project worktreeConfig extension' '
+	# Enable worktree config in both super project & submodule, set an
+	# invalid config in the submodule worktree config
+	test_config extensions.worktreeConfig true &&
+	test_config -C submodule extensions.worktreeConfig true &&
+	test_config -C submodule --worktree index.sparse "invalid non-boolean value" &&
+
+	# Now, disable the worktree config in the submodule. Note that we need
+	# to manually re-enable extensions.worktreeConfig when the test is
+	# finished, otherwise the test_unconfig of index.sparse will not work.
+	test_unconfig -C submodule extensions.worktreeConfig &&
+	test_when_finished "git -C submodule config extensions.worktreeConfig true" &&
+
+	# With extensions.worktreeConfig disabled in the submodule, the invalid
+	# worktree config is not picked up.
+	git ls-files --recurse-submodules 2>err &&
+	! grep "bad boolean config value" err
 '
 
 test_incompatible_with_recurse_submodules () {
 	test_expect_success "--recurse-submodules and $1 are incompatible" "
 		test_must_fail git ls-files --recurse-submodules $1 2>actual &&
-		test_i18ngrep 'unsupported mode' actual
+		test_grep 'unsupported mode' actual
 	"
 }
 
diff --git a/t/t3013-ls-files-format.sh b/t/t3013-ls-files-format.sh
index efb7450..6e6ea0b 100755
--- a/t/t3013-ls-files-format.sh
+++ b/t/t3013-ls-files-format.sh
@@ -38,6 +38,41 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git ls-files --format objecttype' '
+	git ls-files --format="%(objectname)" o1.txt o4.txt o6.txt >objectname &&
+	git cat-file --batch-check="%(objecttype)" >expect <objectname &&
+	git ls-files --format="%(objecttype)" o1.txt o4.txt o6.txt >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize' '
+	cat>expect <<-\EOF &&
+26
+29
+27
+26
+-
+26
+	EOF
+	git ls-files --format="%(objectsize)" >actual &&
+
+	test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize:padded' '
+	cat>expect <<-\EOF &&
+     26
+     29
+     27
+     26
+      -
+     26
+	EOF
+	git ls-files --format="%(objectsize:padded)" >actual &&
+
+	test_cmp expect actual
+'
+
 test_expect_success 'git ls-files --format v.s. --eol' '
 	git ls-files --eol >tmp &&
 	sed -e "s/	/ /g" -e "s/  */ /g" tmp >expect 2>err &&
@@ -54,6 +89,22 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git ls-files --format with relative path' '
+	cat >expect <<-\EOF &&
+	../o1.txt
+	../o2.txt
+	../o3.txt
+	../o4.txt
+	../o5.txt
+	../o6.txt
+	EOF
+	mkdir sub &&
+	cd sub &&
+	git ls-files --format="%(path)" ":/" >../actual &&
+	cd .. &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git ls-files --format with -m' '
 	echo change >o1.txt &&
 	cat >expect <<-\EOF &&
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index c4a72ae..5a06732 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -40,7 +40,7 @@
 	git commit -a -m "remove them all" &&
 
 	# The bug also requires some entry before our directory so that
-	# prune_path will modify the_index.cache
+	# prune_index will modify the_repository->index.cache
 
 	mkdir a_directory_that_sorts_before_sub &&
 	>a_directory_that_sorts_before_sub/file &&
@@ -56,7 +56,7 @@
 '
 
 test_expect_success 'git ls-files --with-tree should succeed from subdir' '
-	# We have to run from a sub-directory to trigger prune_path
+	# We have to run from a sub-directory to trigger prune_index
 	# Then we finally get to run our --with-tree test
 	(
 		cd sub &&
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 5d871fd..4dd42df 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -431,4 +431,15 @@
 match 0 1 0 1 'z' '[Z-y]'
 match 1 1 1 1 'Z' '[Z-y]'
 
+test_expect_success 'matching does not exhibit exponential behavior' '
+	{
+		test-tool wildmatch wildmatch \
+			aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab \
+			"*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a" &
+		pid=$!
+	} &&
+	sleep 2 &&
+	! kill $!
+'
+
 test_done
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 217006d..5af2dac 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -154,6 +154,14 @@
 	test_output
 '
 
+test_expect_success 'ls-tree --no-full-name' '
+	git -C path0 ls-tree --no-full-name $tree a >current &&
+	cat >expected <<-EOF &&
+	040000 tree X	a
+	EOF
+	test_output
+'
+
 test_expect_success 'ls-tree --full-tree' '
 	(
 		cd path1/b/c &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 5a169b6..ccfa6a7 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -8,6 +8,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
@@ -24,10 +25,10 @@
 
 test_expect_success 'git branch --help should not have created a bogus branch' '
 	test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 &&
-	test_path_is_missing .git/refs/heads/--help
+	test_ref_missing refs/heads/--help
 '
 
-test_expect_success 'branch -h in broken repository' '
+test_expect_success REFFILES 'branch -h in broken repository' '
 	mkdir broken &&
 	(
 		cd broken &&
@@ -35,11 +36,12 @@
 		>.git/refs/heads/main &&
 		test_expect_code 129 git branch -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage" broken/usage
+	test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'git branch abc should create a branch' '
-	git branch abc && test_path_is_file .git/refs/heads/abc
+	git branch abc &&
+	test_ref_exists refs/heads/abc
 '
 
 test_expect_success 'git branch abc should fail when abc exists' '
@@ -60,31 +62,33 @@
 '
 
 test_expect_success 'git branch a/b/c should create a branch' '
-	git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
+	git branch a/b/c &&
+	test_ref_exists refs/heads/a/b/c
 '
 
 test_expect_success 'git branch mb main... should create a branch' '
-	git branch mb main... && test_path_is_file .git/refs/heads/mb
+	git branch mb main... &&
+	test_ref_exists refs/heads/mb
 '
 
 test_expect_success 'git branch HEAD should fail' '
 	test_must_fail git branch HEAD
 '
 
-cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from main
-EOF
 test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
 	GIT_COMMITTER_DATE="2005-05-26 23:30" \
 	git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
-	test_path_is_file .git/refs/heads/d/e/f &&
-	test_path_is_file .git/logs/refs/heads/d/e/f &&
-	test_cmp expect .git/logs/refs/heads/d/e/f
+	test_ref_exists refs/heads/d/e/f &&
+	cat >expect <<-EOF &&
+	$HEAD refs/heads/d/e/f@{0}: branch: Created from main
+	EOF
+	git reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
 	git branch -d d/e/f &&
-	test_path_is_missing .git/refs/heads/d/e/f &&
+	test_ref_missing refs/heads/d/e/f &&
 	test_must_fail git reflog exists refs/heads/d/e/f
 '
 
@@ -102,7 +106,7 @@
 
 test_expect_success 'git branch -m dumps usage' '
 	test_expect_code 128 git branch -m 2>err &&
-	test_i18ngrep "branch name required" err
+	test_grep "branch name required" err
 '
 
 test_expect_success 'git branch -m m broken_symref should work' '
@@ -199,10 +203,9 @@
 	test $(git rev-parse --abbrev-ref HEAD) = bam
 '
 
-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
-	msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
-	grep " $ZERO_OID.*$msg$" .git/logs/HEAD &&
-	grep "^$ZERO_OID.*$msg$" .git/logs/HEAD
+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '
+	git reflog show HEAD >actual &&
+	grep "HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam" actual
 '
 
 test_expect_success 'git branch -M should leave orphaned HEAD alone' '
@@ -211,17 +214,20 @@
 		cd orphan &&
 		test_commit initial &&
 		git checkout --orphan lonely &&
-		grep lonely .git/HEAD &&
-		test_path_is_missing .git/refs/head/lonely &&
+		git symbolic-ref HEAD >expect &&
+		echo refs/heads/lonely >actual &&
+		test_cmp expect actual &&
+		test_ref_missing refs/head/lonely &&
 		git branch -M main mistress &&
-		grep lonely .git/HEAD
+		git symbolic-ref HEAD >expect &&
+		test_cmp expect actual
 	)
 '
 
 test_expect_success 'resulting reflog can be shown by log -g' '
 	oid=$(git rev-parse HEAD) &&
 	cat >expect <<-EOF &&
-	HEAD@{0} $oid $msg
+	HEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam
 	HEAD@{2} $oid checkout: moving from foo to baz
 	EOF
 	git log -g --format="%gd %H %gs" -2 HEAD >actual &&
@@ -239,15 +245,34 @@
 	git worktree prune
 '
 
+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '
+	git worktree add -b baz bazdir1 &&
+	git worktree add -f bazdir2 baz &&
+	touch .git/worktrees/bazdir1/HEAD.lock &&
+	test_must_fail git branch -M baz bam &&
+	test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+	git branch -M bam baz &&
+	rm .git/worktrees/bazdir1/HEAD.lock &&
+	touch .git/worktrees/bazdir2/HEAD.lock &&
+	test_must_fail git branch -M baz bam &&
+	test $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&
+	rm -rf bazdir1 bazdir2 &&
+	git worktree prune
+'
+
 test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
 	git checkout -b baz &&
 	git worktree add -f bazdir baz &&
 	(
 		cd bazdir &&
 		git branch -M baz bam &&
-		test $(git rev-parse --abbrev-ref HEAD) = bam
+		echo bam >expect &&
+		git rev-parse --abbrev-ref HEAD >actual &&
+		test_cmp expect actual
 	) &&
-	test $(git rev-parse --abbrev-ref HEAD) = bam &&
+	echo bam >expect &&
+	git rev-parse --abbrev-ref HEAD >actual &&
+	test_cmp expect actual &&
 	rm -r bazdir &&
 	git worktree prune
 '
@@ -271,14 +296,28 @@
 test_expect_success 'git branch -M and -C fail on detached HEAD' '
 	git checkout HEAD^{} &&
 	test_when_finished git checkout - &&
-	echo "fatal: cannot rename the current branch while not on any." >expect &&
+	echo "fatal: cannot rename the current branch while not on any" >expect &&
 	test_must_fail git branch -M must-fail 2>err &&
 	test_cmp expect err &&
-	echo "fatal: cannot copy the current branch while not on any." >expect &&
+	echo "fatal: cannot copy the current branch while not on any" >expect &&
 	test_must_fail git branch -C must-fail 2>err &&
 	test_cmp expect err
 '
 
+test_expect_success 'git branch -m should work with orphan branches' '
+	test_when_finished git checkout - &&
+	test_when_finished git worktree remove -f wt &&
+	git worktree add wt --detach &&
+	# rename orphan in another worktreee
+	git -C wt checkout --orphan orphan-foo-wt &&
+	git branch -m orphan-foo-wt orphan-bar-wt &&
+	test orphan-bar-wt=$(git -C orphan-worktree branch --show-current) &&
+	# rename orphan in the current worktree
+	git checkout --orphan orphan-foo &&
+	git branch -m orphan-foo orphan-bar &&
+	test orphan-bar=$(git branch --show-current)
+'
+
 test_expect_success 'git branch -d on orphan HEAD (merged)' '
 	test_when_finished git checkout main &&
 	git checkout --orphan orphan &&
@@ -401,10 +440,10 @@
 
 test_expect_success 'git branch --column' '
 	COLUMNS=81 git branch --column=column >actual &&
-	cat >expect <<\EOF &&
-  a/b/c   bam     foo     l     * main    n       o/p     r
-  abc     bar     j/k     m/m     mb      o/o     q       topic
-EOF
+	cat >expect <<-\EOF &&
+	  a/b/c   bam     foo     l     * main    n       o/p     r
+	  abc     bar     j/k     m/m     mb      o/o     q       topic
+	EOF
 	test_cmp expect actual
 '
 
@@ -414,25 +453,25 @@
 	test_when_finished "git branch -d $long" &&
 	git branch $long &&
 	COLUMNS=80 git branch --column=column >actual &&
-	cat >expect <<EOF &&
-  a/b/c
-  abc
-  bam
-  bar
-  foo
-  j/k
-  l
-  m/m
-* main
-  mb
-  n
-  o/o
-  o/p
-  q
-  r
-  topic
-  $long
-EOF
+	cat >expect <<-EOF &&
+	  a/b/c
+	  abc
+	  bam
+	  bar
+	  foo
+	  j/k
+	  l
+	  m/m
+	* main
+	  mb
+	  n
+	  o/o
+	  o/p
+	  q
+	  r
+	  topic
+	  $long
+	EOF
 	test_cmp expect actual
 '
 
@@ -442,10 +481,10 @@
 	COLUMNS=80 git branch >actual &&
 	git config --unset column.branch &&
 	git config --unset column.ui &&
-	cat >expect <<\EOF &&
-  a/b/c   bam   foo   l   * main   n     o/p   r
-  abc     bar   j/k   m/m   mb     o/o   q     topic
-EOF
+	cat >expect <<-\EOF &&
+	  a/b/c   bam   foo   l   * main   n     o/p   r
+	  abc     bar   j/k   m/m   mb     o/o   q     topic
+	EOF
 	test_cmp expect actual
 '
 
@@ -457,39 +496,36 @@
 	git config column.ui column &&
 	COLUMNS=80 git branch -v | cut -c -8 | sed "s/ *$//" >actual &&
 	git config --unset column.ui &&
-	cat >expect <<\EOF &&
-  a/b/c
-  abc
-  bam
-  bar
-  foo
-  j/k
-  l
-  m/m
-* main
-  mb
-  n
-  o/o
-  o/p
-  q
-  r
-  topic
-EOF
+	cat >expect <<-\EOF &&
+	  a/b/c
+	  abc
+	  bam
+	  bar
+	  foo
+	  j/k
+	  l
+	  m/m
+	* main
+	  mb
+	  n
+	  o/o
+	  o/p
+	  q
+	  r
+	  topic
+	EOF
 	test_cmp expect actual
 '
 
-mv .git/config .git/config-saved
-
-test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
+test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
+	test_when_finished mv .git/config-saved .git/config &&
+	mv .git/config .git/config-saved &&
 	git branch -m q q2 &&
 	git branch -m q2 q
 '
 
-mv .git/config-saved .git/config
-
-git config branch.s/s.dummy Hello
-
 test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
+	git config branch.s/s.dummy Hello &&
 	git branch --create-reflog s/s &&
 	git reflog exists refs/heads/s/s &&
 	git branch --create-reflog s/t &&
@@ -540,19 +576,19 @@
 
 	# ...and that the comments for those sections are also
 	# preserved.
-	cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+	sed "s/\"source\"/\"dest\"/" config.branch >expect &&
 	sed -n -e "/Note the lack/,\$p" .git/config >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'git branch -c dumps usage' '
 	test_expect_code 128 git branch -c 2>err &&
-	test_i18ngrep "branch name required" err
+	test_grep "branch name required" err
 '
 
 test_expect_success 'git branch --copy dumps usage' '
 	test_expect_code 128 git branch --copy 2>err &&
-	test_i18ngrep "branch name required" err
+	test_grep "branch name required" err
 '
 
 test_expect_success 'git branch -c d e should work' '
@@ -662,7 +698,8 @@
 
 test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
 	msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
-	! grep "$msg$" .git/logs/HEAD
+	git reflog HEAD >actual &&
+	! grep "$msg$" actual
 '
 
 test_expect_success 'git branch -C main should work when main is checked out' '
@@ -765,26 +802,26 @@
 	git symbolic-ref refs/heads/symref refs/heads/target &&
 	echo "Deleted branch symref (was refs/heads/target)." >expect &&
 	git branch -d symref >actual &&
-	test_path_is_file .git/refs/heads/target &&
-	test_path_is_missing .git/refs/heads/symref &&
+	test_ref_exists refs/heads/target &&
+	test_ref_missing refs/heads/symref &&
 	test_cmp expect actual
 '
 
 test_expect_success 'deleting a dangling symref' '
 	git symbolic-ref refs/heads/dangling-symref nowhere &&
-	test_path_is_file .git/refs/heads/dangling-symref &&
+	git symbolic-ref --no-recurse refs/heads/dangling-symref &&
 	echo "Deleted branch dangling-symref (was nowhere)." >expect &&
 	git branch -d dangling-symref >actual &&
-	test_path_is_missing .git/refs/heads/dangling-symref &&
+	test_ref_missing refs/heads/dangling-symref &&
 	test_cmp expect actual
 '
 
 test_expect_success 'deleting a self-referential symref' '
 	git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
-	test_path_is_file .git/refs/heads/self-reference &&
+	test_ref_exists refs/heads/self-reference &&
 	echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
 	git branch -d self-reference >actual &&
-	test_path_is_missing .git/refs/heads/self-reference &&
+	test_ref_missing refs/heads/self-reference &&
 	test_cmp expect actual
 '
 
@@ -792,37 +829,8 @@
 	git symbolic-ref refs/heads/topic refs/heads/main &&
 	test_must_fail git branch -m topic new-topic &&
 	git symbolic-ref refs/heads/topic &&
-	test_path_is_file .git/refs/heads/main &&
-	test_path_is_missing .git/refs/heads/new-topic
-'
-
-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
-	git branch --create-reflog u &&
-	mv .git/logs/refs/heads/u real-u &&
-	ln -s real-u .git/logs/refs/heads/u &&
-	test_must_fail git branch -m u v
-'
-
-test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
-	test_when_finished "rm -rf subdir" &&
-	git init --bare subdir &&
-
-	rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
-	ln -s ../.git/refs subdir/refs &&
-	ln -s ../.git/objects subdir/objects &&
-	ln -s ../.git/packed-refs subdir/packed-refs &&
-
-	git -C subdir rev-parse --absolute-git-dir >subdir.dir &&
-	git rev-parse --absolute-git-dir >our.dir &&
-	! test_cmp subdir.dir our.dir &&
-
-	git -C subdir log &&
-	git -C subdir branch rename-src &&
-	git rev-parse rename-src >expect &&
-	git -C subdir branch -m rename-src rename-dest &&
-	git rev-parse rename-dest >actual &&
-	test_cmp expect actual &&
-	git branch -D rename-dest
+	test_ref_exists refs/heads/main &&
+	test_ref_missing refs/heads/new-topic
 '
 
 test_expect_success 'test tracking setup via --track' '
@@ -908,7 +916,19 @@
 test_expect_success 'deleting currently checked out branch fails' '
 	git worktree add -b my7 my7 &&
 	test_must_fail git -C my7 branch -d my7 &&
-	test_must_fail git branch -d my7 &&
+	test_must_fail git branch -d my7 2>actual &&
+	grep "^error: cannot delete branch .my7. used by worktree at " actual &&
+	rm -r my7 &&
+	git worktree prune
+'
+
+test_expect_success 'deleting in-use branch fails' '
+	git worktree add my7 &&
+	test_commit -C my7 bt7 &&
+	git -C my7 bisect start HEAD HEAD~2 &&
+	test_must_fail git -C my7 branch -d my7 &&
+	test_must_fail git branch -d my7 2>actual &&
+	grep "^error: cannot delete branch .my7. used by worktree at " actual &&
 	rm -r my7 &&
 	git worktree prune
 '
@@ -978,7 +998,7 @@
 test_expect_success '--set-upstream-to fails on detached HEAD' '
 	git checkout HEAD^{} &&
 	test_when_finished git checkout - &&
-	echo "fatal: could not set upstream of HEAD to main when it does not point to any branch." >expect &&
+	echo "fatal: could not set upstream of HEAD to main when it does not point to any branch" >expect &&
 	test_must_fail git branch --set-upstream-to main 2>err &&
 	test_cmp expect err
 '
@@ -991,7 +1011,7 @@
 
 test_expect_success '--set-upstream-to fails on a missing src branch' '
 	test_must_fail git branch --set-upstream-to does-not-exist main 2>err &&
-	test_i18ngrep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
+	test_grep "the requested upstream branch '"'"'does-not-exist'"'"' does not exist" err
 '
 
 test_expect_success '--set-upstream-to fails on a non-ref' '
@@ -1005,7 +1025,7 @@
 	>.git/config.lock &&
 	git branch locked &&
 	test_must_fail git branch --set-upstream-to locked 2>err &&
-	test_i18ngrep "could not lock config file .git/config" err
+	test_grep "could not lock config file .git/config" err
 '
 
 test_expect_success 'use --set-upstream-to modify HEAD' '
@@ -1026,7 +1046,7 @@
 '
 
 test_expect_success '--unset-upstream should fail if given a non-existent branch' '
-	echo "fatal: Branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
+	echo "fatal: branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
 	test_must_fail git branch --unset-upstream i-dont-exist 2>err &&
 	test_cmp expect err
 '
@@ -1036,7 +1056,7 @@
 	git branch --set-upstream-to locked &&
 	>.git/config.lock &&
 	test_must_fail git branch --unset-upstream 2>err &&
-	test_i18ngrep "could not lock config file .git/config" err
+	test_grep "could not lock config file .git/config" err
 '
 
 test_expect_success 'test --unset-upstream on HEAD' '
@@ -1048,7 +1068,7 @@
 	test_must_fail git config branch.main.remote &&
 	test_must_fail git config branch.main.merge &&
 	# fail for a branch without upstream set
-	echo "fatal: Branch '"'"'main'"'"' has no upstream information" >expect &&
+	echo "fatal: branch '"'"'main'"'"' has no upstream information" >expect &&
 	test_must_fail git branch --unset-upstream 2>err &&
 	test_cmp expect err
 '
@@ -1062,7 +1082,7 @@
 test_expect_success '--unset-upstream should fail on detached HEAD' '
 	git checkout HEAD^{} &&
 	test_when_finished git checkout - &&
-	echo "fatal: could not unset upstream of HEAD when it does not point to any branch." >expect &&
+	echo "fatal: could not unset upstream of HEAD when it does not point to any branch" >expect &&
 	test_must_fail git branch --unset-upstream 2>err &&
 	test_cmp expect err
 '
@@ -1089,16 +1109,16 @@
 	test_cmp expect actual
 "
 
-# Keep this test last, as it changes the current branch
-cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from main
-EOF
 test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
+	test_when_finished git checkout main &&
 	GIT_COMMITTER_DATE="2005-05-26 23:30" \
 	git checkout -b g/h/i -l main &&
-	test_path_is_file .git/refs/heads/g/h/i &&
-	test_path_is_file .git/logs/refs/heads/g/h/i &&
-	test_cmp expect .git/logs/refs/heads/g/h/i
+	test_ref_exists refs/heads/g/h/i &&
+	cat >expect <<-EOF &&
+	$HEAD refs/heads/g/h/i@{0}: branch: Created from main
+	EOF
+	git reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'checkout -b makes reflog by default' '
@@ -1134,9 +1154,9 @@
 	hint: tracking ref '\''refs/heads/main'\'':
 	hint:   ambi1
 	hint:   ambi2
-	hint: ''
+	hint:
 	hint: This is typically a configuration error.
-	hint: ''
+	hint:
 	hint: To support setting up tracking branches, ensure that
 	hint: different remotes'\'' fetch refspecs map into different
 	hint: tracking namespaces.
@@ -1472,7 +1492,7 @@
 	set_fake_editor &&
 	git rebase -i HEAD~2 &&
 	git branch --list >actual &&
-	test_i18ngrep "rebasing main" actual
+	test_grep "rebasing main" actual
 '
 
 test_expect_success '--list during rebase from detached HEAD' '
@@ -1484,7 +1504,7 @@
 	set_fake_editor &&
 	git rebase -i HEAD~2 &&
 	git branch --list >actual &&
-	test_i18ngrep "rebasing detached HEAD $oid" actual
+	test_grep "rebasing detached HEAD $oid" actual
 '
 
 test_expect_success 'tracking with unexpected .fetch refspec' '
@@ -1524,9 +1544,10 @@
 
 test_expect_success 'configured committerdate sort' '
 	git init -b main sort &&
+	test_config -C sort branch.sort "committerdate" &&
+
 	(
 		cd sort &&
-		git config branch.sort committerdate &&
 		test_commit initial &&
 		git checkout -b a &&
 		test_commit a &&
@@ -1546,9 +1567,10 @@
 '
 
 test_expect_success 'option override configured sort' '
+	test_config -C sort branch.sort "committerdate" &&
+
 	(
 		cd sort &&
-		git config branch.sort committerdate &&
 		git branch --sort=refname >actual &&
 		cat >expect <<-\EOF &&
 		  a
@@ -1560,10 +1582,70 @@
 	)
 '
 
-test_expect_success 'invalid sort parameter in configuration' '
+test_expect_success '--no-sort cancels config sort keys' '
+	test_config -C sort branch.sort "-refname" &&
+
 	(
 		cd sort &&
-		git config branch.sort "v:notvalid" &&
+
+		# objecttype is identical for all of them, so sort falls back on
+		# default (ascending refname)
+		git branch \
+			--no-sort \
+			--sort="objecttype" >actual &&
+		cat >expect <<-\EOF &&
+		  a
+		* b
+		  c
+		  main
+		EOF
+		test_cmp expect actual
+	)
+
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+	(
+		cd sort &&
+
+		# objecttype is identical for all of them, so sort falls back on
+		# default (ascending refname)
+		git branch \
+			--sort="-refname" \
+			--no-sort \
+			--sort="objecttype" >actual &&
+		cat >expect <<-\EOF &&
+		  a
+		* b
+		  c
+		  main
+		EOF
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected branches' '
+	(
+		cd sort &&
+
+		# Sort the results with `sort` for a consistent comparison
+		# against expected
+		git branch --no-sort | sort >actual &&
+		cat >expect <<-\EOF &&
+		  a
+		  c
+		  main
+		* b
+		EOF
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'invalid sort parameter in configuration' '
+	test_config -C sort branch.sort "v:notvalid" &&
+
+	(
+		cd sort &&
 
 		# this works in the "listing" mode, so bad sort key
 		# is a dying offence.
@@ -1611,4 +1693,14 @@
 	test_cmp_config "" --default "" branch.foo5.merge
 '
 
+test_expect_success 'errors if given a bad branch name' '
+	cat <<-\EOF >expect &&
+	fatal: '\''foo..bar'\'' is not a valid branch name
+	hint: See `man git check-ref-format`
+	hint: Disable this message with "git config advice.refSyntax false"
+	EOF
+	test_must_fail git branch foo..bar >actual 2>&1 &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh
index ea7cfd1..a1139f7 100755
--- a/t/t3202-show-branch.sh
+++ b/t/t3202-show-branch.sh
@@ -4,13 +4,10 @@
 
 . ./test-lib.sh
 
-# arbitrary reference time: 2009-08-30 19:20:00
-GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
-
 test_expect_success 'error descriptions on empty repository' '
 	current=$(git branch --show-current) &&
 	cat >expect <<-EOF &&
-	error: No commit on branch '\''$current'\'' yet.
+	error: no commit on branch '\''$current'\'' yet
 	EOF
 	test_must_fail git branch --edit-description 2>actual &&
 	test_cmp expect actual &&
@@ -21,7 +18,7 @@
 test_expect_success 'fatal descriptions on empty repository' '
 	current=$(git branch --show-current) &&
 	cat >expect <<-EOF &&
-	fatal: No commit on branch '\''$current'\'' yet.
+	fatal: no commit on branch '\''$current'\'' yet
 	EOF
 	test_must_fail git branch --set-upstream-to=non-existent 2>actual &&
 	test_cmp expect actual &&
@@ -119,6 +116,22 @@
 	test_must_be_empty actual.out
 '
 
+test_expect_success 'show-branch --sparse' '
+	test_when_finished "git checkout branch10 && git branch -D branchA" &&
+	git checkout -b branchA branch10 &&
+	git merge -s ours -m "merge 1 and 10 to make A" branch1 &&
+	git commit --allow-empty -m "another" &&
+
+	git show-branch --sparse >out &&
+	grep "merge 1 and 10 to make A" out &&
+
+	git show-branch >out &&
+	! grep "merge 1 and 10 to make A" out &&
+
+	git show-branch --no-sparse >out &&
+	! grep "merge 1 and 10 to make A" out
+'
+
 test_expect_success 'setup show branch --list' '
 	sed "s/^> //" >expect <<-\EOF
 	>   [branch1] branch1
@@ -171,18 +184,6 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'show branch --reflog=2' '
-	sed "s/^>	//" >expect <<-\EOF &&
-	>	! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10
-	>	 ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10
-	>	--
-	>	+  [refs/heads/branch10@{0}] branch10
-	>	++ [refs/heads/branch10@{1}] initial
-	EOF
-	git show-branch --reflog=2 >actual &&
-	test_cmp actual expect
-'
-
 # incompatible options
 while read combo
 do
@@ -197,9 +198,18 @@
 --reflog --current
 EOF
 
+# unnegatable options
+for opt in topo-order date-order reflog
+do
+	test_expect_success "show-branch --no-$opt (should fail)" '
+		test_must_fail git show-branch --no-$opt 2>err &&
+		grep "unknown option .no-$opt." err
+	'
+done
+
 test_expect_success 'error descriptions on non-existent branch' '
 	cat >expect <<-EOF &&
-	error: No branch named '\''non-existent'\'.'
+	error: no branch named '\''non-existent'\''
 	EOF
 	test_must_fail git branch --edit-description non-existent 2>actual &&
 	test_cmp expect actual
@@ -213,7 +223,7 @@
 	test_cmp expect actual &&
 
 	cat >expect <<-EOF &&
-	fatal: No branch named '\''non-existent'\''.
+	fatal: no branch named '\''non-existent'\''
 	EOF
 	test_must_fail git branch -c non-existent new-branch 2>actual &&
 	test_cmp expect actual &&
@@ -221,4 +231,56 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'error descriptions on orphan branch' '
+	test_when_finished git worktree remove -f wt &&
+	git worktree add wt --detach &&
+	git -C wt checkout --orphan orphan-branch &&
+	test_branch_op_in_wt() {
+		test_orphan_error() {
+			test_must_fail git $* 2>actual &&
+			test_grep "no commit on branch .orphan-branch. yet$" actual
+		} &&
+		test_orphan_error -C wt branch $1 $2 &&                # implicit branch
+		test_orphan_error -C wt branch $1 orphan-branch $2 &&  # explicit branch
+		test_orphan_error branch $1 orphan-branch $2           # different worktree
+	} &&
+	test_branch_op_in_wt --edit-description &&
+	test_branch_op_in_wt --set-upstream-to=ne &&
+	test_branch_op_in_wt -c new-branch
+'
+
+test_expect_success 'setup reflogs' '
+	test_commit base &&
+	git checkout -b branch &&
+	test_commit one &&
+	git reset --hard HEAD^ &&
+	test_commit two &&
+	test_commit three
+'
+
+test_expect_success '--reflog shows reflog entries' '
+	cat >expect <<-\EOF &&
+	! [branch@{0}] (0 seconds ago) commit: three
+	 ! [branch@{1}] (60 seconds ago) commit: two
+	  ! [branch@{2}] (2 minutes ago) reset: moving to HEAD^
+	   ! [branch@{3}] (2 minutes ago) commit: one
+	----
+	+    [branch@{0}] three
+	++   [branch@{1}] two
+	   + [branch@{3}] one
+	++++ [branch@{2}] base
+	EOF
+	# the output always contains relative timestamps; use
+	# a known time to get deterministic results
+	GIT_TEST_DATE_NOW=$test_tick \
+	git show-branch --reflog branch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--reflog handles missing reflog' '
+	git reflog expire --expire=now branch &&
+	git show-branch --reflog branch >actual &&
+	test_must_be_empty actual
+'
+
 test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index d34d77f..758963b 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -55,9 +55,17 @@
 EOF
 test_expect_success 'git branch -r shows remote branches' '
 	git branch -r >actual &&
+	test_cmp expect actual &&
+
+	git branch --remotes >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --no-remotes is rejected' '
+	test_must_fail git branch --no-remotes 2>err &&
+	grep "unknown option .no-remotes." err
+'
+
 cat >expect <<'EOF'
   branch-one
   branch-two
@@ -68,9 +76,17 @@
 EOF
 test_expect_success 'git branch -a shows local and remote branches' '
 	git branch -a >actual &&
+	test_cmp expect actual &&
+
+	git branch --all >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --no-all is rejected' '
+	test_must_fail git branch --no-all 2>err &&
+	grep "unknown option .no-all." err
+'
+
 cat >expect <<'EOF'
 two
 one
@@ -337,10 +353,48 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'git branch --format with ahead-behind' '
+	cat >expect <<-\EOF &&
+	(HEAD detached from fromtag) 0 0
+	refs/heads/ambiguous 0 0
+	refs/heads/branch-one 1 0
+	refs/heads/branch-two 0 0
+	refs/heads/main 1 0
+	refs/heads/ref-to-branch 1 0
+	refs/heads/ref-to-remote 1 0
+	EOF
+	git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git branch with --format=%(rest) must fail' '
 	test_must_fail git branch --format="%(rest)" >actual
 '
 
+test_expect_success 'git branch --format --omit-empty' '
+	cat >expect <<-\EOF &&
+	Refname is (HEAD detached from fromtag)
+	Refname is refs/heads/ambiguous
+	Refname is refs/heads/branch-one
+	Refname is refs/heads/branch-two
+
+	Refname is refs/heads/ref-to-branch
+	Refname is refs/heads/ref-to-remote
+	EOF
+	git branch --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF &&
+	Refname is (HEAD detached from fromtag)
+	Refname is refs/heads/ambiguous
+	Refname is refs/heads/branch-one
+	Refname is refs/heads/branch-two
+	Refname is refs/heads/ref-to-branch
+	Refname is refs/heads/ref-to-remote
+	EOF
+	git branch --omit-empty --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'worktree colors correct' '
 	cat >expect <<-EOF &&
 	* <GREEN>(HEAD detached from fromtag)<RESET>
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
index 3399344..594e3e4 100755
--- a/t/t3204-branch-name-interpretation.sh
+++ b/t/t3204-branch-name-interpretation.sh
@@ -9,6 +9,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 expect_branch() {
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index b5f4d6a..7b05bf3 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -195,7 +195,7 @@
 
 test_expect_success 'A^{/..} is not mistaken for a range' '
 	test_must_fail git range-diff topic^.. topic^{/..} -- 2>error &&
-	test_i18ngrep "not a commit range" error
+	test_grep "not a commit range" error
 '
 
 test_expect_success 'trivial reordering' '
@@ -537,7 +537,7 @@
 			main..unmodified >actual &&
 		test_when_finished "rm 000?-*" &&
 		test_line_count = 5 actual &&
-		test_i18ngrep "^Range-diff:$" 0000-* &&
+		test_grep "^Range-diff:$" 0000-* &&
 		grep "= 1: .* s/5/A" 0000-* &&
 		grep "= 2: .* s/4/A" 0000-* &&
 		grep "= 3: .* s/11/B" 0000-* &&
@@ -549,7 +549,7 @@
 	git format-patch --range-diff=HEAD~1 HEAD~1 >actual &&
 	test_when_finished "rm 0001-*" &&
 	test_line_count = 1 actual &&
-	test_i18ngrep "^Range-diff:$" 0001-* &&
+	test_grep "^Range-diff:$" 0001-* &&
 	grep "> 1: .* new message" 0001-*
 '
 
@@ -557,7 +557,7 @@
 	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
 	test_when_finished "rm v2.9-0001-*" &&
 	test_line_count = 1 actual &&
-	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	test_grep "^Range-diff:$" v2.9-0001-* &&
 	grep "> 1: .* new message" v2.9-0001-*
 '
 
@@ -565,7 +565,7 @@
 	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
 	test_when_finished "rm v2-0001-*" &&
 	test_line_count = 1 actual &&
-	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	test_grep "^Range-diff ..* v1:$" v2-0001-* &&
 	grep "> 1: .* new message" v2-0001-*
 '
 
@@ -573,7 +573,7 @@
 	git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
 	test_when_finished "rm v0-0001-*" &&
 	test_line_count = 1 actual &&
-	test_i18ngrep "^Range-diff:$" v0-0001-* &&
+	test_grep "^Range-diff:$" v0-0001-* &&
 	grep "> 1: .* new message" v0-0001-*
 '
 
@@ -662,6 +662,20 @@
 	test_cmp expect actual
 '
 
+# `range-diff` should act like `log` with regards to notes
+test_expect_success 'range-diff with --notes=custom does not show default notes' '
+	git notes add -m "topic note" topic &&
+	git notes add -m "unmodified note" unmodified &&
+	git notes --ref=custom add -m "topic note" topic &&
+	git notes --ref=custom add -m "unmodified note" unmodified &&
+	test_when_finished git notes remove topic unmodified &&
+	test_when_finished git notes --ref=custom remove topic unmodified &&
+	git range-diff --notes=custom main..topic main..unmodified \
+		>actual &&
+	! grep "## Notes ##" actual &&
+	grep "## Notes (custom) ##" actual
+'
+
 test_expect_success 'format-patch --range-diff does not compare notes by default' '
 	git notes add -m "topic note" topic &&
 	git notes add -m "unmodified note" unmodified &&
@@ -670,7 +684,7 @@
 		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
-	test_i18ngrep "^Range-diff:$" 0000-* &&
+	test_grep "^Range-diff:$" 0000-* &&
 	grep "= 1: .* s/5/A" 0000-* &&
 	grep "= 2: .* s/4/A" 0000-* &&
 	grep "= 3: .* s/11/B" 0000-* &&
@@ -679,6 +693,20 @@
 	! grep "note" 0000-*
 '
 
+test_expect_success 'format-patch --notes=custom --range-diff only compares custom notes' '
+	git notes add -m "topic note" topic &&
+	git notes --ref=custom add -m "topic note (custom)" topic &&
+	git notes add -m "unmodified note" unmodified &&
+	git notes --ref=custom add -m "unmodified note (custom)" unmodified &&
+	test_when_finished git notes remove topic unmodified &&
+	test_when_finished git notes --ref=custom remove topic unmodified &&
+	git format-patch --notes=custom --cover-letter --range-diff=$prev \
+		main..unmodified >actual &&
+	test_when_finished "rm 000?-*" &&
+	grep "## Notes (custom) ##" 0000-* &&
+	! grep "## Notes ##" 0000-*
+'
+
 test_expect_success 'format-patch --range-diff with --no-notes' '
 	git notes add -m "topic note" topic &&
 	git notes add -m "unmodified note" unmodified &&
@@ -687,7 +715,7 @@
 		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
-	test_i18ngrep "^Range-diff:$" 0000-* &&
+	test_grep "^Range-diff:$" 0000-* &&
 	grep "= 1: .* s/5/A" 0000-* &&
 	grep "= 2: .* s/4/A" 0000-* &&
 	grep "= 3: .* s/11/B" 0000-* &&
@@ -704,7 +732,7 @@
 		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
-	test_i18ngrep "^Range-diff:$" 0000-* &&
+	test_grep "^Range-diff:$" 0000-* &&
 	grep "= 1: .* s/5/A" 0000-* &&
 	grep "= 2: .* s/4/A" 0000-* &&
 	grep "= 3: .* s/11/B" 0000-* &&
@@ -733,7 +761,7 @@
 		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
-	test_i18ngrep "^Range-diff:$" 0000-* &&
+	test_grep "^Range-diff:$" 0000-* &&
 	grep "= 1: .* s/5/A" 0000-* &&
 	grep "= 2: .* s/4/A" 0000-* &&
 	grep "= 3: .* s/11/B" 0000-* &&
@@ -764,7 +792,7 @@
 		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
-	test_i18ngrep "^Range-diff:$" 0000-* &&
+	test_grep "^Range-diff:$" 0000-* &&
 	grep "= 1: .* s/5/A" 0000-* &&
 	grep "= 2: .* s/4/A" 0000-* &&
 	grep "= 3: .* s/11/B" 0000-* &&
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
deleted file mode 100755
index 07a0ff9..0000000
--- a/t/t3210-pack-refs.sh
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Amos Waterland
-# Copyright (c) 2006 Christian Couder
-#
-
-test_description='git pack-refs should not change the branch semantic
-
-This test runs git pack-refs and git show-ref and checks that the branch
-semantic is still the same.
-'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-test_expect_success 'enable reflogs' '
-	git config core.logallrefupdates true
-'
-
-test_expect_success \
-    'prepare a trivial repository' \
-    'echo Hello > A &&
-     git update-index --add A &&
-     git commit -m "Initial commit." &&
-     HEAD=$(git rev-parse --verify HEAD)'
-
-SHA1=
-
-test_expect_success \
-    'see if git show-ref works as expected' \
-    'git branch a &&
-     SHA1=$(cat .git/refs/heads/a) &&
-     echo "$SHA1 refs/heads/a" >expect &&
-     git show-ref a >result &&
-     test_cmp expect result'
-
-test_expect_success \
-    'see if a branch still exists when packed' \
-    'git branch b &&
-     git pack-refs --all &&
-     rm -f .git/refs/heads/b &&
-     echo "$SHA1 refs/heads/b" >expect &&
-     git show-ref b >result &&
-     test_cmp expect result'
-
-test_expect_success 'git branch c/d should barf if branch c exists' '
-     git branch c &&
-     git pack-refs --all &&
-     rm -f .git/refs/heads/c &&
-     test_must_fail git branch c/d
-'
-
-test_expect_success \
-    'see if a branch still exists after git pack-refs --prune' \
-    'git branch e &&
-     git pack-refs --all --prune &&
-     echo "$SHA1 refs/heads/e" >expect &&
-     git show-ref e >result &&
-     test_cmp expect result'
-
-test_expect_success 'see if git pack-refs --prune remove ref files' '
-     git branch f &&
-     git pack-refs --all --prune &&
-     ! test -f .git/refs/heads/f
-'
-
-test_expect_success 'see if git pack-refs --prune removes empty dirs' '
-     git branch r/s/t &&
-     git pack-refs --all --prune &&
-     ! test -e .git/refs/heads/r
-'
-
-test_expect_success \
-    'git branch g should work when git branch g/h has been deleted' \
-    'git branch g/h &&
-     git pack-refs --all --prune &&
-     git branch -d g/h &&
-     git branch g &&
-     git pack-refs --all &&
-     git branch -d g'
-
-test_expect_success 'git branch i/j/k should barf if branch i exists' '
-     git branch i &&
-     git pack-refs --all --prune &&
-     test_must_fail git branch i/j/k
-'
-
-test_expect_success \
-    'test git branch k after branch k/l/m and k/lm have been deleted' \
-    'git branch k/l &&
-     git branch k/lm &&
-     git branch -d k/l &&
-     git branch k/l/m &&
-     git branch -d k/l/m &&
-     git branch -d k/lm &&
-     git branch k'
-
-test_expect_success \
-    'test git branch n after some branch deletion and pruning' \
-    'git branch n/o &&
-     git branch n/op &&
-     git branch -d n/o &&
-     git branch n/o/p &&
-     git branch -d n/op &&
-     git pack-refs --all --prune &&
-     git branch -d n/o/p &&
-     git branch n'
-
-test_expect_success \
-	'see if up-to-date packed refs are preserved' \
-	'git branch q &&
-	 git pack-refs --all --prune &&
-	 git update-ref refs/heads/q refs/heads/q &&
-	 ! test -f .git/refs/heads/q'
-
-test_expect_success 'pack, prune and repack' '
-	git tag foo &&
-	git pack-refs --all --prune &&
-	git show-ref >all-of-them &&
-	git pack-refs &&
-	git show-ref >again &&
-	test_cmp all-of-them again
-'
-
-test_expect_success 'explicit pack-refs with dangling packed reference' '
-	git commit --allow-empty -m "soon to be garbage-collected" &&
-	git pack-refs --all &&
-	git reset --hard HEAD^ &&
-	git reflog expire --expire=all --all &&
-	git prune --expire=all &&
-	git pack-refs --all 2>result &&
-	test_must_be_empty result
-'
-
-test_expect_success 'delete ref with dangling packed version' '
-	git checkout -b lamb &&
-	git commit --allow-empty -m "future garbage" &&
-	git pack-refs --all &&
-	git reset --hard HEAD^ &&
-	git checkout main &&
-	git reflog expire --expire=all --all &&
-	git prune --expire=all &&
-	git branch -d lamb 2>result &&
-	test_must_be_empty result
-'
-
-test_expect_success 'delete ref while another dangling packed ref' '
-	git branch lamb &&
-	git commit --allow-empty -m "future garbage" &&
-	git pack-refs --all &&
-	git reset --hard HEAD^ &&
-	git reflog expire --expire=all --all &&
-	git prune --expire=all &&
-	git branch -d lamb 2>result &&
-	test_must_be_empty result
-'
-
-test_expect_success 'pack ref directly below refs/' '
-	git update-ref refs/top HEAD &&
-	git pack-refs --all --prune &&
-	grep refs/top .git/packed-refs &&
-	test_path_is_missing .git/refs/top
-'
-
-test_expect_success 'do not pack ref in refs/bisect' '
-	git update-ref refs/bisect/local HEAD &&
-	git pack-refs --all --prune &&
-	! grep refs/bisect/local .git/packed-refs >/dev/null &&
-	test_path_is_file .git/refs/bisect/local
-'
-
-test_expect_success 'disable reflogs' '
-	git config core.logallrefupdates false &&
-	rm -rf .git/logs
-'
-
-test_expect_success 'create packed foo/bar/baz branch' '
-	git branch foo/bar/baz &&
-	git pack-refs --all --prune &&
-	test_path_is_missing .git/refs/heads/foo/bar/baz &&
-	test_must_fail git reflog exists refs/heads/foo/bar/baz
-'
-
-test_expect_success 'notice d/f conflict with existing directory' '
-	test_must_fail git branch foo &&
-	test_must_fail git branch foo/bar
-'
-
-test_expect_success 'existing directory reports concrete ref' '
-	test_must_fail git branch foo 2>stderr &&
-	test_i18ngrep refs/heads/foo/bar/baz stderr
-'
-
-test_expect_success 'notice d/f conflict with existing ref' '
-	test_must_fail git branch foo/bar/baz/extra &&
-	test_must_fail git branch foo/bar/baz/lots/of/extra/components
-'
-
-test_expect_success 'reject packed-refs with unterminated line' '
-	cp .git/packed-refs .git/packed-refs.bak &&
-	test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
-	printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
-	echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
-	test_must_fail git for-each-ref >out 2>err &&
-	test_cmp expected_err err
-'
-
-test_expect_success 'reject packed-refs containing junk' '
-	cp .git/packed-refs .git/packed-refs.bak &&
-	test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
-	printf "%s\n" "bogus content" >>.git/packed-refs &&
-	echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
-	test_must_fail git for-each-ref >out 2>err &&
-	test_cmp expected_err err
-'
-
-test_expect_success 'reject packed-refs with a short SHA-1' '
-	cp .git/packed-refs .git/packed-refs.bak &&
-	test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
-	printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
-	printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
-	test_must_fail git for-each-ref >out 2>err &&
-	test_cmp expected_err err
-'
-
-test_expect_success 'timeout if packed-refs.lock exists' '
-	LOCK=.git/packed-refs.lock &&
-	>"$LOCK" &&
-	test_when_finished "rm -f $LOCK" &&
-	test_must_fail git pack-refs --all --prune
-'
-
-test_expect_success 'retry acquiring packed-refs.lock' '
-	LOCK=.git/packed-refs.lock &&
-	>"$LOCK" &&
-	test_when_finished "wait && rm -f $LOCK" &&
-	{
-		( sleep 1 && rm -f $LOCK ) &
-	} &&
-	git -c core.packedrefstimeout=3000 pack-refs --all --prune
-'
-
-test_expect_success SYMLINKS 'pack symlinked packed-refs' '
-	# First make sure that symlinking works when reading:
-	git update-ref refs/heads/lossy refs/heads/main &&
-	git for-each-ref >all-refs-before &&
-	mv .git/packed-refs .git/my-deviant-packed-refs &&
-	ln -s my-deviant-packed-refs .git/packed-refs &&
-	git for-each-ref >all-refs-linked &&
-	test_cmp all-refs-before all-refs-linked &&
-	git pack-refs --all --prune &&
-	git for-each-ref >all-refs-packed &&
-	test_cmp all-refs-before all-refs-packed &&
-	test -h .git/packed-refs &&
-	test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
-'
-
-test_done
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 3288aae..cf23c06 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -362,6 +362,7 @@
 '
 
 test_expect_success 'create note with combination of -m and -F' '
+	test_when_finished git notes remove HEAD &&
 	cat >expect-combine_m_and_F <<-EOF &&
 		foo
 
@@ -380,6 +381,41 @@
 	test_cmp expect-combine_m_and_F actual
 '
 
+test_expect_success 'create note with combination of -m and -F and --separator' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect-combine_m_and_F <<-\EOF &&
+	foo
+	-------
+	xyzzy
+	-------
+	bar
+	-------
+	zyxxy
+	-------
+	baz
+	EOF
+	echo "xyzzy" >note_a &&
+	echo "zyxxy" >note_b &&
+	git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --separator="-------" &&
+	git notes show >actual &&
+	test_cmp expect-combine_m_and_F actual
+'
+
+test_expect_success 'create note with combination of -m and -F and --no-separator' '
+	cat >expect-combine_m_and_F <<-\EOF &&
+	foo
+	xyzzy
+	bar
+	zyxxy
+	baz
+	EOF
+	echo "xyzzy" >note_a &&
+	echo "zyxxy" >note_b &&
+	git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --no-separator &&
+	git notes show >actual &&
+	test_cmp expect-combine_m_and_F actual
+'
+
 test_expect_success 'remove note with "git notes remove"' '
 	git notes remove HEAD^ &&
 	git notes remove &&
@@ -521,6 +557,112 @@
 	test_must_be_empty actual
 '
 
+test_expect_success 'append: specify a separator with an empty arg' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect <<-\EOF &&
+	notes-1
+
+	notes-2
+	EOF
+
+	git notes add -m "notes-1" &&
+	git notes append --separator="" -m "notes-2" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append: specify a separator without arg' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect <<-\EOF &&
+	notes-1
+
+	notes-2
+	EOF
+
+	git notes add -m "notes-1" &&
+	git notes append --separator -m "notes-2" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append: specify as --no-separator' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect <<-\EOF &&
+	notes-1
+	notes-2
+	EOF
+
+	git notes add -m "notes-1" &&
+	git notes append --no-separator -m "notes-2" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with line break' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect <<-\EOF &&
+	notes-1
+	-------
+	notes-2
+	EOF
+
+	git notes add -m "notes-1" &&
+	git notes append --separator="-------$LF" -m "notes-2" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator without line break' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect <<-\EOF &&
+	notes-1
+	-------
+	notes-2
+	EOF
+
+	git notes add -m "notes-1" &&
+	git notes append --separator="-------" -m "notes-2" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with multiple messages' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect <<-\EOF &&
+	notes-1
+	-------
+	notes-2
+	-------
+	notes-3
+	EOF
+
+	git notes add -m "notes-1" &&
+	git notes append --separator="-------" -m "notes-2" -m "notes-3" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append note with combination of -m and -F and --separator' '
+	test_when_finished git notes remove HEAD &&
+	cat >expect-combine_m_and_F <<-\EOF &&
+	m-notes-1
+	-------
+	f-notes-1
+	-------
+	m-notes-2
+	-------
+	f-notes-2
+	-------
+	m-notes-3
+	EOF
+
+	echo "f-notes-1" >note_a &&
+	echo "f-notes-2" >note_b &&
+	git notes append -m "m-notes-1" -F note_a -m "m-notes-2" -F note_b -m "m-notes-3" --separator="-------" &&
+	git notes show >actual &&
+	test_cmp expect-combine_m_and_F actual
+'
+
 test_expect_success 'append to existing note with "git notes append"' '
 	cat >expect <<-EOF &&
 		Initial set of notes
@@ -818,6 +960,33 @@
 	test_cmp blob actual
 '
 
+test_expect_success 'create note from blob with "-C", also specify "-m", "-F" and "--separator"' '
+	# 8th will be reuseed in following tests, so rollback when the test is done
+	test_when_finished "git notes remove && git notes add -C $(cat blob)" &&
+	commit=$(git rev-parse HEAD) &&
+	cat >expect <<-EOF &&
+		commit $commit
+		Author: A U Thor <author@example.com>
+		Date:   Thu Apr 7 15:20:13 2005 -0700
+
+		${indent}8th
+
+		Notes:
+		${indent}This is a blob object
+		${indent}-------
+		${indent}This is created by -m
+		${indent}-------
+		${indent}This is created by -F
+	EOF
+
+	git notes remove &&
+	echo "This is a blob object" | git hash-object -w --stdin >blob &&
+	echo "This is created by -F" >note_a &&
+	git notes add -C $(cat blob) -m "This is created by -m" -F note_a --separator="-------" &&
+	git log -1 >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'create note from other note with "git notes add -c"' '
 	test_commit 9th &&
 	commit=$(git rev-parse HEAD) &&
@@ -1300,9 +1469,9 @@
 
 test_expect_success 'git notes copy diagnoses too many or too few arguments' '
 	test_must_fail git notes copy 2>error &&
-	test_i18ngrep "too few arguments" error &&
+	test_grep "too few arguments" error &&
 	test_must_fail git notes copy one two three 2>error &&
-	test_i18ngrep "too many arguments" error
+	test_grep "too many arguments" error
 '
 
 test_expect_success 'git notes get-ref expands refs/heads/main to refs/notes/refs/heads/main' '
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 141d3e4..9bd5dbf 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -360,7 +360,12 @@
 
 test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
 	git config core.notesRef refs/notes/y &&
-	test_must_fail git -c notes.mergeStrategy="foo" notes merge z &&
+	cat >expect <<-\EOF &&
+	error: unknown notes merge strategy foo
+	fatal: unable to parse '\''notes.mergeStrategy'\'' from command-line config
+	EOF
+	test_must_fail git -c notes.mergeStrategy="foo" notes merge z 2>actual &&
+	test_cmp expect actual &&
 	# Verify no changes (y)
 	verify_notes y y
 '
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index d3d72e2..597df5e 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -216,7 +216,7 @@
 	git config core.notesRef refs/notes/m &&
 	test_must_fail git notes merge z >output 2>&1 &&
 	# Output should point to where to resolve conflicts
-	test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+	test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
 	# Inspect merge conflicts
 	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
@@ -263,7 +263,7 @@
 	test -d .git/NOTES_MERGE_WORKTREE &&
 	test_must_fail git notes merge z >output 2>&1 &&
 	# Output should indicate what is wrong
-	test_i18ngrep -q "\\.git/NOTES_MERGE_\\* exists" output
+	test_grep -q "\\.git/NOTES_MERGE_\\* exists" output
 '
 
 # Setup non-conflicting merge between x and new notes ref w
@@ -417,7 +417,7 @@
 	git config core.notesRef refs/notes/m &&
 	test_must_fail git notes merge z >output 2>&1 &&
 	# Output should point to where to resolve conflicts
-	test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+	test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
 	# Inspect merge conflicts
 	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
@@ -449,7 +449,7 @@
 test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
 	test_must_fail git notes merge z >output 2>&1 &&
 	# Output should point to where to resolve conflicts
-	test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+	test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
 	# Inspect merge conflicts
 	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
@@ -528,7 +528,7 @@
 	git update-ref refs/notes/m refs/notes/y &&
 	test_must_fail git notes merge z >output 2>&1 &&
 	# Output should point to where to resolve conflicts
-	test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
+	test_grep "\\.git/NOTES_MERGE_WORKTREE" output &&
 	# Inspect merge conflicts
 	ls .git/NOTES_MERGE_WORKTREE >output_conflicts &&
 	test_cmp expect_conflicts output_conflicts &&
@@ -561,9 +561,9 @@
 EOF
 	# Fail to finalize merge
 	test_must_fail git notes merge --commit >output 2>&1 &&
-	# .git/NOTES_MERGE_* must remain
-	test -f .git/NOTES_MERGE_PARTIAL &&
-	test -f .git/NOTES_MERGE_REF &&
+	# NOTES_MERGE_* refs and .git/NOTES_MERGE_* state files must remain
+	git rev-parse --verify NOTES_MERGE_PARTIAL &&
+	git rev-parse --verify NOTES_MERGE_REF &&
 	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
 	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
 	test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
@@ -573,9 +573,9 @@
 	test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
 	test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
 	# Mention refs/notes/m, and its current and expected value in output
-	test_i18ngrep -q "refs/notes/m" output &&
-	test_i18ngrep -q "$(git rev-parse refs/notes/m)" output &&
-	test_i18ngrep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
+	test_grep -q "refs/notes/m" output &&
+	test_grep -q "$(git rev-parse refs/notes/m)" output &&
+	test_grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output &&
 	# Verify that other notes refs has not changed (w, x, y and z)
 	verify_notes w &&
 	verify_notes x &&
diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh
index bff0aea..0fd3328 100755
--- a/t/t3320-notes-merge-worktrees.sh
+++ b/t/t3320-notes-merge-worktrees.sh
@@ -57,7 +57,7 @@
 		cd worktree &&
 		git config core.notesRef refs/notes/y &&
 		test_must_fail git notes merge z 2>err &&
-		test_i18ngrep "a notes merge into refs/notes/y is already in-progress at" err
+		test_grep "a notes merge into refs/notes/y is already in-progress at" err
 	) &&
 	test_must_fail git -C worktree symbolic-ref NOTES_MERGE_REF
 '
@@ -67,7 +67,7 @@
 		cd worktree2 &&
 		git config core.notesRef refs/notes/x &&
 		test_must_fail git notes merge z >out 2>&1 &&
-		test_i18ngrep "Automatic notes merge failed" out &&
+		test_grep "Automatic notes merge failed" out &&
 		grep -v "A notes merge into refs/notes/x is already in-progress in" out
 	) &&
 	echo "refs/notes/x" >expect &&
diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh
new file mode 100755
index 0000000..beca346
--- /dev/null
+++ b/t/t3321-notes-stripspace.sh
@@ -0,0 +1,578 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Teng Long
+#
+
+test_description='Test commit notes with stripspace behavior'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+MULTI_LF="$LF$LF$LF"
+write_script fake_editor <<\EOF
+echo "$MSG" >"$1"
+echo "$MSG" >&2
+EOF
+GIT_EDITOR=./fake_editor
+export GIT_EDITOR
+
+test_expect_success 'setup the commit' '
+	test_commit 1st
+'
+
+test_expect_success 'add note by editor' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add  &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m", "--stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	git notes add -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	git notes add --stripspace -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m" and "--no-stripspace" ' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}first-line${MULTI_LF}second-line
+	EOF
+
+	git notes add --no-stripspace \
+		      -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-m", "--stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	git notes add -m "${LF}" \
+		      -m "first-line" \
+		      -m "${MULTI_LF}" \
+		      -m "second-line" \
+		      -m "${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	git notes add --stripspace -m "${LF}" \
+		      -m "first-line" \
+		      -m "${MULTI_LF}" \
+		      -m "second-line" \
+		      -m "${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-m" and "--no-stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line${LF}
+	EOF
+
+	git notes add --no-stripspace \
+		      -m "${LF}" \
+		      -m "first-line" \
+		      -m "${MULTI_LF}" \
+		      -m "second-line" \
+		      -m "${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F", "--stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	cat >note-file <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	git notes add -F note-file &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	git notes add --stripspace -F note-file &&
+	git notes show >actual
+'
+
+test_expect_success 'add note by specifying single "-F" and "--no-stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	cat >note-file <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	git notes add --no-stripspace -F note-file &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F", "--stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	file-1-first-line
+
+	file-1-second-line
+
+	file-2-first-line
+
+	file-2-second-line
+	EOF
+
+	cat >note-file-1 <<-EOF &&
+	${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+	EOF
+
+	cat >note-file-2 <<-EOF &&
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	git notes add -F note-file-1 -F note-file-2 &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	git notes add --stripspace -F note-file-1 -F note-file-2 &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F" with "--no-stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	cat >note-file-1 <<-EOF &&
+	${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+	EOF
+
+	cat >note-file-2 <<-EOF &&
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	git notes add --no-stripspace -F note-file-1 -F note-file-2 &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append note by editor' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	git notes add -m "first-line" &&
+	MSG="${MULTI_LF}second-line${LF}" git notes append  &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-m"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	git notes add -m "${LF}first-line" &&
+	git notes append -m "${MULTI_LF}second-line${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-m"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	git notes add -m "${LF}first-line" &&
+	git notes append -m "${MULTI_LF}" \
+		      -m "second-line" \
+		      -m "${LF}" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	cat >note-file <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	git notes add -F note-file &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-F"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	file-1-first-line
+
+	file-1-second-line
+
+	file-2-first-line
+
+	file-2-second-line
+	EOF
+
+	cat >note-file-1 <<-EOF &&
+	${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+	EOF
+
+	cat >note-file-2 <<-EOF &&
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	git notes add -F note-file-1 -F note-file-2 &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-F"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	initial-line
+
+	first-line
+
+	second-line
+	EOF
+
+	cat >note-file <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	git notes add -m "initial-line" &&
+	git notes append -F note-file &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append notes by specifying multiple "-F"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	initial-line
+
+	file-1-first-line
+
+	file-1-second-line
+
+	file-2-first-line
+
+	file-2-second-line
+	EOF
+
+	cat >note-file-1 <<-EOF &&
+	${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+	EOF
+
+	cat >note-file-2 <<-EOF &&
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	git notes add -m "initial-line" &&
+	git notes append -F note-file-1 -F note-file-2 &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-F" with "--no-stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	initial-line
+	${LF}${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	cat >note-file-1 <<-EOF &&
+	${LF}
+	file-1-first-line
+	${MULTI_LF}
+	file-1-second-line
+	${LF}
+	EOF
+
+	cat >note-file-2 <<-EOF &&
+	${LF}
+	file-2-first-line
+	${MULTI_LF}
+	file-2-second-line
+	${LF}
+	EOF
+
+	git notes add -m "initial-line" &&
+	git notes append --no-stripspace -F note-file-1 -F note-file-2 &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add notes with empty messages' '
+	rev=$(git rev-parse HEAD) &&
+	git notes add -m "${LF}" \
+		      -m "${MULTI_LF}" \
+		      -m "${LF}" >actual 2>&1 &&
+	test_grep "Removing note for object" actual
+'
+
+test_expect_success 'add note by specifying "-C", "--no-stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	git hash-object -w --stdin <expect >blob &&
+	git notes add -C $(cat blob) &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	git notes add --no-stripspace -C $(cat blob) &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >data <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	git hash-object -w --stdin <data >blob &&
+	git notes add --stripspace -C $(cat blob) &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspace all together' '
+	test_when_finished "git notes remove" &&
+	cat >data <<-EOF &&
+	${LF}
+	first-line
+	${MULTI_LF}
+	second-line
+	${LF}
+	EOF
+
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+
+	third-line
+	EOF
+
+	git hash-object -w --stdin <data >blob &&
+	git notes add -C $(cat blob) -m "third-line" &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not stripspace all together' '
+	test_when_finished "git notes remove" &&
+	cat >data <<-EOF &&
+
+	second-line
+	EOF
+
+	cat >expect <<-EOF &&
+	first-line
+	${LF}
+	second-line
+	EOF
+
+	git hash-object -w --stdin <data >blob &&
+	git notes add -m "first-line" -C $(cat blob)  &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c", "--stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	echo "initial-line" | git hash-object -w --stdin >blob &&
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add -c $(cat blob) &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --stripspace -c $(cat blob) &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c" with "--no-stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}first-line${MULTI_LF}second-line${LF}
+	EOF
+
+	echo "initial-line" | git hash-object -w --stdin >blob &&
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace -c $(cat blob) &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c", "--stripspace" is the default behavior' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	first-line
+
+	second-line
+	EOF
+
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit &&
+	git notes show >actual &&
+	test_cmp expect actual &&
+	git notes remove &&
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit --stripspace &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c" with "--no-stripspace"' '
+	test_when_finished "git notes remove" &&
+	cat >expect <<-EOF &&
+	${LF}first-line${MULTI_LF}second-line${LF}
+	EOF
+
+	MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace &&
+	git notes show >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index d5a8ee3..e1c8c5f 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -143,8 +143,8 @@
 	>B &&
 	test_when_finished "rm -f B" &&
 	test_must_fail git rebase topic 2>output.err >output.out &&
-	test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" output.err &&
-	test_i18ngrep B output.err
+	test_grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+	test_grep B output.err
 '
 
 test_expect_success 'fail when upstream arg is missing and not on branch' '
@@ -388,6 +388,20 @@
 	git rebase main main
 '
 
+test_expect_success 'switch to branch checked out elsewhere fails' '
+	test_when_finished "
+		git worktree remove wt1 &&
+		git worktree remove wt2 &&
+		git branch -d shared
+	" &&
+	git worktree add wt1 -b shared &&
+	git worktree add wt2 -f shared &&
+	# we test in both worktrees to ensure that works
+	# as expected with "first" and "next" worktrees
+	test_must_fail git -C wt1 rebase shared shared &&
+	test_must_fail git -C wt2 rebase shared shared
+'
+
 test_expect_success 'switch to branch not checked out' '
 	git checkout main &&
 	git branch other &&
@@ -407,17 +421,7 @@
 	git checkout main &&
 	git worktree add wt &&
 	test_must_fail git -C wt rebase main main 2>err &&
-	test_i18ngrep "already checked out" err
-'
-
-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
-	git checkout main &&
-	mv .git/logs actual_logs &&
-	cmd //c "mklink /D .git\logs ..\actual_logs" &&
-	git rebase -f HEAD^ &&
-	test -L .git/logs &&
-	rm .git/logs &&
-	mv actual_logs .git/logs
+	test_grep "already used by worktree at" err
 '
 
 test_expect_success 'rebase when inside worktree subdirectory' '
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 7e46f4c..5c67d07 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -8,6 +8,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 T="A quick brown fox
@@ -131,27 +132,6 @@
 	esac
 '
 
-test_expect_success 'rebase -s funny -Xopt' '
-	test_when_finished "rm -fr test-bin funny.was.run" &&
-	mkdir test-bin &&
-	cat >test-bin/git-merge-funny <<-EOF &&
-	#!$SHELL_PATH
-	case "\$1" in --opt) ;; *) exit 2 ;; esac
-	shift &&
-	>funny.was.run &&
-	exec git merge-recursive "\$@"
-	EOF
-	chmod +x test-bin/git-merge-funny &&
-	git reset --hard &&
-	git checkout -b test-funny main^ &&
-	test_commit funny &&
-	(
-		PATH=./test-bin:$PATH &&
-		git rebase -s funny -Xopt main
-	) &&
-	test -f funny.was.run
-'
-
 test_expect_success 'rebase --skip works with two conflicts in a row' '
 	git checkout second-side  &&
 	tr "[A-Z]" "[a-z]" <newfile >tmp &&
@@ -191,7 +171,7 @@
 
 	# Regular rebase fails, because the 1-11 commit is deduplicated
 	test_must_fail git -C repo rebase --merge main 2> err &&
-	test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
+	test_grep "error: could not apply.*add 12 in another branch" err &&
 	git -C repo rebase --abort &&
 
 	# With --reapply-cherry-picks, it works
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index f6e4864..a1911c4 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -108,10 +108,10 @@
 	test_when_finished "git rebase --abort" &&
 	test_must_fail git rebase -i --onto goodbye \
 		amended-goodbye^ amended-goodbye 2>err &&
-	test_i18ngrep "previous cherry-pick is now empty" err &&
-	test_i18ngrep "git rebase --skip" err &&
+	test_grep "previous cherry-pick is now empty" err &&
+	test_grep "git rebase --skip" err &&
 	test_must_fail git commit &&
-	test_i18ngrep "git rebase --skip" err
+	test_grep "git rebase --skip" err
 '
 
 test_expect_success 'correct authorship when committing empty pick' '
@@ -131,10 +131,10 @@
 		test_must_fail env FAKE_LINES="reword 1" git rebase -i \
 			--onto goodbye amended-goodbye^ amended-goodbye 2>err
 	) &&
-	test_i18ngrep "previous cherry-pick is now empty" err &&
-	test_i18ngrep "git rebase --skip" err &&
+	test_grep "previous cherry-pick is now empty" err &&
+	test_grep "git rebase --skip" err &&
 	test_must_fail git commit &&
-	test_i18ngrep "git rebase --skip" err
+	test_grep "git rebase --skip" err
 '
 
 test_expect_success 'correct advice upon editing empty commit' '
@@ -144,10 +144,10 @@
 		test_must_fail env FAKE_LINES="edit 1" git rebase -i \
 			--onto goodbye amended-goodbye^ amended-goodbye 2>err
 	) &&
-	test_i18ngrep "previous cherry-pick is now empty" err &&
-	test_i18ngrep "git rebase --skip" err &&
+	test_grep "previous cherry-pick is now empty" err &&
+	test_grep "git rebase --skip" err &&
 	test_must_fail git commit &&
-	test_i18ngrep "git rebase --skip" err
+	test_grep "git rebase --skip" err
 '
 
 test_expect_success 'correct advice upon cherry-picking an empty commit during a rebase' '
@@ -157,10 +157,10 @@
 		test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_amended-goodbye" \
 			git rebase -i goodbye^ goodbye 2>err
 	) &&
-	test_i18ngrep "previous cherry-pick is now empty" err &&
-	test_i18ngrep "git cherry-pick --skip" err &&
+	test_grep "previous cherry-pick is now empty" err &&
+	test_grep "git cherry-pick --skip" err &&
 	test_must_fail git commit 2>err &&
-	test_i18ngrep "git cherry-pick --skip" err
+	test_grep "git cherry-pick --skip" err
 '
 
 test_expect_success 'correct advice upon multi cherry-pick picking an empty commit during a rebase' '
@@ -170,10 +170,10 @@
 		test_must_fail env FAKE_LINES="1 exec_git_cherry-pick_goodbye_amended-goodbye" \
 			git rebase -i goodbye^^ goodbye 2>err
 	) &&
-	test_i18ngrep "previous cherry-pick is now empty" err &&
-	test_i18ngrep "git cherry-pick --skip" err &&
+	test_grep "previous cherry-pick is now empty" err &&
+	test_grep "git cherry-pick --skip" err &&
 	test_must_fail git commit 2>err &&
-	test_i18ngrep "git cherry-pick --skip" err
+	test_grep "git cherry-pick --skip" err
 '
 
 test_expect_success 'fixup that empties commit fails' '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ff0afad..d1bead6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -153,6 +153,18 @@
 	git rebase --continue
 '
 
+test_expect_success 'cherry-pick works with rebase --exec' '
+	test_when_finished "git cherry-pick --abort; \
+			    git rebase --abort; \
+			    git checkout primary" &&
+	echo "exec git cherry-pick G" >todo &&
+	(
+		set_replace_editor todo &&
+		test_must_fail git rebase -i D D
+	) &&
+	test_cmp_rev G CHERRY_PICK_HEAD
+'
+
 test_expect_success 'rebase -x with empty command fails' '
 	test_when_finished "git rebase --abort ||:" &&
 	test_must_fail env git rebase -x "" @ 2>actual &&
@@ -291,9 +303,9 @@
 	git rm --cached file1 &&
 	git commit -m "remove file in base" &&
 	test_must_fail git rebase -i primary > output 2>&1 &&
-	test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" \
+	test_grep "The following untracked working tree files would be overwritten by checkout:" \
 		output &&
-	test_i18ngrep "file1" output &&
+	test_grep "file1" output &&
 	test_path_is_missing .git/rebase-merge &&
 	rm file1 &&
 	git reset --hard HEAD^
@@ -604,7 +616,8 @@
 	echo "edited again" > file7 &&
 	git add file7 &&
 	test_must_fail git rebase --continue 2>error &&
-	test_i18ngrep "you have staged changes in your working tree" error
+	test_grep "you have staged changes in your working tree" error &&
+	test_grep ! "could not open.*for reading" error
 '
 
 test_expect_success 'rebase a detached HEAD' '
@@ -758,7 +771,7 @@
 	git show HEAD~2 | grep "C changed"
 '
 
-test_expect_success 'no uncommited changes when rewording the todo list is reloaded' '
+test_expect_success 'no uncommitted changes when rewording and the todo list is reloaded' '
 	git checkout E &&
 	test_when_finished "git checkout @{-1}" &&
 	(
@@ -955,7 +968,7 @@
 	git reset --hard execute &&
 	rm -rf exec_output &&
 	EDITOR="echo >invoked_editor" git rebase --exec "echo a line >>exec_output"  HEAD~2 2>actual &&
-	test_i18ngrep  "Successfully rebased and updated" actual &&
+	test_grep  "Successfully rebased and updated" actual &&
 	test_line_count = 2 exec_output &&
 	test_path_is_missing invoked_editor
 '
@@ -963,7 +976,7 @@
 test_expect_success 'rebase -i --exec without <CMD>' '
 	git reset --hard execute &&
 	test_must_fail git rebase -i --exec 2>actual &&
-	test_i18ngrep "requires a value" actual &&
+	test_grep "requires a value" actual &&
 	git checkout primary
 '
 
@@ -1272,24 +1285,38 @@
 		test_set_editor "$(pwd)/dump-raw.sh" &&
 		git rebase -i HEAD~4 >actual
 	) &&
-	test_i18ngrep "^# Rebase ..* onto ..* ([0-9]" actual
+	test_grep "^# Rebase ..* onto ..* ([0-9]" actual
 '
 
 test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
-	git checkout --force branch2 &&
+	git checkout --force A &&
 	git clean -f &&
+	cat >todo <<-EOF &&
+	exec >file2
+	pick $(git rev-parse B) B
+	pick $(git rev-parse C) C
+	pick $(git rev-parse D) D
+	exec cat .git/rebase-merge/done >actual
+	EOF
 	(
-		set_fake_editor &&
-		FAKE_LINES="edit 1 2" git rebase -i A
+		set_replace_editor todo &&
+		test_must_fail git rebase -i A
 	) &&
-	test_cmp_rev HEAD F &&
-	test_path_is_missing file6 &&
-	>file6 &&
-	test_must_fail git rebase --continue &&
-	test_cmp_rev HEAD F &&
-	rm file6 &&
+	test_cmp_rev HEAD B &&
+	test_cmp_rev REBASE_HEAD C &&
+	head -n3 todo >expect &&
+	test_cmp expect .git/rebase-merge/done &&
+	rm file2 &&
+	test_path_is_missing .git/rebase-merge/patch &&
+	echo changed >file1 &&
+	git add file1 &&
+	test_must_fail git rebase --continue 2>err &&
+	grep "error: you have staged changes in your working tree" err &&
+	git reset --hard HEAD &&
 	git rebase --continue &&
-	test_cmp_rev HEAD I
+	test_cmp_rev HEAD D &&
+	tail -n3 todo >>expect &&
+	test_cmp expect actual
 '
 
 test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
@@ -1305,7 +1332,14 @@
 	>file6 &&
 	test_must_fail git rebase --continue &&
 	test_cmp_rev HEAD F &&
+	test_cmp_rev REBASE_HEAD I &&
 	rm file6 &&
+	test_path_is_missing .git/rebase-merge/patch &&
+	echo changed >file1 &&
+	git add file1 &&
+	test_must_fail git rebase --continue 2>err &&
+	grep "error: you have staged changes in your working tree" err &&
+	git reset --hard HEAD &&
 	git rebase --continue &&
 	test $(git cat-file commit HEAD | sed -ne \$p) = I &&
 	git reset --hard original-branch2
@@ -1323,7 +1357,14 @@
 	>file6 &&
 	test_must_fail git rebase --continue &&
 	test $(git cat-file commit HEAD | sed -ne \$p) = F &&
+	test_cmp_rev REBASE_HEAD I &&
 	rm file6 &&
+	test_path_is_missing .git/rebase-merge/patch &&
+	echo changed >file1 &&
+	git add file1 &&
+	test_must_fail git rebase --continue 2>err &&
+	grep "error: you have staged changes in your working tree" err &&
+	git reset --hard HEAD &&
 	git rebase --continue &&
 	test $(git cat-file commit HEAD | sed -ne \$p) = I
 '
@@ -1379,7 +1420,7 @@
 		FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual
 	) &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-	test_i18ngrep \
+	test_grep \
 		"Successfully rebased and updated refs/heads/missing-commit" \
 		actual
 '
@@ -1442,7 +1483,7 @@
 		git rebase --continue 2>actual
 	) &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-	test_i18ngrep \
+	test_grep \
 		"Successfully rebased and updated refs/heads/missing-commit" \
 		actual
 '
@@ -1477,7 +1518,7 @@
 		git rebase --continue 2>actual
 	) &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-	test_i18ngrep \
+	test_grep \
 		"Successfully rebased and updated refs/heads/missing-commit" \
 		actual
 '
@@ -1525,7 +1566,7 @@
 		git rebase --continue 2>actual
 	) &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
-	test_i18ngrep \
+	test_grep \
 		"Successfully rebased and updated refs/heads/missing-commit" \
 		actual
 '
@@ -1585,9 +1626,9 @@
 		set_fake_editor &&
 		test_must_fail env FAKE_LINES="1 2 3 bad 4 5" \
 		git rebase -i --root 2>actual &&
-		test_i18ngrep "pickled $(git rev-list --oneline -1 primary~1)" \
+		test_grep "pickled $(git rev-list --oneline -1 primary~1)" \
 				actual &&
-		test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+		test_grep "You can fix this with .git rebase --edit-todo.." \
 				actual &&
 		FAKE_LINES="1 2 3 drop 4 5" git rebase --edit-todo
 	) &&
@@ -1596,6 +1637,32 @@
 	test C = $(git cat-file commit HEAD^ | sed -ne \$p)
 '
 
+test_expect_success 'the first command cannot be a fixup' '
+	rebase_setup_and_clean fixup-first &&
+
+	cat >orig <<-EOF &&
+	fixup $(git log -1 --format="%h %s" B)
+	pick $(git log -1 --format="%h %s" C)
+	EOF
+
+	(
+		set_replace_editor orig &&
+		test_must_fail git rebase -i A 2>actual
+	) &&
+	grep "cannot .fixup. without a previous commit" actual &&
+	grep "You can fix this with .git rebase --edit-todo.." actual &&
+	# verify that the todo list has not been truncated
+	grep -v "^#" .git/rebase-merge/git-rebase-todo >actual &&
+	test_cmp orig actual &&
+
+	test_must_fail git rebase --edit-todo 2>actual &&
+	grep "cannot .fixup. without a previous commit" actual &&
+	grep "You can fix this with .git rebase --edit-todo.." actual &&
+	# verify that the todo list has not been truncated
+	grep -v "^#" .git/rebase-merge/git-rebase-todo >actual &&
+	test_cmp orig actual
+'
+
 test_expect_success 'tabs and spaces are accepted in the todolist' '
 	rebase_setup_and_clean indented-comment &&
 	write_script add-indent.sh <<-\EOF &&
@@ -1619,8 +1686,8 @@
 		set_fake_editor &&
 		test_must_fail env FAKE_LINES="1 2 edit fakesha 3 4 5 #" \
 			git rebase -i --root 2>actual &&
-			test_i18ngrep "edit XXXXXXX False commit" actual &&
-			test_i18ngrep "You can fix this with .git rebase --edit-todo.." \
+			test_grep "edit XXXXXXX False commit" actual &&
+			test_grep "You can fix this with .git rebase --edit-todo.." \
 					actual &&
 		FAKE_LINES="1 2 4 5 6" git rebase --edit-todo
 	) &&
@@ -1647,7 +1714,7 @@
 		FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
 			HEAD^ >out 2>err
 	) &&
-	test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
+	test_grep "$SQ-S\"S I Gner\"$SQ" err
 '
 
 test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' '
@@ -1658,7 +1725,7 @@
 		FAKE_LINES="edit 1" git rebase -i --gpg-sign="\"S I Gner\"" \
 			HEAD^ >out 2>err
 	) &&
-	test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err
+	test_grep "$SQ-S\"S I Gner\"$SQ" err
 '
 
 test_expect_success 'valid author header after --root swap' '
@@ -1712,7 +1779,7 @@
 	) &&
 	echo x >file1 &&
 	test_must_fail git commit file1 2>err &&
-	test_i18ngrep "cannot do a partial commit during a rebase." err
+	test_grep "cannot do a partial commit during a rebase." err
 '
 
 test_expect_success 'correct error message for commit --amend after empty pick' '
@@ -1725,13 +1792,13 @@
 	) &&
 	echo x>file1 &&
 	test_must_fail git commit -a --amend 2>err &&
-	test_i18ngrep "middle of a rebase -- cannot amend." err
+	test_grep "middle of a rebase -- cannot amend." err
 '
 
 test_expect_success 'todo has correct onto hash' '
 	GIT_SEQUENCE_EDITOR=cat git rebase -i no-conflict-branch~4 no-conflict-branch >actual &&
 	onto=$(git rev-parse --short HEAD~4) &&
-	test_i18ngrep "^# Rebase ..* onto $onto" actual
+	test_grep "^# Rebase ..* onto $onto" actual
 '
 
 test_expect_success 'ORIG_HEAD is updated correctly' '
@@ -2105,7 +2172,7 @@
 	# recorded in the update-refs file. We will force-update the
 	# "second" ref, but "git branch -f" will not work because of
 	# the lock in the update-refs file.
-	git rev-parse third >.git/refs/heads/second &&
+	git update-ref refs/heads/second third &&
 
 	test_must_fail git rebase --continue 2>err &&
 	grep "update_ref failed for ref '\''refs/heads/second'\''" err &&
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index ceca160..a1d7fa7 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -33,24 +33,24 @@
 
 test_expect_success 'rebase against main twice' '
 	git rebase --apply main >out &&
-	test_i18ngrep "Current branch topic is up to date" out
+	test_grep "Current branch topic is up to date" out
 '
 
 test_expect_success 'rebase against main twice with --force' '
 	git rebase --force-rebase --apply main >out &&
-	test_i18ngrep "Current branch topic is up to date, rebase forced" out
+	test_grep "Current branch topic is up to date, rebase forced" out
 '
 
 test_expect_success 'rebase against main twice from another branch' '
 	git checkout topic^ &&
 	git rebase --apply main topic >out &&
-	test_i18ngrep "Current branch topic is up to date" out
+	test_grep "Current branch topic is up to date" out
 '
 
 test_expect_success 'rebase fast-forward to main' '
 	git checkout topic^ &&
 	git rebase --apply topic >out &&
-	test_i18ngrep "Fast-forwarded HEAD to topic" out
+	test_grep "Fast-forwarded HEAD to topic" out
 '
 
 test_expect_success 'rebase --stat' '
@@ -75,14 +75,14 @@
 
 test_expect_success 'rebase --onto outputs the invalid ref' '
 	test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&
-	test_i18ngrep "invalid-ref" err
+	test_grep "invalid-ref" err
 '
 
 test_expect_success 'error out early upon -C<n> or --whitespace=<bad>' '
 	test_must_fail git rebase -Cnot-a-number HEAD 2>err &&
-	test_i18ngrep "numerical value" err &&
+	test_grep "numerical value" err &&
 	test_must_fail git rebase --whitespace=bad HEAD 2>err &&
-	test_i18ngrep "Invalid whitespace option" err
+	test_grep "Invalid whitespace option" err
 '
 
 write_reflog_expect () {
@@ -251,8 +251,8 @@
 	git -C unrelated remote add -f origin "$PWD" &&
 	git -C unrelated branch --set-upstream-to=origin/main &&
 	git -C unrelated -c core.editor=true rebase -i -v --stat >actual &&
-	test_i18ngrep "Changes to " actual &&
-	test_i18ngrep "5 files changed" actual
+	test_grep "Changes to " actual &&
+	test_grep "5 files changed" actual
 '
 
 test_done
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index ebbaed1..9f49c42 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -40,9 +40,24 @@
 		test_path_is_missing "$state_dir"
 	'
 
+	test_expect_success "pre rebase$type head is marked as reachable" '
+		# Clean up the state from the previous one
+		git checkout -f --detach pre-rebase &&
+		test_tick &&
+		git commit --amend --only -m "reworded" &&
+		orig_head=$(git rev-parse HEAD) &&
+		test_must_fail git rebase$type main &&
+		# Stop ORIG_HEAD marking $state_dir/orig-head as reachable
+		git update-ref -d ORIG_HEAD &&
+		git reflog expire --expire="$GIT_COMMITTER_DATE" --all &&
+		git prune --expire=now &&
+		git rebase --abort &&
+		test_cmp_rev $orig_head HEAD
+	'
+
 	test_expect_success "rebase$type --abort after --skip" '
 		# Clean up the state from the previous one
-		git reset --hard pre-rebase &&
+		git checkout -B to-rebase pre-rebase &&
 		test_must_fail git rebase$type main &&
 		test_path_is_dir "$state_dir" &&
 		test_must_fail git rebase --skip &&
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index a364530..fcc40d6 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -43,7 +43,7 @@
 
 	git tag $1 &&
 	test_tick &&
-	git rebase $2 -i HEAD^^^ &&
+	git rebase $2 HEAD^^^ &&
 	git log --oneline >actual &&
 	if test -n "$no_squash"
 	then
@@ -61,15 +61,24 @@
 }
 
 test_expect_success 'auto fixup (option)' '
-	test_auto_fixup final-fixup-option --autosquash
+	test_auto_fixup fixup-option --autosquash &&
+	test_auto_fixup fixup-option-i "--autosquash -i"
 '
 
-test_expect_success 'auto fixup (config)' '
+test_expect_success 'auto fixup (config true)' '
 	git config rebase.autosquash true &&
-	test_auto_fixup final-fixup-config-true &&
+	test_auto_fixup ! fixup-config-true &&
+	test_auto_fixup fixup-config-true-i -i &&
 	test_auto_fixup ! fixup-config-true-no --no-autosquash &&
+	test_auto_fixup ! fixup-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto fixup (config false)' '
 	git config rebase.autosquash false &&
-	test_auto_fixup ! final-fixup-config-false
+	test_auto_fixup ! fixup-config-false &&
+	test_auto_fixup ! fixup-config-false-i -i &&
+	test_auto_fixup fixup-config-false-yes --autosquash &&
+	test_auto_fixup fixup-config-false-i-yes "-i --autosquash"
 '
 
 test_auto_squash () {
@@ -87,7 +96,7 @@
 	git commit -m "squash! first" -m "extra para for first" &&
 	git tag $1 &&
 	test_tick &&
-	git rebase $2 -i HEAD^^^ &&
+	git rebase $2 HEAD^^^ &&
 	git log --oneline >actual &&
 	if test -n "$no_squash"
 	then
@@ -105,15 +114,24 @@
 }
 
 test_expect_success 'auto squash (option)' '
-	test_auto_squash final-squash --autosquash
+	test_auto_squash squash-option --autosquash &&
+	test_auto_squash squash-option-i "--autosquash -i"
 '
 
-test_expect_success 'auto squash (config)' '
+test_expect_success 'auto squash (config true)' '
 	git config rebase.autosquash true &&
-	test_auto_squash final-squash-config-true &&
+	test_auto_squash ! squash-config-true &&
+	test_auto_squash squash-config-true-i -i &&
 	test_auto_squash ! squash-config-true-no --no-autosquash &&
+	test_auto_squash ! squash-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto squash (config false)' '
 	git config rebase.autosquash false &&
-	test_auto_squash ! final-squash-config-false
+	test_auto_squash ! squash-config-false &&
+	test_auto_squash ! squash-config-false-i -i &&
+	test_auto_squash squash-config-false-yes --autosquash &&
+	test_auto_squash squash-config-false-i-yes "-i --autosquash"
 '
 
 test_expect_success 'misspelled auto squash' '
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 130e2f9..127216f 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -62,61 +62,39 @@
 	rm -fr .git/rebase-* &&
 	git reset --hard commit-new-file-F2-on-topic-branch &&
 	test_commit "commit-new-file-F3-on-topic-branch" F3 32 &&
-	test_when_finished "rm -fr test-bin funny.was.run" &&
+	test_when_finished "rm -fr test-bin" &&
 	mkdir test-bin &&
-	cat >test-bin/git-merge-funny <<-EOF &&
-	#!$SHELL_PATH
-	case "\$1" in --opt) ;; *) exit 2 ;; esac
-	shift &&
-	>funny.was.run &&
-	exec git merge-recursive "\$@"
-	EOF
-	chmod +x test-bin/git-merge-funny &&
-	(
-		PATH=./test-bin:$PATH &&
-		test_must_fail git rebase -s funny -Xopt main topic
-	) &&
-	test -f funny.was.run &&
-	rm funny.was.run &&
-	echo "Resolved" >F2 &&
-	git add F2 &&
-	(
-		PATH=./test-bin:$PATH &&
-		git rebase --continue
-	) &&
-	test -f funny.was.run
-'
 
-test_expect_success 'rebase -i --continue handles merge strategy and options' '
-	rm -fr .git/rebase-* &&
-	git reset --hard commit-new-file-F2-on-topic-branch &&
-	test_commit "commit-new-file-F3-on-topic-branch-for-dash-i" F3 32 &&
-	test_when_finished "rm -fr test-bin funny.was.run funny.args" &&
-	mkdir test-bin &&
-	cat >test-bin/git-merge-funny <<-EOF &&
-	#!$SHELL_PATH
-	echo "\$@" >>funny.args
-	case "\$1" in --opt) ;; *) exit 2 ;; esac
-	case "\$2" in --foo) ;; *) exit 2 ;; esac
-	case "\$4" in --) ;; *) exit 2 ;; esac
-	shift 2 &&
-	>funny.was.run &&
-	exec git merge-recursive "\$@"
+	write_script test-bin/git-merge-funny <<-\EOF &&
+	printf "[%s]\n" $# "$1" "$2" "$3" "$5" >actual
+	shift 3 &&
+	exec git merge-recursive "$@"
 	EOF
-	chmod +x test-bin/git-merge-funny &&
+
+	cat >expect <<-\EOF &&
+	[7]
+	[--option=arg with space]
+	[--op"tion\]
+	[--new
+	line ]
+	[--]
+	EOF
+
+	rm -f actual &&
 	(
 		PATH=./test-bin:$PATH &&
-		test_must_fail git rebase -i -s funny -Xopt -Xfoo main topic
+		test_must_fail git rebase -s funny -X"option=arg with space" \
+				-Xop\"tion\\ -X"new${LF}line " main topic
 	) &&
-	test -f funny.was.run &&
-	rm funny.was.run &&
+	test_cmp expect actual &&
+	rm actual &&
 	echo "Resolved" >F2 &&
 	git add F2 &&
 	(
 		PATH=./test-bin:$PATH &&
 		git rebase --continue
 	) &&
-	test -f funny.was.run
+	test_cmp expect actual
 '
 
 test_expect_success 'rebase -r passes merge strategy options correctly' '
@@ -137,15 +115,23 @@
 	test_when_finished "test_might_fail git rebase --abort" &&
 	git checkout -b with-conflicting-fixup &&
 	test_commit wants-fixup &&
-	test_commit "fixup! wants-fixup" wants-fixup.t 1 wants-fixup-1 &&
-	test_commit "fixup! wants-fixup" wants-fixup.t 2 wants-fixup-2 &&
-	test_commit "fixup! wants-fixup" wants-fixup.t 3 wants-fixup-3 &&
+	test_commit "fixup 1" wants-fixup.t 1 wants-fixup-1 &&
+	test_commit "fixup 2" wants-fixup.t 2 wants-fixup-2 &&
+	test_commit "fixup 3" wants-fixup.t 3 wants-fixup-3 &&
 	test_must_fail env FAKE_LINES="1 fixup 2 squash 4" \
 		git rebase -i HEAD~4 &&
 
 	: now there is a conflict, and comments in the commit message &&
-	git show HEAD >out &&
-	grep "fixup! wants-fixup" out &&
+	test_commit_message HEAD <<-\EOF &&
+	# This is a combination of 2 commits.
+	# This is the 1st commit message:
+
+	wants-fixup
+
+	# The commit message #2 will be skipped:
+
+	# fixup 1
+	EOF
 
 	: skip and continue &&
 	echo "cp \"\$1\" .git/copy.txt" | write_script copy-editor.sh &&
@@ -155,33 +141,49 @@
 	test_path_is_missing .git/copy.txt &&
 
 	: now the comments in the commit message should have been cleaned up &&
-	git show HEAD >out &&
-	! grep "fixup! wants-fixup" out &&
+	test_commit_message HEAD -m wants-fixup &&
 
 	: now, let us ensure that "squash" is handled correctly &&
 	git reset --hard wants-fixup-3 &&
-	test_must_fail env FAKE_LINES="1 squash 4 squash 2 squash 4" \
+	test_must_fail env FAKE_LINES="1 squash 2 squash 1 squash 3 squash 1" \
 		git rebase -i HEAD~4 &&
 
-	: the first squash failed, but there are two more in the chain &&
+	: the second squash failed, but there are two more in the chain &&
 	(test_set_editor "$PWD/copy-editor.sh" &&
 	 test_must_fail git rebase --skip) &&
 
 	: not the final squash, no need to edit the commit message &&
 	test_path_is_missing .git/copy.txt &&
 
-	: The first squash was skipped, therefore: &&
-	git show HEAD >out &&
-	test_i18ngrep "# This is a combination of 2 commits" out &&
-	test_i18ngrep "# This is the commit message #2:" out &&
+	: The first and third squashes succeeded, therefore: &&
+	test_commit_message HEAD <<-\EOF &&
+	# This is a combination of 3 commits.
+	# This is the 1st commit message:
+
+	wants-fixup
+
+	# This is the commit message #2:
+
+	fixup 1
+
+	# This is the commit message #3:
+
+	fixup 2
+	EOF
 
 	(test_set_editor "$PWD/copy-editor.sh" && git rebase --skip) &&
-	git show HEAD >out &&
-	test_i18ngrep ! "# This is a combination" out &&
+	test_commit_message HEAD <<-\EOF &&
+	wants-fixup
+
+	fixup 1
+
+	fixup 2
+	EOF
 
 	: Final squash failed, but there was still a squash &&
-	test_i18ngrep "# This is a combination of 2 commits" .git/copy.txt &&
-	test_i18ngrep "# This is the commit message #2:" .git/copy.txt
+	head -n1 .git/copy.txt >first-line &&
+	test_grep "# This is a combination of 3 commits" first-line &&
+	test_grep "# This is the commit message #3:" .git/copy.txt
 '
 
 test_expect_success 'setup rerere database' '
@@ -266,6 +268,24 @@
 	test_path_is_file execed
 '
 
+test_expect_success 'patch file is removed before break command' '
+	test_when_finished "git rebase --abort" &&
+	cat >todo <<-\EOF &&
+	pick commit-new-file-F2-on-topic-branch
+	break
+	EOF
+
+	(
+		set_replace_editor todo &&
+		test_must_fail git rebase -i --onto commit-new-file-F2 HEAD
+	) &&
+	test_path_is_file .git/rebase-merge/patch &&
+	echo 22>F2 &&
+	git add F2 &&
+	git rebase --continue &&
+	test_path_is_missing .git/rebase-merge/patch
+'
+
 test_expect_success '--reschedule-failed-exec' '
 	test_when_finished "git rebase --abort" &&
 	test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ &&
@@ -274,7 +294,7 @@
 	test_must_fail git -c rebase.rescheduleFailedExec=true \
 		rebase -x false HEAD^ 2>err &&
 	grep "^exec false" .git/rebase-merge/git-rebase-todo &&
-	test_i18ngrep "has been rescheduled" err
+	test_grep "has been rescheduled" err
 '
 
 test_expect_success 'rebase.rescheduleFailedExec only affects `rebase -i`' '
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 693934e..1a820f1 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -333,4 +333,14 @@
 	test_cmp_rev not-the-feature-branch unrelated-onto-branch
 '
 
+test_expect_success 'autostash commit is marked as reachable' '
+	echo changed >file0 &&
+	git rebase --autostash --exec "git prune --expire=now" \
+		feature-branch^ feature-branch &&
+	# git rebase succeeds if the stash cannot be applied so we need to check
+	# the contents of file0
+	echo changed >expect &&
+	test_cmp expect file0
+'
+
 test_done
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37..b40f262 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -85,6 +85,11 @@
 		test_must_fail git rebase $opt --reapply-cherry-picks A
 	"
 
+	test_expect_success "$opt incompatible with --rebase-merges" "
+		git checkout B^0 &&
+		test_must_fail git rebase $opt --rebase-merges A
+	"
+
 	test_expect_success "$opt incompatible with --update-refs" "
 		git checkout B^0 &&
 		test_must_fail git rebase $opt --update-refs A
@@ -95,10 +100,10 @@
 		test_must_fail git rebase $opt --root A
 	"
 
-	test_expect_success "$opt incompatible with rebase.autosquash" "
+	test_expect_success "$opt incompatible with rebase.rebaseMerges" "
 		git checkout B^0 &&
-		test_must_fail git -c rebase.autosquash=true rebase $opt A 2>err &&
-		grep -e --no-autosquash err
+		test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+		grep -e --no-rebase-merges err
 	"
 
 	test_expect_success "$opt incompatible with rebase.updateRefs" "
@@ -107,10 +112,10 @@
 		grep -e --no-update-refs err
 	"
 
-	test_expect_success "$opt okay with overridden rebase.autosquash" "
+	test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
 		test_when_finished \"git reset --hard B^0\" &&
 		git checkout B^0 &&
-		git -c rebase.autosquash=true rebase --no-autosquash $opt A
+		git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
 	"
 
 	test_expect_success "$opt okay with overridden rebase.updateRefs" "
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a..1ee6b00 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --merge --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --interactive --empty=ask upstream &&
+	test_must_fail git rebase --interactive --empty=stop upstream &&
 
 	git rebase --skip &&
 
@@ -112,7 +123,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --interactive upstream &&
 
@@ -167,4 +178,42 @@
 	test_path_is_missing .git/MERGE_MSG
 '
 
+test_expect_success 'rebase --exec --empty=drop' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=drop upstream &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=keep upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --exec "true" --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index 48b76f8..1b3e97c 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -74,9 +74,9 @@
 	test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-main main &&
 	: first pick results in no changes &&
 	git rebase --skip &&
-	verbose test "$(commit_message HEAD~2)" = "topic_4" &&
-	verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
-	verbose test "$(commit_message HEAD)" = "Empty commit"
+	test "$(commit_message HEAD~2)" = "topic_4" &&
+	test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+	test "$(commit_message HEAD)" = "Empty commit"
 '
 
 test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
@@ -85,9 +85,9 @@
 	test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-main --root &&
 	: first pick results in no changes &&
 	git rebase --skip &&
-	verbose test "$(commit_message HEAD~2)" = "topic_4" &&
-	verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
-	verbose test "$(commit_message HEAD)" = "Empty commit"
+	test "$(commit_message HEAD~2)" = "topic_4" &&
+	test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+	test "$(commit_message HEAD)" = "Empty commit"
 '
 
 test_done
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c..59b5d6b 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -128,14 +128,24 @@
 '
 
 test_expect_success '`reset` refuses to overwrite untracked files' '
-	git checkout -b refuse-to-reset &&
+	git checkout B &&
 	test_commit dont-overwrite-untracked &&
-	git checkout @{-1} &&
-	: >dont-overwrite-untracked.t &&
-	echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
+	cat >script-from-scratch <<-EOF &&
+	exec >dont-overwrite-untracked.t
+	pick $(git rev-parse B) B
+	reset refs/tags/dont-overwrite-untracked
+	pick $(git rev-parse C) C
+	exec cat .git/rebase-merge/done >actual
+	EOF
 	test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
-	test_must_fail git rebase -ir HEAD &&
-	git rebase --abort
+	test_must_fail git rebase -ir A &&
+	test_cmp_rev HEAD B &&
+	head -n3 script-from-scratch >expect &&
+	test_cmp expect .git/rebase-merge/done &&
+	rm dont-overwrite-untracked.t &&
+	git rebase --continue &&
+	tail -n3 script-from-scratch >>expect &&
+	test_cmp expect actual
 '
 
 test_expect_success '`reset` rejects trees' '
@@ -165,12 +175,16 @@
 	test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 	test_tick &&
 	test_must_fail git rebase -ir HEAD &&
+	test_cmp_rev REBASE_HEAD H^0 &&
 	grep "^merge -C .* G$" .git/rebase-merge/done &&
 	grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
-	test_path_is_file .git/rebase-merge/patch &&
+	test_path_is_missing .git/rebase-merge/patch &&
+	echo changed >file1 &&
+	git add file1 &&
+	test_must_fail git rebase --continue 2>err &&
+	grep "error: you have staged changes in your working tree" err &&
 
 	: fail because of merge conflict &&
-	rm G.t .git/rebase-merge/patch &&
 	git reset --hard conflicting-G &&
 	test_must_fail git rebase --continue &&
 	! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
@@ -250,6 +264,16 @@
 	EOF
 '
 
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+	git checkout -b no-rebase-merges E &&
+	git rebase --rebase-merges --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
 test_expect_success 'do not rebase cousins unless asked for' '
 	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
@@ -268,6 +292,40 @@
 	EOF
 '
 
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b config-rebase-cousins main &&
+	git rebase HEAD^ &&
+	test_cmp_graph HEAD^.. <<-\EOF
+	*   Merge the topic branch '\''onebranch'\''
+	|\
+	| * D
+	| * G
+	|/
+	o H
+	EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+	test_config rebase.rebaseMerges no-rebase-cousins &&
+	git checkout -b override-config-no-rebase-cousins E &&
+	git rebase --no-rebase-merges C &&
+	test_cmp_graph C.. <<-\EOF
+	* B
+	* D
+	o C
+	EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+	test_config rebase.rebaseMerges rebase-cousins &&
+	git checkout -b override-config-rebase-cousins E &&
+	before="$(git rev-parse --verify HEAD)" &&
+	test_tick &&
+	git rebase --rebase-merges C &&
+	test_cmp_rev HEAD $before
+'
+
 test_expect_success 'refs/rewritten/* is worktree-local' '
 	git worktree add wt &&
 	cat >wt/script-from-scratch <<-\EOF &&
@@ -534,4 +592,23 @@
 	EOF
 '
 
+test_expect_success 'progress shows the correct total' '
+	git checkout -b progress H &&
+	git rebase --rebase-merges --force-rebase --verbose A 2> err &&
+	# Expecting "Rebasing (N/14)" here, no bogus total number
+	grep "^Rebasing.*/14.$" err >progress &&
+	test_line_count = 14 progress
+'
+
+test_expect_success 'truncate label names' '
+	commit=$(git commit-tree -p HEAD^ -p HEAD -m "0123456789 我 123" HEAD^{tree}) &&
+	git merge --ff-only $commit &&
+
+	done="$(git rev-parse --git-path rebase-merge/done)" &&
+	git -c rebase.maxLabelLength=14 rebase --rebase-merges -x "cp \"$done\" out" --root &&
+	grep "label 0123456789-我$" out &&
+	git -c rebase.maxLabelLength=13 rebase --rebase-merges -x "cp \"$done\" out" --root &&
+	grep "label 0123456789-$" out
+'
+
 test_done
diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh
index 4bfc779..0bb284d 100755
--- a/t/t3431-rebase-fork-point.sh
+++ b/t/t3431-rebase-fork-point.sh
@@ -84,7 +84,7 @@
 
 test_expect_success '--fork-point and --root both given' '
 	test_must_fail git rebase --fork-point --root 2>err &&
-	test_i18ngrep "cannot be used together" err
+	test_grep "cannot be used together" err
 '
 
 test_expect_success 'rebase.forkPoint set to false' '
diff --git a/t/t3437-rebase-fixup-options.sh b/t/t3437-rebase-fixup-options.sh
index dd3b301..7929e2e 100755
--- a/t/t3437-rebase-fixup-options.sh
+++ b/t/t3437-rebase-fixup-options.sh
@@ -21,21 +21,6 @@
 
 EMPTY=""
 
-# test_commit_message <rev> -m <msg>
-# test_commit_message <rev> <path>
-# Verify that the commit message of <rev> matches
-# <msg> or the content of <path>.
-test_commit_message () {
-	git show --no-patch --pretty=format:%B "$1" >actual &&
-	case "$2" in
-	-m)
-		echo "$3" >expect &&
-		test_cmp expect actual ;;
-	*)
-		test_cmp "$2" actual ;;
-	esac
-}
-
 get_author () {
 	rev="$1" &&
 	git log -1 --pretty=format:"%an %ae %at" "$rev"
diff --git a/t/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
index c614c4f..821f08e 100755
--- a/t/t3438-rebase-broken-files.sh
+++ b/t/t3438-rebase-broken-files.sh
@@ -58,4 +58,13 @@
 	check_resolve_fails
 '
 
+test_expect_success POSIXPERM,SANITY 'unwritable rebased-patches does not leak' '
+	>.git/rebased-patches &&
+	chmod a-w .git/rebased-patches &&
+
+	git checkout -b side HEAD^ &&
+	test_commit unrelated &&
+	test_must_fail git rebase --apply --onto tmp HEAD^
+'
+
 test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index 0458a58..78c3eac 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -16,46 +16,43 @@
 GIT_AUTHOR_EMAIL=bogus_email_address
 export GIT_AUTHOR_EMAIL
 
-test_expect_success \
-    'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
-    'echo First > A &&
-     git update-index --add A &&
-     test_tick &&
-     git commit -m "Add A." &&
+test_expect_success 'prepare repository with topic branch, and check cherry finds the 2 patches from there' '
+	echo First > A &&
+	git update-index --add A &&
+	test_tick &&
+	git commit -m "Add A." &&
 
-     git checkout -b my-topic-branch &&
+	git checkout -b my-topic-branch &&
 
-     echo Second > B &&
-     git update-index --add B &&
-     test_tick &&
-     git commit -m "Add B." &&
+	echo Second > B &&
+	git update-index --add B &&
+	test_tick &&
+	git commit -m "Add B." &&
 
-     echo AnotherSecond > C &&
-     git update-index --add C &&
-     test_tick &&
-     git commit -m "Add C." &&
+	echo AnotherSecond > C &&
+	git update-index --add C &&
+	test_tick &&
+	git commit -m "Add C." &&
 
-     git checkout -f main &&
-     rm -f B C &&
+	git checkout -f main &&
+	rm -f B C &&
 
-     echo Third >> A &&
-     git update-index A &&
-     test_tick &&
-     git commit -m "Modify A." &&
+	echo Third >> A &&
+	git update-index A &&
+	test_tick &&
+	git commit -m "Modify A." &&
 
-     expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
+	expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
 '
 
-test_expect_success \
-    'check that cherry with limit returns only the top patch'\
-    'expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+test_expect_success 'check that cherry with limit returns only the top patch' '
+	expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
 '
 
-test_expect_success \
-    'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
-    'git cherry-pick my-topic-branch^0 &&
-     echo $(git cherry main my-topic-branch) &&
-     expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
+test_expect_success 'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' '
+	git cherry-pick my-topic-branch^0 &&
+	echo $(git cherry main my-topic-branch) &&
+	expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
 '
 
 test_expect_success 'cherry ignores whitespace' '
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 2f3e3e2..411027f 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -1,14 +1,6 @@
 #!/bin/sh
 
-test_description='test cherry-pick and revert with renames
-
-  --
-   + rename2: renames oops to opos
-  +  rename1: renames oops to spoo
-  +  added:   adds extra line to oops
-  ++ initial: has lines in oops
-
-'
+test_description='miscellaneous basic tests for cherry-pick and revert'
 
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
@@ -51,7 +43,7 @@
 	git diff --exit-code HEAD &&
 	test_must_fail git cherry-pick --nonsense 2>msg &&
 	git diff --exit-code HEAD "$pos" &&
-	test_i18ngrep "[Uu]sage:" msg
+	test_grep "[Uu]sage:" msg
 '
 
 test_expect_success 'revert --nonsense' '
@@ -60,9 +52,17 @@
 	git diff --exit-code HEAD &&
 	test_must_fail git revert --nonsense 2>msg &&
 	git diff --exit-code HEAD "$pos" &&
-	test_i18ngrep "[Uu]sage:" msg
+	test_grep "[Uu]sage:" msg
 '
 
+# the following two test cherry-pick and revert with renames
+#
+# --
+#  + rename2: renames oops to opos
+# +  rename1: renames oops to spoo
+# +  added:   adds extra line to oops
+# ++ initial: has lines in oops
+
 test_expect_success 'cherry-pick after renaming branch' '
 
 	git checkout rename2 &&
@@ -99,16 +99,24 @@
 	echo content >extra_file &&
 	git add extra_file &&
 	test_must_fail git revert HEAD 2>errors &&
-	test_i18ngrep "your local changes would be overwritten by " errors
+	test_grep "your local changes would be overwritten by " errors
 
 '
 
 test_expect_success 'cherry-pick on unborn branch' '
-	git checkout --orphan unborn &&
+	git switch --orphan unborn &&
 	git rm --cached -r . &&
-	rm -rf * &&
 	git cherry-pick initial &&
-	git diff --quiet initial &&
+	git diff --exit-code initial &&
+	test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+	git checkout --detach &&
+	git branch -D unborn &&
+	git switch --orphan unborn &&
+	git cherry-pick initial --allow-empty &&
+	git diff --exit-code initial &&
 	test_cmp_rev ! initial HEAD
 '
 
@@ -170,12 +178,36 @@
 	hint: You can instead skip this commit with "git revert --skip".
 	hint: To abort and get back to the state before "git revert",
 	hint: run "git revert --abort".
+	hint: Disable this message with "git config advice.mergeConflict false"
 	EOF
 	test_commit --append --no-tag "double-add dream" dream dream &&
 	test_must_fail git revert HEAD^ 2>actual &&
 	test_cmp expected actual
 '
 
+test_expect_subject () {
+	echo "$1" >expect &&
+	git log -1 --pretty=%s >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'titles of fresh reverts' '
+	test_commit --no-tag A file1 &&
+	test_commit --no-tag B file1 &&
+	git revert --no-edit HEAD &&
+	test_expect_subject "Revert \"B\"" &&
+	git revert --no-edit HEAD &&
+	test_expect_subject "Reapply \"B\"" &&
+	git revert --no-edit HEAD &&
+	test_expect_subject "Revert \"Reapply \"B\"\""
+'
+
+test_expect_success 'title of legacy double revert' '
+	test_commit --no-tag "Revert \"Revert \"B\"\"" file1 &&
+	git revert --no-edit HEAD &&
+	test_expect_subject "Revert \"Revert \"Revert \"B\"\"\""
+'
+
 test_expect_success 'identification of reverted commit (default)' '
 	test_commit to-ident &&
 	test_when_finished "git reset --hard to-ident" &&
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38..9748443 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -84,7 +84,7 @@
 	git commit -m "add file2 on the side"
 '
 
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
 	git reset --hard &&
 	git checkout fork^0 &&
 	test_must_fail git cherry-pick main
@@ -99,4 +99,53 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
+test_expect_success '--empty is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick --empty=stop main 2>output &&
+	test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=drop main &&
+	test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=keep main &&
+	test_commit_message HEAD -m "add file2 on main"
+'
+
 test_done
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index f32799e..f3947b4 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -60,6 +60,7 @@
 	hint: You can instead skip this commit with "git cherry-pick --skip".
 	hint: To abort and get back to the state before "git cherry-pick",
 	hint: run "git cherry-pick --abort".
+	hint: Disable this message with "git config advice.mergeConflict false"
 	EOF
 	test_must_fail git cherry-pick picked 2>actual &&
 
@@ -74,6 +75,7 @@
 	error: could not apply \$picked... picked
 	hint: after resolving the conflicts, mark the corrected paths
 	hint: with 'git add <paths>' or 'git rm <paths>'
+	hint: Disable this message with \"git config advice.mergeConflict false\"
 	EOF
 	test_must_fail git cherry-pick --no-commit picked 2>actual &&
 
@@ -177,7 +179,7 @@
 	git add foo &&
 	test_must_fail git commit foo 2>err &&
 
-	test_i18ngrep "cannot do a partial commit during a cherry-pick." err
+	test_grep "cannot do a partial commit during a cherry-pick." err
 '
 
 test_expect_success 'commit --amend of cherry-pick fails' '
@@ -188,7 +190,7 @@
 	git add foo &&
 	test_must_fail git commit --amend 2>err &&
 
-	test_i18ngrep "in the middle of a cherry-pick -- cannot amend." err
+	test_grep "in the middle of a cherry-pick -- cannot amend." err
 '
 
 test_expect_success 'successful final commit clears cherry-pick state' '
@@ -498,7 +500,7 @@
 test_expect_success 'failed cherry-pick does not forget -s' '
 	pristine_detach initial &&
 	test_must_fail git cherry-pick -s picked &&
-	test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG
+	test_grep -e "Signed-off-by" .git/MERGE_MSG
 '
 
 test_expect_success 'commit after failed cherry-pick does not add duplicated -s' '
@@ -563,7 +565,7 @@
 	echo /unrelated >.git/info/sparse-checkout &&
 	git read-tree --reset -u HEAD &&
 	test_must_fail git cherry-pick -Xours picked>actual &&
-	test_i18ngrep ! "Changes not staged for commit:" actual
+	test_grep ! "Changes not staged for commit:" actual
 '
 
 test_expect_success 'cherry-pick --continue remembers --keep-redundant-commits' '
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 3b0fa66..7eb52b1 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,38 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+	test_must_fail git cherry-pick --skip 2>msg &&
+	test_grep "The previous cherry-pick is now empty" msg &&
+	rm msg &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD^
+'
+
 test_expect_success 'revert persists opts correctly' '
 	pristine_detach initial &&
 	# to make sure that the session to revert a sequence
@@ -154,7 +186,7 @@
 	pristine_detach picked &&
 	test_commit dummy foo d &&
 	test_must_fail git cherry-pick anotherpick 2>err &&
-	test_i18ngrep "git cherry-pick --skip" err &&
+	test_grep "git cherry-pick --skip" err &&
 	git cherry-pick --skip &&
 	test_cmp_rev dummy HEAD
 '
@@ -314,7 +346,7 @@
 	git reset --hard base &&
 	test_must_fail git cherry-pick picked anotherpick &&
 	git cherry-pick --abort 2>actual &&
-	test_i18ngrep "You seem to have moved HEAD" actual &&
+	test_grep "You seem to have moved HEAD" actual &&
 	test_cmp_rev base HEAD
 '
 
@@ -520,7 +552,7 @@
 	test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
 	git checkout HEAD -- unrelated &&
 	test_must_fail git cherry-pick --continue 2>msg &&
-	test_i18ngrep "The previous cherry-pick is now empty" msg
+	test_grep "The previous cherry-pick is now empty" msg
 '
 
 test_expect_success 'follow advice and skip nil patch' '
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 0e8afe4..98259e2 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -276,7 +276,7 @@
 	blob=$(echo blob | git hash-object -w --stdin) &&
 	printf "100644 $blob %d\tblob\n" 1 2 3 | git update-index --index-info &&
 	git rm blob >msg 2>&1 &&
-	test_i18ngrep ! "needs merge" msg &&
+	test_grep ! "needs merge" msg &&
 	test_must_fail git ls-files -s --error-unmatch blob
 '
 
@@ -631,7 +631,7 @@
 	test_path_is_missing submod/.git &&
 	git status -s -uno --ignore-submodules=none >actual &&
 	test_file_not_empty actual &&
-	test_i18ngrep Migrating output.err
+	test_grep Migrating output.err
 '
 
 cat >expect.deepmodified <<EOF
@@ -722,7 +722,7 @@
 	test_path_is_missing submod/subsubmod/.git &&
 	git status -s -uno --ignore-submodules=none >actual &&
 	test_file_not_empty actual &&
-	test_i18ngrep Migrating output.err
+	test_grep Migrating output.err
 '
 
 test_expect_success 'checking out a commit after submodule removal needs manual updates' '
@@ -731,7 +731,7 @@
 	git submodule update &&
 	git checkout -q HEAD^ &&
 	git checkout -q main 2>actual &&
-	test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
+	test_grep "^warning: unable to rmdir '\''submod'\'':" actual &&
 	git status -s submod >actual &&
 	echo "?? submod/" >expected &&
 	test_cmp expected actual &&
diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
index a2a0c82..7cef129 100755
--- a/t/t3601-rm-pathspec-file.sh
+++ b/t/t3601-rm-pathspec-file.sh
@@ -67,14 +67,14 @@
 	echo fileA.t >list &&
 
 	test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git rm --pathspec-file-nul 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
 	>empty_list &&
 	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
-	test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
+	test_grep -e "No pathspec was given. Which files should I remove?" err
 '
 
 test_done
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
new file mode 100755
index 0000000..3896702
--- /dev/null
+++ b/t/t3650-replay-basics.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+
+test_description='basic git replay tests'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success 'setup' '
+	test_commit A &&
+	test_commit B &&
+
+	git switch -c topic1 &&
+	test_commit C &&
+	git switch -c topic2 &&
+	test_commit D &&
+	test_commit E &&
+	git switch topic1 &&
+	test_commit F &&
+	git switch -c topic3 &&
+	test_commit G &&
+	test_commit H &&
+	git switch -c topic4 main &&
+	test_commit I &&
+	test_commit J &&
+
+	git switch -c next main &&
+	test_commit K &&
+	git merge -m "Merge topic1" topic1 &&
+	git merge -m "Merge topic2" topic2 &&
+	git merge -m "Merge topic3" topic3 &&
+	>evil &&
+	git add evil &&
+	git commit --amend &&
+	git merge -m "Merge topic4" topic4 &&
+
+	git switch main &&
+	test_commit L &&
+	test_commit M &&
+
+	git switch -c conflict B &&
+	test_commit C.conflict C.t conflict
+'
+
+test_expect_success 'setup bare' '
+	git clone --bare . bare
+'
+
+test_expect_success 'using replay to rebase two branches, one on top of other' '
+	git replay --onto main topic1..topic2 >result &&
+
+	test_line_count = 1 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 &&
+
+	printf "update refs/heads/topic2 " >expect &&
+	printf "%s " $(cut -f 3 -d " " result) >>expect &&
+	git rev-parse topic2 >>expect &&
+
+	test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
+	git -C bare replay --onto main topic1..topic2 >result-bare &&
+	test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase with a conflict' '
+	test_expect_code 1 git replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay on bare repo to rebase with a conflict' '
+	test_expect_code 1 git -C bare replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay to perform basic cherry-pick' '
+	# The differences between this test and previous ones are:
+	#   --advance vs --onto
+	# 2nd field of result is refs/heads/main vs. refs/heads/topic2
+	# 4th field of result is hash for main instead of hash for topic2
+
+	git replay --advance main topic1..topic2 >result &&
+
+	test_line_count = 1 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 &&
+
+	printf "update refs/heads/main " >expect &&
+	printf "%s " $(cut -f 3 -d " " result) >>expect &&
+	git rev-parse main >>expect &&
+
+	test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
+	git -C bare replay --advance main topic1..topic2 >result-bare &&
+	test_cmp expect result-bare
+'
+
+test_expect_success 'replay on bare repo fails with both --advance and --onto' '
+	test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare
+'
+
+test_expect_success 'replay fails when both --advance and --onto are omitted' '
+	test_must_fail git replay topic1..topic2 >result
+'
+
+test_expect_success 'using replay to also rebase a contained branch' '
+	git replay --contained --onto main main..topic3 >result &&
+
+	test_line_count = 2 result &&
+	cut -f 3 -d " " result >new-branch-tips &&
+
+	git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+	test_write_lines F C M L B A >expect &&
+	test_cmp expect actual &&
+
+	git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+	test_write_lines H G F C M L B A >expect &&
+	test_cmp expect actual &&
+
+	printf "update refs/heads/topic1 " >expect &&
+	printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+	git rev-parse topic1 >>expect &&
+	printf "update refs/heads/topic3 " >>expect &&
+	printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+	git rev-parse topic3 >>expect &&
+
+	test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to also rebase a contained branch' '
+	git -C bare replay --contained --onto main main..topic3 >result-bare &&
+	test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase multiple divergent branches' '
+	git replay --onto main ^topic1 topic2 topic4 >result &&
+
+	test_line_count = 2 result &&
+	cut -f 3 -d " " result >new-branch-tips &&
+
+	git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+	test_write_lines E D M L B A >expect &&
+	test_cmp expect actual &&
+
+	git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+	test_write_lines J I M L B A >expect &&
+	test_cmp expect actual &&
+
+	printf "update refs/heads/topic2 " >expect &&
+	printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+	git rev-parse topic2 >>expect &&
+	printf "update refs/heads/topic4 " >>expect &&
+	printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+	git rev-parse topic4 >>expect &&
+
+	test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase multiple divergent branches, including contained ones' '
+	git -C bare replay --contained --onto main ^main topic2 topic3 topic4 >result &&
+
+	test_line_count = 4 result &&
+	cut -f 3 -d " " result >new-branch-tips &&
+
+	>expect &&
+	for i in 2 1 3 4
+	do
+		printf "update refs/heads/topic$i " >>expect &&
+		printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect &&
+		git -C bare rev-parse topic$i >>expect || return 1
+	done &&
+
+	test_cmp expect result &&
+
+	test_write_lines F C M L B A >expect1 &&
+	test_write_lines E D C M L B A >expect2 &&
+	test_write_lines H G F C M L B A >expect3 &&
+	test_write_lines J I M L B A >expect4 &&
+
+	for i in 1 2 3 4
+	do
+		git -C bare log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual &&
+		test_cmp expect$i actual || return 1
+	done
+'
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 51afbd7..839c904 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -24,17 +24,27 @@
 	esac
 }
 
-test_expect_success \
-    'Test of git add' \
-    'touch foo && git add foo'
+test_expect_success 'Test of git add' '
+	touch foo && git add foo
+'
 
-test_expect_success \
-    'Post-check that foo is in the index' \
-    'git ls-files foo | grep foo'
+test_expect_success 'Test with no pathspecs' '
+	cat >expect <<-EOF &&
+	Nothing specified, nothing added.
+	hint: Maybe you wanted to say ${SQ}git add .${SQ}?
+	hint: Disable this message with "git config advice.addEmptyPathspec false"
+	EOF
+	git add 2>actual &&
+	test_cmp expect actual
+'
 
-test_expect_success \
-    'Test that "git add -- -q" works' \
-    'touch -- -q && git add -- -q'
+test_expect_success 'Post-check that foo is in the index' '
+	git ls-files foo | grep foo
+'
+
+test_expect_success 'Test that "git add -- -q" works' '
+	touch -- -q && git add -- -q
+'
 
 BATCH_CONFIGURATION='-c core.fsync=loose-object -c core.fsyncmethod=batch'
 
@@ -106,24 +116,32 @@
 
 test_expect_success '.gitignore is honored' '
 	git add . &&
-	! (git ls-files | grep "\\.ig")
+	git ls-files >files &&
+	sed -n "/\\.ig/p" <files >actual &&
+	test_must_be_empty actual
 '
 
 test_expect_success 'error out when attempting to add ignored ones without -f' '
 	test_must_fail git add a.?? &&
-	! (git ls-files | grep "\\.ig")
+	git ls-files >files &&
+	sed -n "/\\.ig/p" <files >actual &&
+	test_must_be_empty actual
 '
 
 test_expect_success 'error out when attempting to add ignored ones without -f' '
 	test_must_fail git add d.?? &&
-	! (git ls-files | grep "\\.ig")
+	git ls-files >files &&
+	sed -n "/\\.ig/p" <files >actual &&
+	test_must_be_empty actual
 '
 
 test_expect_success 'error out when attempting to add ignored ones but add others' '
 	touch a.if &&
 	test_must_fail git add a.?? &&
-	! (git ls-files | grep "\\.ig") &&
-	(git ls-files | grep a.if)
+	git ls-files >files &&
+	sed -n "/\\.ig/p" <files >actual &&
+	test_must_be_empty actual &&
+	grep a.if files
 '
 
 test_expect_success 'add ignored ones with -f' '
@@ -276,14 +294,14 @@
 rm -f foo2
 
 test_expect_success POSIXPERM,SANITY '--no-ignore-errors overrides config' '
-       git config add.ignore-errors 1 &&
-       git reset --hard &&
-       date >foo1 &&
-       date >foo2 &&
-       chmod 0 foo2 &&
-       test_must_fail git add --verbose --no-ignore-errors . &&
-       ! ( git ls-files foo1 | grep foo1 ) &&
-       git config add.ignore-errors 0
+	git config add.ignore-errors 1 &&
+	git reset --hard &&
+	date >foo1 &&
+	date >foo2 &&
+	chmod 0 foo2 &&
+	test_must_fail git add --verbose --no-ignore-errors . &&
+	! ( git ls-files foo1 | grep foo1 ) &&
+	git config add.ignore-errors 0
 '
 rm -f foo2
 
@@ -331,6 +349,40 @@
 	)
 '
 
+test_expect_success '"git add" a embedded repository' '
+	rm -fr outer && git init outer &&
+	(
+		cd outer &&
+		for i in 1 2
+		do
+			name=inner$i &&
+			git init $name &&
+			git -C $name commit --allow-empty -m $name ||
+				return 1
+		done &&
+		git add . 2>actual &&
+		cat >expect <<-EOF &&
+		warning: adding embedded git repository: inner1
+		hint: You${SQ}ve added another git repository inside your current repository.
+		hint: Clones of the outer repository will not contain the contents of
+		hint: the embedded repository and will not know how to obtain it.
+		hint: If you meant to add a submodule, use:
+		hint:
+		hint: 	git submodule add <url> inner1
+		hint:
+		hint: If you added this path by mistake, you can remove it from the
+		hint: index with:
+		hint:
+		hint: 	git rm --cached inner1
+		hint:
+		hint: See "git help submodule" for more information.
+		hint: Disable this message with "git config advice.addEmbeddedRepo false"
+		warning: adding embedded git repository: inner2
+		EOF
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'error on a repository with no commits' '
 	rm -fr empty &&
 	git init empty &&
@@ -362,8 +414,7 @@
 The following paths are ignored by one of your .gitignore files:
 ignored-file
 hint: Use -f if you really want to add them.
-hint: Turn this message off by running
-hint: "git config advice.addIgnoredFile false"
+hint: Disable this message with "git config advice.addIgnoredFile false"
 EOF
 cat >expect.out <<\EOF
 add 'track-this'
@@ -430,7 +481,7 @@
 	test_ln_s_add foo foo3 &&
 	touch foo4 &&
 	test_must_fail git add --chmod=+x foo3 foo4 2>stderr &&
-	test_i18ngrep "cannot chmod +x .foo3." stderr &&
+	test_grep "cannot chmod +x .foo3." stderr &&
 	test_mode_in_index 120000 foo3 &&
 	test_mode_in_index 100755 foo4
 '
@@ -447,12 +498,12 @@
 	git reset --hard &&
 	test_ln_s_add foo foo4 &&
 	test_must_fail git add --chmod=+x --dry-run foo4 2>stderr &&
-	test_i18ngrep "cannot chmod +x .foo4." stderr
+	test_grep "cannot chmod +x .foo4." stderr
 '
 
 test_expect_success 'git add --chmod --dry-run reports error for unmatched pathspec' '
 	test_must_fail git add --chmod=+x --dry-run nonexistent 2>stderr &&
-	test_i18ngrep "pathspec .nonexistent. did not match any files" stderr
+	test_grep "pathspec .nonexistent. did not match any files" stderr
 '
 
 test_expect_success 'no file status change if no pathspec is given' '
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 3a99837..bc55255 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -7,12 +7,6 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-if test_have_prereq !PERL
-then
-	skip_all='skipping add -i (scripted) tests, perl not available'
-	test_done
-fi
-
 diff_cmp () {
 	for x
 	do
@@ -311,9 +305,11 @@
 	echo content >>file &&
 	chmod +x file &&
 	printf "y\\ny\\n" | git add -p &&
-	git diff --cached file | grep "new mode" &&
-	git diff --cached file | grep "+content" &&
-	test -z "$(git diff file)"
+	git diff --cached file >out &&
+	grep "new mode" out &&
+	grep "+content" out &&
+	git diff file >out &&
+	test_must_be_empty out
 '
 
 # end of tests disabled when filemode is not usable
@@ -329,9 +325,9 @@
 	git -c core.filemode=true add -p >actual &&
 	sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
 	cat >expect <<-\EOF &&
-	(1/1) Stage deletion [y,n,q,a,d,?]?
-	(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
-	(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
+	(1/1) Stage deletion [y,n,q,a,d,p,?]?
+	(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
+	(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
 	EOF
 	test_cmp expect actual.filtered
 '
@@ -339,12 +335,12 @@
 test_expect_success 'correct message when there is nothing to do' '
 	git reset --hard &&
 	git add -p 2>err &&
-	test_i18ngrep "No changes" err &&
+	test_grep "No changes" err &&
 	printf "\\0123" >binary &&
 	git add binary &&
 	printf "\\0abc" >binary &&
 	git add -p 2>err &&
-	test_i18ngrep "Only binary files changed" err
+	test_grep "Only binary files changed" err
 '
 
 test_expect_success 'setup again' '
@@ -501,7 +497,7 @@
 
 		echo y | git checkout -p added-file -- >actual &&
 		test_path_is_file empty &&
-		test_i18ngrep "Apply addition to index and worktree" actual
+		test_grep "Apply addition to index and worktree" actual
 	)
 '
 
@@ -518,13 +514,13 @@
 test_expect_success 'goto hunk' '
 	test_when_finished "git reset" &&
 	tr _ " " >expect <<-EOF &&
-	(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1:  -1,2 +1,3          +15
+	(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1:  -1,2 +1,3          +15
 	_ 2:  -2,4 +3,8          +21
 	go to which hunk? @@ -1,2 +1,3 @@
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+	(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
 	EOF
 	test_write_lines s y g 1 | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -534,11 +530,11 @@
 test_expect_success 'navigate to hunk via regex' '
 	test_when_finished "git reset" &&
 	tr _ " " >expect <<-EOF &&
-	(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
+	(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+	(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
 	EOF
 	test_write_lines s y /1,2 | git add -p >actual &&
 	tail -n 5 <actual >actual.trimmed &&
@@ -719,25 +715,63 @@
 	<BLUE>+<RESET><BLUE>new<RESET>
 	<CYAN> more-context<RESET>
 	<BLUE>+<RESET><BLUE>another-one<RESET>
-	<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
+	<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
 	<MAGENTA>@@ -1,3 +1,3 @@<RESET>
 	<CYAN> context<RESET>
 	<BOLD>-old<RESET>
 	<BLUE>+<RESET><BLUE>new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
+	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
 	<CYAN> more-context<RESET>
 	<BLUE>+<RESET><BLUE>another-one<RESET>
-	<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
+	<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
 	<CYAN> context<RESET>
 	<BOLD>-old<RESET>
 	<BLUE>+new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET>
+	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
 	EOF
 	test_cmp expect actual
 '
 
+test_expect_success 'brackets appear without color' '
+	git reset --hard &&
+	test_when_finished "git rm -f bracket-test" &&
+	test_write_lines context old more-context >bracket-test &&
+	git add bracket-test &&
+	test_write_lines context new more-context another-one >bracket-test &&
+
+	test_write_lines quit >input &&
+	git add -i >actual <input &&
+
+	sed "s/^|//" >expect <<-\EOF &&
+	|           staged     unstaged path
+	|  1:        +3/-0        +2/-1 bracket-test
+	|
+	|*** Commands ***
+	|  1: [s]tatus	  2: [u]pdate	  3: [r]evert	  4: [a]dd untracked
+	|  5: [p]atch	  6: [d]iff	  7: [q]uit	  8: [h]elp
+	|What now> Bye.
+	EOF
+
+	test_cmp expect actual
+'
+
+test_expect_success 'colors can be skipped with color.ui=false' '
+	git reset --hard &&
+	test_when_finished "git rm -f color-test" &&
+	test_write_lines context old more-context >color-test &&
+	git add color-test &&
+	test_write_lines context new more-context another-one >color-test &&
+
+	test_write_lines help quit >input &&
+	force_color git \
+		-c color.ui=false \
+		add -i >actual.raw <input &&
+	test_decode_color <actual.raw >actual &&
+	test_cmp actual.raw actual
+'
+
 test_expect_success 'colorized diffs respect diff.wsErrorHighlight' '
 	git reset --hard &&
 
@@ -804,7 +838,7 @@
 	git add file &&
 	echo changed >file &&
 	test_must_fail git -c diff.algorithm=bogus add -p 2>err &&
-	test_i18ngrep "error: option diff-algorithm accepts " err
+	test_grep "error: option diff-algorithm accepts " err
 '
 
 test_expect_success 'patch-mode via -i prompts for files' '
@@ -1075,4 +1109,25 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'reset -p with unmerged files' '
+	test_when_finished "git checkout --force main" &&
+	test_commit one conflict &&
+	git checkout -B side HEAD^ &&
+	test_commit two conflict &&
+	test_must_fail git merge one &&
+
+	# this is a noop with only an unmerged entry
+	git reset -p &&
+
+	# add files that sort before and after unmerged entry
+	echo a >a &&
+	echo z >z &&
+	git add a z &&
+
+	# confirm that we can reset those files
+	printf "%s\n" y y | git reset -p &&
+	git diff-index --cached --diff-filter=u HEAD >staged &&
+	test_must_be_empty staged
+'
+
 test_done
diff --git a/t/t3704-add-pathspec-file.sh b/t/t3704-add-pathspec-file.sh
index 4e6b517..3aa59f6 100755
--- a/t/t3704-add-pathspec-file.sh
+++ b/t/t3704-add-pathspec-file.sh
@@ -138,23 +138,23 @@
 	>empty_list &&
 
 	test_must_fail git add --pathspec-from-file=list --interactive 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
 	test_must_fail git add --pathspec-from-file=list --patch 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
 	test_must_fail git add --pathspec-from-file=list --edit 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--edit. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--edit. cannot be used together" err &&
 
 	test_must_fail git add --pathspec-from-file=list -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git add --pathspec-file-nul 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
 	# This case succeeds, but still prints to stderr
 	git add --pathspec-from-file=empty_list 2>err &&
-	test_i18ngrep -e "Nothing specified, nothing added." err
+	test_grep -e "Nothing specified, nothing added." err
 '
 
 test_done
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index bfab245..f27d09c 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -45,7 +45,7 @@
 	printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
 		>"$HOME/invalid" &&
 	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-	test_i18ngrep "did not conform" "$HOME"/stderr
+	test_grep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 overlong sequences rejected' '
@@ -55,7 +55,7 @@
 	printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
 		>"$HOME/invalid" &&
 	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-	test_i18ngrep "did not conform" "$HOME"/stderr
+	test_grep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 non-characters refused' '
@@ -64,7 +64,7 @@
 	printf "Commit message\n\nNon-character:\364\217\277\276\n" \
 		>"$HOME/invalid" &&
 	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-	test_i18ngrep "did not conform" "$HOME"/stderr
+	test_grep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 non-characters refused' '
@@ -73,7 +73,7 @@
 	printf "Commit message\n\nNon-character:\357\267\220\n" \
 		>"$HOME/invalid" &&
 	git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-	test_i18ngrep "did not conform" "$HOME"/stderr
+	test_grep "did not conform" "$HOME"/stderr
 '
 
 for H in ISO8859-1 eucJP ISO-2022-JP
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4f16a73..4b37f78 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -298,7 +298,7 @@
 
 	# commit-tree will warn that the commit message does not contain valid UTF-8
 	# as mailinfo did not convert it
-	test_i18ngrep "did not conform" err &&
+	test_grep "did not conform" err &&
 
 	check_encoding 2
 '
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 376cc8f..00db82f 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -200,7 +200,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
 	git init repo &&
 	(
 		cd repo &&
@@ -213,16 +213,16 @@
 	new_oid="$(git -C repo rev-parse stash@{0})" &&
 
 	cat >expect <<-EOF &&
-	$(test_oid zero) $old_oid
-	$old_oid $new_oid
+	$new_oid
+	$old_oid
 	EOF
-	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	git -C repo reflog show refs/stash --format=%H >actual &&
 	test_cmp expect actual &&
 
 	git -C repo stash drop stash@{1} &&
-	cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+	git -C repo reflog show refs/stash --format=%H >actual &&
 	cat >expect <<-EOF &&
-	$(test_oid zero) $new_oid
+	$new_oid
 	EOF
 	test_cmp expect actual
 '
@@ -395,7 +395,7 @@
 
 test_expect_success 'dont assume push with non-option args' '
 	test_must_fail git stash -q drop 2>err &&
-	test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
+	test_grep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
 '
 
 test_expect_success 'stash --invalid-option' '
@@ -596,7 +596,7 @@
 	for type in apply pop "branch stash-branch"
 	do
 		test_must_fail git stash $type stash@{0} stash@{1} 2>err &&
-		test_i18ngrep "Too many revisions" err &&
+		test_grep "Too many revisions" err &&
 		test 123456789 = $(test-tool chmtime -g file2) || return 1
 	done
 '
@@ -604,14 +604,14 @@
 test_expect_success 'drop: too many arguments errors out (does nothing)' '
 	git stash list >expect &&
 	test_must_fail git stash drop stash@{0} stash@{1} 2>err &&
-	test_i18ngrep "Too many revisions" err &&
+	test_grep "Too many revisions" err &&
 	git stash list >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'show: too many arguments errors out (does nothing)' '
 	test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&
-	test_i18ngrep "Too many revisions" err &&
+	test_grep "Too many revisions" err &&
 	test_must_be_empty out
 '
 
@@ -654,7 +654,7 @@
 
 test_expect_success 'stash branch complains with no arguments' '
 	test_must_fail git stash branch 2>err &&
-	test_i18ngrep "No branch name specified" err
+	test_grep "No branch name specified" err
 '
 
 test_expect_success 'stash show format defaults to --stat' '
@@ -931,6 +931,10 @@
 	test_must_fail git stash store foo
 '
 
+test_expect_success 'store called with non-stash commit' '
+	test_must_fail git stash store HEAD
+'
+
 test_expect_success 'store updates stash ref and reflog' '
 	git stash clear &&
 	git reset --hard &&
@@ -1211,19 +1215,19 @@
 '
 
 test_expect_success 'stash with pathspec matching multiple paths' '
-       echo original >file &&
-       echo original >other-file &&
-       git commit -m "two" file other-file &&
-       echo modified >file &&
-       echo modified >other-file &&
-       git stash push -- "*file" &&
-       echo original >expect &&
-       test_cmp expect file &&
-       test_cmp expect other-file &&
-       git stash pop &&
-       echo modified >expect &&
-       test_cmp expect file &&
-       test_cmp expect other-file
+	echo original >file &&
+	echo original >other-file &&
+	git commit -m "two" file other-file &&
+	echo modified >file &&
+	echo modified >other-file &&
+	git stash push -- "*file" &&
+	echo original >expect &&
+	test_cmp expect file &&
+	test_cmp expect other-file &&
+	git stash pop &&
+	echo modified >expect &&
+	test_cmp expect file &&
+	test_cmp expect other-file
 '
 
 test_expect_success 'stash push -p with pathspec shows no changes only once' '
@@ -1512,4 +1516,56 @@
 	)
 '
 
+test_expect_success 'stash create reports a locked index' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A A.file &&
+		echo change >A.file &&
+		touch .git/index.lock &&
+
+		cat >expect <<-EOF &&
+		error: could not write index
+		EOF
+		test_must_fail git stash create 2>err &&
+		test_cmp expect err
+	)
+'
+
+test_expect_success 'stash push reports a locked index' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A A.file &&
+		echo change >A.file &&
+		touch .git/index.lock &&
+
+		cat >expect <<-EOF &&
+		error: could not write index
+		EOF
+		test_must_fail git stash push 2>err &&
+		test_cmp expect err
+	)
+'
+
+test_expect_success 'stash apply reports a locked index' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A A.file &&
+		echo change >A.file &&
+		git stash push &&
+		touch .git/index.lock &&
+
+		cat >expect <<-EOF &&
+		error: could not write index
+		EOF
+		test_must_fail git stash apply 2>err &&
+		test_cmp expect err
+	)
+'
+
 test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index accfe38..368fc2a 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -3,12 +3,6 @@
 test_description='stash -p'
 . ./lib-patch-mode.sh
 
-if ! test_have_prereq PERL
-then
-	skip_all='skipping stash -p tests, perl not available'
-	test_done
-fi
-
 test_expect_success 'setup' '
 	mkdir dir &&
 	echo parent > dir/foo &&
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 5390eec..1289ae3 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -404,7 +404,7 @@
 	) &&
 	w_commit=$(git commit-tree -p HEAD -p "$i_commit" -p "$u_commit" -m "WIP on any-branch" "$tree") &&
 	test_must_fail git stash show --include-untracked "$w_commit" 2>err &&
-	test_i18ngrep "worktree and untracked commit have duplicate entries: tracked" err
+	test_grep "worktree and untracked commit have duplicate entries: tracked" err
 '
 
 test_expect_success 'stash show --{include,only}-untracked on stashes without untracked entries' '
diff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh
index dead9f1..73f2dbd 100755
--- a/t/t3909-stash-pathspec-file.sh
+++ b/t/t3909-stash-pathspec-file.sh
@@ -88,13 +88,13 @@
 	echo fileA.t >list &&
 
 	test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
 	test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git stash push --pathspec-file-nul 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
 '
 
 test_done
diff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
index 67fd234..50ae222 100755
--- a/t/t3920-crlf-messages.sh
+++ b/t/t3920-crlf-messages.sh
@@ -10,7 +10,7 @@
 create_crlf_ref () {
 	branch="$1" &&
 	cat >.crlf-orig-$branch.txt &&
-	cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+	append_cr <.crlf-orig-$branch.txt >.crlf-message-$branch.txt &&
 	grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
 	grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
 	LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
@@ -97,7 +97,7 @@
 	git branch -v >tmp &&
 	# Remove first two columns, and the line for the currently checked out branch
 	current=$(git branch --show-current) &&
-	grep -v $current <tmp | awk "{\$1=\$2=\"\"}1"  >actual &&
+	awk "/$current/ { next } { \$1 = \$2 = \"\" } 1" <tmp >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index bfcaae3..8d50331 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -5,6 +5,9 @@
 
 test_description='Test built-in diff output engine.
 
+We happen to know that all diff plumbing and diff Porcelain share the
+same command line parser, so testing one should be sufficient; pick
+diff-files as a representative.
 '
 
 TEST_PASSES_SANITIZE_LEAK=true
@@ -16,9 +19,11 @@
 line 3'
 cat path0 >path1
 chmod +x path1
+mkdir path2
+>path2/path3
 
 test_expect_success 'update-index --add two files with and without +x.' '
-	git update-index --add path0 path1
+	git update-index --add path0 path1 path2/path3
 '
 
 mv path0 path0-
@@ -91,4 +96,31 @@
 	test_must_be_empty err
 '
 
+
+# Smudge path2/path3 so that dirstat has something to show
+date >path2/path3
+
+for format in stat raw numstat shortstat summary \
+	dirstat cumulative dirstat-by-file \
+	patch-with-raw patch-with-stat compact-summary
+do
+	test_expect_success "--no-patch in 'git diff-files --no-patch --$format' is a no-op" '
+		git diff-files --no-patch "--$format" >actual &&
+		git diff-files "--$format" >expect &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "--no-patch clears all previous ones" '
+		git diff-files --$format -s -p >actual &&
+		git diff-files -p >expect &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "--no-patch in 'git diff --no-patch --$format' is a no-op" '
+		git diff --no-patch "--$format" >actual &&
+		git diff "--$format" >expect &&
+		test_cmp expect actual
+	'
+done
+
 test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 3dc9047..49c042a 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -135,25 +135,25 @@
 	mkdir subdir &&
 	git mv another-path subdir/path1 &&
 	git status >out &&
-	test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+	test_grep "renamed: .*path1 -> subdir/path1" out
 '
 
 test_expect_success 'test diff.renames=true for git status' '
 	git -c diff.renames=true status >out &&
-	test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+	test_grep "renamed: .*path1 -> subdir/path1" out
 '
 
 test_expect_success 'test diff.renames=false for git status' '
 	git -c diff.renames=false status >out &&
-	test_i18ngrep ! "renamed: .*path1 -> subdir/path1" out &&
-	test_i18ngrep "new file: .*subdir/path1" out &&
-	test_i18ngrep "deleted: .*[^/]path1" out
+	test_grep ! "renamed: .*path1 -> subdir/path1" out &&
+	test_grep "new file: .*subdir/path1" out &&
+	test_grep "deleted: .*[^/]path1" out
 '
 
 test_expect_success 'favour same basenames even with minor differences' '
 	git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
 	git status >out &&
-	test_i18ngrep "renamed: .*path1 -> subdir/path1" out
+	test_grep "renamed: .*path1 -> subdir/path1" out
 '
 
 test_expect_success 'two files with same basename and same content' '
@@ -165,7 +165,7 @@
 	git commit -m 2 &&
 	git mv dir other-dir &&
 	git status >out &&
-	test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file" out
+	test_grep "renamed: .*dir/A/file -> other-dir/A/file" out
 '
 
 test_expect_success 'setup for many rename source candidates' '
@@ -202,9 +202,9 @@
 	git mv a/b/c c/b/a &&
 	git commit -m "a/b/c -> c/b/a" &&
 	git diff -M --summary HEAD^ HEAD >output &&
-	test_i18ngrep " a/b/c => c/b/a " output &&
+	test_grep " a/b/c => c/b/a " output &&
 	git diff -M --stat HEAD^ HEAD >output &&
-	test_i18ngrep " a/b/c => c/b/a " output
+	test_grep " a/b/c => c/b/a " output
 '
 
 test_expect_success 'rename pretty print with common prefix' '
@@ -212,9 +212,9 @@
 	git mv c/b/a c/d/e &&
 	git commit -m "c/b/a -> c/d/e" &&
 	git diff -M --summary HEAD^ HEAD >output &&
-	test_i18ngrep " c/{b/a => d/e} " output &&
+	test_grep " c/{b/a => d/e} " output &&
 	git diff -M --stat HEAD^ HEAD >output &&
-	test_i18ngrep " c/{b/a => d/e} " output
+	test_grep " c/{b/a => d/e} " output
 '
 
 test_expect_success 'rename pretty print with common suffix' '
@@ -222,9 +222,9 @@
 	git mv c/d/e d/e &&
 	git commit -m "c/d/e -> d/e" &&
 	git diff -M --summary HEAD^ HEAD >output &&
-	test_i18ngrep " {c/d => d}/e " output &&
+	test_grep " {c/d => d}/e " output &&
 	git diff -M --stat HEAD^ HEAD >output &&
-	test_i18ngrep " {c/d => d}/e " output
+	test_grep " {c/d => d}/e " output
 '
 
 test_expect_success 'rename pretty print with common prefix and suffix' '
@@ -232,9 +232,9 @@
 	git mv d/e d/f/e &&
 	git commit -m "d/e -> d/f/e" &&
 	git diff -M --summary HEAD^ HEAD >output &&
-	test_i18ngrep " d/{ => f}/e " output &&
+	test_grep " d/{ => f}/e " output &&
 	git diff -M --stat HEAD^ HEAD >output &&
-	test_i18ngrep " d/{ => f}/e " output
+	test_grep " d/{ => f}/e " output
 '
 
 test_expect_success 'rename pretty print common prefix and suffix overlap' '
@@ -242,9 +242,9 @@
 	git mv d/f/e d/f/f/e &&
 	git commit -m "d/f/e d/f/f/e" &&
 	git diff -M --summary HEAD^ HEAD >output &&
-	test_i18ngrep " d/f/{ => f}/e " output &&
+	test_grep " d/f/{ => f}/e " output &&
 	git diff -M --stat HEAD^ HEAD >output &&
-	test_i18ngrep " d/f/{ => f}/e " output
+	test_grep " d/f/{ => f}/e " output
 '
 
 test_expect_success 'diff-tree -l0 defaults to a big rename limit, not zero' '
@@ -286,4 +286,28 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'last line matters too' '
+	{
+		test_write_lines a 0 1 2 3 4 5 6 7 8 9 &&
+		printf "git ignores final up to 63 characters if not newline terminated"
+	} >no-final-lf &&
+	git add no-final-lf &&
+	git commit -m "original version of file with no final newline" &&
+
+	# Change ONLY the first character of the whole file
+	{
+		test_write_lines b 0 1 2 3 4 5 6 7 8 9 &&
+		printf "git ignores final up to 63 characters if not newline terminated"
+	} >no-final-lf &&
+	git add no-final-lf &&
+	git mv no-final-lf still-absent-final-lf &&
+	git commit -a -m "rename no-final-lf -> still-absent-final-lf" &&
+	git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
+	sed -e "s/^R[0-9]*	/R	/" actual >actual.munged &&
+	cat >expected <<-\EOF &&
+	R	no-final-lf	still-absent-final-lf
+	EOF
+	test_cmp expected actual.munged
+'
+
 test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index ea52e5b..cb33070 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -284,132 +284,131 @@
     test_cmp "$1" .test-tmp
 }
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree $tree_O $tree_A >.test-a &&
-     cmp -s .test-a .test-plain-OA'
+test_expect_success 'diff-tree of known trees.' '
+	git diff-tree $tree_O $tree_A >.test-a &&
+	cmp -s .test-a .test-plain-OA
+'
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree -r $tree_O $tree_A >.test-a &&
-     cmp -s .test-a .test-recursive-OA'
+test_expect_success 'diff-tree of known trees.' '
+	git diff-tree -r $tree_O $tree_A >.test-a &&
+	cmp -s .test-a .test-recursive-OA
+'
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree $tree_O $tree_B >.test-a &&
-     cmp -s .test-a .test-plain-OB'
+test_expect_success 'diff-tree of known trees.' '
+	git diff-tree $tree_O $tree_B >.test-a &&
+	cmp -s .test-a .test-plain-OB
+'
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree -r $tree_O $tree_B >.test-a &&
-     cmp -s .test-a .test-recursive-OB'
+test_expect_success 'diff-tree of known trees.' '
+	git diff-tree -r $tree_O $tree_B >.test-a &&
+	cmp -s .test-a .test-recursive-OB
+'
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree $tree_A $tree_B >.test-a &&
-     cmp -s .test-a .test-plain-AB'
+test_expect_success 'diff-tree of known trees.' '
+	git diff-tree $tree_A $tree_B >.test-a &&
+	cmp -s .test-a .test-plain-AB
+'
 
-test_expect_success \
-    'diff-tree of known trees.' \
-    'git diff-tree -r $tree_A $tree_B >.test-a &&
-     cmp -s .test-a .test-recursive-AB'
+test_expect_success 'diff-tree of known trees.' '
+	git diff-tree -r $tree_A $tree_B >.test-a &&
+	cmp -s .test-a .test-recursive-AB
+'
 
-test_expect_success \
-    'diff-tree --stdin of known trees.' \
-    'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
-     echo $tree_A $tree_B > .test-plain-ABx &&
-     cat .test-plain-AB >> .test-plain-ABx &&
-     cmp -s .test-a .test-plain-ABx'
+test_expect_success 'diff-tree --stdin of known trees.' '
+	echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+	echo $tree_A $tree_B > .test-plain-ABx &&
+	cat .test-plain-AB >> .test-plain-ABx &&
+	cmp -s .test-a .test-plain-ABx
+'
 
-test_expect_success \
-    'diff-tree --stdin of known trees.' \
-    'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
-     echo $tree_A $tree_B > .test-recursive-ABx &&
-     cat .test-recursive-AB >> .test-recursive-ABx &&
-     cmp -s .test-a .test-recursive-ABx'
+test_expect_success 'diff-tree --stdin of known trees.' '
+	echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+	echo $tree_A $tree_B > .test-recursive-ABx &&
+	cat .test-recursive-AB >> .test-recursive-ABx &&
+	cmp -s .test-a .test-recursive-ABx
+'
 
-test_expect_success \
-    'diff-cache O with A in cache' \
-    'git read-tree $tree_A &&
-     git diff-index --cached $tree_O >.test-a &&
-     cmp -s .test-a .test-recursive-OA'
+test_expect_success 'diff-cache O with A in cache' '
+	git read-tree $tree_A &&
+	git diff-index --cached $tree_O >.test-a &&
+	cmp -s .test-a .test-recursive-OA
+'
 
-test_expect_success \
-    'diff-cache O with B in cache' \
-    'git read-tree $tree_B &&
-     git diff-index --cached $tree_O >.test-a &&
-     cmp -s .test-a .test-recursive-OB'
+test_expect_success 'diff-cache O with B in cache' '
+	git read-tree $tree_B &&
+	git diff-index --cached $tree_O >.test-a &&
+	cmp -s .test-a .test-recursive-OB
+'
 
-test_expect_success \
-    'diff-cache A with B in cache' \
-    'git read-tree $tree_B &&
-     git diff-index --cached $tree_A >.test-a &&
-     cmp -s .test-a .test-recursive-AB'
+test_expect_success 'diff-cache A with B in cache' '
+	git read-tree $tree_B &&
+	git diff-index --cached $tree_A >.test-a &&
+	cmp -s .test-a .test-recursive-AB
+'
 
-test_expect_success \
-    'diff-files with O in cache and A checked out' \
-    'rm -fr Z [A-Z][A-Z] &&
-     git read-tree $tree_A &&
-     git checkout-index -f -a &&
-     git read-tree --reset $tree_O &&
-     test_must_fail git update-index --refresh -q &&
-     git diff-files >.test-a &&
-     cmp_diff_files_output .test-a .test-recursive-OA'
+test_expect_success 'diff-files with O in cache and A checked out' '
+	rm -fr Z [A-Z][A-Z] &&
+	git read-tree $tree_A &&
+	git checkout-index -f -a &&
+	git read-tree --reset $tree_O &&
+	test_must_fail git update-index --refresh -q &&
+	git diff-files >.test-a &&
+	cmp_diff_files_output .test-a .test-recursive-OA
+'
 
-test_expect_success \
-    'diff-files with O in cache and B checked out' \
-    'rm -fr Z [A-Z][A-Z] &&
-     git read-tree $tree_B &&
-     git checkout-index -f -a &&
-     git read-tree --reset $tree_O &&
-     test_must_fail git update-index --refresh -q &&
-     git diff-files >.test-a &&
-     cmp_diff_files_output .test-a .test-recursive-OB'
+test_expect_success 'diff-files with O in cache and B checked out' '
+	rm -fr Z [A-Z][A-Z] &&
+	git read-tree $tree_B &&
+	git checkout-index -f -a &&
+	git read-tree --reset $tree_O &&
+	test_must_fail git update-index --refresh -q &&
+	git diff-files >.test-a &&
+	cmp_diff_files_output .test-a .test-recursive-OB
+'
 
-test_expect_success \
-    'diff-files with A in cache and B checked out' \
-    'rm -fr Z [A-Z][A-Z] &&
-     git read-tree $tree_B &&
-     git checkout-index -f -a &&
-     git read-tree --reset $tree_A &&
-     test_must_fail git update-index --refresh -q &&
-     git diff-files >.test-a &&
-     cmp_diff_files_output .test-a .test-recursive-AB'
+test_expect_success 'diff-files with A in cache and B checked out' '
+	rm -fr Z [A-Z][A-Z] &&
+	git read-tree $tree_B &&
+	git checkout-index -f -a &&
+	git read-tree --reset $tree_A &&
+	test_must_fail git update-index --refresh -q &&
+	git diff-files >.test-a &&
+	cmp_diff_files_output .test-a .test-recursive-AB
+'
 
 ################################################################
 # Now we have established the baseline, we do not have to
 # rely on individual object ID values that much.
 
-test_expect_success \
-    'diff-tree O A == diff-tree -R A O' \
-    'git diff-tree $tree_O $tree_A >.test-a &&
-    git diff-tree -R $tree_A $tree_O >.test-b &&
-    cmp -s .test-a .test-b'
+test_expect_success 'diff-tree O A == diff-tree -R A O' '
+	git diff-tree $tree_O $tree_A >.test-a &&
+	git diff-tree -R $tree_A $tree_O >.test-b &&
+	cmp -s .test-a .test-b
+'
 
-test_expect_success \
-    'diff-tree -r O A == diff-tree -r -R A O' \
-    'git diff-tree -r $tree_O $tree_A >.test-a &&
-    git diff-tree -r -R $tree_A $tree_O >.test-b &&
-    cmp -s .test-a .test-b'
+test_expect_success 'diff-tree -r O A == diff-tree -r -R A O' '
+	git diff-tree -r $tree_O $tree_A >.test-a &&
+	git diff-tree -r -R $tree_A $tree_O >.test-b &&
+	cmp -s .test-a .test-b
+'
 
-test_expect_success \
-    'diff-tree B A == diff-tree -R A B' \
-    'git diff-tree $tree_B $tree_A >.test-a &&
-    git diff-tree -R $tree_A $tree_B >.test-b &&
-    cmp -s .test-a .test-b'
+test_expect_success 'diff-tree B A == diff-tree -R A B' '
+	git diff-tree $tree_B $tree_A >.test-a &&
+	git diff-tree -R $tree_A $tree_B >.test-b &&
+	cmp -s .test-a .test-b
+'
 
-test_expect_success \
-    'diff-tree -r B A == diff-tree -r -R A B' \
-    'git diff-tree -r $tree_B $tree_A >.test-a &&
-    git diff-tree -r -R $tree_A $tree_B >.test-b &&
-    cmp -s .test-a .test-b'
+test_expect_success 'diff-tree -r B A == diff-tree -r -R A B' '
+	git diff-tree -r $tree_B $tree_A >.test-a &&
+	git diff-tree -r -R $tree_A $tree_B >.test-b &&
+	cmp -s .test-a .test-b'
 
-test_expect_success \
-    'diff can read from stdin' \
-    'test_must_fail git diff --no-index -- MN - < NN |
-        grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
-    test_must_fail git diff --no-index -- MN NN |
-        grep -v "^index" >.test-b &&
-    test_cmp .test-a .test-b'
+test_expect_success 'diff can read from stdin' '
+	test_must_fail git diff --no-index -- MN - < NN |
+		sed "/^index/d; s#/-#/NN#" >.test-a &&
+	test_must_fail git diff --no-index -- MN NN |
+		grep -v "^index" >.test-b &&
+	test_cmp .test-a .test-b
+'
 
 test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index 181e968..ebe0918 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -11,20 +11,20 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
-test_expect_success \
-    'prepare reference tree' \
-    'COPYING_test_data >COPYING &&
-     echo frotz >rezrov &&
-    git update-index --add COPYING rezrov &&
-    tree=$(git write-tree) &&
-    echo $tree'
+test_expect_success 'prepare reference tree' '
+	COPYING_test_data >COPYING &&
+	echo frotz >rezrov &&
+	git update-index --add COPYING rezrov &&
+	tree=$(git write-tree) &&
+	echo $tree
+'
 
-test_expect_success \
-    'prepare work tree' \
-    'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
-    sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
-    rm -f COPYING &&
-    git update-index --add --remove COPYING COPYING.?'
+test_expect_success 'prepare work tree' '
+	sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+	sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+	rm -f COPYING &&
+	git update-index --add --remove COPYING COPYING.?
+'
 
 # tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,
 # both are slightly edited, and unchanged rezrov.  So we say you
@@ -57,14 +57,14 @@
 +	This file is licensed under the G.P.L v2, or a later version
 EOF
 
-test_expect_success \
-    'validate output from rename/copy detection (#1)' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#1)' '
+	compare_diff_patch current expected
+'
 
-test_expect_success \
-    'prepare work tree again' \
-    'mv COPYING.2 COPYING &&
-     git update-index --add --remove COPYING COPYING.1 COPYING.2'
+test_expect_success 'prepare work tree again' '
+	mv COPYING.2 COPYING &&
+	git update-index --add --remove COPYING COPYING.1 COPYING.2
+'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
 # both are slightly edited, and unchanged rezrov.  So we say you
@@ -95,14 +95,14 @@
 + However, in order to allow a migration to GPLv3 if that seems like
 EOF
 
-test_expect_success \
-    'validate output from rename/copy detection (#2)' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#2)' '
+	compare_diff_patch current expected
+'
 
-test_expect_success \
-    'prepare work tree once again' \
-    'COPYING_test_data >COPYING &&
-     git update-index --add --remove COPYING COPYING.1'
+test_expect_success 'prepare work tree once again' '
+	COPYING_test_data >COPYING &&
+	git update-index --add --remove COPYING COPYING.1
+'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
 # but COPYING is not edited.  We say you copy-and-edit COPYING.1; this
@@ -123,8 +123,8 @@
 + However, in order to allow a migration to GPLv3 if that seems like
 EOF
 
-test_expect_success \
-    'validate output from rename/copy detection (#3)' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate output from rename/copy detection (#3)' '
+	compare_diff_patch current expected
+'
 
 test_done
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index 8def4d4..1d70d4d 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -14,21 +14,21 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
-test_expect_success SYMLINKS \
-    'prepare reference tree' \
-    'echo xyzzy | tr -d '\\\\'012 >yomin &&
-     ln -s xyzzy frotz &&
-    git update-index --add frotz yomin &&
-    tree=$(git write-tree) &&
-    echo $tree'
+test_expect_success SYMLINKS 'prepare reference tree' '
+	echo xyzzy | tr -d '\\\\'012 >yomin &&
+	ln -s xyzzy frotz &&
+	git update-index --add frotz yomin &&
+	tree=$(git write-tree) &&
+	echo $tree
+'
 
-test_expect_success SYMLINKS \
-    'prepare work tree' \
-    'mv frotz rezrov &&
-     rm -f yomin &&
-     ln -s xyzzy nitfol &&
-     ln -s xzzzy bozbar &&
-    git update-index --add --remove frotz rezrov nitfol bozbar yomin'
+test_expect_success SYMLINKS 'prepare work tree' '
+	mv frotz rezrov &&
+	rm -f yomin &&
+	ln -s xyzzy nitfol &&
+	ln -s xzzzy bozbar &&
+	git update-index --add --remove frotz rezrov nitfol bozbar yomin
+'
 
 # tree has frotz pointing at xyzzy, and yomin that contains xyzzy to
 # confuse things.  work tree has rezrov (xyzzy) nitfol (xyzzy) and
@@ -36,9 +36,9 @@
 # rezrov and nitfol are rename/copy of frotz and bozbar should be
 # a new creation.
 
-test_expect_success SYMLINKS 'setup diff output' "
-    GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
-    cat >expected <<\EOF
+test_expect_success SYMLINKS 'setup diff output' '
+	GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current &&
+	cat >expected <<\EOF
 diff --git a/bozbar b/bozbar
 new file mode 120000
 --- /dev/null
@@ -62,10 +62,10 @@
 -xyzzy
 \ No newline at end of file
 EOF
-"
+'
 
-test_expect_success SYMLINKS \
-    'validate diff output' \
-    'compare_diff_patch current expected'
+test_expect_success SYMLINKS 'validate diff output' '
+	compare_diff_patch current expected
+'
 
 test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index dfcf3a0..3855d68 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -178,32 +178,29 @@
 V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
 while read magic cmd
 do
-	status=success
 	case "$magic" in
 	'' | '#'*)
 		continue ;;
-	:*)
-		magic=${magic#:}
+	:noellipses)
+		magic=noellipses
 		label="$magic-$cmd"
-		case "$magic" in
-		noellipses) ;;
-		failure)
-			status=failure
-			magic=
-			label="$cmd" ;;
-		*)
-			BUG "unknown magic $magic" ;;
-		esac ;;
+		;;
+	:*)
+		BUG "unknown magic $magic"
+		;;
 	*)
-		cmd="$magic $cmd" magic=
-		label="$cmd" ;;
+		cmd="$magic $cmd"
+		magic=
+		label="$cmd"
+		;;
 	esac
+
 	test=$(echo "$label" | sed -e 's|[/ ][/ ]*|_|g')
 	pfx=$(printf "%04d" $test_count)
 	expect="$TEST_DIRECTORY/t4013/diff.$test"
 	actual="$pfx-diff.$test"
 
-	test_expect_$status "git $cmd # magic is ${magic:-(not used)}" '
+	test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
 		{
 			echo "$ git $cmd"
 			case "$magic" in
@@ -473,6 +470,14 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'log --dd matches --diff-merges=1 -p' '
+	git log --diff-merges=1 -p master >result &&
+	process_diffs result >expected &&
+	git log --dd master >result &&
+	process_diffs result >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'deny wrong log.diffMerges config' '
 	test_config log.diffMerges wrong-value &&
 	test_expect_code 128 git log
@@ -514,7 +519,7 @@
 '
 
 test_expect_success 'diff --cached on unborn branch' '
-	echo ref: refs/heads/unborn >.git/HEAD &&
+	git symbolic-ref HEAD refs/heads/unborn &&
 	git diff --cached >result &&
 	process_diffs result >actual &&
 	process_diffs "$TEST_DIRECTORY/t4013/diff.diff_--cached" >expected &&
@@ -613,7 +618,90 @@
 
 test_expect_success 'diff -I<regex>: detect malformed regex' '
 	test_expect_code 129 git diff --ignore-matching-lines="^[124-9" 2>error &&
-	test_i18ngrep "invalid regex given to -I: " error
+	test_grep "invalid regex given to -I: " error
+'
+
+# check_prefix <patch> <src> <dst>
+# check only lines with paths to avoid dependency on exact oid/contents
+check_prefix () {
+	grep -E '^(diff|---|\+\+\+) ' "$1" >actual.paths &&
+	cat >expect <<-EOF &&
+	diff --git $2 $3
+	--- $2
+	+++ $3
+	EOF
+	test_cmp expect actual.paths
+}
+
+test_expect_success 'diff-files does not respect diff.noPrefix' '
+	git -c diff.noPrefix diff-files -p >actual &&
+	check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff-files respects --no-prefix' '
+	git diff-files -p --no-prefix >actual &&
+	check_prefix actual file0 file0
+'
+
+test_expect_success 'diff respects diff.noPrefix' '
+	git -c diff.noPrefix diff >actual &&
+	check_prefix actual file0 file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.noPrefix' '
+	git -c diff.noPrefix diff --default-prefix >actual &&
+	check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.mnemonicPrefix' '
+	git -c diff.mnemonicPrefix diff >actual &&
+	check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.mnemonicPrefix' '
+	git -c diff.mnemonicPrefix diff --default-prefix >actual &&
+	check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.srcPrefix' '
+	git -c diff.srcPrefix=x/ diff >actual &&
+	check_prefix actual x/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.dstPrefix' '
+	git -c diff.dstPrefix=y/ diff >actual &&
+	check_prefix actual a/file0 y/file0
+'
+
+test_expect_success 'diff --src-prefix overrides diff.srcPrefix' '
+	git -c diff.srcPrefix=y/ diff --src-prefix=z/ >actual &&
+	check_prefix actual z/file0 b/file0
+'
+
+test_expect_success 'diff --dst-prefix overrides diff.dstPrefix' '
+	git -c diff.dstPrefix=y/ diff --dst-prefix=z/ >actual &&
+	check_prefix actual a/file0 z/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.noPrefix' '
+	git -c diff.dstPrefix=y/ -c diff.srcPrefix=x/ -c diff.noPrefix diff >actual &&
+	check_prefix actual file0 file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.mnemonicPrefix' '
+	git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ -c diff.mnemonicPrefix diff >actual &&
+	check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with --default-prefix' '
+	git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ diff --default-prefix >actual &&
+	check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff --no-renames cannot be abbreviated' '
+	test_expect_code 129 git diff --no-rename >actual 2>error &&
+	test_must_be_empty actual &&
+	grep "invalid option: --no-rename" error
 '
 
 test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index f3313b8..e37a141 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -59,6 +59,10 @@
 	test_tick &&
 	git commit -m "patchid 3" &&
 
+	git checkout -b empty main &&
+	test_tick &&
+	git commit --allow-empty -m "empty commit" &&
+
 	git checkout main
 '
 
@@ -128,6 +132,12 @@
 	grep "^Side .* with .* backslash-n" actual
 '
 
+test_expect_success 'format-patch empty commit' '
+	git format-patch --stdout main..empty >empty &&
+	grep "^From " empty >from &&
+	test_line_count = 1 from
+'
+
 test_expect_success 'extra headers' '
 	git config format.headers "To: R E Cipient <rcipient@example.com>
 " &&
@@ -445,13 +455,13 @@
 
 cat >expect.thread <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <0>
 EOF
@@ -460,17 +470,22 @@
 	check_threading expect.thread --thread main
 '
 
+test_expect_success '--thread overrides format.thread=deep' '
+	test_config format.thread deep &&
+	check_threading expect.thread --thread main
+'
+
 cat >expect.in-reply-to <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <1>
 References: <1>
 EOF
@@ -482,17 +497,17 @@
 
 cat >expect.cover-letter <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <0>
 References: <0>
 EOF
@@ -503,21 +518,21 @@
 
 cat >expect.cl-irt <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <1>
 	<0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <0>
 References: <1>
 	<0>
 ---
-Message-Id: <4>
+Message-ID: <4>
 In-Reply-To: <0>
 References: <1>
 	<0>
@@ -535,13 +550,13 @@
 
 cat >expect.deep <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <1>
 References: <0>
 	<1>
@@ -553,16 +568,16 @@
 
 cat >expect.deep-irt <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <1>
 	<0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <2>
 References: <1>
 	<0>
@@ -576,18 +591,18 @@
 
 cat >expect.deep-cl <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 ---
-Message-Id: <1>
+Message-ID: <1>
 In-Reply-To: <0>
 References: <0>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <1>
 References: <0>
 	<1>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <2>
 References: <0>
 	<1>
@@ -600,22 +615,22 @@
 
 cat >expect.deep-cl-irt <<EOF
 ---
-Message-Id: <0>
+Message-ID: <0>
 In-Reply-To: <1>
 References: <1>
 ---
-Message-Id: <2>
+Message-ID: <2>
 In-Reply-To: <0>
 References: <1>
 	<0>
 ---
-Message-Id: <3>
+Message-ID: <3>
 In-Reply-To: <2>
 References: <1>
 	<0>
 	<2>
 ---
-Message-Id: <4>
+Message-ID: <4>
 In-Reply-To: <3>
 References: <1>
 	<0>
@@ -1358,7 +1373,27 @@
 	Subject: [RFC PATCH 1/1] header with . in it
 	EOF
 	git format-patch -n -1 --stdout --rfc >patch &&
-	grep ^Subject: patch >actual &&
+	grep "^Subject:" patch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--rfc does not overwrite prefix' '
+	cat >expect <<-\EOF &&
+	Subject: [RFC PATCH foobar 1/1] header with . in it
+	EOF
+	git -c format.subjectPrefix="PATCH foobar" \
+		format-patch -n -1 --stdout --rfc >patch &&
+	grep "^Subject:" patch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--rfc is argument order independent' '
+	cat >expect <<-\EOF &&
+	Subject: [RFC PATCH foobar 1/1] header with . in it
+	EOF
+	git format-patch -n -1 --stdout --rfc \
+		--subject-prefix="PATCH foobar" >patch &&
+	grep "^Subject:" patch >actual &&
 	test_cmp expect actual
 '
 
@@ -1871,6 +1906,16 @@
 	grep "^body$" actual
 '
 
+test_expect_success 'cover letter with --cover-from-description subject (UTF-8 subject line)' '
+	test_config branch.rebuild-1.description "Café?
+
+body" &&
+	git checkout rebuild-1 &&
+	git format-patch --stdout --cover-letter --cover-from-description subject --encode-email-headers main >actual &&
+	grep "^Subject: \[PATCH 0/2\] =?UTF-8?q?Caf=C3=A9=3F?=$" actual &&
+	! grep "Café" actual
+'
+
 test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '
 	test_config branch.rebuild-1.description "config subject
 
@@ -1976,6 +2021,20 @@
 	grep hello actual
 '
 
+test_expect_success 'cover letter with --description-file' '
+	test_when_finished "rm -f description.txt" &&
+	cat >description.txt <<-\EOF &&
+	subject from file
+
+	body from file
+	EOF
+	git checkout rebuild-1 &&
+	git format-patch --stdout --cover-letter --cover-from-description auto \
+		--description-file description.txt main >actual &&
+	grep "^Subject: \[PATCH 0/2\] subject from file$" actual &&
+	grep "^body from file$" actual
+'
+
 test_expect_success 'cover letter with nothing' '
 	git format-patch --stdout --cover-letter >actual &&
 	test_line_count = 0 actual
@@ -2354,25 +2413,25 @@
 	--q
 	EOF
 	git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
-	test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
-	test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
+	test_grep "^Interdiff:$" 0000-cover-letter.patch &&
+	test_grep ! "^Interdiff:$" 0001-fleep.patch &&
 	sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'interdiff: reroll-count' '
 	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
-	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+	test_grep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: reroll-count with a non-integer' '
 	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
-	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+	test_grep "^Interdiff:$" v2.2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: reroll-count with a integer' '
 	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
-	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+	test_grep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: solo-patch' '
@@ -2381,9 +2440,25 @@
 
 	EOF
 	git format-patch --interdiff=boop~2 -1 boop &&
-	test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
+	test_grep "^Interdiff:$" 0001-fleep.patch &&
 	sed "1,/^  @@ /d; /^$/q" 0001-fleep.patch >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'format-patch does not respect diff.noprefix' '
+	git -c diff.noprefix format-patch -1 --stdout >actual &&
+	grep "^--- a/blorp" actual
+'
+
+test_expect_success 'format-patch respects format.noprefix' '
+	git -c format.noprefix format-patch -1 --stdout >actual &&
+	grep "^--- blorp" actual
+'
+
+test_expect_success 'format-patch --default-prefix overrides format.noprefix' '
+	git -c format.noprefix \
+		format-patch -1 --default-prefix --stdout >actual &&
+	grep "^--- a/blorp" actual
+'
+
 test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index b298f22..b443626 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2006 Johannes E. Schindelin
-#
+# Copyright (c) 2023 Google LLC
 
 test_description='Test special whitespace in diff engine.
 
@@ -11,6 +11,43 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
+for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \
+	       --raw! --name-only! --name-status!
+do
+	opts=${opt_res%!} expect_failure=
+	test "$opts" = "$opt_res" ||
+		expect_failure="test_expect_code 1"
+
+	test_expect_success "status with $opts (different)" '
+		echo foo >x &&
+		git add x &&
+		echo bar >x &&
+		test_expect_code 1 git diff -w $opts --exit-code x
+	'
+
+	test_expect_success POSIXPERM "status with $opts (mode differs)" '
+		test_when_finished "git update-index --chmod=-x x" &&
+		echo foo >x &&
+		git add x &&
+		git update-index --chmod=+x x &&
+		test_expect_code 1 git diff -w $opts --exit-code x
+	'
+
+	test_expect_success "status with $opts (removing an empty file)" '
+		: >x &&
+		git add x &&
+		rm x &&
+		test_expect_code 1 git diff -w $opts --exit-code -- x
+	'
+
+	test_expect_success "status with $opts (different but equivalent)" '
+		echo foo >x &&
+		git add x &&
+		echo " foo" >x &&
+		$expect_failure git diff -w $opts --exit-code x
+	'
+done
+
 test_expect_success "Ray Lehtiniemi's example" '
 	cat <<-\EOF >x &&
 	do {
@@ -909,7 +946,7 @@
 	git commit -m "the other side" x &&
 	git config core.autocrlf true &&
 	test_must_fail git merge one-side >actual &&
-	test_i18ngrep "Automatic merge failed" actual &&
+	test_grep "Automatic merge failed" actual &&
 
 	git diff >actual.raw &&
 	sed -e "1,/^@@@/d" actual.raw >actual &&
@@ -2187,27 +2224,27 @@
 
 test_expect_success 'bogus settings in move detection erroring out' '
 	test_must_fail git diff --color-moved=bogus 2>err &&
-	test_i18ngrep "must be one of" err &&
-	test_i18ngrep bogus err &&
+	test_grep "must be one of" err &&
+	test_grep bogus err &&
 
 	test_must_fail git -c diff.colormoved=bogus diff 2>err &&
-	test_i18ngrep "must be one of" err &&
-	test_i18ngrep "from command-line config" err &&
+	test_grep "must be one of" err &&
+	test_grep "from command-line config" err &&
 
 	test_must_fail git diff --color-moved-ws=bogus 2>err &&
-	test_i18ngrep "possible values" err &&
-	test_i18ngrep bogus err &&
+	test_grep "possible values" err &&
+	test_grep bogus err &&
 
 	test_must_fail git -c diff.colormovedws=bogus diff 2>err &&
-	test_i18ngrep "possible values" err &&
-	test_i18ngrep "from command-line config" err
+	test_grep "possible values" err &&
+	test_grep "from command-line config" err
 '
 
 test_expect_success 'compare whitespace delta incompatible with other space options' '
 	test_must_fail git diff \
 		--color-moved-ws=allow-indentation-change,ignore-all-space \
 		2>err &&
-	test_i18ngrep allow-indentation-change err
+	test_grep allow-indentation-change err
 '
 
 EMPTY=''
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
index 5bc28ad..f439f46 100755
--- a/t/t4017-diff-retval.sh
+++ b/t/t4017-diff-retval.sh
@@ -138,4 +138,9 @@
 	git reset --hard
 '
 
+test_expect_success 'option errors are not confused by --exit-code' '
+	test_must_fail git diff --exit-code --nonsense 2>err &&
+	grep '^usage:' err
+'
+
 test_done
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 42a2b9a..e026fac 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -53,15 +53,34 @@
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index \
 			A.java B.java 2>msg &&
-		test_i18ngrep ! fatal msg &&
-		test_i18ngrep ! error msg
+		test_grep ! fatal msg &&
+		test_grep ! error msg
 	'
 	test_expect_success "builtin $p wordRegex pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
 		test_expect_code 1 git diff --no-index --word-diff \
 			A.java B.java 2>msg &&
-		test_i18ngrep ! fatal msg &&
-		test_i18ngrep ! error msg
+		test_grep ! fatal msg &&
+		test_grep ! error msg
+	'
+
+	test_expect_success "builtin $p pattern compiles on bare repo with --attr-source" '
+		test_when_finished "rm -rf bare.git" &&
+		git checkout -B master &&
+		git add . &&
+		echo "*.java diff=notexist" >.gitattributes &&
+		git add .gitattributes &&
+		git commit -am "changing gitattributes" &&
+		git checkout -B branchA &&
+		echo "*.java diff=$p" >.gitattributes &&
+		git add .gitattributes &&
+		git commit -am "changing gitattributes" &&
+		git clone --bare --no-local . bare.git &&
+		git -C bare.git symbolic-ref HEAD refs/heads/master &&
+		test_expect_code 1 git -C bare.git --attr-source=branchA \
+			diff --exit-code HEAD:A.java HEAD:B.java 2>msg &&
+		test_grep ! fatal msg &&
+		test_grep ! error msg
 	'
 done
 
@@ -69,7 +88,7 @@
 	echo "*.java diff=java" >.gitattributes &&
 	test_config diff.java.funcname "!static" &&
 	test_expect_code 128 git diff --no-index A.java B.java 2>msg &&
-	test_i18ngrep ": Last expression must not be negated:" msg
+	test_grep ": Last expression must not be negated:" msg
 '
 
 test_expect_success 'setup hunk header tests' '
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index c1ac09e..fdd865f 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -232,7 +232,7 @@
 test_expect_success 'external diff with autocrlf = true' '
 	test_config core.autocrlf true &&
 	GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
-	test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+	test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
 '
 
 test_expect_success 'diff --cached' '
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 1c89050..6fed993 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -24,7 +24,7 @@
 test_expect_success 'detect rewrite' '
 
 	actual=$(git diff-files -B --summary test) &&
-	verbose expr "$actual" : " rewrite test ([0-9]*%)$"
+	expr "$actual" : " rewrite test ([0-9]*%)$"
 
 '
 
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
index eacc669..c4394a2 100755
--- a/t/t4031-diff-rewrite-binary.sh
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -53,7 +53,7 @@
 test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
 	git diff -B --stat --summary >diff &&
 	grep "Bin" diff &&
-	test_i18ngrep "0 insertions.*0 deletions" diff &&
+	test_grep "0 insertions.*0 deletions" diff &&
 	grep " rewrite file" diff
 '
 
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 15764ee..74586f3 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -69,6 +69,10 @@
 		echo "* diff='"$lang"'" >.gitattributes &&
 		word_diff --color-words
 	'
+	test_expect_success "diff driver '$lang' in Islandic" '
+		LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+		word_diff --color-words
+	'
 }
 
 test_expect_success setup '
diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh
index e70e020..eec3d73 100755
--- a/t/t4040-whitespace-status.sh
+++ b/t/t4040-whitespace-status.sh
@@ -28,8 +28,7 @@
 
 test_expect_success 'diff-tree -b --exit-code' '
 	git diff -b --exit-code HEAD^ HEAD &&
-	git diff-tree -b -p --exit-code HEAD^ HEAD &&
-	git diff-tree -b --exit-code HEAD^ HEAD
+	git diff-tree -b -p --exit-code HEAD^ HEAD
 '
 
 test_expect_success 'diff-index --cached --exit-code' '
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh
index bf33aed..8ebfa3c 100755
--- a/t/t4042-diff-textconv-caching.sh
+++ b/t/t4042-diff-textconv-caching.sh
@@ -118,4 +118,26 @@
 	git log --no-walk -p refs/notes/textconv/magic HEAD
 '
 
+test_expect_success 'caching is silently ignored outside repo' '
+	mkdir -p non-repo &&
+	echo one >non-repo/one &&
+	echo two >non-repo/two &&
+	echo "* diff=test" >attr &&
+	test_expect_code 1 \
+	nongit git -c core.attributesFile="$PWD/attr" \
+		   -c diff.test.textconv="tr a-z A-Z <" \
+		   -c diff.test.cachetextconv=true \
+		   diff --no-index one two >actual &&
+	cat >expect <<-\EOF &&
+	diff --git a/one b/two
+	index 5626abf..f719efd 100644
+	--- a/one
+	+++ b/two
+	@@ -1 +1 @@
+	-ONE
+	+TWO
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
index 7fec2cb..7b73462 100755
--- a/t/t4047-diff-dirstat.sh
+++ b/t/t4047-diff-dirstat.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='diff --dirstat tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # set up two commits where the second commit has these files
@@ -941,37 +943,37 @@
 	test_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
 	test_debug "cat actual_error" &&
 	test_must_be_empty actual_diff_dirstat &&
-	test_i18ngrep -q "future_param" actual_error &&
-	test_i18ngrep -q "\--dirstat" actual_error
+	test_grep -q "future_param" actual_error &&
+	test_grep -q "\--dirstat" actual_error
 '
 
 test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '
 	test_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
 	test_debug "cat actual_error" &&
 	test_must_be_empty actual_diff_dirstat &&
-	test_i18ngrep -q "dummy1" actual_error &&
-	test_i18ngrep -q "2dummy" actual_error &&
-	test_i18ngrep -q "\--dirstat" actual_error
+	test_grep -q "dummy1" actual_error &&
+	test_grep -q "2dummy" actual_error &&
+	test_grep -q "\--dirstat" actual_error
 '
 
 test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '
 	git -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&
 	test_debug "cat actual_error" &&
 	test_cmp expect_diff_dirstat actual_diff_dirstat &&
-	test_i18ngrep -q "future_param" actual_error &&
-	test_i18ngrep -q "diff\\.dirstat" actual_error &&
+	test_grep -q "future_param" actual_error &&
+	test_grep -q "diff\\.dirstat" actual_error &&
 
 	git -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&
 	test_debug "cat actual_error" &&
 	test_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&
-	test_i18ngrep -q "future_param" actual_error &&
-	test_i18ngrep -q "diff\\.dirstat" actual_error &&
+	test_grep -q "future_param" actual_error &&
+	test_grep -q "diff\\.dirstat" actual_error &&
 
 	git -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&
 	test_debug "cat actual_error" &&
 	test_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&
-	test_i18ngrep -q "future_param" actual_error &&
-	test_i18ngrep -q "diff\\.dirstat" actual_error
+	test_grep -q "future_param" actual_error &&
+	test_grep -q "diff\\.dirstat" actual_error
 '
 
 test_expect_success '--shortstat --dirstat should output only one dirstat' '
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index 3ee27e2..7badd72 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -12,7 +12,7 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-# 120 character name
+# 120-character name
 name=aaaaaaaaaa
 name=$name$name$name$name$name$name$name$name$name$name$name$name
 test_expect_success 'preparation' '
@@ -49,12 +49,41 @@
 EOF
 
 cat >expect.60 <<-'EOF'
- ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
 EOF
 cat >expect.6030 <<-'EOF'
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
 EOF
-cat >expect2.60 <<-'EOF'
+while read verb expect cmd args
+do
+	# No width limit applied when statNameWidth is ignored
+	case "$expect" in expect72|expect.6030)
+		test_expect_success "$cmd $verb diff.statNameWidth with long name" '
+			git -c diff.statNameWidth=30 $cmd $args >output &&
+			grep " | " output >actual &&
+			test_cmp $expect actual
+		';;
+	esac
+	# Maximum width limit still applied when statNameWidth is ignored
+	case "$expect" in expect.60|expect.6030)
+		test_expect_success "$cmd --stat=width $verb diff.statNameWidth with long name" '
+			git -c diff.statNameWidth=30 $cmd $args --stat=60 >output &&
+			grep " | " output >actual &&
+			test_cmp $expect actual
+		';;
+	esac
+done <<\EOF
+ignores expect72 format-patch -1 --stdout
+ignores expect.60 format-patch -1 --stdout
+respects expect.6030 diff HEAD^ HEAD --stat
+respects expect.6030 show --stat
+respects expect.6030 log -1 --stat
+EOF
+
+cat >expect.40 <<-'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
+EOF
+cat >expect2.40 <<-'EOF'
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
 EOF
@@ -67,22 +96,22 @@
 	test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
 		git $cmd $args --stat=40 >output &&
 		grep " | " output >actual &&
-		test_cmp $expect.60 actual
+		test_cmp $expect.40 actual
 	'
 
 	test_expect_success "$cmd --stat-width=width with long name" '
 		git $cmd $args --stat-width=40 >output &&
 		grep " | " output >actual &&
-		test_cmp $expect.60 actual
+		test_cmp $expect.40 actual
 	'
 
-	test_expect_success "$cmd --stat=...,name-width with long name" '
+	test_expect_success "$cmd --stat=width,name-width with long name" '
 		git $cmd $args --stat=60,30 >output &&
 		grep " | " output >actual &&
 		test_cmp $expect.6030 actual
 	'
 
-	test_expect_success "$cmd --stat-name-width with long name" '
+	test_expect_success "$cmd --stat-name-width=width with long name" '
 		git $cmd $args --stat-name-width=30 >output &&
 		grep " | " output >actual &&
 		test_cmp $expect.6030 actual
@@ -94,8 +123,7 @@
 expect log -1 --stat
 EOF
 
-
-test_expect_success 'preparation for big change tests' '
+test_expect_success 'preparation for big-change tests' '
 	>abcd &&
 	git add abcd &&
 	git commit -m message &&
@@ -111,7 +139,7 @@
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success "format-patch --cover-letter ignores COLUMNS (big change)" '
+test_expect_success "format-patch --cover-letter ignores COLUMNS with big change" '
 	COLUMNS=200 git format-patch -1 --stdout --cover-letter >output &&
 	grep " | " output >actual &&
 	test_cmp expect72 actual
@@ -131,7 +159,7 @@
 EOF
 while read verb expect cmd args
 do
-	test_expect_success "$cmd $verb COLUMNS (big change)" '
+	test_expect_success "$cmd $verb COLUMNS with big change" '
 		COLUMNS=200 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
@@ -139,7 +167,7 @@
 
 	case "$cmd" in diff|show) continue;; esac
 
-	test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+	test_expect_success "$cmd --graph $verb COLUMNS with big change" '
 		COLUMNS=200 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
@@ -159,7 +187,7 @@
 EOF
 while read verb expect cmd args
 do
-	test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
+	test_expect_success "$cmd $verb not enough COLUMNS with big change" '
 		COLUMNS=40 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
@@ -167,7 +195,7 @@
 
 	case "$cmd" in diff|show) continue;; esac
 
-	test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+	test_expect_success "$cmd --graph $verb not enough COLUMNS with big change" '
 		COLUMNS=40 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
@@ -187,7 +215,7 @@
 EOF
 while read verb expect cmd args
 do
-	test_expect_success "$cmd $verb statGraphWidth config" '
+	test_expect_success "$cmd $verb diff.statGraphWidth" '
 		git -c diff.statGraphWidth=26 $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
@@ -195,7 +223,7 @@
 
 	case "$cmd" in diff|show) continue;; esac
 
-	test_expect_success "$cmd --graph $verb statGraphWidth config" '
+	test_expect_success "$cmd --graph $verb diff.statGraphWidth" '
 		git -c diff.statGraphWidth=26 $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
@@ -207,7 +235,6 @@
 respects expect40 log -1 --stat
 EOF
 
-
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++
 EOF
@@ -228,7 +255,7 @@
 		test_cmp expect actual
 	'
 
-	test_expect_success "$cmd --stat-graph-width with big change" '
+	test_expect_success "$cmd --stat-graph-width=width with big change" '
 		git $cmd $args --stat-graph-width=26 >output &&
 		grep " | " output >actual &&
 		test_cmp expect actual
@@ -242,7 +269,7 @@
 		test_cmp expect-graph actual
 	'
 
-	test_expect_success "$cmd --stat-graph-width --graph with big change" '
+	test_expect_success "$cmd --stat-graph-width=width --graph with big change" '
 		git $cmd $args --stat-graph-width=26 --graph >output &&
 		grep " | " output >actual &&
 		test_cmp expect-graph actual
@@ -254,7 +281,7 @@
 log -1 --stat
 EOF
 
-test_expect_success 'preparation for long filename tests' '
+test_expect_success 'preparation for long-name tests' '
 	cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
 	git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
 	git commit -m message
@@ -302,7 +329,7 @@
 EOF
 while read verb expect cmd args
 do
-	test_expect_success "$cmd $verb COLUMNS (long filename)" '
+	test_expect_success "$cmd $verb COLUMNS with long name" '
 		COLUMNS=200 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
@@ -310,7 +337,7 @@
 
 	case "$cmd" in diff|show) continue;; esac
 
-	test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+	test_expect_success "$cmd --graph $verb COLUMNS with long name" '
 		COLUMNS=200 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
@@ -331,7 +358,7 @@
 while read verb expect cmd args
 do
 	test_expect_success COLUMNS_CAN_BE_1 \
-		"$cmd $verb prefix greater than COLUMNS (big change)" '
+		"$cmd $verb prefix greater than COLUMNS with big change" '
 		COLUMNS=1 git $cmd $args >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect" actual
@@ -340,7 +367,7 @@
 	case "$cmd" in diff|show) continue;; esac
 
 	test_expect_success COLUMNS_CAN_BE_1 \
-		"$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+		"$cmd --graph $verb prefix greater than COLUMNS with big change" '
 		COLUMNS=1 git $cmd $args --graph >output &&
 		grep " | " output >actual &&
 		test_cmp "$expect-graph" actual
@@ -355,8 +382,14 @@
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success 'merge --stat respects COLUMNS (big change)' '
-	git checkout -b branch HEAD^^ &&
+test_expect_success 'merge --stat respects diff.statGraphWidth with big change' '
+	git checkout -b branch1 HEAD^^ &&
+	git -c diff.statGraphWidth=26 merge --stat --no-ff main^ >output &&
+	grep " | " output >actual &&
+	test_cmp expect40 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with big change' '
+	git checkout -b branch2 HEAD^^ &&
 	COLUMNS=100 git merge --stat --no-ff main^ >output &&
 	grep " | " output >actual &&
 	test_cmp expect actual
@@ -365,7 +398,17 @@
 cat >expect <<'EOF'
  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success 'merge --stat respects COLUMNS (long filename)' '
+cat >expect.30 <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects diff.statNameWidth with long name' '
+	git switch branch1 &&
+	git -c diff.statNameWidth=30 merge --stat --no-ff main >output &&
+	grep " | " output >actual &&
+	test_cmp expect.30 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with long name' '
+	git switch branch2 &&
 	COLUMNS=100 git merge --stat --no-ff main >output &&
 	grep " | " output >actual &&
 	test_cmp expect actual
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 4e9fa04..651ec77 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -56,7 +56,7 @@
 		export GIT_CEILING_DIRECTORIES &&
 		cd non/git &&
 		test_must_fail git diff --no-index a 2>actual.err &&
-		test_i18ngrep "usage: git diff --no-index" actual.err
+		test_grep "usage: git diff --no-index" actual.err
 	)
 '
 
@@ -205,4 +205,95 @@
 	test_cmp expected actual
 '
 
+test_expect_success POSIXPERM 'external diff with mode-only change' '
+	echo content >not-executable &&
+	echo content >executable &&
+	chmod +x executable &&
+	echo executable executable $(test_oid zero) 100755 \
+		not-executable $(test_oid zero) 100644 not-executable \
+		>expect &&
+	test_expect_code 1 git -c diff.external=echo diff \
+		--no-index executable not-executable >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success "diff --no-index treats '-' as stdin" '
+	cat >expect <<-EOF &&
+	diff --git a/- b/a/1
+	index $ZERO_OID..$(git hash-object --stdin <a/1) 100644
+	--- a/-
+	+++ b/a/1
+	@@ -1 +1 @@
+	-x
+	+1
+	EOF
+
+	test_write_lines x | test_expect_code 1 \
+		git -c core.abbrev=no diff --no-index -- - a/1 >actual &&
+	test_cmp expect actual &&
+
+	test_write_lines 1 | git diff --no-index -- a/1 - >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success "diff --no-index -R treats '-' as stdin" '
+	cat >expect <<-EOF &&
+	diff --git b/a/1 a/-
+	index $(git hash-object --stdin <a/1)..$ZERO_OID 100644
+	--- b/a/1
+	+++ a/-
+	@@ -1 +1 @@
+	-1
+	+x
+	EOF
+
+	test_write_lines x | test_expect_code 1 \
+		git -c core.abbrev=no diff --no-index -R -- - a/1 >actual &&
+	test_cmp expect actual &&
+
+	test_write_lines 1 | git diff --no-index -R -- a/1 - >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'diff --no-index refuses to diff stdin and a directory' '
+	test_must_fail git diff --no-index -- - a </dev/null 2>err &&
+	grep "fatal: cannot compare stdin to a directory" err
+'
+
+test_expect_success PIPE 'diff --no-index refuses to diff a named pipe and a directory' '
+	test_when_finished "rm -f pipe" &&
+	mkfifo pipe &&
+	test_must_fail git diff --no-index -- pipe a 2>err &&
+	grep "fatal: cannot compare a named pipe to a directory" err
+'
+
+test_expect_success PIPE,SYMLINKS 'diff --no-index reads from pipes' '
+	test_when_finished "rm -f old new new-link" &&
+	mkfifo old &&
+	mkfifo new &&
+	ln -s new new-link &&
+	{
+		(test_write_lines a b c >old) &
+	} &&
+	test_when_finished "kill $! || :" &&
+	{
+		(test_write_lines a x c >new) &
+	} &&
+	test_when_finished "kill $! || :" &&
+
+	cat >expect <<-EOF &&
+	diff --git a/old b/new-link
+	--- a/old
+	+++ b/new-link
+	@@ -1,3 +1,3 @@
+	 a
+	-b
+	+x
+	 c
+	EOF
+
+	test_expect_code 1 git diff --no-index old new-link >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 73048d0..3ea9ae9 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -74,13 +74,13 @@
 test_expect_success 'non-integer config parsing' '
 	git config diff.context no &&
 	test_must_fail git diff 2>output &&
-	test_i18ngrep "bad numeric config value" output
+	test_grep "bad numeric config value" output
 '
 
 test_expect_success 'negative integer config parsing' '
 	git config diff.context -1 &&
 	test_must_fail git diff 2>output &&
-	test_i18ngrep "bad config variable" output
+	test_grep "bad config variable" output
 '
 
 test_expect_success '-U0 is valid, so is diff.context=0' '
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
index 9aaa068..a90b46b 100755
--- a/t/t4062-diff-pickaxe.sh
+++ b/t/t4062-diff-pickaxe.sh
@@ -24,7 +24,7 @@
 
 test_expect_success '-S --pickaxe-regex' '
 	git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
-	verbose test 4096-zeroes.txt = "$(cat out)"
+	test 4096-zeroes.txt = "$(cat out)"
 '
 
 test_done
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index f60f5cb..7af3a08 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -151,7 +151,7 @@
 
 	# Ensure no fetches.
 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
-	! test_path_exists trace
+	test_path_is_missing trace
 '
 
 test_expect_success 'diff --break-rewrites fetches only if necessary, and batches blobs if it does' '
@@ -171,7 +171,7 @@
 
 	# Ensure no fetches.
 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
-	! test_path_exists trace &&
+	test_path_is_missing trace &&
 
 	# But with --break-rewrites, ensure that there is exactly 1 negotiation
 	# by checking that there is only 1 "done" line sent. ("done" marks the
diff --git a/t/t4068-diff-symmetric-merge-base.sh b/t/t4068-diff-symmetric-merge-base.sh
index 2d650d8..eff63c1 100755
--- a/t/t4068-diff-symmetric-merge-base.sh
+++ b/t/t4068-diff-symmetric-merge-base.sh
@@ -34,7 +34,7 @@
 	echo c >c &&
 	git add c &&
 	git commit -m C &&
-	git tag commit-C &&
+	git tag -m commit-C commit-C &&
 	git merge -m D main &&
 	git tag commit-D &&
 	git checkout main &&
@@ -68,27 +68,27 @@
 
 test_expect_success 'diff with no merge bases' '
 	test_must_fail git diff br2...br3 2>err &&
-	test_i18ngrep "fatal: br2...br3: no merge base" err
+	test_grep "fatal: br2...br3: no merge base" err
 '
 
 test_expect_success 'diff with too many symmetric differences' '
 	test_must_fail git diff br1...main br2...br3 2>err &&
-	test_i18ngrep "usage" err
+	test_grep "usage" err
 '
 
 test_expect_success 'diff with symmetric difference and extraneous arg' '
 	test_must_fail git diff main br1...main 2>err &&
-	test_i18ngrep "usage" err
+	test_grep "usage" err
 '
 
 test_expect_success 'diff with two ranges' '
 	test_must_fail git diff main br1..main br2..br3 2>err &&
-	test_i18ngrep "usage" err
+	test_grep "usage" err
 '
 
 test_expect_success 'diff with ranges and extra arg' '
 	test_must_fail git diff main br1..main commit-D 2>err &&
-	test_i18ngrep "usage" err
+	test_grep "usage" err
 '
 
 test_expect_success 'diff --merge-base with no commits' '
@@ -97,7 +97,7 @@
 
 test_expect_success 'diff --merge-base with three commits' '
 	test_must_fail git diff --merge-base br1 br2 main 2>err &&
-	test_i18ngrep "usage" err
+	test_grep "usage" err
 '
 
 for cmd in diff-index diff
@@ -109,6 +109,13 @@
 		test_cmp expect actual
 	'
 
+	test_expect_success "$cmd --merge-base with annotated tag" '
+		git checkout main &&
+		git $cmd commit-C >expect &&
+		git $cmd --merge-base commit-C >actual &&
+		test_cmp expect actual
+	'
+
 	test_expect_success "$cmd --merge-base with one commit and unstaged changes" '
 		git checkout main &&
 		test_when_finished git reset --hard &&
@@ -143,19 +150,19 @@
 	test_expect_success "$cmd --merge-base with non-commit" '
 		git checkout main &&
 		test_must_fail git $cmd --merge-base main^{tree} 2>err &&
-		test_i18ngrep "fatal: --merge-base only works with commits" err
+		test_grep "is a tree, not a commit" err
 	'
 
 	test_expect_success "$cmd --merge-base with no merge bases and one commit" '
 		git checkout main &&
 		test_must_fail git $cmd --merge-base br3 2>err &&
-		test_i18ngrep "fatal: no merge base found" err
+		test_grep "fatal: no merge base found" err
 	'
 
 	test_expect_success "$cmd --merge-base with multiple merge bases and one commit" '
 		git checkout main &&
 		test_must_fail git $cmd --merge-base br1 2>err &&
-		test_i18ngrep "fatal: multiple merge bases found" err
+		test_grep "fatal: multiple merge bases found" err
 	'
 done
 
@@ -169,28 +176,28 @@
 
 	test_expect_success "$cmd --merge-base commit and non-commit" '
 		test_must_fail git $cmd --merge-base br2 main^{tree} 2>err &&
-		test_i18ngrep "fatal: --merge-base only works with commits" err
+		test_grep "is a tree, not a commit" err
 	'
 
 	test_expect_success "$cmd --merge-base with no merge bases and two commits" '
 		test_must_fail git $cmd --merge-base br2 br3 2>err &&
-		test_i18ngrep "fatal: no merge base found" err
+		test_grep "fatal: no merge base found" err
 	'
 
 	test_expect_success "$cmd --merge-base with multiple merge bases and two commits" '
 		test_must_fail git $cmd --merge-base main br1 2>err &&
-		test_i18ngrep "fatal: multiple merge bases found" err
+		test_grep "fatal: multiple merge bases found" err
 	'
 done
 
 test_expect_success 'diff-tree --merge-base with one commit' '
 	test_must_fail git diff-tree --merge-base main 2>err &&
-	test_i18ngrep "fatal: --merge-base only works with two commits" err
+	test_grep "fatal: --merge-base only works with two commits" err
 '
 
 test_expect_success 'diff --merge-base with range' '
 	test_must_fail git diff --merge-base br2..br3 2>err &&
-	test_i18ngrep "fatal: --merge-base does not work with ranges" err
+	test_grep "fatal: --merge-base does not work with ranges" err
 '
 
 test_done
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index e95e6d4..cbef0a5 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -74,7 +74,7 @@
 	error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
 	EOF
 	test_cmp expected_stderr stderr &&
-	! test_path_exists .git/create-me
+	test_path_is_missing .git/create-me
 '
 
 test_expect_success SYMLINKS 'symlink escape when modifying file' '
@@ -136,7 +136,7 @@
 
 	ln -s foo file.t.rej &&
 	test_must_fail git apply patch --reject 2>err &&
-	test_i18ngrep "Rejected hunk" err &&
+	test_grep "Rejected hunk" err &&
 	test_path_is_missing foo &&
 	test_path_is_file file.t.rej
 '
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index 497b628..697e86c 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -31,7 +31,7 @@
 test_expect_success 'apply with too large -p' '
 	cp file1.saved file1 &&
 	test_must_fail git apply --stat -p3 patch.file 2>err &&
-	test_i18ngrep "removing 3 leading" err
+	test_grep "removing 3 leading" err
 '
 
 test_expect_success 'apply (-p2) traditional diff with funny filenames' '
@@ -53,7 +53,7 @@
 test_expect_success 'apply with too large -p and fancy filename' '
 	cp file1.saved file1 &&
 	test_must_fail git apply --stat -p3 patch.escaped 2>err &&
-	test_i18ngrep "removing 3 leading" err
+	test_grep "removing 3 leading" err
 '
 
 test_expect_success 'apply (-p2) diff, mode change only' '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 9696537..2089d84 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -95,19 +95,19 @@
 
 	# same input creates a confusing symbolic link
 	test_must_fail git apply patch 2>error-wt &&
-	test_i18ngrep "beyond a symbolic link" error-wt &&
+	test_grep "beyond a symbolic link" error-wt &&
 	test_path_is_missing arch/x86_64/dir &&
 	test_path_is_missing arch/i386/dir/file &&
 
 	test_must_fail git apply --index patch 2>error-ix &&
-	test_i18ngrep "beyond a symbolic link" error-ix &&
+	test_grep "beyond a symbolic link" error-ix &&
 	test_path_is_missing arch/x86_64/dir &&
 	test_path_is_missing arch/i386/dir/file &&
 	test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
 	test_must_fail git ls-files --error-unmatch arch/i386/dir &&
 
 	test_must_fail git apply --cached patch 2>error-ct &&
-	test_i18ngrep "beyond a symbolic link" error-ct &&
+	test_grep "beyond a symbolic link" error-ct &&
 	test_must_fail git ls-files --error-unmatch arch/x86_64/dir &&
 	test_must_fail git ls-files --error-unmatch arch/i386/dir &&
 
@@ -135,23 +135,23 @@
 	git add arch/x86_64/dir &&
 
 	test_must_fail git apply add_file.patch 2>error-wt-add &&
-	test_i18ngrep "beyond a symbolic link" error-wt-add &&
+	test_grep "beyond a symbolic link" error-wt-add &&
 	test_path_is_missing arch/i386/dir/file &&
 
 	mkdir arch/i386/dir &&
 	>arch/i386/dir/file &&
 	test_must_fail git apply del_file.patch 2>error-wt-del &&
-	test_i18ngrep "beyond a symbolic link" error-wt-del &&
+	test_grep "beyond a symbolic link" error-wt-del &&
 	test_path_is_file arch/i386/dir/file &&
 	rm arch/i386/dir/file &&
 
 	test_must_fail git apply --index add_file.patch 2>error-ix-add &&
-	test_i18ngrep "beyond a symbolic link" error-ix-add &&
+	test_grep "beyond a symbolic link" error-ix-add &&
 	test_path_is_missing arch/i386/dir/file &&
 	test_must_fail git ls-files --error-unmatch arch/i386/dir &&
 
 	test_must_fail git apply --cached add_file.patch 2>error-ct-file &&
-	test_i18ngrep "beyond a symbolic link" error-ct-file &&
+	test_grep "beyond a symbolic link" error-ct-file &&
 	test_must_fail git ls-files --error-unmatch arch/i386/dir
 '
 
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index ece9fae..56210b5 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -66,4 +66,28 @@
 	git diff --exit-code
 '
 
+test_expect_success !MINGW 'apply with no-contents and a funny pathname' '
+	test_when_finished "rm -fr \"funny \"; git reset --hard" &&
+
+	mkdir "funny " &&
+	>"funny /empty" &&
+	git add "funny /empty" &&
+	git diff HEAD -- "funny /" >sample.patch &&
+	git diff -R HEAD -- "funny /" >elpmas.patch &&
+
+	git reset --hard &&
+
+	git apply --stat --check --apply sample.patch &&
+	test_must_be_empty "funny /empty" &&
+
+	git apply --stat --check --apply elpmas.patch &&
+	test_path_is_missing "funny /empty" &&
+
+	git apply -R --stat --check --apply elpmas.patch &&
+	test_must_be_empty "funny /empty" &&
+
+	git apply -R --stat --check --apply sample.patch &&
+	test_path_is_missing "funny /empty"
+'
+
 test_done
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index a1c7686..4eb8444 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -41,7 +41,8 @@
 	chmod +x file &&
 	git add file &&
 	git apply --cached patch-0.txt &&
-	git ls-files -s file | grep "^100755"
+	git ls-files -s file >ls-files-output &&
+	test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'mode update (no index)' '
@@ -60,19 +61,20 @@
 test_expect_success FILEMODE 'mode update (index only)' '
 	git reset --hard &&
 	git apply --cached patch-1.txt &&
-	git ls-files -s file | grep "^100755"
+	git ls-files -s file >ls-files-output &&
+	test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'empty mode is rejected' '
 	git reset --hard &&
 	test_must_fail git apply patch-empty-mode.txt 2>err &&
-	test_i18ngrep "invalid mode" err
+	test_grep "invalid mode" err
 '
 
 test_expect_success FILEMODE 'bogus mode is rejected' '
 	git reset --hard &&
 	test_must_fail git apply patch-bogus-mode.txt 2>err &&
-	test_i18ngrep "invalid mode" err
+	test_grep "invalid mode" err
 '
 
 test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree files' '
@@ -101,4 +103,31 @@
 	)
 '
 
+test_expect_success 'git apply respects core.fileMode' '
+	test_config core.fileMode false &&
+	echo true >script.sh &&
+	git add --chmod=+x script.sh &&
+	git ls-files -s script.sh >ls-files-output &&
+	test_grep "^100755" ls-files-output &&
+	test_tick && git commit -m "Add script" &&
+	git ls-tree -r HEAD script.sh >ls-tree-output &&
+	test_grep "^100755" ls-tree-output &&
+
+	echo true >>script.sh &&
+	test_tick && git commit -m "Modify script" script.sh &&
+	git format-patch -1 --stdout >patch &&
+	test_grep "^index.*100755$" patch &&
+
+	git switch -c branch HEAD^ &&
+	git apply --index patch 2>err &&
+	test_grep ! "has type 100644, expected 100755" err &&
+	git reset --hard &&
+
+	git apply patch 2>err &&
+	test_grep ! "has type 100644, expected 100755" err &&
+
+	git apply --cached patch 2>err &&
+	test_grep ! "has type 100644, expected 100755" err
+'
+
 test_done
diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh
index 35f1060..c21ddb2 100755
--- a/t/t4133-apply-filenames.sh
+++ b/t/t4133-apply-filenames.sh
@@ -32,9 +32,9 @@
 
 test_expect_success 'apply diff with inconsistent filenames in headers' '
 	test_must_fail git apply bad1.patch 2>err &&
-	test_i18ngrep "inconsistent new filename" err &&
+	test_grep "inconsistent new filename" err &&
 	test_must_fail git apply bad2.patch 2>err &&
-	test_i18ngrep "inconsistent old filename" err
+	test_grep "inconsistent old filename" err
 '
 
 test_expect_success 'apply diff with new filename missing from headers' '
@@ -46,7 +46,7 @@
 	+1
 	EOF
 	test_must_fail git apply missing_new_filename.diff 2>err &&
-	test_i18ngrep "lacks filename information" err
+	test_grep "lacks filename information" err
 '
 
 test_expect_success 'apply diff with old filename missing from headers' '
@@ -58,7 +58,7 @@
 	-1
 	EOF
 	test_must_fail git apply missing_old_filename.diff 2>err &&
-	test_i18ngrep "lacks filename information" err
+	test_grep "lacks filename information" err
 '
 
 test_done
diff --git a/t/t4141-apply-too-large.sh b/t/t4141-apply-too-large.sh
index 58742d4..20cc120 100755
--- a/t/t4141-apply-too-large.sh
+++ b/t/t4141-apply-too-large.sh
@@ -17,7 +17,7 @@
 		EOF
 		test-tool genzeros
 	} | test_copy_bytes $sz | test_must_fail git apply 2>err &&
-	grep "git apply: failed to read" err
+	grep "patch too large" err
 '
 
 test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 78cf1c8..5e2b6c8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -103,7 +103,7 @@
 
 	git format-patch --stdout first >patch1 &&
 	{
-		echo "Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
+		echo "Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
 		echo "X-Fake-Field: Line One" &&
 		echo "X-Fake-Field: Line Two" &&
 		echo "X-Fake-Field: Line Three" &&
@@ -779,7 +779,7 @@
 	test_must_fail git am --resolved >err &&
 	test_path_is_dir .git/rebase-apply &&
 	test_cmp_rev second HEAD &&
-	test_i18ngrep "still have unmerged paths" err
+	test_grep "still have unmerged paths" err
 '
 
 test_expect_success 'am takes patches from a Pine mailbox' '
@@ -913,7 +913,7 @@
 	test_tick &&
 	sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
 	git am <patchnl >output.out 2>&1 &&
-	test_i18ngrep "^Applying: second \\\n foo$" output.out
+	test_grep "^Applying: second \\\n foo$" output.out
 '
 
 test_expect_success 'am -q is quiet' '
@@ -942,7 +942,7 @@
 	git am --message-id patch1.eml &&
 	test_path_is_missing .git/rebase-apply &&
 	git cat-file commit HEAD | tail -n1 >actual &&
-	grep Message-Id patch1.eml >expected &&
+	grep Message-ID patch1.eml >expected &&
 	test_cmp expected actual
 '
 
@@ -954,7 +954,7 @@
 	git am patch1.eml &&
 	test_path_is_missing .git/rebase-apply &&
 	git cat-file commit HEAD | tail -n1 >actual &&
-	grep Message-Id patch1.eml >expected &&
+	grep Message-ID patch1.eml >expected &&
 	test_cmp expected actual
 '
 
@@ -965,7 +965,7 @@
 	git am -s --message-id patch1.eml &&
 	test_path_is_missing .git/rebase-apply &&
 	git cat-file commit HEAD | tail -n2 | head -n1 >actual &&
-	grep Message-Id patch1.eml >expected &&
+	grep Message-ID patch1.eml >expected &&
 	test_cmp expected actual
 '
 
@@ -1224,8 +1224,8 @@
 
 test_expect_success 'skip an empty patch in the middle of an am session' '
 	git checkout empty-commit^ &&
-	test_must_fail git am empty-commit.patch >err &&
-	grep "Patch is empty." err &&
+	test_must_fail git am empty-commit.patch >out 2>err &&
+	grep "Patch is empty." out &&
 	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
 	git am --skip &&
 	test_path_is_missing .git/rebase-apply &&
@@ -1236,8 +1236,8 @@
 
 test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
 	git checkout empty-commit^ &&
-	test_must_fail git am empty-commit.patch >err &&
-	grep "Patch is empty." err &&
+	test_must_fail git am empty-commit.patch >out 2>err &&
+	grep "Patch is empty." out &&
 	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
 	git am --allow-empty >output &&
 	grep "No changes - recorded it as an empty commit." output &&
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 5ed7e22..edb38da 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -46,7 +46,7 @@
 
 	test_expect_success "am$with3 --skip continue after failed am$with3" '
 		test_must_fail git am$with3 --skip >output &&
-		test_i18ngrep "^Applying: 6$" output &&
+		test_grep "^Applying: 6$" output &&
 		test_cmp file-2-expect file-2 &&
 		test ! -f .git/MERGE_RR
 	'
diff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh
index b7c3861..4add7c7 100755
--- a/t/t4153-am-resume-override-opts.sh
+++ b/t/t4153-am-resume-override-opts.sh
@@ -53,7 +53,7 @@
 	# Applying side1 will be quiet.
 	test_must_fail git am --quiet side[123].eml >out &&
 	test_path_is_dir .git/rebase-apply &&
-	test_i18ngrep ! "^Applying: " out &&
+	test_grep ! "^Applying: " out &&
 	echo side1 >file &&
 	git add file &&
 
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 7025cfd..fb53ddd 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -433,13 +433,13 @@
 	git update-index --index-info <failedmerge &&
 	cp file3.conflict file3 &&
 	test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
-	test_i18ngrep [Uu]sage err &&
+	test_grep [Uu]sage err &&
 	test_must_fail git update-index --refresh
 '
 
 test_expect_success 'rerere -h' '
 	test_must_fail git rerere -h >help &&
-	test_i18ngrep [Uu]sage help
+	test_grep [Uu]sage help
 '
 
 concat_insert () {
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 8e4effe..f698d0c 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -139,7 +139,7 @@
 
 test_expect_success 'shortlog from non-git directory refuses extra arguments' '
 	test_must_fail env GIT_DIR=non-existing git shortlog foo 2>out &&
-	test_i18ngrep "too many arguments" out
+	test_grep "too many arguments" out
 '
 
 test_expect_success 'shortlog should add newline when input line matches wraplen' '
@@ -312,6 +312,38 @@
 	test_cmp expect actual
 '
 
+# Trailers that have unfolded (single line) and folded (multiline) values which
+# are otherwise identical are treated as the same trailer for de-duplication.
+test_expect_success 'shortlog de-duplicates trailers in a single commit (folded/unfolded values)' '
+	git commit --allow-empty -F - <<-\EOF &&
+	subject one
+
+	this message has two distinct values, plus a repeat (folded)
+
+	Repeated-trailer: Foo foo foo
+	Repeated-trailer: Bar
+	Repeated-trailer: Foo
+	  foo foo
+	EOF
+
+	git commit --allow-empty -F - <<-\EOF &&
+	subject two
+
+	similar to the previous, but without the second distinct value
+
+	Repeated-trailer: Foo foo foo
+	Repeated-trailer: Foo
+	  foo foo
+	EOF
+
+	cat >expect <<-\EOF &&
+	     2	Foo foo foo
+	     1	Bar
+	EOF
+	git shortlog -ns --group=trailer:repeated-trailer -2 HEAD >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'shortlog can match multiple groups' '
 	git commit --allow-empty -F - <<-\EOF &&
 	subject one
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 2ce2b41..60fe60d 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -187,6 +187,21 @@
 	git log --
 '
 
+test_expect_success 'git log --follow rejects unsupported pathspec magic' '
+	test_must_fail git log --follow ":(top,glob,icase)ichi" 2>stderr &&
+	# check full error message; we want to be sure we mention both
+	# of the rejected types (glob,icase), but not the allowed one (top)
+	echo "fatal: pathspec magic not supported by --follow: ${SQ}glob${SQ}, ${SQ}icase${SQ}" >expect &&
+	test_cmp expect stderr
+'
+
+test_expect_success 'log.follow disabled with unsupported pathspec magic' '
+	test_config log.follow true &&
+	git log --format=%s ":(glob,icase)ichi" >actual &&
+	echo third >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git config log.follow is overridden by --no-follow' '
 	test_config log.follow true &&
 	git log --no-follow --pretty="format:%s" ichi >actual &&
@@ -835,6 +850,21 @@
 
 '
 
+test_expect_success 'parse log.excludeDecoration with no value' '
+	cp .git/config .git/config.orig &&
+	test_when_finished mv .git/config.orig .git/config &&
+
+	cat >>.git/config <<-\EOF &&
+	[log]
+		excludeDecoration
+	EOF
+	cat >expect <<-\EOF &&
+	error: missing value for '\''log.excludeDecoration'\''
+	EOF
+	git log --decorate=short 2>actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'decorate-refs with glob' '
 	cat >expect.decorate <<-\EOF &&
 	Merge-tag-reach
@@ -1854,7 +1884,7 @@
 
 test_expect_success '--reverse and --graph conflict' '
 	test_must_fail git log --reverse --graph 2>stderr &&
-	test_i18ngrep "cannot be used together" stderr
+	test_grep "cannot be used together" stderr
 '
 
 test_expect_success '--reverse --graph --no-graph works' '
@@ -1865,7 +1895,7 @@
 
 test_expect_success '--show-linear-break and --graph conflict' '
 	test_must_fail git log --show-linear-break --graph 2>stderr &&
-	test_i18ngrep "cannot be used together" stderr
+	test_grep "cannot be used together" stderr
 '
 
 test_expect_success '--show-linear-break --graph --no-graph works' '
@@ -1876,7 +1906,7 @@
 
 test_expect_success '--no-walk and --graph conflict' '
 	test_must_fail git log --no-walk --graph 2>stderr &&
-	test_i18ngrep "cannot be used together" stderr
+	test_grep "cannot be used together" stderr
 '
 
 test_expect_success '--no-walk --graph --no-graph works' '
@@ -1887,8 +1917,8 @@
 
 test_expect_success '--walk-reflogs and --graph conflict' '
 	test_must_fail git log --walk-reflogs --graph 2>stderr &&
-	(test_i18ngrep "cannot combine" stderr ||
-		test_i18ngrep "cannot be used together" stderr)
+	(test_grep "cannot combine" stderr ||
+		test_grep "cannot be used together" stderr)
 '
 
 test_expect_success '--walk-reflogs --graph --no-graph works' '
@@ -2222,24 +2252,7 @@
 	git init empty &&
 	test_when_finished "rm -rf empty" &&
 	test_must_fail git -C empty log 2>stderr &&
-	test_i18ngrep does.not.have.any.commits stderr
-'
-
-test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
-	git init empty &&
-	test_when_finished "rm -rf empty" &&
-	echo 1234abcd >empty/.git/refs/heads/main &&
-	test_must_fail git -C empty log 2>stderr &&
-	test_i18ngrep broken stderr
-'
-
-test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '
-	git init empty &&
-	echo "ref: refs/heads/invalid.lock" > empty/.git/HEAD &&
-	test_must_fail git -C empty log 2>stderr &&
-	test_i18ngrep broken stderr &&
-	test_must_fail git -C empty log --default totally-bogus 2>stderr &&
-	test_i18ngrep broken stderr
+	test_grep does.not.have.any.commits stderr
 '
 
 test_expect_success 'log does not default to HEAD when rev input is given' '
@@ -2328,10 +2341,10 @@
 '
 
 test_expect_success 'log --end-of-options' '
-       git update-ref refs/heads/--source HEAD &&
-       git log --end-of-options --source >actual &&
-       git log >expect &&
-       test_cmp expect actual
+	git update-ref refs/heads/--source HEAD &&
+	git log --end-of-options --source >actual &&
+	git log >expect &&
+	test_cmp expect actual
 '
 
 test_expect_success 'set up commits with different authors' '
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index fa7f987..8a88dd7 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -360,7 +360,7 @@
 	cp default.map .mailmap &&
 
 	git -c mailmap.blob=HEAD: shortlog HEAD >actual 2>err &&
-	test_i18ngrep "mailmap is not a blob" err &&
+	test_grep "mailmap is not a blob" err &&
 	test_cmp expect actual
 '
 
@@ -466,7 +466,7 @@
 	Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@laptop.(none)>
 	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-	Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
+	Author Jane D. <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
 	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 	EOF
 	git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
@@ -494,7 +494,7 @@
 	Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@example.com>
 	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-	Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
+	Author Jane D. <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
 	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 	EOF
 	git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 4cf8a77..158b49d 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -30,40 +30,46 @@
 	>bar &&
 	git add foo &&
 	test_tick &&
-	git config i18n.commitEncoding $test_encoding &&
+	test_config i18n.commitEncoding $test_encoding &&
 	commit_msg $test_encoding | git commit -F - &&
 	git add bar &&
 	test_tick &&
-	git commit -m "add bar" &&
-	git config --unset i18n.commitEncoding
+	git commit -m "add bar"
 '
 
 test_expect_success 'alias builtin format' '
 	git log --pretty=oneline >expected &&
-	git config pretty.test-alias oneline &&
+	test_config pretty.test-alias oneline &&
 	git log --pretty=test-alias >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'alias masking builtin format' '
 	git log --pretty=oneline >expected &&
-	git config pretty.oneline "%H" &&
+	test_config pretty.oneline "%H" &&
 	git log --pretty=oneline >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'alias user-defined format' '
 	git log --pretty="format:%h" >expected &&
-	git config pretty.test-alias "format:%h" &&
+	test_config pretty.test-alias "format:%h" &&
 	git log --pretty=test-alias >actual &&
 	test_cmp expected actual
 '
 
+test_expect_success 'alias user-defined format is matched case-insensitively' '
+	git log --pretty="format:%h" >expected &&
+	test_config pretty.testone "format:%h" &&
+	test_config pretty.testtwo testOne &&
+	git log --pretty=testTwo >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' '
-	git config i18n.logOutputEncoding $test_encoding &&
+	test_config i18n.logOutputEncoding $test_encoding &&
 	git log --oneline >expected-s &&
 	git log --pretty="tformat:%h %s" >actual-s &&
-	git config --unset i18n.logOutputEncoding &&
 	test_cmp expected-s actual-s
 '
 
@@ -75,34 +81,34 @@
 
 test_expect_success 'alias user-defined tformat' '
 	git log --pretty="tformat:%h" >expected &&
-	git config pretty.test-alias "tformat:%h" &&
+	test_config pretty.test-alias "tformat:%h" &&
 	git log --pretty=test-alias >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'alias non-existent format' '
-	git config pretty.test-alias format-that-will-never-exist &&
+	test_config pretty.test-alias format-that-will-never-exist &&
 	test_must_fail git log --pretty=test-alias
 '
 
 test_expect_success 'alias of an alias' '
 	git log --pretty="tformat:%h" >expected &&
-	git config pretty.test-foo "tformat:%h" &&
-	git config pretty.test-bar test-foo &&
+	test_config pretty.test-foo "tformat:%h" &&
+	test_config pretty.test-bar test-foo &&
 	git log --pretty=test-bar >actual && test_cmp expected actual
 '
 
 test_expect_success 'alias masking an alias' '
 	git log --pretty=format:"Two %H" >expected &&
-	git config pretty.duplicate "format:One %H" &&
-	git config --add pretty.duplicate "format:Two %H" &&
+	test_config pretty.duplicate "format:One %H" &&
+	test_config pretty.duplicate "format:Two %H" --add &&
 	git log --pretty=duplicate >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'alias loop' '
-	git config pretty.test-foo test-bar &&
-	git config pretty.test-bar test-foo &&
+	test_config pretty.test-foo test-bar &&
+	test_config pretty.test-bar test-foo &&
 	test_must_fail git log --pretty=test-foo
 '
 
@@ -156,7 +162,7 @@
 	for r in $revs
 	do
 		git show -s --pretty=oneline "$r" >raw &&
-		cat raw | lf_to_nul || return 1
+		lf_to_nul <raw || return 1
 	done >expect &&
 	# the trailing NUL is already produced so we do not need to
 	# output another one
@@ -576,6 +582,38 @@
 	test_cmp expected actual1
 '
 
+test_expect_success 'pretty format %decorate' '
+	git checkout -b foo &&
+	git commit --allow-empty -m "new commit" &&
+	git tag bar &&
+	git branch qux &&
+
+	echo " (HEAD -> foo, tag: bar, qux)" >expect1 &&
+	git log --format="%(decorate)" -1 >actual1 &&
+	test_cmp expect1 actual1 &&
+
+	echo "HEAD -> foo, tag: bar, qux" >expect2 &&
+	git log --format="%(decorate:prefix=,suffix=)" -1 >actual2 &&
+	test_cmp expect2 actual2 &&
+
+	echo "[ bar; qux; foo ]" >expect3 &&
+	git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B ,tag=)" \
+		--decorate-refs=refs/ -1 >actual3 &&
+	test_cmp expect3 actual3 &&
+
+	# Try with a typo (in "separator"), in which case the placeholder should
+	# not be replaced.
+	echo "%(decorate:prefix=[ ,suffix= ],separater=; )" >expect4 &&
+	git log --format="%(decorate:prefix=[ ,suffix= ],separater=%x3B )" \
+		-1 >actual4 &&
+	test_cmp expect4 actual4 &&
+
+	echo "HEAD->foo bar qux" >expect5 &&
+	git log --format="%(decorate:prefix=,suffix=,separator= ,tag=,pointer=->)" \
+		-1 >actual5 &&
+	test_cmp expect5 actual5
+'
+
 cat >trailers <<EOF
 Signed-off-by: A U Thor <author@example.com>
 Acked-by: A U Thor <author@example.com>
@@ -924,6 +962,36 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'setup more commits for %S with --bisect' '
+	test_commit four &&
+	test_commit five &&
+
+	head1=$(git rev-parse --verify HEAD~0) &&
+	head2=$(git rev-parse --verify HEAD~1) &&
+	head3=$(git rev-parse --verify HEAD~2) &&
+	head4=$(git rev-parse --verify HEAD~3)
+'
+
+test_expect_success '%S with --bisect labels commits with refs/bisect/bad ref' '
+	git update-ref refs/bisect/bad-$head1 $head1 &&
+	git update-ref refs/bisect/go $head1 &&
+	git update-ref refs/bisect/bad-$head2 $head2 &&
+	git update-ref refs/bisect/b $head3 &&
+	git update-ref refs/bisect/bad-$head4 $head4 &&
+	git update-ref refs/bisect/good-$head4 $head4 &&
+
+	# We expect to see the range of commits betwee refs/bisect/good-$head4
+	# and refs/bisect/bad-$head1. The "source" ref is the nearest bisect ref
+	# from which the commit is reachable.
+	cat >expect <<-EOF &&
+	$head1 refs/bisect/bad-$head1
+	$head2 refs/bisect/bad-$head2
+	$head3 refs/bisect/bad-$head2
+	EOF
+	git log --bisect --format="%H %S" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'log --pretty=reference' '
 	git log --pretty="tformat:%h (%s, %as)" >expect &&
 	git log --pretty=reference >actual &&
@@ -1012,10 +1080,25 @@
 
 test_expect_success '%(describe:abbrev=...) vs git describe --abbrev=...' '
 	test_when_finished "git tag -d tagname" &&
+
+	# Case 1: We have commits between HEAD and the most recent tag
+	#	  reachable from it
+	test_commit --no-tag file &&
+	git describe --abbrev=15 >expect &&
+	git log -1 --format="%(describe:abbrev=15)" >actual &&
+	test_cmp expect actual &&
+
+	# Make sure the hash used is at least 15 digits long
+	sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+	test 16 -le $(wc -c <hexpart) &&
+
+	# Case 2: We have a tag at HEAD, describe directly gives the
+	#	  name of the tag
 	git tag -a -m tagged tagname &&
 	git describe --abbrev=15 >expect &&
 	git log -1 --format="%(describe:abbrev=15)" >actual &&
-	test_cmp expect actual
+	test_cmp expect actual &&
+	test tagname = $(cat actual)
 '
 
 test_expect_success 'log --pretty with space stealing' '
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 33ecf82..9167b03 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -16,29 +16,29 @@
 Line 3
 '
 
-test_expect_success \
-    'add a file path0 and commit.' \
-    'git add path0 &&
-     git commit -m "Add path0"'
+test_expect_success 'add a file path0 and commit.' '
+	git add path0 &&
+	git commit -m "Add path0"
+'
 
 echo >path0 'New line 1
 New line 2
 New line 3
 '
-test_expect_success \
-    'Change path0.' \
-    'git add path0 &&
-     git commit -m "Change path0"'
+test_expect_success 'Change path0.' '
+	git add path0 &&
+	git commit -m "Change path0"
+'
 
 cat <path0 >path1
-test_expect_success \
-    'copy path0 to path1.' \
-    'git add path1 &&
-     git commit -m "Copy path1 from path0"'
+test_expect_success 'copy path0 to path1.' '
+	git add path1 &&
+	git commit -m "Copy path1 from path0"
+'
 
-test_expect_success \
-    'find the copy path0 -> path1 harder' \
-    'git log --follow --name-status --pretty="format:%s"  path1 > current'
+test_expect_success 'find the copy path0 -> path1 harder' '
+	git log --follow --name-status --pretty="format:%s"  path1 > current
+'
 
 cat >expected <<\EOF
 Copy path1 from path0
@@ -51,8 +51,8 @@
 A	path0
 EOF
 
-test_expect_success \
-    'validate the output.' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate the output.' '
+	compare_diff_patch current expected
+'
 
 test_done
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index ded33a8..73ea9e5 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -53,35 +53,45 @@
 # to this test since it does not contain any decoration, hence --first-parent
 test_expect_success 'commit decorations colored correctly' '
 	cat >expect <<-EOF &&
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
-${c_reset}${c_branch}main${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
-${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit}, \
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
 ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset} \
-On main: Changes to A.t
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
 	EOF
 
 	git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual &&
 	cmp_filtered_decorations
 '
 
+remove_replace_refs () {
+	git for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&
+	git update-ref --stdin <in &&
+	rm in
+}
+
 test_expect_success 'test coloring with replace-objects' '
-	test_when_finished rm -rf .git/refs/replace* &&
+	test_when_finished remove_replace_refs &&
 	test_commit C &&
 	test_commit D &&
 
 	git replace HEAD~1 HEAD~2 &&
 
 	cat >expect <<-EOF &&
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
-${c_reset}${c_branch}main${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: D${c_reset}${c_commit})${c_reset} D
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: C${c_reset}${c_commit}, \
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit})${c_reset} D
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_commit}, \
 ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
 EOF
 
 	git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
@@ -95,18 +105,20 @@
 '
 
 test_expect_success 'test coloring with grafted commit' '
-	test_when_finished rm -rf .git/refs/replace* &&
+	test_when_finished remove_replace_refs &&
 
 	git replace --graft HEAD HEAD~2 &&
 
 	cat >expect <<-EOF &&
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \
-${c_reset}${c_branch}main${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: D${c_reset}${c_commit}, \
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\
+${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit}, \
 ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \
-${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
-	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
+${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
+	${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
+${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
 	EOF
 
 	git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual &&
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 2e8f5ad..806b280 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -21,7 +21,7 @@
 test_expect_success '"git log :/a" should be ambiguous (applied both rev and worktree)' '
 	: >a &&
 	test_must_fail git log :/a 2>error &&
-	test_i18ngrep ambiguous error
+	test_grep ambiguous error
 '
 
 test_expect_success '"git log :/a -- " should not be ambiguous' '
@@ -65,7 +65,7 @@
 
 test_expect_success '"git log :" should be ambiguous' '
 	test_must_fail git log : 2>error &&
-	test_i18ngrep ambiguous error
+	test_grep ambiguous error
 '
 
 test_expect_success 'git log -- :' '
@@ -104,7 +104,7 @@
 
 test_expect_success '"git log :(unknown-magic) complains of bogus magic' '
 	test_must_fail git log ":(unknown-magic)" 2>error &&
-	test_i18ngrep pathspec.magic error
+	test_grep pathspec.magic error
 '
 
 test_expect_success 'command line pathspec parsing for "git log"' '
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh
index 7f6bb27..64e1623 100755
--- a/t/t4209-log-pickaxe.sh
+++ b/t/t4209-log-pickaxe.sh
@@ -57,10 +57,10 @@
 
 test_expect_success 'usage' '
 	test_expect_code 129 git log -S 2>err &&
-	test_i18ngrep "switch.*requires a value" err &&
+	test_grep "switch.*requires a value" err &&
 
 	test_expect_code 129 git log -G 2>err &&
-	test_i18ngrep "switch.*requires a value" err &&
+	test_grep "switch.*requires a value" err &&
 
 	test_expect_code 128 git log -Gregex -Sstring 2>err &&
 	grep "cannot be used together" err &&
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index c6540e8..02d76dc 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -19,7 +19,7 @@
 
 	# -L requires there is no pathspec
 	test_must_fail git log -L1,1:b.c -- b.c 2>error &&
-	test_i18ngrep "cannot be used with pathspec" error &&
+	test_grep "cannot be used with pathspec" error &&
 
 	# This would fail because --follow wants a single path, but
 	# we may fail due to incompatibility between -L/--follow in
@@ -50,7 +50,7 @@
 test_bad_opts () {
 	test_expect_success "invalid args: $1" "
 		test_must_fail git log $1 2>errors &&
-		test_i18ngrep '$2' errors
+		test_grep '$2' errors
 	"
 }
 
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index e89e1f5..e6b5912 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -8,15 +8,16 @@
 test_expect_success 'setup' '
 	test_commit foo &&
 
-	git cat-file commit HEAD |
-	sed "/^author /s/>/>-<>/" >broken_email.commit &&
+	git cat-file commit HEAD >ok.commit &&
+	sed "s/>/>-<>/" <ok.commit >broken_email.commit &&
+
 	git hash-object --literally -w -t commit broken_email.commit >broken_email.hash &&
 	git update-ref refs/heads/broken_email $(cat broken_email.hash)
 '
 
 test_expect_success 'fsck notices broken commit' '
 	test_must_fail git fsck 2>actual &&
-	test_i18ngrep invalid.author actual
+	test_grep invalid.author actual
 '
 
 test_expect_success 'git log with broken author email' '
@@ -43,6 +44,11 @@
 	test_must_be_empty actual.err
 '
 
+test_expect_success '--until handles broken email' '
+	git rev-list --until=1980-01-01 broken_email >actual &&
+	test_must_be_empty actual
+'
+
 munge_author_date () {
 	git cat-file commit "$1" >commit.orig &&
 	sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
@@ -86,4 +92,45 @@
 	git log -1 --format=%ad $commit
 '
 
+test_expect_success 'create commits with whitespace committer dates' '
+	# It is important that this subject line is numeric, since we want to
+	# be sure we are not confused by skipping whitespace and accidentally
+	# parsing the subject as a timestamp.
+	#
+	# Do not use munge_author_date here. Besides not hitting the committer
+	# line, it leaves the timezone intact, and we want nothing but
+	# whitespace.
+	#
+	# We will make two munged commits here. The first, ws_commit, will
+	# be purely spaces. The second contains a vertical tab, which is
+	# considered a space by strtoumax(), but not by our isspace().
+	test_commit 1234567890 &&
+	git cat-file commit HEAD >commit.orig &&
+	sed "s/>.*/>    /" <commit.orig >commit.munge &&
+	ws_commit=$(git hash-object --literally -w -t commit commit.munge) &&
+	sed "s/>.*/>   $(printf "\013")/" <commit.orig >commit.munge &&
+	vt_commit=$(git hash-object --literally -w -t commit commit.munge)
+'
+
+test_expect_success '--until treats whitespace date as sentinel' '
+	echo $ws_commit >expect &&
+	git rev-list --until=1980-01-01 $ws_commit >actual &&
+	test_cmp expect actual &&
+
+	echo $vt_commit >expect &&
+	git rev-list --until=1980-01-01 $vt_commit >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty-printer handles whitespace date' '
+	# as with the %ad test above, we will show these as the empty string,
+	# not the 1970 epoch date. This is intentional; see 7d9a281941 (t4212:
+	# test bogus timestamps with git-log, 2014-02-24) for more discussion.
+	echo : >expect &&
+	git log -1 --format="%at:%ct" $ws_commit >actual &&
+	test_cmp expect actual &&
+	git log -1 --format="%at:%ct" $vt_commit >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
index f70c46b..7905597 100755
--- a/t/t4214-log-graph-octopus.sh
+++ b/t/t4214-log-graph-octopus.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
diff --git a/t/t4215-log-skewed-merges.sh b/t/t4215-log-skewed-merges.sh
index 28d0779..b877ac7 100755
--- a/t/t4215-log-skewed-merges.sh
+++ b/t/t4215-log-skewed-merges.sh
@@ -2,6 +2,7 @@
 
 test_description='git log --graph of skewed merges'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index fa9d32f..2ba0324 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -5,6 +5,7 @@
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_COMMIT_GRAPH=0
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -404,4 +405,53 @@
 	)
 '
 
+corrupt_graph () {
+	graph=.git/objects/info/commit-graph &&
+	test_when_finished "rm -rf $graph" &&
+	git commit-graph write --reachable --changed-paths &&
+	corrupt_chunk_file $graph "$@"
+}
+
+check_corrupt_graph () {
+	corrupt_graph "$@" &&
+	git -c core.commitGraph=false log -- A/B/file2 >expect.out &&
+	git -c core.commitGraph=true log -- A/B/file2 >out 2>err &&
+	test_cmp expect.out out
+}
+
+test_expect_success 'Bloom reader notices too-small data chunk' '
+	check_corrupt_graph BDAT clear 00000000 &&
+	echo "warning: ignoring too-small changed-path chunk" \
+		"(4 < 12) in commit-graph file" >expect.err &&
+	test_cmp expect.err err
+'
+
+test_expect_success 'Bloom reader notices out-of-bounds filter offsets' '
+	check_corrupt_graph BIDX 12 FFFFFFFF &&
+	# use grep to avoid depending on exact chunk size
+	grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err
+'
+
+test_expect_success 'Bloom reader notices too-small index chunk' '
+	# replace the index with a single entry, making most
+	# lookups out-of-bounds
+	check_corrupt_graph BIDX clear 00000000 &&
+	echo "warning: commit-graph changed-path index chunk" \
+		"is too small" >expect.err &&
+	test_cmp expect.err err
+'
+
+test_expect_success 'Bloom reader notices out-of-order index offsets' '
+	# we do not know any real offsets, but we can pick
+	# something plausible; we should not get to the point of
+	# actually reading from the bogus offsets anyway.
+	corrupt_graph BIDX 4 0000000c00000005 &&
+	echo "warning: ignoring decreasing changed-path index offsets" \
+		"(12 > 5) for positions 1 and 2 of .git/objects/info/commit-graph" >expect.err &&
+	git -c core.commitGraph=false log -- A/B/file2 >expect.out &&
+	git -c core.commitGraph=true log -- A/B/file2 >out 2>err &&
+	test_cmp expect.out out &&
+	test_cmp expect.err err
+'
+
 test_done
diff --git a/t/t4217-log-limit.sh b/t/t4217-log-limit.sh
index 6e01e26..613f071 100755
--- a/t/t4217-log-limit.sh
+++ b/t/t4217-log-limit.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with filter options limiting the output'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup test' '
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index 45f1d4f..661feb6 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -59,7 +59,7 @@
 # Also, it had the unwanted side-effect of deleting f.
 test_expect_success 'try to apply corrupted patch' '
 	test_when_finished "git am --abort" &&
-	test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
+	test_must_fail git -c advice.amWorkDir=false -c advice.mergeConflict=false am bad-patch.diff 2>actual &&
 	echo "error: git diff header lacks filename information (line 4)" >expected &&
 	test_path_is_file f &&
 	test_cmp expected actual
diff --git a/t/t4256-am-format-flowed.sh b/t/t4256-am-format-flowed.sh
index 1015273..92d8c8b 100755
--- a/t/t4256-am-format-flowed.sh
+++ b/t/t4256-am-format-flowed.sh
@@ -13,7 +13,7 @@
 
 test_expect_success 'am with format=flowed' '
 	git am <"$TEST_DIRECTORY/t4256/1/patch" 2>stderr &&
-	test_i18ngrep "warning: Patch sent with format=flowed" stderr &&
+	test_grep "warning: Patch sent with format=flowed" stderr &&
 	test_cmp "$TEST_DIRECTORY/t4256/1/mailinfo.c" mailinfo.c
 '
 
diff --git a/t/t4258/mbox b/t/t4258/mbox
index c62819f..1ae528b 100644
--- a/t/t4258/mbox
+++ b/t/t4258/mbox
@@ -2,7 +2,7 @@
 To: list@example.org
 Subject: [PATCH v2] sample
 Date: Mon,  3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
 Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: base64
 
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
index c52c8a2..9c19726 100755
--- a/t/t4300-merge-tree.sh
+++ b/t/t4300-merge-tree.sh
@@ -86,6 +86,33 @@
 	test_cmp expected actual
 '
 
+test_expect_success '3-way merge with --attr-source' '
+	test_when_finished rm -rf 3-way &&
+	git init 3-way &&
+	(
+		cd 3-way &&
+		test_commit initial file1 foo &&
+		base=$(git rev-parse HEAD) &&
+		git checkout -b brancha &&
+		echo bar >>file1 &&
+		git commit -am "adding bar" &&
+		source=$(git rev-parse HEAD) &&
+		git checkout @{-1} &&
+		git checkout -b branchb &&
+		echo baz >>file1 &&
+		git commit -am "adding baz" &&
+		merge=$(git rev-parse HEAD) &&
+		git checkout -b gitattributes &&
+		test_commit "gitattributes" .gitattributes "file1 merge=union" &&
+		git checkout @{-1} &&
+		tree=$(git --attr-source=gitattributes merge-tree --write-tree \
+		--merge-base "$base" --end-of-options "$source" "$merge") &&
+		test_write_lines foo bar baz >expect &&
+		git cat-file -p "$tree:file1" >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'file change A, B (same)' '
 	git reset --hard initial &&
 	test_commit "change-a-b-same-A" "initial-file" "AAA" &&
@@ -334,4 +361,22 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'merge-tree respects core.useReplaceRefs=false' '
+	test_commit merge-to &&
+	test_commit valid base &&
+	git reset --hard HEAD^ &&
+	test_commit malicious base &&
+
+	test_when_finished "git replace -d $(git rev-parse valid^0)" &&
+	git replace valid^0 malicious^0 &&
+
+	tree=$(git -c core.useReplaceRefs=true merge-tree --write-tree merge-to valid) &&
+	merged=$(git cat-file -p $tree:base) &&
+	test malicious = $merged &&
+
+	tree=$(git -c core.useReplaceRefs=false merge-tree --write-tree merge-to valid) &&
+	merged=$(git cat-file -p $tree:base) &&
+	test valid = $merged
+'
+
 test_done
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index 250f721..eea1990 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -22,6 +22,7 @@
 	git branch side1 &&
 	git branch side2 &&
 	git branch side3 &&
+	git branch side4 &&
 
 	git checkout side1 &&
 	test_write_lines 1 2 3 4 5 6 >numbers &&
@@ -46,6 +47,13 @@
 	test_tick &&
 	git commit -m rename-numbers &&
 
+	git checkout side4 &&
+	test_write_lines 0 1 2 3 4 5 >numbers &&
+	echo yo >greeting &&
+	git add numbers greeting &&
+	test_tick &&
+	git commit -m other-content-modifications &&
+
 	git switch --orphan unrelated &&
 	>something-else &&
 	git add something-else &&
@@ -97,6 +105,21 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'Auto resolve conflicts by "ours" strategy option' '
+	git checkout side1^0 &&
+
+	# make sure merge conflict exists
+	test_must_fail git merge side4 &&
+	git merge --abort &&
+
+	git merge -X ours side4 &&
+	git rev-parse HEAD^{tree} >expected &&
+
+	git merge-tree -X ours side1 side4 >actual &&
+
+	test_cmp expected actual
+'
+
 test_expect_success 'Barf on misspelled option, with exit code other than 0 or 1' '
 	# Mis-spell with single "s" instead of double "s"
 	test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect &&
@@ -290,7 +313,7 @@
 		# First, check that the bar that appears at stage 3 does not
 		# correspond to an individual blob anywhere in history
 		#
-		hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+		hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
 		git rev-list --objects --all >all_blobs &&
 		! grep $hash all_blobs &&
 
@@ -357,7 +380,7 @@
 		# First, check that the bar that appears at stage 3 does not
 		# correspond to an individual blob anywhere in history
 		#
-		hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+		hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
 		git rev-list --objects --all >all_blobs &&
 		! grep $hash all_blobs &&
 
@@ -607,8 +630,8 @@
 		# conflict entries do not appear as individual blobs anywhere
 		# in history.
 		#
-		hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
-		hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+		hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
+		hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
 		git rev-list --objects --all >all_blobs &&
 		! grep $hash1 all_blobs &&
 		! grep $hash2 all_blobs &&
@@ -864,7 +887,7 @@
 test_expect_success '--merge-base is incompatible with --stdin' '
 	test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
 
-	grep "^fatal: --merge-base is incompatible with --stdin" expect
+	grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
 '
 
 # specify merge-base as parent of branch2
@@ -922,4 +945,49 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--merge-base with tree OIDs' '
+	git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
+	git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
+	test_cmp with-commits with-trees
+'
+
+test_expect_success 'error out on missing tree objects' '
+	git init --bare missing-tree.git &&
+	git rev-list side3 >list &&
+	git rev-parse side3^: >>list &&
+	git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list &&
+	side3=$(git rev-parse side3) &&
+	test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err &&
+	test_grep "Could not read $(git rev-parse $side3:)" err &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing blob objects' '
+	echo 1 | git hash-object -w --stdin >blob1 &&
+	echo 2 | git hash-object -w --stdin >blob2 &&
+	echo 3 | git hash-object -w --stdin >blob3 &&
+	printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 &&
+	printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 &&
+	printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 &&
+	git init --bare missing-blob.git &&
+	cat blob1 blob3 tree1 tree2 tree3 |
+	git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing &&
+	test_must_fail git --git-dir=missing-blob.git >actual 2>err \
+		merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) &&
+	test_grep "unable to read blob object $(cat blob2)" err &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing commits as well' '
+	git init --bare missing-commit.git &&
+	git rev-list --objects side1 side3 >list-including-initial &&
+	grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
+	git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
+	side1=$(git rev-parse side1) &&
+	side3=$(git rev-parse side3) &&
+	test_must_fail git --git-dir=missing-commit.git \
+		merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
+	test_must_be_empty actual
+'
+
 test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 918a2fc..72b8d0f 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -124,6 +124,16 @@
 	EOF
 '
 
+test_expect_success '--list notices extra parameters' '
+	test_must_fail git archive --list blah &&
+	test_must_fail git archive --remote=. --list blah
+'
+
+test_expect_success 'end-of-options is correctly eaten' '
+	git archive --list --end-of-options &&
+	git archive --remote=. --list --end-of-options
+'
+
 test_expect_success 'populate workdir' '
 	mkdir a &&
 	echo simple textfile >a/a &&
@@ -185,6 +195,7 @@
 '
 
 check_tar b
+check_mtime b a/a 1117231200
 
 test_expect_success 'git archive --mtime' '
 	git archive --mtime=2002-02-02T02:02:02-0200 HEAD >with_mtime.tar
@@ -257,14 +268,6 @@
 	test_cmp_bin b.tar b5-nick.tar
 '
 
-test_expect_success 'validate file modification time' '
-	mkdir extract &&
-	"$TAR" xf b.tar -C extract a/a &&
-	test-tool chmtime --get extract/a/a >b.mtime &&
-	echo "1117231200" >expected.mtime &&
-	test_cmp expected.mtime b.mtime
-'
-
 test_expect_success 'git get-tar-commit-id' '
 	git get-tar-commit-id <b.tar >actual &&
 	git rev-parse HEAD >expect &&
@@ -433,6 +436,19 @@
 	test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
 '
 
+test_expect_success 'reject paths outside the current directory' '
+	test_must_fail git -C a/bin archive HEAD .. >/dev/null 2>err &&
+	grep "outside the current directory" err
+'
+
+test_expect_success 'allow pathspecs that resolve to the current directory' '
+	git -C a/bin archive -v HEAD ../bin >/dev/null 2>actual &&
+	cat >expect <<-\EOF &&
+	sh
+	EOF
+	test_cmp expect actual
+'
+
 # Pull the size and date of each entry in a tarfile using the system tar.
 #
 # We'll pull out only the year from the date; that avoids any question of
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index 04d300e..eaf959d 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -33,6 +33,13 @@
 	echo ignored-by-tree.d export-ignore >>.gitattributes &&
 	git add ignored-by-tree ignored-by-tree.d .gitattributes &&
 
+	mkdir subdir &&
+	>subdir/included &&
+	>subdir/ignored-by-subtree &&
+	>subdir/ignored-by-tree &&
+	echo ignored-by-subtree export-ignore >subdir/.gitattributes &&
+	git add subdir &&
+
 	echo ignored by worktree >ignored-by-worktree &&
 	echo ignored-by-worktree export-ignore >.gitattributes &&
 	git add ignored-by-worktree &&
@@ -93,6 +100,15 @@
 test_expect_missing	archive-pathspec-wildcard/excluded-by-pathspec.d
 test_expect_missing	archive-pathspec-wildcard/excluded-by-pathspec.d/file
 
+test_expect_success 'git -C subdir archive' '
+	git -C subdir archive HEAD >archive-subdir.tar &&
+	extract_tar_to_dir archive-subdir
+'
+
+test_expect_exists	archive-subdir/included
+test_expect_missing	archive-subdir/ignored-by-subtree
+test_expect_missing	archive-subdir/ignored-by-tree
+
 test_expect_success 'git archive with worktree attributes' '
 	git archive --worktree-attributes HEAD >worktree.tar &&
 	(mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
@@ -122,7 +138,7 @@
 '
 
 test_expect_missing	bare-worktree/ignored
-test_expect_exists	bare-worktree/ignored-by-tree
+test_expect_missing	bare-worktree/ignored-by-tree
 test_expect_exists	bare-worktree/ignored-by-worktree
 
 test_expect_success 'export-subst' '
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index fc499cd..961c6aa 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -239,4 +239,38 @@
 check_added with_untracked2 untracked one/untracked
 check_added with_untracked2 untracked two/untracked
 
+# Test remote archive over HTTP protocol.
+#
+# Note: this should be the last part of this test suite, because
+# by including lib-httpd.sh, the test may end early if httpd tests
+# should not be run.
+#
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success "setup for HTTP protocol" '
+	cp -R bare.git "$HTTPD_DOCUMENT_ROOT_PATH/bare.git" &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/bare.git" \
+		config http.uploadpack true &&
+	set_askpass user@host pass@host
+'
+
+setup_askpass_helper
+
+test_expect_success 'remote archive does not work with protocol v1' '
+	test_must_fail git -c protocol.version=1 archive \
+		--remote="$HTTPD_URL/auth/smart/bare.git" \
+		--output=remote-http.zip HEAD >actual 2>&1 &&
+	cat >expect <<-EOF &&
+	fatal: can${SQ}t connect to subservice git-upload-archive
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'archive remote http repository' '
+	git archive --remote="$HTTPD_URL/auth/smart/bare.git" \
+		--output=remote-http.zip HEAD &&
+	test_cmp_bin d.zip remote-http.zip
+'
+
 test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index db11cab..c8d0655 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -70,7 +70,7 @@
 
 	git mailsplit -d3 -o. "$DATA/nul-plain" &&
 	test_cmp "$DATA/nul-plain" 001 &&
-	(cat 001 | git mailinfo msg patch) &&
+	git mailinfo msg patch <001 &&
 	test_line_count = 4 patch
 
 '
@@ -268,4 +268,26 @@
 	test_must_be_empty quoted-cr/0002.err
 '
 
+test_expect_success 'from line with unterminated quoted string' '
+	echo "From: bob \"unterminated string smith <bob@example.com>" >in &&
+	git mailinfo /dev/null /dev/null <in >actual &&
+	cat >expect <<-\EOF &&
+	Author: bob unterminated string smith
+	Email: bob@example.com
+
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'from line with unterminated comment' '
+	echo "From: bob (unterminated comment smith <bob@example.com>" >in &&
+	git mailinfo /dev/null /dev/null <in >actual &&
+	cat >expect <<-\EOF &&
+	Author: bob (unterminated comment smith
+	Email: bob@example.com
+
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t5100/comment.expect b/t/t5100/comment.expect
index 7228177..bd71956 100644
--- a/t/t5100/comment.expect
+++ b/t/t5100/comment.expect
@@ -1,4 +1,4 @@
-Author: A U Thor (this is (really) a comment (honestly))
+Author: (this is (really) a "comment" (honestly)) A U Thor
 Email: somebody@example.com
 Subject: testing comments
 Date: Sun, 25 May 2008 00:38:18 -0700
diff --git a/t/t5100/comment.in b/t/t5100/comment.in
index c53a192..0b7e903 100644
--- a/t/t5100/comment.in
+++ b/t/t5100/comment.in
@@ -1,5 +1,5 @@
 From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
-From: "A U Thor" <somebody@example.com> (this is \(really\) a comment (honestly))
+From: (this is \(really\) a "comment" (honestly)) "A U Thor" <somebody@example.com>
 Date: Sun, 25 May 2008 00:38:18 -0700
 Subject: [PATCH] testing comments
 
diff --git a/t/t5100/msg0002 b/t/t5100/msg0002
index e2546ec..1089382 100644
--- a/t/t5100/msg0002
+++ b/t/t5100/msg0002
@@ -3,7 +3,7 @@
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
diff --git a/t/t5100/msg0003 b/t/t5100/msg0003
index 1ac6810..3402b53 100644
--- a/t/t5100/msg0003
+++ b/t/t5100/msg0003
@@ -3,7 +3,7 @@
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
diff --git a/t/t5100/msg0012--message-id b/t/t5100/msg0012--message-id
index 376e26e..4448295 100644
--- a/t/t5100/msg0012--message-id
+++ b/t/t5100/msg0012--message-id
@@ -5,4 +5,4 @@
 python-docutils. В то время как сам rest2web не нужен.
 
 Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
diff --git a/t/t5100/quoted-cr.mbox b/t/t5100/quoted-cr.mbox
index 909021b..a529d4d 100644
--- a/t/t5100/quoted-cr.mbox
+++ b/t/t5100/quoted-cr.mbox
@@ -3,7 +3,7 @@
 To: list@example.org
 Subject: [PATCH v2] sample
 Date: Mon,  3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
 Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: base64
 
@@ -27,7 +27,7 @@
 To: list@example.org
 Subject: [PATCH v2] sample
 Date: Mon,  3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id2@example.com>
+Message-ID: <msg-id2@example.com>
 Content-Type: text/plain; charset="utf-8"
 Content-Transfer-Encoding: base64
 
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index 6d4d0e4..4a54ee5 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -35,7 +35,7 @@
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
@@ -78,7 +78,7 @@
 
 From: Nit Picker <nit.picker@example.net>
 Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
 
 Hopefully this would fix the problem stated there.
 
@@ -508,7 +508,7 @@
 From: Dmitriy Blinov <bda@mnsspb.ru>
 To: navy-patches@dinar.mns.mnsspb.ru
 Date: Wed, 12 Nov 2008 17:54:41 +0300
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
 X-Mailer: git-send-email 1.5.6.5
 MIME-Version: 1.0
 Content-Type: text/plain;
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index f8a0f30..61e2be2 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -208,7 +208,7 @@
 '
 
 test_expect_success 'unpack with OFS_DELTA (core.fsyncmethod=batch)' '
-       check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
+	check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
 '
 
 test_expect_success 'compare delta flavors' '
@@ -263,97 +263,97 @@
 	)
 '
 
-test_expect_success \
-    'verify pack' \
-    'git verify-pack	test-1-${packname_1}.idx \
-			test-2-${packname_2}.idx \
-			test-3-${packname_3}.idx'
+test_expect_success 'verify pack' '
+	git verify-pack test-1-${packname_1}.idx \
+		test-2-${packname_2}.idx \
+		test-3-${packname_3}.idx
+'
 
-test_expect_success \
-    'verify pack -v' \
-    'git verify-pack -v	test-1-${packname_1}.idx \
-			test-2-${packname_2}.idx \
-			test-3-${packname_3}.idx'
+test_expect_success 'verify pack -v' '
+	git verify-pack -v test-1-${packname_1}.idx \
+		test-2-${packname_2}.idx \
+		test-3-${packname_3}.idx
+'
 
-test_expect_success \
-    'verify-pack catches mismatched .idx and .pack files' \
-    'cat test-1-${packname_1}.idx >test-3.idx &&
-     cat test-2-${packname_2}.pack >test-3.pack &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
+test_expect_success 'verify-pack catches mismatched .idx and .pack files' '
+	cat test-1-${packname_1}.idx >test-3.idx &&
+	cat test-2-${packname_2}.pack >test-3.pack &&
+	if git verify-pack test-3.idx
+	then false
+	else :;
+	fi
+'
 
-test_expect_success \
-    'verify-pack catches a corrupted pack signature' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
+test_expect_success 'verify-pack catches a corrupted pack signature' '
+	cat test-1-${packname_1}.pack >test-3.pack &&
+	echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+	if git verify-pack test-3.idx
+	then false
+	else :;
+	fi
+'
 
-test_expect_success \
-    'verify-pack catches a corrupted pack version' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
+test_expect_success 'verify-pack catches a corrupted pack version' '
+	cat test-1-${packname_1}.pack >test-3.pack &&
+	echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+	if git verify-pack test-3.idx
+	then false
+	else :;
+	fi
+'
 
-test_expect_success \
-    'verify-pack catches a corrupted type/size of the 1st packed object data' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
-     if git verify-pack test-3.idx
-     then false
-     else :;
-     fi'
+test_expect_success 'verify-pack catches a corrupted type/size of the 1st packed object data' '
+	cat test-1-${packname_1}.pack >test-3.pack &&
+	echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+	if git verify-pack test-3.idx
+	then false
+	else :;
+	fi
+'
 
-test_expect_success \
-    'verify-pack catches a corrupted sum of the index file itself' \
-    'l=$(wc -c <test-3.idx) &&
-     l=$(expr $l - 20) &&
-     cat test-1-${packname_1}.pack >test-3.pack &&
-     printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
-     if git verify-pack test-3.pack
-     then false
-     else :;
-     fi'
+test_expect_success 'verify-pack catches a corrupted sum of the index file itself' '
+	l=$(wc -c <test-3.idx) &&
+	l=$(expr $l - 20) &&
+	cat test-1-${packname_1}.pack >test-3.pack &&
+	printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+	if git verify-pack test-3.pack
+	then false
+	else :;
+	fi
+'
 
-test_expect_success \
-    'build pack index for an existing pack' \
-    'cat test-1-${packname_1}.pack >test-3.pack &&
-     git index-pack -o tmp.idx test-3.pack &&
-     cmp tmp.idx test-1-${packname_1}.idx &&
+test_expect_success 'build pack index for an existing pack' '
+	cat test-1-${packname_1}.pack >test-3.pack &&
+	git index-pack -o tmp.idx test-3.pack &&
+	cmp tmp.idx test-1-${packname_1}.idx &&
 
-     git index-pack --promisor=message test-3.pack &&
-     cmp test-3.idx test-1-${packname_1}.idx &&
-     echo message >expect &&
-     test_cmp expect test-3.promisor &&
+	git index-pack --promisor=message test-3.pack &&
+	cmp test-3.idx test-1-${packname_1}.idx &&
+	echo message >expect &&
+	test_cmp expect test-3.promisor &&
 
-     cat test-2-${packname_2}.pack >test-3.pack &&
-     git index-pack -o tmp.idx test-2-${packname_2}.pack &&
-     cmp tmp.idx test-2-${packname_2}.idx &&
+	cat test-2-${packname_2}.pack >test-3.pack &&
+	git index-pack -o tmp.idx test-2-${packname_2}.pack &&
+	cmp tmp.idx test-2-${packname_2}.idx &&
 
-     git index-pack test-3.pack &&
-     cmp test-3.idx test-2-${packname_2}.idx &&
+	git index-pack test-3.pack &&
+	cmp test-3.idx test-2-${packname_2}.idx &&
 
-     cat test-3-${packname_3}.pack >test-3.pack &&
-     git index-pack -o tmp.idx test-3-${packname_3}.pack &&
-     cmp tmp.idx test-3-${packname_3}.idx &&
+	cat test-3-${packname_3}.pack >test-3.pack &&
+	git index-pack -o tmp.idx test-3-${packname_3}.pack &&
+	cmp tmp.idx test-3-${packname_3}.idx &&
 
-     git index-pack test-3.pack &&
-     cmp test-3.idx test-3-${packname_3}.idx &&
+	git index-pack test-3.pack &&
+	cmp test-3.idx test-3-${packname_3}.idx &&
 
-     cat test-1-${packname_1}.pack >test-4.pack &&
-     rm -f test-4.keep &&
-     git index-pack --keep=why test-4.pack &&
-     cmp test-1-${packname_1}.idx test-4.idx &&
-     test -f test-4.keep &&
+	cat test-1-${packname_1}.pack >test-4.pack &&
+	rm -f test-4.keep &&
+	git index-pack --keep=why test-4.pack &&
+	cmp test-1-${packname_1}.idx test-4.idx &&
+	test -f test-4.keep &&
 
-     :'
+	:
+'
 
 test_expect_success 'unpacking with --strict' '
 
@@ -441,6 +441,47 @@
 	)
 '
 
+test_expect_success 'setup for --strict and --fsck-objects downgrading fsck msgs' '
+	git init strict &&
+	(
+		cd strict &&
+		test_commit first hello &&
+		cat >commit <<-EOF &&
+		tree $(git rev-parse HEAD^{tree})
+		parent $(git rev-parse HEAD)
+		author A U Thor
+		committer A U Thor
+
+		commit: this is a commit with bad emails
+
+		EOF
+		git hash-object --literally -t commit -w --stdin <commit >commit_list &&
+		git pack-objects test <commit_list >pack-name
+	)
+'
+
+test_with_bad_commit () {
+	must_fail_arg="$1" &&
+	must_pass_arg="$2" &&
+	(
+		cd strict &&
+		test_must_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack" &&
+		git index-pack "$must_pass_arg" "test-$(cat pack-name).pack"
+	)
+}
+
+test_expect_success 'index-pack with --strict downgrading fsck msgs' '
+	test_with_bad_commit --strict --strict="missingEmail=ignore"
+'
+
+test_expect_success 'index-pack with --fsck-objects downgrading fsck msgs' '
+	test_with_bad_commit --fsck-objects --fsck-objects="missingEmail=ignore"
+'
+
+test_expect_success 'cleanup for --strict and --fsck-objects downgrading fsck msgs' '
+	rm -rf strict
+'
+
 test_expect_success 'honor pack.packSizeLimit' '
 	git config pack.packSizeLimit 3m &&
 	packname_10=$(git pack-objects test-10 <obj-list) &&
@@ -541,7 +582,7 @@
 	(
 		cd corrupt &&
 		test_must_fail git index-pack -o ../bad.idx ../test-3.pack 2>msg &&
-		test_i18ngrep "SHA1 COLLISION FOUND" msg
+		test_grep "SHA1 COLLISION FOUND" msg
 	)
 '
 
@@ -549,7 +590,7 @@
 	(
 		cd corrupt &&
 		test_must_fail git -c core.bigfilethreshold=1 index-pack -o ../bad.idx ../test-3.pack 2>msg &&
-		test_i18ngrep "SHA1 COLLISION FOUND" msg
+		test_grep "SHA1 COLLISION FOUND" msg
 	)
 '
 
@@ -589,141 +630,6 @@
 	test_line_count = 1 donelines
 '
 
-test_expect_success 'setup for --stdin-packs tests' '
-	git init stdin-packs &&
-	(
-		cd stdin-packs &&
-
-		test_commit A &&
-		test_commit B &&
-		test_commit C &&
-
-		for id in A B C
-		do
-			git pack-objects .git/objects/pack/pack-$id \
-				--incremental --revs <<-EOF || exit 1
-			refs/tags/$id
-			EOF
-		done &&
-
-		ls -la .git/objects/pack
-	)
-'
-
-test_expect_success '--stdin-packs with excluded packs' '
-	(
-		cd stdin-packs &&
-
-		PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
-		PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
-		PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-
-		git pack-objects test --stdin-packs <<-EOF &&
-		$PACK_A
-		^$PACK_B
-		$PACK_C
-		EOF
-
-		(
-			git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
-			git show-index <$(ls .git/objects/pack/pack-C-*.idx)
-		) >expect.raw &&
-		git show-index <$(ls test-*.idx) >actual.raw &&
-
-		cut -d" " -f2 <expect.raw | sort >expect &&
-		cut -d" " -f2 <actual.raw | sort >actual &&
-		test_cmp expect actual
-	)
-'
-
-test_expect_success '--stdin-packs is incompatible with --filter' '
-	(
-		cd stdin-packs &&
-		test_must_fail git pack-objects --stdin-packs --stdout \
-			--filter=blob:none </dev/null 2>err &&
-		test_i18ngrep "cannot use --filter with --stdin-packs" err
-	)
-'
-
-test_expect_success '--stdin-packs is incompatible with --revs' '
-	(
-		cd stdin-packs &&
-		test_must_fail git pack-objects --stdin-packs --revs out \
-			</dev/null 2>err &&
-		test_i18ngrep "cannot use internal rev list with --stdin-packs" err
-	)
-'
-
-test_expect_success '--stdin-packs with loose objects' '
-	(
-		cd stdin-packs &&
-
-		PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
-		PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
-		PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-
-		test_commit D && # loose
-
-		git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
-		$PACK_A
-		^$PACK_B
-		$PACK_C
-		EOF
-
-		(
-			git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
-			git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
-			git rev-list --objects --no-object-names \
-				refs/tags/C..refs/tags/D
-
-		) >expect.raw &&
-		ls -la . &&
-		git show-index <$(ls test2-*.idx) >actual.raw &&
-
-		cut -d" " -f2 <expect.raw | sort >expect &&
-		cut -d" " -f2 <actual.raw | sort >actual &&
-		test_cmp expect actual
-	)
-'
-
-test_expect_success '--stdin-packs with broken links' '
-	(
-		cd stdin-packs &&
-
-		# make an unreachable object with a bogus parent
-		git cat-file -p HEAD >commit &&
-		sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
-		git hash-object -w -t commit --stdin >in &&
-
-		git pack-objects .git/objects/pack/pack-D <in &&
-
-		PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
-		PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
-		PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
-		PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
-
-		git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
-		$PACK_A
-		^$PACK_B
-		$PACK_C
-		$PACK_D
-		EOF
-
-		(
-			git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
-			git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
-			git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
-			git rev-list --objects --no-object-names \
-				refs/tags/C..refs/tags/D
-		) >expect.raw &&
-		git show-index <$(ls test3-*.idx) >actual.raw &&
-
-		cut -d" " -f2 <expect.raw | sort >expect &&
-		cut -d" " -f2 <actual.raw | sort >actual &&
-		test_cmp expect actual
-	)
-'
-
 test_expect_success 'negative window clamps to 0' '
 	git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr &&
 	check_deltas stderr = 0
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 3ccaaeb..226490d 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -8,55 +8,55 @@
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'rm -f .git/index* &&
-     for i in a b c
-     do
-         echo $i >$i &&
-	 test-tool genrandom "$i" 32768 >>$i &&
-         git update-index --add $i || return 1
-     done &&
-     echo d >d && cat c >>d && git update-index --add d &&
-     tree=$(git write-tree) &&
-     commit1=$(git commit-tree $tree </dev/null) &&
-     git update-ref HEAD $commit1 &&
-     git repack -a -d &&
-     test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
-     pack1=$(ls .git/objects/pack/*.pack) &&
-     test -f "$pack1"'
+test_expect_success 'setup' '
+	rm -f .git/index* &&
+	for i in a b c
+	do
+	echo $i >$i &&
+	test-tool genrandom "$i" 32768 >>$i &&
+	git update-index --add $i || return 1
+	done &&
+	echo d >d && cat c >>d && git update-index --add d &&
+	tree=$(git write-tree) &&
+	commit1=$(git commit-tree $tree </dev/null) &&
+	git update-ref HEAD $commit1 &&
+	git repack -a -d &&
+	test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+	pack1=$(ls .git/objects/pack/*.pack) &&
+	test -f "$pack1"
+'
 
-test_expect_success \
-    'verify-pack -v, defaults' \
-    'git verify-pack -v "$pack1"'
+test_expect_success 'verify-pack -v, defaults' '
+	git verify-pack -v "$pack1"
+'
 
-test_expect_success \
-    'verify-pack -v, packedGitWindowSize == 1 page' \
-    'git config core.packedGitWindowSize 512 &&
-     git verify-pack -v "$pack1"'
+test_expect_success 'verify-pack -v, packedGitWindowSize == 1 page' '
+	git config core.packedGitWindowSize 512 &&
+	git verify-pack -v "$pack1"
+'
 
-test_expect_success \
-    'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' \
-    'git config core.packedGitWindowSize 512 &&
-     git config core.packedGitLimit 512 &&
-     git verify-pack -v "$pack1"'
+test_expect_success 'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' '
+	git config core.packedGitWindowSize 512 &&
+	git config core.packedGitLimit 512 &&
+	git verify-pack -v "$pack1"
+'
 
-test_expect_success \
-    'repack -a -d, packedGit{WindowSize,Limit} == 1 page' \
-    'git config core.packedGitWindowSize 512 &&
-     git config core.packedGitLimit 512 &&
-     commit2=$(git commit-tree $tree -p $commit1 </dev/null) &&
-     git update-ref HEAD $commit2 &&
-     git repack -a -d &&
-     test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
-     pack2=$(ls .git/objects/pack/*.pack) &&
-     test -f "$pack2" &&
-     test "$pack1" \!= "$pack2"'
+test_expect_success 'repack -a -d, packedGit{WindowSize,Limit} == 1 page' '
+	git config core.packedGitWindowSize 512 &&
+	git config core.packedGitLimit 512 &&
+	commit2=$(git commit-tree $tree -p $commit1 </dev/null) &&
+	git update-ref HEAD $commit2 &&
+	git repack -a -d &&
+	test "$(git count-objects)" = "0 objects, 0 kilobytes" &&
+	pack2=$(ls .git/objects/pack/*.pack) &&
+	test -f "$pack2" &&
+	test "$pack1" \!= "$pack2"
+'
 
-test_expect_success \
-    'verify-pack -v, defaults' \
-    'git config --unset core.packedGitWindowSize &&
-     git config --unset core.packedGitLimit &&
-     git verify-pack -v "$pack2"'
+test_expect_success 'verify-pack -v, defaults' '
+	git config --unset core.packedGitWindowSize &&
+	git config --unset core.packedGitLimit &&
+	git verify-pack -v "$pack2"
+'
 
 test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index f89809b..d88e6f1 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -282,8 +282,8 @@
 test_expect_success 'index-pack -v --stdin produces progress for both phases' '
 	pack=$(git pack-objects --all pack </dev/null) &&
 	GIT_PROGRESS_DELAY=0 git index-pack -v --stdin <pack-$pack.pack 2>err &&
-	test_i18ngrep "Receiving objects" err &&
-	test_i18ngrep "Resolving deltas" err
+	test_grep "Receiving objects" err &&
+	test_grep "Resolving deltas" err
 '
 
 test_expect_success 'too-large packs report the breach' '
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 2926e8d..61469ef 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -59,304 +59,304 @@
 
 printf '\0' > zero
 
-test_expect_success \
-    'initial setup validation' \
-    'create_test_files &&
-     create_new_pack &&
-     git prune-packed &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'initial setup validation' '
+	create_test_files &&
+	create_new_pack &&
+	git prune-packed &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'create corruption in header of first object' \
-    'do_corrupt_object $blob_1 0 < zero &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'create corruption in header of first object' '
+	do_corrupt_object $blob_1 0 < zero &&
+	test_must_fail git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_1 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_1 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and loose copy of first delta allows for partial recovery' \
-    'git prune-packed &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and loose copy of first delta allows for partial recovery' '
+	git prune-packed &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	test_must_fail git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'create corruption in data of first object' \
-    'create_new_pack &&
-     git prune-packed &&
-     chmod +w ${pack}.pack &&
-     perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'create corruption in data of first object' '
+	create_new_pack &&
+	git prune-packed &&
+	chmod +w ${pack}.pack &&
+	perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
+	test_must_fail git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_1 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_1 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and loose copy of second object allows for partial recovery' \
-    'git prune-packed &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and loose copy of second object allows for partial recovery' '
+	git prune-packed &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	test_must_fail git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'create corruption in header of first delta' \
-    'create_new_pack &&
-     git prune-packed &&
-     do_corrupt_object $blob_2 0 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'create corruption in header of first delta' '
+	create_new_pack &&
+	git prune-packed &&
+	do_corrupt_object $blob_2 0 < zero &&
+	git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and then a repack "clears" the corruption' '
+	do_repack &&
+	git prune-packed &&
+	git verify-pack ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'create corruption in data of first delta' \
-    'create_new_pack &&
-     git prune-packed &&
-     chmod +w ${pack}.pack &&
-     perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'create corruption in data of first delta' '
+	create_new_pack &&
+	git prune-packed &&
+	chmod +w ${pack}.pack &&
+	perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and then a repack "clears" the corruption' '
+	do_repack &&
+	git prune-packed &&
+	git verify-pack ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
-    'create_new_pack &&
-     git prune-packed &&
-     do_corrupt_object $blob_2 2 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' '
+	create_new_pack &&
+	git prune-packed &&
+	do_corrupt_object $blob_2 2 < zero &&
+	git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and then a repack "clears" the corruption' '
+	do_repack &&
+	git prune-packed &&
+	git verify-pack ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
-    'create_new_pack --delta-base-offset &&
-     git prune-packed &&
-     do_corrupt_object $blob_2 2 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' '
+	create_new_pack --delta-base-offset &&
+	git prune-packed &&
+	do_corrupt_object $blob_2 2 < zero &&
+	git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack --delta-base-offset &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and then a repack "clears" the corruption' '
+	do_repack --delta-base-offset &&
+	git prune-packed &&
+	git verify-pack ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
-    'create_new_pack --delta-base-offset &&
-     git prune-packed &&
-     printf "\001" | do_corrupt_object $blob_2 2 &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' '
+	create_new_pack --delta-base-offset &&
+	git prune-packed &&
+	printf "\001" | do_corrupt_object $blob_2 2 &&
+	git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_2 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_2 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack --delta-base-offset &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and then a repack "clears" the corruption' '
+	do_repack --delta-base-offset &&
+	git prune-packed &&
+	git verify-pack ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and a redundant pack allows for full recovery too' \
-    'do_corrupt_object $blob_2 2 < zero &&
-     git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null &&
-     mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_1 &&
-     git hash-object -t blob -w file_2 &&
-     printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
-     git prune-packed &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and a redundant pack allows for full recovery too' '
+	do_corrupt_object $blob_2 2 < zero &&
+	git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null &&
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_1 &&
+	git hash-object -t blob -w file_2 &&
+	printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
+	git prune-packed &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'corruption of delta base reference pointing to wrong object' \
-    'create_new_pack --delta-base-offset &&
-     git prune-packed &&
-     printf "\220\033" | do_corrupt_object $blob_3 2 &&
-     git cat-file blob $blob_1 >/dev/null &&
-     git cat-file blob $blob_2 >/dev/null &&
-     test_must_fail git cat-file blob $blob_3 >/dev/null'
+test_expect_success 'corruption of delta base reference pointing to wrong object' '
+	create_new_pack --delta-base-offset &&
+	git prune-packed &&
+	printf "\220\033" | do_corrupt_object $blob_3 2 &&
+	git cat-file blob $blob_1 >/dev/null &&
+	git cat-file blob $blob_2 >/dev/null &&
+	test_must_fail git cat-file blob $blob_3 >/dev/null
+'
 
-test_expect_success \
-    '... but having a loose copy allows for full recovery' \
-    'mv ${pack}.idx tmp &&
-     git hash-object -t blob -w file_3 &&
-     mv tmp ${pack}.idx &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... but having a loose copy allows for full recovery' '
+	mv ${pack}.idx tmp &&
+	git hash-object -t blob -w file_3 &&
+	mv tmp ${pack}.idx &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    '... and then a repack "clears" the corruption' \
-    'do_repack --delta-base-offset --no-reuse-delta &&
-     git prune-packed &&
-     git verify-pack ${pack}.pack &&
-     git cat-file blob $blob_1 > /dev/null &&
-     git cat-file blob $blob_2 > /dev/null &&
-     git cat-file blob $blob_3 > /dev/null'
+test_expect_success '... and then a repack "clears" the corruption' '
+	do_repack --delta-base-offset --no-reuse-delta &&
+	git prune-packed &&
+	git verify-pack ${pack}.pack &&
+	git cat-file blob $blob_1 > /dev/null &&
+	git cat-file blob $blob_2 > /dev/null &&
+	git cat-file blob $blob_3 > /dev/null
+'
 
-test_expect_success \
-    'corrupting header to have too small output buffer fails unpack' \
-    'create_new_pack &&
-     git prune-packed &&
-     printf "\262\001" | do_corrupt_object $blob_1 0 &&
-     test_must_fail git cat-file blob $blob_1 > /dev/null &&
-     test_must_fail git cat-file blob $blob_2 > /dev/null &&
-     test_must_fail git cat-file blob $blob_3 > /dev/null'
+test_expect_success 'corrupting header to have too small output buffer fails unpack' '
+	create_new_pack &&
+	git prune-packed &&
+	printf "\262\001" | do_corrupt_object $blob_1 0 &&
+	test_must_fail git cat-file blob $blob_1 > /dev/null &&
+	test_must_fail git cat-file blob $blob_2 > /dev/null &&
+	test_must_fail git cat-file blob $blob_3 > /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \1 - one literal byte (X)
-test_expect_success \
-    'apply good minimal delta' \
-    'printf "\0\1\1X" > minimal_delta &&
-     test-tool delta -p /dev/null minimal_delta /dev/null'
+test_expect_success 'apply good minimal delta' '
+	printf "\0\1\1X" > minimal_delta &&
+	test-tool delta -p /dev/null minimal_delta /dev/null
+'
 
 # \0 - empty base
 # \1 - 1 byte in result
 # \2 - two literal bytes (one too many)
-test_expect_success \
-    'apply delta with too many literal bytes' \
-    'printf "\0\1\2XX" > too_big_literal &&
-     test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null'
+test_expect_success 'apply delta with too many literal bytes' '
+	printf "\0\1\2XX" > too_big_literal &&
+	test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null
+'
 
 # \4 - four bytes in base
 # \1 - one byte in result
 # \221 - copy, one byte offset, one byte size
 #   \0 - copy from offset 0
 #   \2 - copy two bytes (one too many)
-test_expect_success \
-    'apply delta with too many copied bytes' \
-    'printf "\4\1\221\0\2" > too_big_copy &&
-     printf base >base &&
-     test_must_fail test-tool delta -p base too_big_copy /dev/null'
+test_expect_success 'apply delta with too many copied bytes' '
+	printf "\4\1\221\0\2" > too_big_copy &&
+	printf base >base &&
+	test_must_fail test-tool delta -p base too_big_copy /dev/null
+'
 
 # \0 - empty base
 # \2 - two bytes in result
 # \2 - two literal bytes (we are short one)
-test_expect_success \
-    'apply delta with too few literal bytes' \
-    'printf "\0\2\2X" > truncated_delta &&
-     test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null'
+test_expect_success 'apply delta with too few literal bytes' '
+	printf "\0\2\2X" > truncated_delta &&
+	test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \221 - copy, one byte offset, one byte size
 #   \0 - copy from offset 0
 #   \1 - copy one byte (we are short one)
-test_expect_success \
-    'apply delta with too few bytes in base' \
-    'printf "\0\1\221\0\1" > truncated_base &&
-     test_must_fail test-tool delta -p /dev/null truncated_base /dev/null'
+test_expect_success 'apply delta with too few bytes in base' '
+	printf "\0\1\221\0\1" > truncated_base &&
+	test_must_fail test-tool delta -p /dev/null truncated_base /dev/null
+'
 
 # \4 - four bytes in base
 # \2 - two bytes in result
@@ -366,20 +366,20 @@
 #
 # Note that the literal byte is necessary to get past the uninteresting minimum
 # delta size check.
-test_expect_success \
-    'apply delta with truncated copy parameters' \
-    'printf "\4\2\1X\221" > truncated_copy_delta &&
-     printf base >base &&
-     test_must_fail test-tool delta -p base truncated_copy_delta /dev/null'
+test_expect_success 'apply delta with truncated copy parameters' '
+	printf "\4\2\1X\221" > truncated_copy_delta &&
+	printf base >base &&
+	test_must_fail test-tool delta -p base truncated_copy_delta /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \1 - one literal byte (X)
 # \1 - trailing garbage command
-test_expect_success \
-    'apply delta with trailing garbage literal' \
-    'printf "\0\1\1X\1" > tail_garbage_literal &&
-     test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null'
+test_expect_success 'apply delta with trailing garbage literal' '
+	printf "\0\1\1X\1" > tail_garbage_literal &&
+	test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null
+'
 
 # \4 - four bytes in base
 # \1 - one byte in result
@@ -387,19 +387,19 @@
 # \221 - copy, one byte offset, one byte size
 #   \0 - copy from offset 0
 #   \1 - copy 1 byte
-test_expect_success \
-    'apply delta with trailing garbage copy' \
-    'printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
-     printf base >base &&
-     test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null'
+test_expect_success 'apply delta with trailing garbage copy' '
+	printf "\4\1\1X\221\0\1" > tail_garbage_copy &&
+	printf base >base &&
+	test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null
+'
 
 # \0 - empty base
 # \1 - one byte in result
 # \1 - one literal byte (X)
 # \0 - bogus opcode
-test_expect_success \
-    'apply delta with trailing garbage opcode' \
-    'printf "\0\1\1X\0" > tail_garbage_opcode &&
-     test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null'
+test_expect_success 'apply delta with trailing garbage opcode' '
+	printf "\0\1\1X\0" > tail_garbage_opcode &&
+	test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null
+'
 
 test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index d65a5f9..1f1f664 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -16,7 +16,7 @@
 	before=$(git count-objects | sed "s/ .*//") &&
 	BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
 	BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
-	verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
 	test_path_is_file $BLOB_FILE &&
 	test-tool chmtime =+0 $BLOB_FILE
 }
@@ -51,34 +51,42 @@
 test_expect_success 'prune --expire' '
 	add_blob &&
 	git prune --expire=1.hour.ago &&
-	verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
 	test_path_is_file $BLOB_FILE &&
 	test-tool chmtime =-86500 $BLOB_FILE &&
 	git prune --expire 1.day &&
-	verbose test $before = $(git count-objects | sed "s/ .*//") &&
+	test $before = $(git count-objects | sed "s/ .*//") &&
 	test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc: implicit prune --expire' '
 	add_blob &&
 	test-tool chmtime =-$((2*$week-30)) $BLOB_FILE &&
-	git gc &&
-	verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+	git gc --no-cruft &&
+	test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
 	test_path_is_file $BLOB_FILE &&
 	test-tool chmtime =-$((2*$week+1)) $BLOB_FILE &&
-	git gc &&
-	verbose test $before = $(git count-objects | sed "s/ .*//") &&
+	git gc --no-cruft &&
+	test $before = $(git count-objects | sed "s/ .*//") &&
 	test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
-	git config gc.pruneExpire invalid &&
-	test_must_fail git gc
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	>repo/.git/config &&
+	git -C repo config gc.pruneExpire invalid &&
+	cat >expect <<-\EOF &&
+	error: Invalid gc.pruneexpire: '\''invalid'\''
+	fatal: bad config variable '\''gc.pruneexpire'\'' in file '\''.git/config'\'' at line 2
+	EOF
+	test_must_fail git -C repo gc 2>actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'gc: start with ok gc.pruneExpire' '
 	git config gc.pruneExpire 2.days.ago &&
-	git gc
+	git gc --no-cruft
 '
 
 test_expect_success 'prune: prune nonsense parameters' '
@@ -129,44 +137,44 @@
 	add_blob &&
 	test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
 	git config gc.pruneExpire 2.days.ago &&
-	git gc --no-prune &&
-	verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+	git gc --no-prune --no-cruft &&
+	test 1 = $(git count-objects | sed "s/ .*//") &&
 	test_path_is_file $BLOB_FILE
 '
 
 test_expect_success 'gc respects gc.pruneExpire' '
 	git config gc.pruneExpire 5002.days.ago &&
-	git gc &&
+	git gc --no-cruft &&
 	test_path_is_file $BLOB_FILE &&
 	git config gc.pruneExpire 5000.days.ago &&
-	git gc &&
+	git gc --no-cruft &&
 	test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc --prune=<date>' '
 	add_blob &&
 	test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
-	git gc --prune=5002.days.ago &&
+	git gc --prune=5002.days.ago --no-cruft &&
 	test_path_is_file $BLOB_FILE &&
-	git gc --prune=5000.days.ago &&
+	git gc --prune=5000.days.ago --no-cruft &&
 	test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc --prune=never' '
 	add_blob &&
-	git gc --prune=never &&
+	git gc --prune=never --no-cruft &&
 	test_path_is_file $BLOB_FILE &&
-	git gc --prune=now &&
+	git gc --prune=now --no-cruft &&
 	test_path_is_missing $BLOB_FILE
 '
 
 test_expect_success 'gc respects gc.pruneExpire=never' '
 	git config gc.pruneExpire never &&
 	add_blob &&
-	git gc &&
+	git gc --no-cruft &&
 	test_path_is_file $BLOB_FILE &&
 	git config gc.pruneExpire now &&
-	git gc &&
+	git gc --no-cruft &&
 	test_path_is_missing $BLOB_FILE
 '
 
@@ -184,10 +192,10 @@
 	git clone --no-hardlinks . aclone &&
 	(
 		cd aclone &&
-		verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+		test 1 = $(git count-objects | sed "s/ .*//") &&
 		test_path_is_file $BLOB_FILE &&
-		git gc --prune &&
-		verbose test 0 = $(git count-objects | sed "s/ .*//") &&
+		git gc --prune --no-cruft &&
+		test 0 = $(git count-objects | sed "s/ .*//") &&
 		test_path_is_missing $BLOB_FILE
 	)
 '
@@ -229,7 +237,7 @@
 	>.git/objects/pack/fake2.keep &&
 	>.git/objects/pack/fake2.idx &&
 	>.git/objects/pack/fake3.keep &&
-	git gc &&
+	git gc --no-cruft &&
 	git count-objects -v 2>stderr &&
 	grep "^warning:" stderr | sort >actual &&
 	cat >expected <<\EOF &&
@@ -310,10 +318,10 @@
 
 test_expect_success 'prune: handle expire option correctly' '
 	test_must_fail git prune --expire 2>error &&
-	test_i18ngrep "requires a value" error &&
+	test_grep "requires a value" error &&
 
 	test_must_fail git prune --expire=nyah 2>error &&
-	test_i18ngrep "malformed expiration" error &&
+	test_grep "malformed expiration" error &&
 
 	git prune --no-expire
 '
@@ -342,4 +350,18 @@
 	test_must_fail git cat-file -e $to_drop
 '
 
+test_expect_success 'gc.recentObjectsHook' '
+	add_blob &&
+	test-tool chmtime =-86500 $BLOB_FILE &&
+
+	write_script precious-objects <<-EOF &&
+	echo $BLOB
+	EOF
+	test_config gc.recentObjectsHook ./precious-objects &&
+
+	git prune --expire=now &&
+
+	git cat-file -p $BLOB
+'
+
 test_done
diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh
index 846c5ca..0d50c6b 100755
--- a/t/t5306-pack-nobase.sh
+++ b/t/t5306-pack-nobase.sh
@@ -12,18 +12,17 @@
 
 # Create A-B chain
 #
-test_expect_success \
-    'setup base' \
-    'test_write_lines a b c d e f g h i >text &&
-     echo side >side &&
-     git update-index --add text side &&
-     A=$(echo A | git commit-tree $(git write-tree)) &&
+test_expect_success 'setup base' '
+	test_write_lines a b c d e f g h i >text &&
+	echo side >side &&
+	git update-index --add text side &&
+	A=$(echo A | git commit-tree $(git write-tree)) &&
 
-     echo m >>text &&
-     git update-index text &&
-     B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
-     git update-ref HEAD $B
-    '
+	echo m >>text &&
+	git update-index text &&
+	B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
+	git update-ref HEAD $B
+'
 
 # Create repository with C whose parent is B.
 # Repository contains C, C^{tree}, C:text, B, B^{tree}.
@@ -31,52 +30,49 @@
 # Repository is missing A (parent of B).
 # Repository is missing A:side.
 #
-test_expect_success \
-    'setup patch_clone' \
-    'base_objects=$(pwd)/.git/objects &&
-     (mkdir patch_clone &&
-      cd patch_clone &&
-      git init &&
-      echo "$base_objects" >.git/objects/info/alternates &&
-      echo q >>text &&
-      git read-tree $B &&
-      git update-index text &&
-      git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
-      rm .git/objects/info/alternates &&
+test_expect_success 'setup patch_clone' '
+	base_objects=$(pwd)/.git/objects &&
+	(mkdir patch_clone &&
+	cd patch_clone &&
+	git init &&
+	echo "$base_objects" >.git/objects/info/alternates &&
+	echo q >>text &&
+	git read-tree $B &&
+	git update-index text &&
+	git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
+	rm .git/objects/info/alternates &&
 
-      git --git-dir=../.git cat-file commit $B |
-      git hash-object -t commit -w --stdin &&
+	git --git-dir=../.git cat-file commit $B |
+	git hash-object -t commit -w --stdin &&
 
-      git --git-dir=../.git cat-file tree "$B^{tree}" |
-      git hash-object -t tree -w --stdin
-     ) &&
-     C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
-    '
+	git --git-dir=../.git cat-file tree "$B^{tree}" |
+	git hash-object -t tree -w --stdin
+	) &&
+	C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
+'
 
 # Clone patch_clone indirectly by cloning base and fetching.
 #
-test_expect_success \
-    'indirectly clone patch_clone' \
-    '(mkdir user_clone &&
-      cd user_clone &&
-      git init &&
-      git pull ../.git &&
-      test $(git rev-parse HEAD) = $B &&
+test_expect_success 'indirectly clone patch_clone' '
+	(mkdir user_clone &&
+	 cd user_clone &&
+	 git init &&
+	 git pull ../.git &&
+	 test $(git rev-parse HEAD) = $B &&
 
-      git pull ../patch_clone/.git &&
-      test $(git rev-parse HEAD) = $C
-     )
-    '
+	 git pull ../patch_clone/.git &&
+	 test $(git rev-parse HEAD) = $C
+	)
+'
 
 # Cloning the patch_clone directly should fail.
 #
-test_expect_success \
-    'clone of patch_clone is incomplete' \
-    '(mkdir user_direct &&
-      cd user_direct &&
-      git init &&
-      test_must_fail git fetch ../patch_clone/.git
-     )
-    '
+test_expect_success 'clone of patch_clone is incomplete' '
+	(mkdir user_direct &&
+	 cd user_direct &&
+	 git init &&
+	 test_must_fail git fetch ../patch_clone/.git
+	)
+'
 
 test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 7d8dee4..d7fd713 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -9,6 +9,10 @@
 # their place.
 GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
 
+# Likewise, allow individual tests to control whether or not they use
+# the boundary-based traversal.
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
 objpath () {
 	echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
 }
@@ -267,7 +271,7 @@
 		mv -f $bitmap.tmp $bitmap &&
 		git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
 		test_cmp expect actual &&
-		test_i18ngrep corrupt.ewah.bitmap stderr
+		test_grep corrupt.ewah.bitmap stderr
 	'
 
 	test_expect_success 'truncated bitmap fails gracefully (cache)' '
@@ -280,7 +284,7 @@
 		mv -f $bitmap.tmp $bitmap &&
 		git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
 		test_cmp expect actual &&
-		test_i18ngrep corrupted.bitmap.index stderr
+		test_grep corrupted.bitmap.index stderr
 	'
 
 	# Create a state of history with these properties:
@@ -404,6 +408,26 @@
 		)
 	'
 
+	test_expect_success 'pack.preferBitmapTips' '
+		git init repo &&
+		test_when_finished "rm -rf repo" &&
+		(
+			cd repo &&
+			git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+			test_commit_bulk --message="%s" 103 &&
+
+			cat >>.git/config <<-\EOF &&
+			[pack]
+				preferBitmapTips
+			EOF
+			cat >expect <<-\EOF &&
+			error: missing value for '\''pack.preferbitmaptips'\''
+			EOF
+			git repack -adb 2>actual &&
+			test_cmp expect actual
+		)
+	'
+
 	test_expect_success 'complains about multiple pack bitmaps' '
 		rm -fr repo &&
 		git init repo &&
@@ -437,10 +461,17 @@
 
 test_bitmap_cases
 
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
+export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
+test_bitmap_cases
+
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
 test_expect_success 'incremental repack fails when bitmaps are requested' '
 	test_commit more-1 &&
 	test_must_fail git repack -d 2>err &&
-	test_i18ngrep "Incremental repacks are incompatible with bitmap" err
+	test_grep "Incremental repacks are incompatible with bitmap" err
 '
 
 test_expect_success 'incremental repack can disable bitmaps' '
@@ -448,6 +479,33 @@
 	git repack -d --no-write-bitmap-index
 '
 
+test_expect_success 'boundary-based traversal is used when requested' '
+	git repack -a -d --write-bitmap-index &&
+
+	for argv in \
+		"git -c pack.useBitmapBoundaryTraversal=true" \
+		"git -c feature.experimental=true" \
+		"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 git"
+	do
+		eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+			--use-bitmap-index second..other 2>perf" &&
+		grep "\"region_enter\".*\"label\":\"haves/boundary\"" perf ||
+			return 1
+	done &&
+
+	for argv in \
+		"git -c pack.useBitmapBoundaryTraversal=false" \
+		"git -c feature.experimental=true -c pack.useBitmapBoundaryTraversal=false" \
+		"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c pack.useBitmapBoundaryTraversal=true" \
+		"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c feature.experimental=true"
+	do
+		eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+			--use-bitmap-index second..other 2>perf" &&
+		grep "\"region_enter\".*\"label\":\"haves/classic\"" perf ||
+			return 1
+	done
+'
+
 test_bitmap_cases "pack.writeBitmapLookupTable"
 
 test_expect_success 'verify writing bitmap lookup table when enabled' '
@@ -466,7 +524,7 @@
 	mv -f $bitmap.tmp $bitmap &&
 	git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
 	test_cmp expect actual &&
-	test_i18ngrep corrupted.bitmap.index stderr
+	test_grep corrupted.bitmap.index stderr
 '
 
 test_done
diff --git a/t/t5311-pack-bitmaps-shallow.sh b/t/t5311-pack-bitmaps-shallow.sh
index 9dae60f..4fe71fe 100755
--- a/t/t5311-pack-bitmaps-shallow.sh
+++ b/t/t5311-pack-bitmaps-shallow.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check bitmap operation with shallow repositories'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # We want to create a situation where the shallow, grafted
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index 230cb38..d8d2e30 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -111,30 +111,4 @@
 	test_cmp expect actual
 '
 
-# we do not want to count on running pack-refs to
-# actually pack it, as it is perfectly reasonable to
-# skip processing a broken ref
-test_expect_success REFFILES 'create packed-refs file with broken ref' '
-	rm -f .git/refs/heads/main &&
-	cat >.git/packed-refs <<-EOF &&
-	$missing refs/heads/main
-	$recoverable refs/heads/other
-	EOF
-	echo $missing >expect &&
-	git rev-parse refs/heads/main >actual &&
-	test_cmp expect actual
-'
-
-test_expect_success REFFILES 'pack-refs does not silently delete broken packed ref' '
-	git pack-refs --all --prune &&
-	git rev-parse refs/heads/main >actual &&
-	test_cmp expect actual
-'
-
-test_expect_success REFFILES  'pack-refs does not drop broken refs during deletion' '
-	git update-ref -d refs/heads/other &&
-	git rev-parse refs/heads/main >actual &&
-	test_cmp expect actual
-'
-
 test_done
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index b26d476..79552d6 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -53,6 +53,14 @@
 	! grep blob verify_result
 '
 
+test_expect_success 'verify blob:none packfile without --stdout' '
+	git -C r1 pack-objects --revs --filter=blob:none mypackname >packhash <<-EOF &&
+	HEAD
+	EOF
+	git -C r1 verify-pack -v "mypackname-$(cat packhash).pack" >verify_result &&
+	! grep blob verify_result
+'
+
 test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
 	git -C r1 verify-pack -v ../all.pack >verify_result &&
 	grep -E "commit|tree" verify_result |
@@ -447,7 +455,7 @@
 	test_parse_ls_files_stage_oids <ls_files_result |
 	sort >expected &&
 
-	for id in `cat expected | sed "s|..|&/|"`
+	for id in `sed "s|..|&/|" expected`
 	do
 		rm r1/.git/objects/$id || return 1
 	done
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 049c5fc..a2b4442 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -2,6 +2,7 @@
 
 test_description='commit graph'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
 
@@ -24,12 +25,10 @@
 	test_cmp expect actual
 '
 
+objdir=".git/objects"
+
 test_expect_success 'setup full repo' '
-	mkdir full &&
-	cd "$TRASH_DIRECTORY/full" &&
-	git init &&
-	git config core.commitGraph true &&
-	objdir=".git/objects"
+	git init full
 '
 
 test_expect_success POSIXPERM 'tweak umask for modebit tests' '
@@ -37,31 +36,28 @@
 '
 
 test_expect_success 'verify graph with no graph file' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph verify
+	git -C full commit-graph verify
 '
 
 test_expect_success 'write graph with no packs' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write --object-dir $objdir &&
-	test_path_is_missing $objdir/info/commit-graph
+	git -C full commit-graph write --object-dir $objdir &&
+	test_path_is_missing full/$objdir/info/commit-graph
 '
 
 test_expect_success 'exit with correct error on bad input to --stdin-packs' '
-	cd "$TRASH_DIRECTORY/full" &&
 	echo doesnotexist >in &&
-	test_expect_code 1 git commit-graph write --stdin-packs <in 2>stderr &&
-	test_i18ngrep "error adding pack" stderr
+	test_expect_code 1 git -C full commit-graph write --stdin-packs \
+		<in 2>stderr &&
+	test_grep "error adding pack" stderr
 '
 
 test_expect_success 'create commits and repack' '
-	cd "$TRASH_DIRECTORY/full" &&
 	for i in $(test_seq 3)
 	do
-		test_commit $i &&
-		git branch commits/$i || return 1
+		test_commit -C full $i &&
+		git -C full branch commits/$i || return 1
 	done &&
-	git repack
+	git -C full repack
 '
 
 . "$TEST_DIRECTORY"/lib-commit-graph.sh
@@ -69,117 +65,106 @@
 graph_git_behavior 'no graph' full commits/3 commits/1
 
 test_expect_success 'exit with correct error on bad input to --stdin-commits' '
-	cd "$TRASH_DIRECTORY/full" &&
 	# invalid, non-hex OID
-	echo HEAD >in &&
-	test_expect_code 1 git commit-graph write --stdin-commits <in 2>stderr &&
-	test_i18ngrep "unexpected non-hex object ID: HEAD" stderr &&
+	echo HEAD | test_expect_code 1 git -C full commit-graph write \
+		--stdin-commits 2>stderr &&
+	test_grep "unexpected non-hex object ID: HEAD" stderr &&
 	# non-existent OID
-	echo $ZERO_OID >in &&
-	test_expect_code 1 git commit-graph write --stdin-commits <in 2>stderr &&
-	test_i18ngrep "invalid object" stderr &&
+	echo $ZERO_OID | test_expect_code 1 git -C full commit-graph write \
+		--stdin-commits 2>stderr &&
+	test_grep "invalid object" stderr &&
 	# valid commit and tree OID
-	git rev-parse HEAD HEAD^{tree} >in &&
-	git commit-graph write --stdin-commits <in &&
-	graph_read_expect 3 generation_data
+	git -C full rev-parse HEAD HEAD^{tree} >in &&
+	git -C full commit-graph write --stdin-commits <in &&
+	graph_read_expect -C full 3 generation_data
 '
 
 test_expect_success 'write graph' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "3" generation_data
+	git -C full commit-graph write &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 3 generation_data
 '
 
 test_expect_success POSIXPERM 'write graph has correct permissions' '
-	test_path_is_file $objdir/info/commit-graph &&
+	test_path_is_file full/$objdir/info/commit-graph &&
 	echo "-r--r--r--" >expect &&
-	test_modebits $objdir/info/commit-graph >actual &&
+	test_modebits full/$objdir/info/commit-graph >actual &&
 	test_cmp expect actual
 '
 
 graph_git_behavior 'graph exists' full commits/3 commits/1
 
 test_expect_success 'Add more commits' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git reset --hard commits/1 &&
+	git -C full reset --hard commits/1 &&
 	for i in $(test_seq 4 5)
 	do
-		test_commit $i &&
-		git branch commits/$i || return 1
+		test_commit -C full $i &&
+		git -C full branch commits/$i || return 1
 	done &&
-	git reset --hard commits/2 &&
+	git -C full reset --hard commits/2 &&
 	for i in $(test_seq 6 7)
 	do
-		test_commit $i &&
-		git branch commits/$i || return 1
+		test_commit -C full $i &&
+		git -C full branch commits/$i || return 1
 	done &&
-	git reset --hard commits/2 &&
-	git merge commits/4 &&
-	git branch merge/1 &&
-	git reset --hard commits/4 &&
-	git merge commits/6 &&
-	git branch merge/2 &&
-	git reset --hard commits/3 &&
-	git merge commits/5 commits/7 &&
-	git branch merge/3 &&
-	git repack
+	git -C full reset --hard commits/2 &&
+	git -C full merge commits/4 &&
+	git -C full branch merge/1 &&
+	git -C full reset --hard commits/4 &&
+	git -C full merge commits/6 &&
+	git -C full branch merge/2 &&
+	git -C full reset --hard commits/3 &&
+	git -C full merge commits/5 commits/7 &&
+	git -C full branch merge/3 &&
+	git -C full repack
 '
 
 test_expect_success 'commit-graph write progress off for redirected stderr' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write 2>err &&
+	git -C full commit-graph write 2>err &&
 	test_must_be_empty err
 '
 
 test_expect_success 'commit-graph write force progress on for stderr' '
-	cd "$TRASH_DIRECTORY/full" &&
-	GIT_PROGRESS_DELAY=0 git commit-graph write --progress 2>err &&
+	GIT_PROGRESS_DELAY=0 git -C full commit-graph write --progress 2>err &&
 	test_file_not_empty err
 '
 
 test_expect_success 'commit-graph write with the --no-progress option' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write --no-progress 2>err &&
+	git -C full commit-graph write --no-progress 2>err &&
 	test_must_be_empty err
 '
 
 test_expect_success 'commit-graph write --stdin-commits progress off for redirected stderr' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git rev-parse commits/5 >in &&
-	git commit-graph write --stdin-commits <in 2>err &&
+	git -C full rev-parse commits/5 >in &&
+	git -C full commit-graph write --stdin-commits <in 2>err &&
 	test_must_be_empty err
 '
 
 test_expect_success 'commit-graph write --stdin-commits force progress on for stderr' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git rev-parse commits/5 >in &&
-	GIT_PROGRESS_DELAY=0 git commit-graph write --stdin-commits --progress <in 2>err &&
-	test_i18ngrep "Collecting commits from input" err
+	git -C full rev-parse commits/5 >in &&
+	GIT_PROGRESS_DELAY=0 git -C full commit-graph write --stdin-commits \
+		--progress <in 2>err &&
+	test_grep "Collecting commits from input" err
 '
 
 test_expect_success 'commit-graph write --stdin-commits with the --no-progress option' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git rev-parse commits/5 >in &&
-	git commit-graph write --stdin-commits --no-progress <in 2>err &&
+	git -C full rev-parse commits/5 >in &&
+	git -C full commit-graph write --stdin-commits --no-progress <in 2>err &&
 	test_must_be_empty err
 '
 
 test_expect_success 'commit-graph verify progress off for redirected stderr' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph verify 2>err &&
+	git -C full commit-graph verify 2>err &&
 	test_must_be_empty err
 '
 
 test_expect_success 'commit-graph verify force progress on for stderr' '
-	cd "$TRASH_DIRECTORY/full" &&
-	GIT_PROGRESS_DELAY=0 git commit-graph verify --progress 2>err &&
+	GIT_PROGRESS_DELAY=0 git -C full commit-graph verify --progress 2>err &&
 	test_file_not_empty err
 '
 
 test_expect_success 'commit-graph verify with the --no-progress option' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph verify --no-progress 2>err &&
+	git -C full commit-graph verify --no-progress 2>err &&
 	test_must_be_empty err
 '
 
@@ -194,10 +179,9 @@
 # 1
 
 test_expect_success 'write graph with merges' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "10" "generation_data extra_edges"
+	git -C full commit-graph write &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 10 "generation_data extra_edges"
 '
 
 graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -205,12 +189,11 @@
 graph_git_behavior 'merge 2 vs 3' full merge/2 merge/3
 
 test_expect_success 'Add one more commit' '
-	cd "$TRASH_DIRECTORY/full" &&
-	test_commit 8 &&
-	git branch commits/8 &&
-	ls $objdir/pack | grep idx >existing-idx &&
-	git repack &&
-	ls $objdir/pack| grep idx | grep -v -f existing-idx >new-idx
+	test_commit -C full 8 &&
+	git -C full branch commits/8 &&
+	ls full/$objdir/pack | grep idx >existing-idx &&
+	git -C full repack &&
+	ls full/$objdir/pack| grep idx | grep -v -f existing-idx >new-idx
 '
 
 # Current graph structure:
@@ -229,114 +212,101 @@
 graph_git_behavior 'mixed mode, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'write graph with new commit' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "11" "generation_data extra_edges"
+	git -C full commit-graph write &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'full graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'write graph with nothing new' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "11" "generation_data extra_edges"
+	git -C full commit-graph write &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'cleared graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph from latest pack with closure' '
-	cd "$TRASH_DIRECTORY/full" &&
-	cat new-idx | git commit-graph write --stdin-packs &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "9" "generation_data extra_edges"
+	git -C full commit-graph write --stdin-packs <new-idx &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 9 "generation_data extra_edges"
 '
 
 graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'graph from pack, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph from commits with closure' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git tag -a -m "merge" tag/merge merge/2 &&
-	git rev-parse tag/merge >commits-in &&
-	git rev-parse merge/1 >>commits-in &&
-	cat commits-in | git commit-graph write --stdin-commits &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "6" "generation_data"
+	git -C full tag -a -m "merge" tag/merge merge/2 &&
+	git -C full rev-parse tag/merge >commits-in &&
+	git -C full rev-parse merge/1 >>commits-in &&
+	git -C full commit-graph write --stdin-commits <commits-in &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 6 "generation_data"
 '
 
 graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'graph from commits, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph from commits with append' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "10" "generation_data extra_edges"
+	git -C full rev-parse merge/3 >in &&
+	git -C full commit-graph write --stdin-commits --append <in &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 10 "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'build graph using --reachable' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit-graph write --reachable &&
-	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "11" "generation_data extra_edges"
+	git -C full commit-graph write --reachable &&
+	test_path_is_file full/$objdir/info/commit-graph &&
+	graph_read_expect -C full 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 
 test_expect_success 'setup bare repo' '
-	cd "$TRASH_DIRECTORY" &&
-	git clone --bare --no-local full bare &&
-	cd bare &&
-	git config core.commitGraph true &&
-	baredir="./objects"
+	git clone --bare --no-local full bare
 '
 
 graph_git_behavior 'bare repo, commit 8 vs merge 1' bare commits/8 merge/1
 graph_git_behavior 'bare repo, commit 8 vs merge 2' bare commits/8 merge/2
 
 test_expect_success 'write graph in bare repo' '
-	cd "$TRASH_DIRECTORY/bare" &&
-	git commit-graph write &&
-	test_path_is_file $baredir/info/commit-graph &&
-	graph_read_expect "11" "generation_data extra_edges"
+	git -C bare commit-graph write &&
+	test_path_is_file bare/objects/info/commit-graph &&
+	graph_read_expect -C bare 11 "generation_data extra_edges"
 '
 
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
 
 test_expect_success 'perform fast-forward merge in full repo' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git checkout -b merge-5-to-8 commits/5 &&
-	git merge commits/8 &&
-	git show-ref -s merge-5-to-8 >output &&
-	git show-ref -s commits/8 >expect &&
+	git -C full checkout -b merge-5-to-8 commits/5 &&
+	git -C full merge commits/8 &&
+	git -C full show-ref -s merge-5-to-8 >output &&
+	git -C full show-ref -s commits/8 >expect &&
 	test_cmp expect output
 '
 
 test_expect_success 'check that gc computes commit-graph' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git commit --allow-empty -m "blank" &&
-	git commit-graph write --reachable &&
-	cp $objdir/info/commit-graph commit-graph-before-gc &&
-	git reset --hard HEAD~1 &&
-	git config gc.writeCommitGraph true &&
-	git gc &&
-	cp $objdir/info/commit-graph commit-graph-after-gc &&
+	test_commit -C full --no-tag blank &&
+	git -C full commit-graph write --reachable &&
+	cp full/$objdir/info/commit-graph commit-graph-before-gc &&
+	git -C full reset --hard HEAD~1 &&
+	test_config -C full gc.writeCommitGraph true &&
+	git -C full gc &&
+	cp full/$objdir/info/commit-graph commit-graph-after-gc &&
 	! test_cmp_bin commit-graph-before-gc commit-graph-after-gc &&
-	git commit-graph write --reachable &&
-	test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
+	git -C full commit-graph write --reachable &&
+	test_cmp_bin commit-graph-after-gc full/$objdir/info/commit-graph
 '
 
 test_expect_success 'replace-objects invalidates commit-graph' '
-	cd "$TRASH_DIRECTORY" &&
 	test_when_finished rm -rf replace &&
 	git clone full replace &&
 	(
@@ -359,7 +329,6 @@
 '
 
 test_expect_success 'commit grafts invalidate commit-graph' '
-	cd "$TRASH_DIRECTORY" &&
 	test_when_finished rm -rf graft &&
 	git clone --template= full graft &&
 	(
@@ -384,7 +353,6 @@
 '
 
 test_expect_success 'replace-objects invalidates commit-graph' '
-	cd "$TRASH_DIRECTORY" &&
 	test_when_finished rm -rf shallow &&
 	git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
 	(
@@ -416,35 +384,36 @@
 		cd sha1 &&
 		mv ../cg-sha256 .git/objects/info/commit-graph &&
 		git log -1 2>err &&
-		test_i18ngrep "commit-graph hash version 2 does not match version 1" err
+		test_grep "commit-graph hash version 2 does not match version 1" err
 	) &&
 	(
 		cd sha256 &&
 		mv ../cg-sha1 .git/objects/info/commit-graph &&
 		git log -1 2>err &&
-		test_i18ngrep "commit-graph hash version 1 does not match version 2" err
+		test_grep "commit-graph hash version 1 does not match version 2" err
 	)
 '
 
 test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
-	cd "$TRASH_DIRECTORY/full" &&
 	UNIX_EPOCH_ZERO="@0 +0000" &&
 	FUTURE_DATE="@4147483646 +0000" &&
-	rm -f .git/objects/info/commit-graph &&
-	test_commit --date "$FUTURE_DATE" future-1 &&
-	test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
-	git commit-graph write --reachable &&
-	test_commit --date "$FUTURE_DATE" future-2 &&
-	test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
-	git commit-graph write --reachable --split=no-merge &&
-	test_commit extra &&
-	git commit-graph write --reachable --split=no-merge &&
-	git commit-graph write --reachable &&
-	graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
-	mv .git/objects/info/commit-graph commit-graph-upgraded &&
-	git commit-graph write --reachable &&
-	graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
-	test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+	rm -f full/.git/objects/info/commit-graph &&
+	test_commit -C full --date "$FUTURE_DATE" future-1 &&
+	test_commit -C full --date "$UNIX_EPOCH_ZERO" old-1 &&
+	git -C full commit-graph write --reachable &&
+	test_commit -C full --date "$FUTURE_DATE" future-2 &&
+	test_commit -C full --date "$UNIX_EPOCH_ZERO" old-2 &&
+	git -C full commit-graph write --reachable --split=no-merge &&
+	test_commit -C full extra &&
+	git -C full commit-graph write --reachable --split=no-merge &&
+	git -C full commit-graph write --reachable &&
+	graph_read_expect -C full 16 \
+		"generation_data generation_data_overflow extra_edges" &&
+	mv full/.git/objects/info/commit-graph commit-graph-upgraded &&
+	git -C full commit-graph write --reachable &&
+	graph_read_expect -C full 16 \
+		"generation_data generation_data_overflow extra_edges" &&
+	test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
 '
 
 # the verify tests below expect the commit-graph to contain
@@ -454,10 +423,11 @@
 # and the tests will likely break.
 
 test_expect_success 'git commit-graph verify' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git rev-parse commits/8 | git -c commitGraph.generationVersion=1 commit-graph write --stdin-commits &&
-	git commit-graph verify >output &&
-	graph_read_expect 9 extra_edges 1
+	git -C full rev-parse commits/8 >in &&
+	git -C full -c commitGraph.generationVersion=1 commit-graph write \
+		--stdin-commits <in &&
+	git -C full commit-graph verify >output &&
+	graph_read_expect -C full 9 extra_edges 1
 '
 
 NUM_COMMITS=9
@@ -481,39 +451,39 @@
 GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
 GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
 GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
+GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
 GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
 GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
 GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
 GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
 GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
 GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
+GRAPH_BYTE_COMMIT_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH))
 GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
-GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
 GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
 			     $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
 GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
 GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
 
 corrupt_graph_setup() {
-	cd "$TRASH_DIRECTORY/full" &&
-	test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
-	cp $objdir/info/commit-graph commit-graph-backup &&
-	chmod u+w $objdir/info/commit-graph
+	test_when_finished mv commit-graph-backup full/$objdir/info/commit-graph &&
+	cp full/$objdir/info/commit-graph commit-graph-backup &&
+	chmod u+w full/$objdir/info/commit-graph
 }
 
 corrupt_graph_verify() {
 	grepstr=$1
-	test_must_fail git commit-graph verify 2>test_err &&
+	test_must_fail git -C full commit-graph verify 2>test_err &&
 	grep -v "^+" test_err >err &&
-	test_i18ngrep "$grepstr" err &&
+	test_grep "$grepstr" err &&
 	if test "$2" != "no-copy"
 	then
-		cp $objdir/info/commit-graph commit-graph-pre-write-test
+		cp full/$objdir/info/commit-graph commit-graph-pre-write-test
 	fi &&
-	git status --short &&
-	GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git commit-graph write &&
-	chmod u+w $objdir/info/commit-graph &&
-	git commit-graph verify
+	git -C full status --short &&
+	GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git -C full commit-graph write &&
+	chmod u+w full/$objdir/info/commit-graph &&
+	git -C full commit-graph verify
 }
 
 # usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
@@ -527,24 +497,24 @@
 	data="${2:-\0}"
 	grepstr=$3
 	corrupt_graph_setup &&
-	orig_size=$(wc -c < $objdir/info/commit-graph) &&
+	orig_size=$(wc -c <full/$objdir/info/commit-graph) &&
 	zero_pos=${4:-${orig_size}} &&
-	printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
-	dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
-	test-tool genzeros $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
+	printf "$data" | dd of="full/$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
+	dd of="full/$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
+	test-tool genzeros $(($orig_size - $zero_pos)) >>"full/$objdir/info/commit-graph" &&
 	corrupt_graph_verify "$grepstr"
 
 }
 
 test_expect_success POSIXPERM,SANITY 'detect permission problem' '
 	corrupt_graph_setup &&
-	chmod 000 $objdir/info/commit-graph &&
+	chmod 000 full/$objdir/info/commit-graph &&
 	corrupt_graph_verify "Could not open" "no-copy"
 '
 
 test_expect_success 'detect too small' '
 	corrupt_graph_setup &&
-	echo "a small graph" >$objdir/info/commit-graph &&
+	echo "a small graph" >full/$objdir/info/commit-graph &&
 	corrupt_graph_verify "too small"
 '
 
@@ -570,17 +540,17 @@
 
 test_expect_success 'detect missing OID fanout chunk' '
 	corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
-		"missing the OID Fanout chunk"
+		"commit-graph required OID fanout chunk missing or corrupted"
 '
 
 test_expect_success 'detect missing OID lookup chunk' '
 	corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
-		"missing the OID Lookup chunk"
+		"commit-graph required OID lookup chunk missing or corrupted"
 '
 
 test_expect_success 'detect missing commit data chunk' '
 	corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
-		"missing the Commit Data chunk"
+		"commit-graph required commit data chunk missing or corrupted"
 '
 
 test_expect_success 'detect incorrect fanout' '
@@ -590,7 +560,7 @@
 
 test_expect_success 'detect incorrect fanout final value' '
 	corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
-		"fanout value"
+		"OID lookup chunk is the wrong size"
 '
 
 test_expect_success 'detect incorrect OID order' '
@@ -628,11 +598,6 @@
 		"generation for commit"
 '
 
-test_expect_success 'detect incorrect generation number' '
-	corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
-		"non-zero generation number"
-'
-
 test_expect_success 'detect incorrect commit date' '
 	corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
 		"commit date"
@@ -654,34 +619,51 @@
 		$GRAPH_CHUNK_LOOKUP_OFFSET
 '
 
+test_expect_success 'detect mixed generation numbers (non-zero to zero)' '
+	corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \
+		"both zero and non-zero generations"
+'
+
+test_expect_success 'detect mixed generation numbers (zero to non-zero)' '
+	corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \
+		"both zero and non-zero generations"
+'
+
 test_expect_success 'git fsck (checks commit-graph when config set to true)' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git fsck &&
+	git -C full fsck &&
 	corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
 		"incorrect checksum" &&
-	cp commit-graph-pre-write-test $objdir/info/commit-graph &&
-	test_must_fail git -c core.commitGraph=true fsck
+	cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+	test_must_fail git -C full -c core.commitGraph=true fsck
 '
 
 test_expect_success 'git fsck (ignores commit-graph when config set to false)' '
-	cd "$TRASH_DIRECTORY/full" &&
-	git fsck &&
+	git -C full fsck &&
 	corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
 		"incorrect checksum" &&
-	cp commit-graph-pre-write-test $objdir/info/commit-graph &&
-	git -c core.commitGraph=false fsck
+	cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+	git -C full -c core.commitGraph=false fsck
 '
 
 test_expect_success 'git fsck (checks commit-graph when config unset)' '
-	cd "$TRASH_DIRECTORY/full" &&
-	test_when_finished "git config core.commitGraph true" &&
+	test_when_finished "git -C full config core.commitGraph true" &&
 
-	git fsck &&
+	git -C full fsck &&
 	corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
 		"incorrect checksum" &&
-	test_unconfig core.commitGraph &&
-	cp commit-graph-pre-write-test $objdir/info/commit-graph &&
-	test_must_fail git fsck
+	test_unconfig -C full core.commitGraph &&
+	cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
+	test_must_fail git -C full fsck
+'
+
+test_expect_success 'git fsck shows commit-graph output with --progress' '
+	git -C "$TRASH_DIRECTORY/full" fsck --progress 2>err &&
+	grep "Verifying commits in commit graph" err
+'
+
+test_expect_success 'git fsck suppresses commit-graph output with --no-progress' '
+	git -C "$TRASH_DIRECTORY/full" fsck --no-progress 2>err &&
+	! grep "Verifying commits in commit graph" err
 '
 
 test_expect_success 'setup non-the_repository tests' '
@@ -739,7 +721,7 @@
 		git commit-tree -p "$broken" -m "good commit" "$empty" >good &&
 		test_must_fail git commit-graph write --stdin-commits \
 			<good 2>test_err &&
-		test_i18ngrep "unable to parse commit" test_err
+		test_grep "unable to parse commit" test_err
 	)
 '
 
@@ -760,7 +742,7 @@
 		git commit-tree -p "$broken" -m "good" "$tree" >good &&
 		test_must_fail git commit-graph write --stdin-commits \
 			<good 2>test_err &&
-		test_i18ngrep "unable to parse commit" test_err
+		test_grep "unable to parse commit" test_err
 	)
 '
 
@@ -782,32 +764,33 @@
 #
 
 test_expect_success 'set up and verify repo with generation data overflow chunk' '
-	objdir=".git/objects" &&
 	UNIX_EPOCH_ZERO="@0 +0000" &&
 	FUTURE_DATE="@2147483646 +0000" &&
-	cd "$TRASH_DIRECTORY" &&
-	mkdir repo &&
-	cd repo &&
-	git init &&
-	test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
-	test_commit 2 &&
-	test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
-	git commit-graph write --reachable &&
-	graph_read_expect 3 generation_data &&
-	test_commit --date "$FUTURE_DATE" 4 &&
-	test_commit 5 &&
-	test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
-	git branch left &&
-	git reset --hard 3 &&
-	test_commit 7 &&
-	test_commit --date "$FUTURE_DATE" 8 &&
-	test_commit 9 &&
-	git branch right &&
-	git reset --hard 3 &&
-	test_merge M left right &&
-	git commit-graph write --reachable &&
-	graph_read_expect 10 "generation_data generation_data_overflow" &&
-	git commit-graph verify
+
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+		test_commit 2 &&
+		test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+		git commit-graph write --reachable &&
+		graph_read_expect 3 generation_data &&
+		test_commit --date "$FUTURE_DATE" 4 &&
+		test_commit 5 &&
+		test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+		git branch left &&
+		git reset --hard 3 &&
+		test_commit 7 &&
+		test_commit --date "$FUTURE_DATE" 8 &&
+		test_commit 9 &&
+		git branch right &&
+		git reset --hard 3 &&
+		test_merge M left right &&
+		git commit-graph write --reachable &&
+		graph_read_expect 10 "generation_data generation_data_overflow" &&
+		git commit-graph verify
+	)
 '
 
 graph_git_behavior 'generation data overflow chunk repo' repo left right
@@ -839,4 +822,127 @@
 	)
 '
 
+corrupt_chunk () {
+	graph=full/.git/objects/info/commit-graph &&
+	test_when_finished "rm -rf $graph" &&
+	git -C full commit-graph write --reachable &&
+	corrupt_chunk_file $graph "$@"
+}
+
+check_corrupt_chunk () {
+	corrupt_chunk "$@" &&
+	git -C full -c core.commitGraph=false log >expect.out &&
+	git -C full -c core.commitGraph=true log >out 2>err &&
+	test_cmp expect.out out
+}
+
+test_expect_success 'reader notices too-small oid fanout chunk' '
+	# make it big enough that the graph file is plausible,
+	# otherwise we hit an earlier check
+	check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
+	cat >expect.err <<-\EOF &&
+	error: commit-graph oid fanout chunk is wrong size
+	error: commit-graph required OID fanout chunk missing or corrupted
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices fanout/lookup table mismatch' '
+	check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
+	cat >expect.err <<-\EOF &&
+	error: commit-graph OID lookup chunk is the wrong size
+	error: commit-graph required OID lookup chunk missing or corrupted
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds fanout' '
+	# Rather than try to corrupt a specific hash, we will just
+	# wreck them all. But we cannot just set them all to 0xFFFFFFFF or
+	# similar, as they are used for hi/lo starts in a binary search (so if
+	# they are identical, that indicates that the search should abort
+	# immediately). Instead, we will give them high values that differ by
+	# 2^24, ensuring that any that are used would cause an out-of-bounds
+	# read.
+	check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+	cat >expect.err <<-\EOF &&
+	error: commit-graph fanout values out of order
+	error: commit-graph required OID fanout chunk missing or corrupted
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small commit data chunk' '
+	check_corrupt_chunk CDAT clear 00000000 &&
+	cat >expect.err <<-\EOF &&
+	error: commit-graph commit data chunk is wrong size
+	error: commit-graph required commit data chunk missing or corrupted
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds extra edge' '
+	check_corrupt_chunk EDGE clear &&
+	cat >expect.err <<-\EOF &&
+	error: commit-graph extra-edges pointer out of bounds
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small generations chunk' '
+	check_corrupt_chunk GDA2 clear 00000000 &&
+	cat >expect.err <<-\EOF &&
+	error: commit-graph generations chunk is wrong size
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'stale commit cannot be parsed when given directly' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		test_commit B &&
+		git commit-graph write --reachable &&
+
+		oid=$(git rev-parse B) &&
+		rm .git/objects/"$(test_oid_to_path "$oid")" &&
+
+		# Verify that it is possible to read the commit from the
+		# commit graph when not being paranoid, ...
+		git rev-list B &&
+		# ... but parsing the commit when double checking that
+		# it actually exists in the object database should fail.
+		test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
+	)
+'
+
+test_expect_success 'stale commit cannot be parsed when traversing graph' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit A &&
+		test_commit B &&
+		test_commit C &&
+		git commit-graph write --reachable &&
+
+		# Corrupt the repository by deleting the intermediate commit
+		# object. Commands should notice that this object is absent and
+		# thus that the repository is corrupt even if the commit graph
+		# exists.
+		oid=$(git rev-parse B) &&
+		rm .git/objects/"$(test_oid_to_path "$oid")" &&
+
+		# Again, we should be able to parse the commit when not
+		# being paranoid about commit graph staleness...
+		git rev-parse HEAD~2 &&
+		# ... but fail when we are paranoid.
+		test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error &&
+		grep "error: commit $oid exists in commit-graph but not in the object database" error
+	)
+'
+
 test_done
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 499d5d4..dd09134 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -2,6 +2,7 @@
 
 test_description='multi-pack-indexes'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_MULTI_PACK_INDEX=0
 objdir=.git/objects
@@ -183,6 +184,18 @@
 
 compare_results_with_midx "mixed mode (one pack + extra)"
 
+test_expect_success 'write with no objects and preferred pack' '
+	test_when_finished "rm -rf empty" &&
+	git init empty &&
+	test_must_fail git -C empty multi-pack-index write \
+		--stdin-packs --preferred-pack=does-not-exist </dev/null 2>err &&
+	cat >expect <<-EOF &&
+	warning: unknown preferred pack: ${SQ}does-not-exist${SQ}
+	error: no pack files to index.
+	EOF
+	test_cmp expect err
+'
+
 test_expect_success 'write progress off for redirected stderr' '
 	git multi-pack-index --object-dir=$objdir write 2>err &&
 	test_line_count = 0 err
@@ -267,13 +280,13 @@
 		cd sha1 &&
 		mv ../mpi-sha256 .git/objects/pack/multi-pack-index &&
 		git log -1 2>err &&
-		test_i18ngrep "multi-pack-index hash version 2 does not match version 1" err
+		test_grep "multi-pack-index hash version 2 does not match version 1" err
 	) &&
 	(
 		cd sha256 &&
 		mv ../mpi-sha1 .git/objects/pack/multi-pack-index &&
 		git log -1 2>err &&
-		test_i18ngrep "multi-pack-index hash version 1 does not match version 2" err
+		test_grep "multi-pack-index hash version 1 does not match version 2" err
 	)
 '
 
@@ -374,7 +387,7 @@
 	printf "$DATA" | dd of="$FILE" bs=1 seek="$POS" conv=notrunc &&
 	test_must_fail $COMMAND 2>test_err &&
 	grep -v "^+" test_err >err &&
-	test_i18ngrep "$GREPSTR" err
+	test_grep "$GREPSTR" err
 }
 
 test_expect_success 'verify bad signature' '
@@ -426,7 +439,7 @@
 
 test_expect_success 'verify missing required chunk' '
 	corrupt_midx_and_verify $MIDX_BYTE_CHUNK_ID "\01" $objdir \
-		"missing required"
+		"required pack-name chunk missing"
 '
 
 test_expect_success 'verify invalid chunk offset' '
@@ -473,11 +486,23 @@
 	git -c core.multiPackIndex=false fsck
 '
 
+test_expect_success 'git fsck shows MIDX output with --progress' '
+	git fsck --progress 2>err &&
+	grep "Verifying OID order in multi-pack-index" err &&
+	grep "Verifying object offsets" err
+'
+
+test_expect_success 'git fsck suppresses MIDX output with --no-progress' '
+	git fsck --no-progress 2>err &&
+	! grep "Verifying OID order in multi-pack-index" err &&
+	! grep "Verifying object offsets" err
+'
+
 test_expect_success 'corrupt MIDX is not reused' '
 	corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
 		"incorrect object offset" &&
 	git multi-pack-index write 2>err &&
-	test_i18ngrep checksum.mismatch err &&
+	test_grep checksum.mismatch err &&
 	git multi-pack-index verify
 '
 
@@ -1007,7 +1032,7 @@
 
 test_expect_success 'usage shown without sub-command' '
 	test_expect_code 129 git multi-pack-index 2>err &&
-	! test_i18ngrep "unrecognized subcommand" err
+	! test_grep "unrecognized subcommand" err
 '
 
 test_expect_success 'complains when run outside of a repository' '
@@ -1031,4 +1056,154 @@
 	)
 '
 
+corrupt_chunk () {
+	midx=.git/objects/pack/multi-pack-index &&
+	test_when_finished "rm -rf $midx" &&
+	git repack -ad --write-midx &&
+	corrupt_chunk_file $midx "$@"
+}
+
+test_expect_success 'reader notices too-small oid fanout chunk' '
+	corrupt_chunk OIDF clear 00000000 &&
+	test_must_fail git log 2>err &&
+	cat >expect <<-\EOF &&
+	error: multi-pack-index OID fanout is of the wrong size
+	fatal: multi-pack-index required OID fanout chunk missing or corrupted
+	EOF
+	test_cmp expect err
+'
+
+test_expect_success 'reader notices too-small oid lookup chunk' '
+	corrupt_chunk OIDL clear 00000000 &&
+	test_must_fail git log 2>err &&
+	cat >expect <<-\EOF &&
+	error: multi-pack-index OID lookup chunk is the wrong size
+	fatal: multi-pack-index required OID lookup chunk missing or corrupted
+	EOF
+	test_cmp expect err
+'
+
+test_expect_success 'reader notices too-small pack names chunk' '
+	# There is no NUL to terminate the name here, so the
+	# chunk is too short.
+	corrupt_chunk PNAM clear 70656666 &&
+	test_must_fail git log 2>err &&
+	cat >expect <<-\EOF &&
+	fatal: multi-pack-index pack-name chunk is too short
+	EOF
+	test_cmp expect err
+'
+
+test_expect_success 'reader handles unaligned chunks' '
+	# A 9-byte PNAM means all of the subsequent chunks
+	# will no longer be 4-byte aligned, but it is still
+	# a valid one-pack chunk on its own (it is "foo.pack\0").
+	corrupt_chunk PNAM clear 666f6f2e7061636b00 &&
+	git -c core.multipackindex=false log >expect.out &&
+	git -c core.multipackindex=true log >out 2>err &&
+	test_cmp expect.out out &&
+	cat >expect.err <<-\EOF &&
+	error: chunk id 4f494446 not 4-byte aligned
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices too-small object offset chunk' '
+	corrupt_chunk OOFF clear 00000000 &&
+	test_must_fail git log 2>err &&
+	cat >expect <<-\EOF &&
+	error: multi-pack-index object offset chunk is the wrong size
+	fatal: multi-pack-index required object offsets chunk missing or corrupted
+	EOF
+	test_cmp expect err
+'
+
+test_expect_success 'reader bounds-checks large offset table' '
+	# re-use the objects64 dir here to cheaply get access to a midx
+	# with large offsets.
+	git init repo &&
+	test_when_finished "rm -rf repo" &&
+	(
+		cd repo &&
+		(cd ../objects64 && pwd) >.git/objects/info/alternates &&
+		git multi-pack-index --object-dir=../objects64 write &&
+		midx=../objects64/pack/multi-pack-index &&
+		corrupt_chunk_file $midx LOFF clear &&
+		# using only %(objectsize) is important here; see the commit
+		# message for more details
+		test_must_fail git cat-file --batch-all-objects \
+			--batch-check="%(objectsize)" 2>err &&
+		cat >expect <<-\EOF &&
+		fatal: multi-pack-index large offset out of bounds
+		EOF
+		test_cmp expect err
+	)
+'
+
+test_expect_success 'reader notices too-small revindex chunk' '
+	# We only get a revindex with bitmaps (and likewise only
+	# load it when they are asked for).
+	test_config repack.writeBitmaps true &&
+	corrupt_chunk RIDX clear 00000000 &&
+	git -c core.multipackIndex=false rev-list \
+		--all --use-bitmap-index >expect.out &&
+	git -c core.multipackIndex=true rev-list \
+		--all --use-bitmap-index >out 2>err &&
+	test_cmp expect.out out &&
+	cat >expect.err <<-\EOF &&
+	error: multi-pack-index reverse-index chunk is the wrong size
+	warning: multi-pack bitmap is missing required reverse index
+	EOF
+	test_cmp expect.err err
+'
+
+test_expect_success 'reader notices out-of-bounds fanout' '
+	# This is similar to the out-of-bounds fanout test in t5318. The values
+	# in adjacent entries should be large but not identical (they
+	# are used as hi/lo starts for a binary search, which would then abort
+	# immediately).
+	corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+	test_must_fail git log 2>err &&
+	cat >expect <<-\EOF &&
+	error: oid fanout out of order: fanout[254] = fe000000 > 5c = fanout[255]
+	fatal: multi-pack-index required OID fanout chunk missing or corrupted
+	EOF
+	test_cmp expect err
+'
+
+test_expect_success 'bitmapped packs are stored via the BTMP chunk' '
+	test_when_finished "rm -fr repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		for i in 1 2 3 4 5
+		do
+			test_commit "$i" &&
+			git repack -d || return 1
+		done &&
+
+		find $objdir/pack -type f -name "*.idx" | xargs -n 1 basename |
+		sort >packs &&
+
+		git multi-pack-index write --stdin-packs <packs &&
+		test_must_fail test-tool read-midx --bitmap $objdir 2>err &&
+		cat >expect <<-\EOF &&
+		error: MIDX does not contain the BTMP chunk
+		EOF
+		test_cmp expect err &&
+
+		git multi-pack-index write --stdin-packs --bitmap \
+			--preferred-pack="$(head -n1 <packs)" <packs  &&
+		test-tool read-midx --bitmap $objdir >actual &&
+		for i in $(test_seq $(wc -l <packs))
+		do
+			sed -ne "${i}s/\.idx$/\.pack/p" packs &&
+			echo "  bitmap_pos: $((($i - 1) * 3))" &&
+			echo "  bitmap_nr: 3" || return 1
+		done >expect &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index 669ddc6..281266f 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -1,7 +1,10 @@
 #!/bin/sh
 
 test_description='split commit graph'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-chunk.sh
 
 GIT_TEST_COMMIT_GRAPH=0
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
@@ -281,7 +284,33 @@
 		corrupt_file "$base_file" $(test_oid shallow) "\01" &&
 		test_must_fail git commit-graph verify --shallow 2>test_err &&
 		grep -v "^+" test_err >err &&
-		test_i18ngrep "incorrect checksum" err
+		test_grep "incorrect checksum" err
+	)
+'
+
+test_expect_success 'verify notices chain slice which is bogus (base)' '
+	git clone --no-hardlinks . verify-chain-bogus-base &&
+	(
+		cd verify-chain-bogus-base &&
+		git commit-graph verify &&
+		base_file=$graphdir/graph-$(sed -n 1p $graphdir/commit-graph-chain).graph &&
+		echo "garbage" >$base_file &&
+		test_must_fail git commit-graph verify 2>test_err &&
+		grep -v "^+" test_err >err &&
+		grep "commit-graph file is too small" err
+	)
+'
+
+test_expect_success 'verify notices chain slice which is bogus (tip)' '
+	git clone --no-hardlinks . verify-chain-bogus-tip &&
+	(
+		cd verify-chain-bogus-tip &&
+		git commit-graph verify &&
+		tip_file=$graphdir/graph-$(sed -n 2p $graphdir/commit-graph-chain).graph &&
+		echo "garbage" >$tip_file &&
+		test_must_fail git commit-graph verify 2>test_err &&
+		grep -v "^+" test_err >err &&
+		grep "commit-graph file is too small" err
 	)
 '
 
@@ -291,11 +320,11 @@
 		cd verify-shallow &&
 		git commit-graph verify &&
 		base_file=$graphdir/graph-$(head -n 1 $graphdir/commit-graph-chain).graph &&
-		corrupt_file "$base_file" 1000 "\01" &&
+		corrupt_file "$base_file" 1500 "\01" &&
 		git commit-graph verify --shallow &&
 		test_must_fail git commit-graph verify 2>test_err &&
 		grep -v "^+" test_err >err &&
-		test_i18ngrep "incorrect checksum" err
+		test_grep "incorrect checksum" err
 	)
 '
 
@@ -306,24 +335,51 @@
 		git commit-graph verify &&
 		base_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
 		corrupt_file "$base_file" $(test_oid base) "\01" &&
-		git commit-graph verify --shallow 2>test_err &&
+		test_must_fail git commit-graph verify --shallow 2>test_err &&
 		grep -v "^+" test_err >err &&
-		test_i18ngrep "commit-graph chain does not match" err
+		test_grep "commit-graph chain does not match" err
 	)
 '
 
-test_expect_success 'verify after commit-graph-chain corruption' '
-	git clone --no-hardlinks . verify-chain &&
+test_expect_success 'verify after commit-graph-chain corruption (base)' '
+	git clone --no-hardlinks . verify-chain-base &&
 	(
-		cd verify-chain &&
-		corrupt_file "$graphdir/commit-graph-chain" 60 "G" &&
-		git commit-graph verify 2>test_err &&
+		cd verify-chain-base &&
+		corrupt_file "$graphdir/commit-graph-chain" 30 "G" &&
+		test_must_fail git commit-graph verify 2>test_err &&
 		grep -v "^+" test_err >err &&
-		test_i18ngrep "invalid commit-graph chain" err &&
-		corrupt_file "$graphdir/commit-graph-chain" 60 "A" &&
-		git commit-graph verify 2>test_err &&
+		test_grep "invalid commit-graph chain" err &&
+		corrupt_file "$graphdir/commit-graph-chain" 30 "A" &&
+		test_must_fail git commit-graph verify 2>test_err &&
 		grep -v "^+" test_err >err &&
-		test_i18ngrep "unable to find all commit-graph files" err
+		test_grep "unable to find all commit-graph files" err
+	)
+'
+
+test_expect_success 'verify after commit-graph-chain corruption (tip)' '
+	git clone --no-hardlinks . verify-chain-tip &&
+	(
+		cd verify-chain-tip &&
+		corrupt_file "$graphdir/commit-graph-chain" 70 "G" &&
+		test_must_fail git commit-graph verify 2>test_err &&
+		grep -v "^+" test_err >err &&
+		test_grep "invalid commit-graph chain" err &&
+		corrupt_file "$graphdir/commit-graph-chain" 70 "A" &&
+		test_must_fail git commit-graph verify 2>test_err &&
+		grep -v "^+" test_err >err &&
+		test_grep "unable to find all commit-graph files" err
+	)
+'
+
+test_expect_success 'verify notices too-short chain file' '
+	git clone --no-hardlinks . verify-chain-short &&
+	(
+		cd verify-chain-short &&
+		git commit-graph verify &&
+		echo "garbage" >$graphdir/commit-graph-chain &&
+		test_must_fail git commit-graph verify 2>test_err &&
+		grep -v "^+" test_err >err &&
+		grep "commit-graph chain file too small" err
 	)
 '
 
@@ -338,10 +394,23 @@
 		test_commit extra &&
 		git commit-graph write --reachable --split &&
 		tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
-		corrupt_file "$tip_file" 100 "\01" &&
+		corrupt_file "$tip_file" 1500 "\01" &&
 		test_must_fail git commit-graph verify --shallow 2>test_err &&
 		grep -v "^+" test_err >err &&
-		test_i18ngrep "commit-graph has incorrect fanout value" err
+		test_grep "incorrect checksum" err
+	)
+'
+
+test_expect_success 'reader bounds-checks base-graph chunk' '
+	git clone --no-hardlinks . corrupt-base-chunk &&
+	(
+		cd corrupt-base-chunk &&
+		tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
+		corrupt_chunk_file "$tip_file" BASE clear 01020304 &&
+		git -c core.commitGraph=false log >expect.out &&
+		git -c core.commitGraph=true log >out 2>err &&
+		test_cmp expect.out out &&
+		grep "commit-graph base graphs chunk is too small" err
 	)
 '
 
@@ -351,8 +420,9 @@
 	git branch merge/octopus &&
 	git commit-graph write --reachable --split &&
 	git commit-graph verify --progress 2>err &&
-	test_line_count = 3 err &&
-	test_i18ngrep ! warning err &&
+	test_line_count = 1 err &&
+	grep "Verifying commits in commit graph: 100% (18/18)" err &&
+	test_grep ! warning err &&
 	test_line_count = 3 $graphdir/commit-graph-chain
 '
 
@@ -454,7 +524,7 @@
 	git init dup &&
 	git -C dup commit --allow-empty -m one &&
 	git -C dup -c core.commitGraph=false commit-graph write --split=no-merge --reachable 2>err &&
-	test_i18ngrep "attempting to write a commit-graph" err &&
+	test_grep "attempting to write a commit-graph" err &&
 	git -C dup commit-graph write --split=no-merge --reachable &&
 	git -C dup commit --allow-empty -m two &&
 	git -C dup commit-graph write --split=no-merge --reachable &&
diff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh
index d042d26..431a603 100755
--- a/t/t5325-reverse-index.sh
+++ b/t/t5325-reverse-index.sh
@@ -1,17 +1,20 @@
 #!/bin/sh
 
 test_description='on-disk reverse index'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # The below tests want control over the 'pack.writeReverseIndex' setting
 # themselves to assert various combinations of it with other options.
-sane_unset GIT_TEST_WRITE_REV_INDEX
+sane_unset GIT_TEST_NO_WRITE_REV_INDEX
 
 packdir=.git/objects/pack
 
 test_expect_success 'setup' '
 	test_commit base &&
 
+	test_config pack.writeReverseIndex false &&
 	pack=$(git pack-objects --all $packdir/pack) &&
 	rev=$packdir/pack-$pack.rev &&
 
@@ -94,6 +97,17 @@
 		--batch-check="%(objectsize:disk)" <tip
 '
 
+test_expect_success 'reverse index is ignored when pack.readReverseIndex is false' '
+	test_index_pack true &&
+	test_path_is_file $rev &&
+
+	test_config pack.readReverseIndex false &&
+
+	git rev-parse HEAD >tip &&
+	GIT_TEST_REV_INDEX_DIE_ON_DISK=1 git cat-file \
+		--batch-check="%(objectsize:disk)" <tip
+'
+
 test_expect_success 'revindex in-memory vs on-disk' '
 	git init repo &&
 	test_when_finished "rm -fr repo" &&
@@ -117,4 +131,78 @@
 		test_cmp on-disk in-core
 	)
 '
+
+test_expect_success 'fsck succeeds on good rev-index' '
+	test_when_finished rm -fr repo &&
+	git init repo &&
+	(
+		cd repo &&
+
+		test_commit commit &&
+		git -c pack.writeReverseIndex=true repack -ad &&
+		git fsck 2>err &&
+		test_must_be_empty err
+	)
+'
+
+test_expect_success 'set up rev-index corruption tests' '
+	git init corrupt &&
+	(
+		cd corrupt &&
+
+		test_commit commit &&
+		git -c pack.writeReverseIndex=true repack -ad &&
+
+		revfile=$(ls .git/objects/pack/pack-*.rev) &&
+		chmod a+w $revfile &&
+		cp $revfile $revfile.bak
+	)
+'
+
+corrupt_rev_and_verify () {
+	(
+		pos="$1" &&
+		value="$2" &&
+		error="$3" &&
+
+		cd corrupt &&
+		revfile=$(ls .git/objects/pack/pack-*.rev) &&
+
+		# Reset to original rev-file.
+		cp $revfile.bak $revfile &&
+
+		printf "$value" | dd of=$revfile bs=1 seek="$pos" conv=notrunc &&
+		test_must_fail git fsck 2>err &&
+		grep "$error" err
+	)
+}
+
+test_expect_success 'fsck catches invalid checksum' '
+	revfile=$(ls corrupt/.git/objects/pack/pack-*.rev) &&
+	orig_size=$(wc -c <$revfile) &&
+	hashpos=$((orig_size - 10)) &&
+	corrupt_rev_and_verify $hashpos bogus \
+		"invalid checksum"
+'
+
+test_expect_success 'fsck catches invalid row position' '
+	corrupt_rev_and_verify 14 "\07" \
+		"invalid rev-index position"
+'
+
+test_expect_success 'fsck catches invalid header: magic number' '
+	corrupt_rev_and_verify 1 "\07" \
+		"reverse-index file .* has unknown signature"
+'
+
+test_expect_success 'fsck catches invalid header: version' '
+	corrupt_rev_and_verify 7 "\02" \
+		"reverse-index file .* has unsupported version"
+'
+
+test_expect_success 'fsck catches invalid header: hash function' '
+	corrupt_rev_and_verify 11 "\03" \
+		"reverse-index file .* has unsupported hash id"
+'
+
 test_done
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 0882cbb..70d1b58 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -434,4 +434,83 @@
 	)
 '
 
+corrupt_file () {
+	chmod a+w "$1" &&
+	printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc
+}
+
+test_expect_success 'git fsck correctly identifies good and bad bitmaps' '
+	git init valid &&
+	test_when_finished rm -rf valid &&
+
+	test_commit_bulk 20 &&
+	git repack -adbf &&
+
+	# Move pack-bitmap aside so it is not deleted
+	# in next repack.
+	packbitmap=$(ls .git/objects/pack/pack-*.bitmap) &&
+	mv "$packbitmap" "$packbitmap.bak" &&
+
+	test_commit_bulk 10 &&
+	git repack -b --write-midx &&
+	midxbitmap=$(ls .git/objects/pack/multi-pack-index-*.bitmap) &&
+
+	# Copy MIDX bitmap to backup. Copy pack bitmap from backup.
+	cp "$midxbitmap" "$midxbitmap.bak" &&
+	cp "$packbitmap.bak" "$packbitmap" &&
+
+	# fsck works at first
+	git fsck 2>err &&
+	test_must_be_empty err &&
+
+	corrupt_file "$packbitmap" &&
+	test_must_fail git fsck 2>err &&
+	grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err &&
+
+	cp "$packbitmap.bak" "$packbitmap" &&
+	corrupt_file "$midxbitmap" &&
+	test_must_fail git fsck 2>err &&
+	grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+
+	corrupt_file "$packbitmap" &&
+	test_must_fail git fsck 2>err &&
+	grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+	grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err
+'
+
+test_expect_success 'corrupt MIDX with bitmap causes fallback' '
+	git init corrupt-midx-bitmap &&
+	(
+		cd corrupt-midx-bitmap &&
+
+		test_commit first &&
+		git repack -d &&
+		test_commit second &&
+		git repack -d &&
+
+		git multi-pack-index write --bitmap &&
+		checksum=$(midx_checksum $objdir) &&
+		for f in $midx $midx-$checksum.bitmap
+		do
+			mv $f $f.bak || return 1
+		done &&
+
+		# pack everything together, invalidating the MIDX
+		git repack -ad &&
+		# then restore the now-stale MIDX
+		for f in $midx $midx-$checksum.bitmap
+		do
+			mv $f.bak $f || return 1
+		done &&
+
+		git rev-list --count --objects --use-bitmap-index HEAD >out 2>err &&
+		# should attempt opening the broken pack twice (once
+		# from the attempt to load it via the stale bitmap, and
+		# again when attempting to load it from the stale MIDX)
+		# before falling back to the non-MIDX case
+		test 2 -eq $(grep -c "could not open pack" err) &&
+		test 6 -eq $(cat out)
+	)
+'
+
 test_done
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh
index 093f0c0..fc6a242 100755
--- a/t/t5328-commit-graph-64bit-time.sh
+++ b/t/t5328-commit-graph-64bit-time.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='commit graph with 64-bit timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
@@ -10,6 +12,7 @@
 fi
 
 . "$TEST_DIRECTORY"/lib-commit-graph.sh
+. "$TEST_DIRECTORY/lib-chunk.sh"
 
 UNIX_EPOCH_ZERO="@0 +0000"
 FUTURE_DATE="@4147483646 +0000"
@@ -37,30 +40,48 @@
 graph_git_behavior 'overflow' '' HEAD~2 HEAD
 
 test_expect_success 'set up and verify repo with generation data overflow chunk' '
-	mkdir repo &&
-	cd repo &&
-	git init &&
-	test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
-	test_commit 2 &&
-	test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
-	git commit-graph write --reachable &&
-	graph_read_expect 3 generation_data &&
-	test_commit --date "$FUTURE_DATE" 4 &&
-	test_commit 5 &&
-	test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
-	git branch left &&
-	git reset --hard 3 &&
-	test_commit 7 &&
-	test_commit --date "$FUTURE_DATE" 8 &&
-	test_commit 9 &&
-	git branch right &&
-	git reset --hard 3 &&
-	test_merge M left right &&
-	git commit-graph write --reachable &&
-	graph_read_expect 10 "generation_data generation_data_overflow" &&
-	git commit-graph verify
+	git init repo &&
+	(
+		cd repo &&
+		test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+		test_commit 2 &&
+		test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+		git commit-graph write --reachable &&
+		graph_read_expect 3 generation_data &&
+		test_commit --date "$FUTURE_DATE" 4 &&
+		test_commit 5 &&
+		test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+		git branch left &&
+		git reset --hard 3 &&
+		test_commit 7 &&
+		test_commit --date "$FUTURE_DATE" 8 &&
+		test_commit 9 &&
+		git branch right &&
+		git reset --hard 3 &&
+		test_merge M left right &&
+		git commit-graph write --reachable &&
+		graph_read_expect 10 "generation_data generation_data_overflow" &&
+		git commit-graph verify
+	)
 '
 
 graph_git_behavior 'overflow 2' repo left right
 
+test_expect_success 'single commit with generation data exceeding UINT32_MAX' '
+	git init repo-uint32-max &&
+	test_commit -C repo-uint32-max --date "@4294967297 +0000" 1 &&
+	git -C repo-uint32-max commit-graph write --reachable &&
+	graph_read_expect -C repo-uint32-max 1 "generation_data" &&
+	git -C repo-uint32-max commit-graph verify
+'
+
+test_expect_success 'reader notices out-of-bounds generation overflow' '
+	graph=.git/objects/info/commit-graph &&
+	test_when_finished "rm -rf $graph" &&
+	git commit-graph write --reachable &&
+	corrupt_chunk_file $graph GDO2 clear &&
+	test_must_fail git log 2>err &&
+	grep "commit-graph overflow generation data is too small" err
+'
+
 test_done
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
index 303f7a5..fc5fedb 100755
--- a/t/t5329-pack-objects-cruft.sh
+++ b/t/t5329-pack-objects-cruft.sh
@@ -573,23 +573,54 @@
 	)
 '
 
-test_expect_success 'cruft repack ignores --max-pack-size' '
+write_blob () {
+	test-tool genrandom "$@" >in &&
+	git hash-object -w -t blob in
+}
+
+find_pack () {
+	for idx in $(ls $packdir/pack-*.idx)
+	do
+		git show-index <$idx >out &&
+		if grep -q "$1" out
+		then
+			echo $idx
+		fi || return 1
+	done
+}
+
+test_expect_success 'cruft repack with --max-pack-size' '
 	git init max-pack-size &&
 	(
 		cd max-pack-size &&
 		test_commit base &&
+
 		# two cruft objects which exceed the maximum pack size
-		test-tool genrandom foo 1048576 | git hash-object --stdin -w &&
-		test-tool genrandom bar 1048576 | git hash-object --stdin -w &&
+		foo=$(write_blob foo 1048576) &&
+		bar=$(write_blob bar 1048576) &&
+		test-tool chmtime --get -1000 \
+			"$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
+		test-tool chmtime --get -2000 \
+			"$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
 		git repack --cruft --max-pack-size=1M &&
 		find $packdir -name "*.mtimes" >cruft &&
-		test_line_count = 1 cruft &&
-		test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects &&
-		test_line_count = 2 objects
+		test_line_count = 2 cruft &&
+
+		foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
+		bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
+		test-tool pack-mtimes $foo_mtimes >foo.actual &&
+		test-tool pack-mtimes $bar_mtimes >bar.actual &&
+
+		echo "$foo $(cat foo.mtime)" >foo.expect &&
+		echo "$bar $(cat bar.mtime)" >bar.expect &&
+
+		test_cmp foo.expect foo.actual &&
+		test_cmp bar.expect bar.actual &&
+		test "$foo_mtimes" != "$bar_mtimes"
 	)
 '
 
-test_expect_success 'cruft repack ignores pack.packSizeLimit' '
+test_expect_success 'cruft repack with pack.packSizeLimit' '
 	(
 		cd max-pack-size &&
 		# repack everything back together to remove the existing cruft
@@ -599,9 +630,12 @@
 		# ensure the same post condition is met when --max-pack-size
 		# would otherwise be inferred from the configuration
 		find $packdir -name "*.mtimes" >cruft &&
-		test_line_count = 1 cruft &&
-		test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects &&
-		test_line_count = 2 objects
+		test_line_count = 2 cruft &&
+		for pack in $(cat cruft)
+		do
+			test-tool pack-mtimes "$(basename $pack)" >objects &&
+			test_line_count = 1 objects || return 1
+		done
 	)
 '
 
@@ -739,4 +773,175 @@
 	)
 '
 
+test_expect_success 'gc.recentObjectsHook' '
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
+		cd repo &&
+
+		# Create a handful of objects.
+		#
+		#   - one reachable commit, "base", designated for the reachable
+		#     pack
+		#   - one unreachable commit, "cruft.discard", which is marked
+		#     for deletion
+		#   - one unreachable commit, "cruft.old", which would be marked
+		#     for deletion, but is rescued as an extra cruft tip
+		#   - one unreachable commit, "cruft.new", which is not marked
+		#     for deletion
+		test_commit base &&
+		git branch -M main &&
+
+		git checkout --orphan discard &&
+		git rm -fr . &&
+		test_commit --no-tag cruft.discard &&
+
+		git checkout --orphan old &&
+		git rm -fr . &&
+		test_commit --no-tag cruft.old &&
+		cruft_old="$(git rev-parse HEAD)" &&
+
+		git checkout --orphan new &&
+		git rm -fr . &&
+		test_commit --no-tag cruft.new &&
+		cruft_new="$(git rev-parse HEAD)" &&
+
+		git checkout main &&
+		git branch -D discard old new &&
+		git reflog expire --all --expire=all &&
+
+		# mark cruft.old with an mtime that is many minutes
+		# older than the expiration period, and mark cruft.new
+		# with an mtime that is in the future (and thus not
+		# eligible for pruning).
+		test-tool chmtime -2000 "$objdir/$(test_oid_to_path $cruft_old)" &&
+		test-tool chmtime +1000 "$objdir/$(test_oid_to_path $cruft_new)" &&
+
+		# Write the list of cruft objects we expect to
+		# accumulate, which is comprised of everything reachable
+		# from cruft.old and cruft.new, but not cruft.discard.
+		git rev-list --objects --no-object-names \
+			$cruft_old $cruft_new >cruft.raw &&
+		sort cruft.raw >cruft.expect &&
+
+		# Write the script to list extra tips, which are limited
+		# to cruft.old, in this case.
+		write_script extra-tips <<-EOF &&
+		echo $cruft_old
+		EOF
+		git config gc.recentObjectsHook ./extra-tips &&
+
+		git repack --cruft --cruft-expiration=now -d &&
+
+		mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+		git show-index <${mtimes%.mtimes}.idx >cruft &&
+		cut -d" " -f2 cruft | sort >cruft.actual &&
+		test_cmp cruft.expect cruft.actual &&
+
+		# Ensure that the "old" objects are removed after
+		# dropping the gc.recentObjectsHook hook.
+		git config --unset gc.recentObjectsHook &&
+		git repack --cruft --cruft-expiration=now -d &&
+
+		mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+		git show-index <${mtimes%.mtimes}.idx >cruft &&
+		cut -d" " -f2 cruft | sort >cruft.actual &&
+
+		git rev-list --objects --no-object-names $cruft_new >cruft.raw &&
+		cp cruft.expect cruft.old &&
+		sort cruft.raw >cruft.expect &&
+		test_cmp cruft.expect cruft.actual &&
+
+		# ensure objects which are no longer in the cruft pack were
+		# removed from the repository
+		for object in $(comm -13 cruft.expect cruft.old)
+		do
+			test_must_fail git cat-file -t $object || return 1
+		done
+	)
+'
+
+test_expect_success 'multi-valued gc.recentObjectsHook' '
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
+		cd repo &&
+
+		test_commit base &&
+		git branch -M main &&
+
+		git checkout --orphan cruft.a &&
+		git rm -fr . &&
+		test_commit --no-tag cruft.a &&
+		cruft_a="$(git rev-parse HEAD)" &&
+
+		git checkout --orphan cruft.b &&
+		git rm -fr . &&
+		test_commit --no-tag cruft.b &&
+		cruft_b="$(git rev-parse HEAD)" &&
+
+		git checkout main &&
+		git branch -D cruft.a cruft.b &&
+		git reflog expire --all --expire=all &&
+
+		echo "echo $cruft_a" | write_script extra-tips.a &&
+		echo "echo $cruft_b" | write_script extra-tips.b &&
+		echo "false" | write_script extra-tips.c &&
+
+		git rev-list --objects --no-object-names $cruft_a $cruft_b \
+			>cruft.raw &&
+		sort cruft.raw >cruft.expect &&
+
+		# ensure that each extra cruft tip is saved by its
+		# respective hook
+		git config --add gc.recentObjectsHook ./extra-tips.a &&
+		git config --add gc.recentObjectsHook ./extra-tips.b &&
+		git repack --cruft --cruft-expiration=now -d &&
+
+		mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+		git show-index <${mtimes%.mtimes}.idx >cruft &&
+		cut -d" " -f2 cruft | sort >cruft.actual &&
+		test_cmp cruft.expect cruft.actual &&
+
+		# ensure that a dirty exit halts cruft pack generation
+		git config --add gc.recentObjectsHook ./extra-tips.c &&
+		test_must_fail git repack --cruft --cruft-expiration=now -d 2>err &&
+		grep "unable to enumerate additional recent objects" err &&
+
+		# and that the existing cruft pack is left alone
+		test_path_is_file "$mtimes"
+	)
+'
+
+test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
+		cd repo &&
+
+		test_commit base &&
+
+		blob=$(echo "unreachable" | git hash-object -w --stdin) &&
+
+		# mark the unreachable blob we wrote above as having
+		# aged out of the retention period
+		test-tool chmtime -2000 "$objdir/$(test_oid_to_path $blob)" &&
+
+		# Write the script to list extra tips, which is just the
+		# extra blob as above.
+		write_script extra-tips <<-EOF &&
+		echo $blob
+		EOF
+		git config gc.recentObjectsHook ./extra-tips &&
+
+		git repack --cruft --cruft-expiration=now -d &&
+
+		mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+		git show-index <${mtimes%.mtimes}.idx >cruft &&
+		cut -d" " -f2 cruft >actual &&
+		echo $blob >expect &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh
new file mode 100755
index 0000000..2dcf1ee
--- /dev/null
+++ b/t/t5331-pack-objects-stdin.sh
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+test_description='pack-objects --stdin'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+packed_objects () {
+	git show-index <"$1" >tmp-object-list &&
+	cut -d' ' -f2 tmp-object-list | sort &&
+	rm tmp-object-list
+ }
+
+test_expect_success 'setup for --stdin-packs tests' '
+	git init stdin-packs &&
+	(
+		cd stdin-packs &&
+
+		test_commit A &&
+		test_commit B &&
+		test_commit C &&
+
+		for id in A B C
+		do
+			git pack-objects .git/objects/pack/pack-$id \
+				--incremental --revs <<-EOF || exit 1
+			refs/tags/$id
+			EOF
+		done &&
+
+		ls -la .git/objects/pack
+	)
+'
+
+test_expect_success '--stdin-packs with excluded packs' '
+	(
+		cd stdin-packs &&
+
+		PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+		PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+		PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+
+		git pack-objects test --stdin-packs <<-EOF &&
+		$PACK_A
+		^$PACK_B
+		$PACK_C
+		EOF
+
+		(
+			git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+			git show-index <$(ls .git/objects/pack/pack-C-*.idx)
+		) >expect.raw &&
+		git show-index <$(ls test-*.idx) >actual.raw &&
+
+		cut -d" " -f2 <expect.raw | sort >expect &&
+		cut -d" " -f2 <actual.raw | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--stdin-packs is incompatible with --filter' '
+	(
+		cd stdin-packs &&
+		test_must_fail git pack-objects --stdin-packs --stdout \
+			--filter=blob:none </dev/null 2>err &&
+		test_grep "cannot use --filter with --stdin-packs" err
+	)
+'
+
+test_expect_success '--stdin-packs is incompatible with --revs' '
+	(
+		cd stdin-packs &&
+		test_must_fail git pack-objects --stdin-packs --revs out \
+			</dev/null 2>err &&
+		test_grep "cannot use internal rev list with --stdin-packs" err
+	)
+'
+
+test_expect_success '--stdin-packs with loose objects' '
+	(
+		cd stdin-packs &&
+
+		PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+		PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+		PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+
+		test_commit D && # loose
+
+		git pack-objects test2 --stdin-packs --unpacked <<-EOF &&
+		$PACK_A
+		^$PACK_B
+		$PACK_C
+		EOF
+
+		(
+			git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+			git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
+			git rev-list --objects --no-object-names \
+				refs/tags/C..refs/tags/D
+
+		) >expect.raw &&
+		ls -la . &&
+		git show-index <$(ls test2-*.idx) >actual.raw &&
+
+		cut -d" " -f2 <expect.raw | sort >expect &&
+		cut -d" " -f2 <actual.raw | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--stdin-packs with broken links' '
+	(
+		cd stdin-packs &&
+
+		# make an unreachable object with a bogus parent
+		git cat-file -p HEAD >commit &&
+		sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit |
+		git hash-object -w -t commit --stdin >in &&
+
+		git pack-objects .git/objects/pack/pack-D <in &&
+
+		PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" &&
+		PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" &&
+		PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" &&
+		PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" &&
+
+		git pack-objects test3 --stdin-packs --unpacked <<-EOF &&
+		$PACK_A
+		^$PACK_B
+		$PACK_C
+		$PACK_D
+		EOF
+
+		(
+			git show-index <$(ls .git/objects/pack/pack-A-*.idx) &&
+			git show-index <$(ls .git/objects/pack/pack-C-*.idx) &&
+			git show-index <$(ls .git/objects/pack/pack-D-*.idx) &&
+			git rev-list --objects --no-object-names \
+				refs/tags/C..refs/tags/D
+		) >expect.raw &&
+		git show-index <$(ls test3-*.idx) >actual.raw &&
+
+		cut -d" " -f2 <expect.raw | sort >expect &&
+		cut -d" " -f2 <actual.raw | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'pack-objects --stdin with duplicate packfile' '
+	test_when_finished "rm -fr repo" &&
+
+	git init repo &&
+	(
+		cd repo &&
+		test_commit "commit" &&
+		git repack -ad &&
+
+		{
+			basename .git/objects/pack/pack-*.pack &&
+			basename .git/objects/pack/pack-*.pack
+		} >packfiles &&
+
+		git pack-objects --stdin-packs generated-pack <packfiles &&
+		packed_objects .git/objects/pack/pack-*.idx >expect &&
+		packed_objects generated-pack-*.idx >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'pack-objects --stdin with same packfile excluded and included' '
+	test_when_finished "rm -fr repo" &&
+
+	git init repo &&
+	(
+		cd repo &&
+		test_commit "commit" &&
+		git repack -ad &&
+
+		{
+			basename .git/objects/pack/pack-*.pack &&
+			printf "^%s\n" "$(basename .git/objects/pack/pack-*.pack)"
+		} >packfiles &&
+
+		git pack-objects --stdin-packs generated-pack <packfiles &&
+		packed_objects generated-pack-*.idx >packed-objects &&
+		test_must_be_empty packed-objects
+	)
+'
+
+test_expect_success 'pack-objects --stdin with packfiles from alternate object database' '
+	test_when_finished "rm -fr shared member" &&
+
+	# Set up a shared repository with a single packfile.
+	git init shared &&
+	test_commit -C shared "shared-objects" &&
+	git -C shared repack -ad &&
+	basename shared/.git/objects/pack/pack-*.pack >packfile &&
+
+	# Set up a repository that is connected to the shared repository. This
+	# repository has no objects on its own, but we still expect to be able
+	# to pack objects from its alternate.
+	git clone --shared shared member &&
+	git -C member pack-objects --stdin-packs generated-pack <packfile &&
+	test_cmp shared/.git/objects/pack/pack-*.pack member/generated-pack-*.pack
+'
+
+test_expect_success 'pack-objects --stdin with packfiles from main and alternate object database' '
+	test_when_finished "rm -fr shared member" &&
+
+	# Set up a shared repository with a single packfile.
+	git init shared &&
+	test_commit -C shared "shared-commit" &&
+	git -C shared repack -ad &&
+
+	# Set up a repository that is connected to the shared repository. This
+	# repository has a second packfile so that we can verify that it is
+	# possible to write packs that include packfiles from different object
+	# databases.
+	git clone --shared shared member &&
+	test_commit -C member "local-commit" &&
+	git -C member repack -dl &&
+
+	{
+		basename shared/.git/objects/pack/pack-*.pack &&
+		basename member/.git/objects/pack/pack-*.pack
+	} >packfiles &&
+
+	{
+		packed_objects shared/.git/objects/pack/pack-*.idx &&
+		packed_objects member/.git/objects/pack/pack-*.idx
+	} | sort >expected-objects &&
+
+	git -C member pack-objects --stdin-packs generated-pack <packfiles &&
+	packed_objects member/generated-pack-*.idx >actual-objects &&
+	test_cmp expected-objects actual-objects
+'
+
+test_done
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
new file mode 100755
index 0000000..3c20738
--- /dev/null
+++ b/t/t5332-multi-pack-reuse.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+
+test_description='pack-objects multi-pack reuse'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_pack_reused () {
+	test_trace2_data pack-objects pack-reused "$1"
+}
+
+test_packs_reused () {
+	test_trace2_data pack-objects packs-reused "$1"
+}
+
+
+# pack_position <object> </path/to/pack.idx
+pack_position () {
+	git show-index >objects &&
+	grep "$1" objects | cut -d" " -f1
+}
+
+# test_pack_objects_reused_all <pack-reused> <packs-reused>
+test_pack_objects_reused_all () {
+	: >trace2.txt &&
+	GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+		git pack-objects --stdout --revs --all --delta-base-offset \
+		>/dev/null &&
+
+	test_pack_reused "$1" <trace2.txt &&
+	test_packs_reused "$2" <trace2.txt
+}
+
+# test_pack_objects_reused <pack-reused> <packs-reused>
+test_pack_objects_reused () {
+	: >trace2.txt &&
+	GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+		git pack-objects --stdout --revs >/dev/null &&
+
+	test_pack_reused "$1" <trace2.txt &&
+	test_packs_reused "$2" <trace2.txt
+}
+
+test_expect_success 'preferred pack is reused for single-pack reuse' '
+	test_config pack.allowPackReuse single &&
+
+	for i in A B
+	do
+		test_commit "$i" &&
+		git repack -d || return 1
+	done &&
+
+	git multi-pack-index write --bitmap &&
+
+	test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'multi-pack reuse is disabled by default' '
+	test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'feature.experimental implies multi-pack reuse' '
+	test_config feature.experimental true &&
+
+	test_pack_objects_reused_all 6 2
+'
+
+test_expect_success 'multi-pack reuse can be disabled with feature.experimental' '
+	test_config feature.experimental true &&
+	test_config pack.allowPackReuse single &&
+
+	test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'enable multi-pack reuse' '
+	git config pack.allowPackReuse multi
+'
+
+test_expect_success 'reuse all objects from subset of bitmapped packs' '
+	test_commit C &&
+	git repack -d &&
+
+	git multi-pack-index write --bitmap &&
+
+	cat >in <<-EOF &&
+	$(git rev-parse C)
+	^$(git rev-parse A)
+	EOF
+
+	test_pack_objects_reused 6 2 <in
+'
+
+test_expect_success 'reuse all objects from all packs' '
+	test_pack_objects_reused_all 9 3
+'
+
+test_expect_success 'reuse objects from first pack with middle gap' '
+	for i in D E F
+	do
+		test_commit "$i" || return 1
+	done &&
+
+	# Set "pack.window" to zero to ensure that we do not create any
+	# deltas, which could alter the amount of pack reuse we perform
+	# (if, for e.g., we are not sending one or more bases).
+	D="$(git -c pack.window=0 pack-objects --all --unpacked $packdir/pack)" &&
+
+	d_pos="$(pack_position $(git rev-parse D) <$packdir/pack-$D.idx)" &&
+	e_pos="$(pack_position $(git rev-parse E) <$packdir/pack-$D.idx)" &&
+	f_pos="$(pack_position $(git rev-parse F) <$packdir/pack-$D.idx)" &&
+
+	# commits F, E, and D, should appear in that order at the
+	# beginning of the pack
+	test $f_pos -lt $e_pos &&
+	test $e_pos -lt $d_pos &&
+
+	# Ensure that the pack we are constructing sorts ahead of any
+	# other packs in lexical/bitmap order by choosing it as the
+	# preferred pack.
+	git multi-pack-index write --bitmap --preferred-pack="pack-$D.idx" &&
+
+	cat >in <<-EOF &&
+	$(git rev-parse E)
+	^$(git rev-parse D)
+	EOF
+
+	test_pack_objects_reused 3 1 <in
+'
+
+test_expect_success 'reuse objects from middle pack with middle gap' '
+	rm -fr $packdir/multi-pack-index* &&
+
+	# Ensure that the pack we are constructing sort into any
+	# position *but* the first one, by choosing a different pack as
+	# the preferred one.
+	git multi-pack-index write --bitmap --preferred-pack="pack-$A.idx" &&
+
+	cat >in <<-EOF &&
+	$(git rev-parse E)
+	^$(git rev-parse D)
+	EOF
+
+	test_pack_objects_reused 3 1 <in
+'
+
+test_expect_success 'omit delta with uninteresting base (same pack)' '
+	git repack -adk &&
+
+	test_seq 32 >f &&
+	git add f &&
+	test_tick &&
+	git commit -m "delta" &&
+	delta="$(git rev-parse HEAD)" &&
+
+	test_seq 64 >f &&
+	test_tick &&
+	git commit -a -m "base" &&
+	base="$(git rev-parse HEAD)" &&
+
+	test_commit other &&
+
+	git repack -d &&
+
+	have_delta "$(git rev-parse $delta:f)" "$(git rev-parse $base:f)" &&
+
+	git multi-pack-index write --bitmap &&
+
+	cat >in <<-EOF &&
+	$(git rev-parse other)
+	^$base
+	EOF
+
+	# We can only reuse the 3 objects corresponding to "other" from
+	# the latest pack.
+	#
+	# This is because even though we want "delta", we do not want
+	# "base", meaning that we have to inflate the delta/base-pair
+	# corresponding to the blob in commit "delta", which bypasses
+	# the pack-reuse mechanism.
+	#
+	# The remaining objects from the other pack are similarly not
+	# reused because their objects are on the uninteresting side of
+	# the query.
+	test_pack_objects_reused 3 1 <in
+'
+
+test_expect_success 'omit delta from uninteresting base (cross pack)' '
+	cat >in <<-EOF &&
+	$(git rev-parse $base)
+	^$(git rev-parse $delta)
+	EOF
+
+	P="$(git pack-objects --revs $packdir/pack <in)" &&
+
+	git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&
+
+	packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
+	objects_nr="$(git rev-list --count --all --objects)" &&
+
+	test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
+'
+
+test_done
diff --git a/t/t5351-unpack-large-objects.sh b/t/t5351-unpack-large-objects.sh
index 8c8af99..43cbcd5 100755
--- a/t/t5351-unpack-large-objects.sh
+++ b/t/t5351-unpack-large-objects.sh
@@ -55,7 +55,7 @@
 
 	cat >expect &&
 	sed -n \
-		-e '/^{"event":"data",.*"category":"fsync",/ {
+		-e '/^{"event":"counter",.*"category":"fsync",/ {
 			s/.*"category":"fsync",//;
 			s/}$//;
 			p;
@@ -78,8 +78,8 @@
 		flush_count=1
 	fi &&
 	check_fsync_events trace2.txt <<-EOF &&
-	"key":"fsync/writeout-only","value":"6"
-	"key":"fsync/hardware-flush","value":"$flush_count"
+	"name":"writeout-only","count":6
+	"name":"hardware-flush","count":$flush_count
 	EOF
 
 	test_dir_is_empty dest.git/objects/pack &&
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 001b7a1..d8cadee 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -123,7 +123,7 @@
 remote: STDERR post-update
 EOF
 test_expect_success 'send-pack stderr contains hook messages' '
-	grep ^remote: send.err | sed "s/ *\$//" >actual &&
+	sed -n "/^remote:/s/ *\$//p" send.err >actual &&
 	test_cmp expect actual
 '
 
@@ -133,10 +133,8 @@
 	EOF
 	rm -f victim.git/hooks/update victim.git/hooks/post-update &&
 
-	for v in $(test_seq 100 999)
-	do
-		git branch branch_$v main || return
-	done &&
+	printf "create refs/heads/branch_%d main\n" $(test_seq 100 999) >input &&
+	git update-ref --stdin <input &&
 	git push ./victim.git "+refs/heads/*:refs/heads/*"
 '
 
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
index cc07889..51737ee 100755
--- a/t/t5404-tracking-branches.sh
+++ b/t/t5404-tracking-branches.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 5f3ff05..ad7f8c6 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -17,6 +17,12 @@
 	git checkout A^0 &&
 	test_commit E bar E &&
 	test_commit F foo F &&
+	git checkout B &&
+	git merge E &&
+	git tag merge-E &&
+	test_commit G G &&
+	test_commit H H &&
+	test_commit I I &&
 	git checkout main &&
 
 	test_hook --setup post-rewrite <<-EOF
@@ -173,6 +179,48 @@
 	)
 }
 
+test_expect_success 'git rebase with failed pick' '
+	clear_hook_input &&
+	cat >todo <<-\EOF &&
+	exec >bar
+	merge -C merge-E E
+	exec >G
+	pick G
+	exec >H 2>I
+	pick H
+	fixup I
+	EOF
+
+	(
+		set_replace_editor todo &&
+		test_must_fail git rebase -i D D 2>err
+	) &&
+	grep "would be overwritten" err &&
+	rm bar &&
+
+	test_must_fail git rebase --continue 2>err &&
+	grep "would be overwritten" err &&
+	rm G &&
+
+	test_must_fail git rebase --continue 2>err &&
+	grep "would be overwritten" err &&
+	rm H &&
+
+	test_must_fail git rebase --continue 2>err &&
+	grep "would be overwritten" err &&
+	rm I &&
+
+	git rebase --continue &&
+	echo rebase >expected.args &&
+	cat >expected.data <<-EOF &&
+	$(git rev-parse merge-E) $(git rev-parse HEAD~2)
+	$(git rev-parse G) $(git rev-parse HEAD~1)
+	$(git rev-parse H) $(git rev-parse HEAD)
+	$(git rev-parse I) $(git rev-parse HEAD)
+	EOF
+	verify_hook_input
+'
+
 test_expect_success 'git rebase -i (unchanged)' '
 	git reset --hard D &&
 	clear_hook_input &&
diff --git a/t/t5411/test-0026-push-options.sh b/t/t5411/test-0026-push-options.sh
index 6dfc7b1..510fff3 100644
--- a/t/t5411/test-0026-push-options.sh
+++ b/t/t5411/test-0026-push-options.sh
@@ -18,7 +18,7 @@
 		HEAD:refs/for/main/topic \
 		>out-$test_count 2>&1 &&
 	make_user_friendly_and_stable_output <out-$test_count >actual &&
-	test_i18ngrep "fatal: the receiving end does not support push options" \
+	test_grep "fatal: the receiving end does not support push options" \
 		actual &&
 
 	test_cmp_refs -C "$upstream" <<-EOF
diff --git a/t/t5411/test-0027-push-options--porcelain.sh b/t/t5411/test-0027-push-options--porcelain.sh
index 768880b..9435457 100644
--- a/t/t5411/test-0027-push-options--porcelain.sh
+++ b/t/t5411/test-0027-push-options--porcelain.sh
@@ -19,7 +19,7 @@
 		HEAD:refs/for/main/topic \
 		>out-$test_count 2>&1 &&
 	make_user_friendly_and_stable_output <out-$test_count >actual &&
-	test_i18ngrep "fatal: the receiving end does not support push options" \
+	test_grep "fatal: the receiving end does not support push options" \
 		actual &&
 
 	test_cmp_refs -C "$upstream" <<-EOF
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index d18f282..1bc15a3 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -132,13 +132,18 @@
 '
 
 test_expect_success 'single given branch clone' '
-	git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
-	test_must_fail git --git-dir=branch-a/.git rev-parse origin/B
+	GIT_TRACE2_EVENT="$(pwd)/branch-a/trace2_event" \
+		git clone --single-branch --branch A "file://$(pwd)/." branch-a &&
+	test_must_fail git --git-dir=branch-a/.git rev-parse origin/B &&
+	grep \"fetch-info\".*\"haves\":0 branch-a/trace2_event &&
+	grep \"fetch-info\".*\"wants\":1 branch-a/trace2_event
 '
 
 test_expect_success 'clone shallow depth 1' '
-	git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
-	test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1
+	GIT_TRACE2_EVENT="$(pwd)/shallow0/trace2_event" \
+		git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 &&
+	test "$(git --git-dir=shallow0/.git rev-list --count HEAD)" = 1 &&
+	grep \"fetch-info\".*\"depth\":1 shallow0/trace2_event
 '
 
 test_expect_success 'clone shallow depth 1 with fsck' '
@@ -235,7 +240,10 @@
 test_expect_success 'deepening pull in shallow repo' '
 	(
 		cd shallow &&
-		git pull --depth 4 .. B
+		GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+			git pull --depth 4 .. B &&
+		grep \"fetch-info\".*\"depth\":4 trace2_event &&
+		grep \"fetch-info\".*\"shallows\":2 trace2_event
 	)
 '
 
@@ -306,9 +314,12 @@
 test_expect_success 'turn shallow to complete repository' '
 	(
 		cd shallow &&
-		git fetch --unshallow &&
+		GIT_TRACE2_EVENT="$(pwd)/trace2_event" \
+			git fetch --unshallow &&
 		! test -f .git/shallow &&
-		git fsck --full
+		git fsck --full &&
+		grep \"fetch-info\".*\"shallows\":2 trace2_event &&
+		grep \"fetch-info\".*\"depth\":2147483647 trace2_event
 	)
 '
 
@@ -403,7 +414,7 @@
 	test_commit -C myserver bar &&
 
 	git -C myclient fetch --progress origin 2>log &&
-	test_i18ngrep "remote: Total 3 " log
+	test_grep "remote: Total 3 " log
 '
 
 test_expect_success 'in_vain resetted upon ACK' '
@@ -435,7 +446,7 @@
 	# the client reports that first_anotherbranch_commit is common.
 	GIT_TRACE2_EVENT="$(pwd)/trace2" git -C myclient fetch --progress origin main 2>log &&
 	grep \"key\":\"total_rounds\",\"value\":\"6\" trace2 &&
-	test_i18ngrep "Total 3 " log
+	test_grep "Total 3 " log
 '
 
 test_expect_success 'fetch in shallow repo unreachable shallow objects' '
@@ -459,7 +470,7 @@
 		git fetch --depth=1 --progress 2>actual &&
 		# This should fetch only the empty commit, no tree or
 		# blob objects
-		test_i18ngrep "remote: Total 1" actual
+		test_grep "remote: Total 1" actual
 	)
 '
 
@@ -694,7 +705,7 @@
 	# unadvertised objects, so restrict this test to v0.
 	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C client fetch-pack ../server \
 		$(git -C server rev-parse refs/heads/main^) 2>err &&
-	test_i18ngrep "Server does not allow request for unadvertised object" err
+	test_grep "Server does not allow request for unadvertised object" err
 '
 
 check_prot_path () {
@@ -826,13 +837,15 @@
 '
 
 test_expect_success 'fetch shallow since ...' '
-	git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
+	GIT_TRACE2_EVENT=$(pwd)/shallow11/trace2_event \
+		git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
 	git -C shallow11 log --pretty=tformat:%s origin/main >actual &&
 	cat >expected <<-\EOF &&
 	three
 	two
 	EOF
-	test_cmp expected actual
+	test_cmp expected actual &&
+	grep \"fetch-info\".*\"deepen-since\":true shallow11/trace2_event
 '
 
 test_expect_success 'clone shallow since selects no commits' '
@@ -987,13 +1000,16 @@
 	test_config -C server uploadpack.allowfilter 1 &&
 
 	test_create_repo client &&
-	git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
+	GIT_TRACE2_EVENT=$(pwd)/client/trace2_event \
+		git -C client fetch-pack --filter=blob:limit=0 ../server HEAD &&
 
 	# Ensure that object is not inadvertently fetched
 	commit=$(git -C server rev-parse HEAD) &&
 	blob=$(git hash-object server/one.t) &&
 	git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
-	! grep "$blob" oids
+	! grep "$blob" oids &&
+
+	grep \"fetch-info\".*\"filter\":\"blob:limit\" client/trace2_event
 '
 
 test_expect_success 'filtering by size has no effect if support for it is not advertised' '
@@ -1010,7 +1026,7 @@
 	git -C client rev-list --objects --missing=allow-any "$commit" >oids &&
 	grep "$blob" oids &&
 
-	test_i18ngrep "filtering not recognized by server" err
+	test_grep "filtering not recognized by server" err
 '
 
 fetch_filter_blob_limit_zero () {
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index 0b8ab4a..138e677 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -144,7 +144,7 @@
 
 test_expect_success 'fsck with no skipList input' '
 	test_must_fail git fsck 2>err &&
-	test_i18ngrep "missingEmail" err
+	test_grep "missingEmail" err
 '
 
 test_expect_success 'setup sorted and unsorted skipLists' '
@@ -169,9 +169,9 @@
 test_expect_success 'fsck with invalid or bogus skipList input' '
 	git -c fsck.skipList=/dev/null -c fsck.missingEmail=ignore fsck &&
 	test_must_fail git -c fsck.skipList=does-not-exist -c fsck.missingEmail=ignore fsck 2>err &&
-	test_i18ngrep "could not open.*: does-not-exist" err &&
+	test_grep "could not open.*: does-not-exist" err &&
 	test_must_fail git -c fsck.skipList=.git/config -c fsck.missingEmail=ignore fsck 2>err &&
-	test_i18ngrep "invalid object name: \[core\]" err
+	test_grep "invalid object name: \[core\]" err
 '
 
 test_expect_success 'fsck with other accepted skipList input (comments & empty lines)' '
@@ -180,14 +180,14 @@
 	$(test_oid 001)
 	EOF
 	test_must_fail git -c fsck.skipList=SKIP.with-comment fsck 2>err-with-comment &&
-	test_i18ngrep "missingEmail" err-with-comment &&
+	test_grep "missingEmail" err-with-comment &&
 	cat >SKIP.with-empty-line <<-EOF &&
 	$(test_oid 001)
 
 	$(test_oid 002)
 	EOF
 	test_must_fail git -c fsck.skipList=SKIP.with-empty-line fsck 2>err-with-empty-line &&
-	test_i18ngrep "missingEmail" err-with-empty-line
+	test_grep "missingEmail" err-with-empty-line
 '
 
 test_expect_success 'fsck no garbage output from comments & empty lines errors' '
@@ -198,7 +198,7 @@
 test_expect_success 'fsck with invalid abbreviated skipList input' '
 	echo $commit | test_copy_bytes 20 >SKIP.abbreviated &&
 	test_must_fail git -c fsck.skipList=SKIP.abbreviated fsck 2>err-abbreviated &&
-	test_i18ngrep "^fatal: invalid object name: " err-abbreviated
+	test_grep "^fatal: invalid object name: " err-abbreviated
 '
 
 test_expect_success 'fsck with exhaustive accepted skipList input (various types of comments etc.)' '
@@ -231,10 +231,10 @@
 	test_must_fail git push --porcelain dst bogus &&
 	git --git-dir=dst/.git config receive.fsck.skipList does-not-exist &&
 	test_must_fail git push --porcelain dst bogus 2>err &&
-	test_i18ngrep "could not open.*: does-not-exist" err &&
+	test_grep "could not open.*: does-not-exist" err &&
 	git --git-dir=dst/.git config receive.fsck.skipList config &&
 	test_must_fail git push --porcelain dst bogus 2>err &&
-	test_i18ngrep "invalid object name: \[core\]" err &&
+	test_grep "invalid object name: \[core\]" err &&
 
 	git --git-dir=dst/.git config receive.fsck.skipList SKIP &&
 	git push --porcelain dst bogus
@@ -260,10 +260,10 @@
 	test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec &&
 	git --git-dir=dst/.git config fetch.fsck.skipList does-not-exist &&
 	test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err &&
-	test_i18ngrep "could not open.*: does-not-exist" err &&
+	test_grep "could not open.*: does-not-exist" err &&
 	git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/config &&
 	test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err &&
-	test_i18ngrep "invalid object name: \[core\]" err &&
+	test_grep "invalid object name: \[core\]" err &&
 
 	git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/SKIP &&
 	git --git-dir=dst/.git fetch "file://$(pwd)" $refspec
@@ -271,7 +271,7 @@
 
 test_expect_success 'fsck.<unknownmsg-id> dies' '
 	test_must_fail git -c fsck.whatEver=ignore fsck 2>err &&
-	test_i18ngrep "Unhandled message id: whatever" err
+	test_grep "Unhandled message id: whatever" err
 '
 
 test_expect_success 'push with receive.fsck.missingEmail=warn' '
@@ -293,7 +293,7 @@
 		receive.fsck.missingEmail warn &&
 	git push --porcelain dst bogus >act 2>&1 &&
 	grep "missingEmail" act &&
-	test_i18ngrep "skipping unknown msg id.*whatever" act &&
+	test_grep "skipping unknown msg id.*whatever" act &&
 	git --git-dir=dst/.git branch -D bogus &&
 	git --git-dir=dst/.git config --add \
 		receive.fsck.missingEmail ignore &&
@@ -321,7 +321,7 @@
 		fetch.fsck.missingEmail warn &&
 	git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 &&
 	grep "missingEmail" act &&
-	test_i18ngrep "Skipping unknown msg id.*whatever" act &&
+	test_grep "Skipping unknown msg id.*whatever" act &&
 	rm -rf dst &&
 	git init dst &&
 	git --git-dir=dst/.git config fetch.fsckobjects true &&
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 43b7bcd..7789ff1 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -1075,7 +1075,7 @@
 		cd eight &&
 		git remote prune origin
 	) >err 2>&1 &&
-	test_i18ngrep "has become dangling" err &&
+	test_grep "has become dangling" err &&
 
 	: And the dangling symref will not cause other annoying errors &&
 	(
@@ -1087,7 +1087,7 @@
 		cd eight &&
 		test_must_fail git branch nomore origin
 	) 2>err &&
-	test_i18ngrep "dangling symref" err
+	test_grep "dangling symref" err
 '
 
 test_expect_success 'show empty remote' '
@@ -1419,7 +1419,7 @@
 test_extra_arg () {
 	test_expect_success "extra args: $*" "
 		test_must_fail git remote $* bogus_extra_arg 2>actual &&
-		test_i18ngrep '^usage:' actual
+		test_grep '^usage:' actual
 	"
 }
 
@@ -1453,12 +1453,12 @@
 				oid=$(git rev-parse some-tag^{$type})
 			fi &&
 			test_must_fail git push origin $oid:dst 2>err &&
-			test_i18ngrep "error: The destination you" err &&
-			test_i18ngrep "hint: Did you mean" err &&
+			test_grep "error: The destination you" err &&
+			test_grep "hint: Did you mean" err &&
 			test_must_fail git -c advice.pushUnqualifiedRefName=false \
 				push origin $oid:dst 2>err &&
-			test_i18ngrep "error: The destination you" err &&
-			test_i18ngrep ! "hint: Did you mean" err ||
+			test_grep "error: The destination you" err &&
+			test_grep ! "hint: Did you mean" err ||
 			exit 1
 		done
 	)
@@ -1479,16 +1479,16 @@
 		git fetch --no-tags two &&
 
 		test_must_fail git push origin refs/remotes/two/another:dst 2>err &&
-		test_i18ngrep "error: The destination you" err &&
+		test_grep "error: The destination you" err &&
 
 		test_must_fail git push origin refs/remotes/tags-from-two/my-tag:dst-tag 2>err &&
-		test_i18ngrep "error: The destination you" err &&
+		test_grep "error: The destination you" err &&
 
 		test_must_fail git push origin refs/remotes/trees-from-two/my-head-tree:dst-tree 2>err &&
-		test_i18ngrep "error: The destination you" err &&
+		test_grep "error: The destination you" err &&
 
 		test_must_fail git push origin refs/remotes/blobs-from-two/my-file-blob:dst-blob 2>err &&
-		test_i18ngrep "error: The destination you" err
+		test_grep "error: The destination you" err
 	)
 '
 
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 34a1261..33d34d5 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -169,6 +169,7 @@
 	git clone . prune-fail &&
 	cd prune-fail &&
 	git update-ref refs/remotes/origin/extrabranch main &&
+	git pack-refs --all &&
 	: this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds  &&
 	>.git/packed-refs.new &&
 
@@ -415,9 +416,9 @@
 	(
 		cd descriptive &&
 		git fetch o 2>actual &&
-		test_i18ngrep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
-		test_i18ngrep "new tag.* -> descriptive-tag$" actual &&
-		test_i18ngrep "new ref.* -> crazy$" actual
+		test_grep "new branch.* -> refs/crazyheads/descriptive-branch$" actual &&
+		test_grep "new tag.* -> descriptive-tag$" actual &&
+		test_grep "new ref.* -> crazy$" actual
 	) &&
 	git checkout main
 '
@@ -802,7 +803,8 @@
 		cd super-clone &&
 		rm -rf .git/objects/info &&
 		git -c fetch.writeCommitGraph=true fetch origin &&
-		test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
+		test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain &&
+		git -c fetch.writeCommitGraph=true fetch --recurse-submodules origin
 	)
 '
 
@@ -1113,63 +1115,65 @@
 		git config gc.autoPackLimit 1 &&
 		git config gc.autoDetach false &&
 		GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
-		test_i18ngrep "Auto packing the repository" fetch.out &&
+		test_grep "Auto packing the repository" fetch.out &&
 		! grep "Should I try again" fetch.out
 	)
 '
 
-test_expect_success 'fetch aligned output' '
-	git clone . full-output &&
-	test_commit looooooooooooong-tag &&
-	(
-		cd full-output &&
-		git -c fetch.output=full fetch origin >actual 2>&1 &&
-		grep -e "->" actual | cut -c 22- >../actual
-	) &&
-	cat >expect <<-\EOF &&
-	main                 -> origin/main
-	looooooooooooong-tag -> looooooooooooong-tag
-	EOF
-	test_cmp expect actual
+for section in fetch transfer
+do
+	test_expect_success "$section.hideRefs affects connectivity check" '
+		GIT_TRACE="$PWD"/trace git -c $section.hideRefs=refs -c \
+			$section.hideRefs="!refs/tags/" fetch &&
+		grep "git rev-list .*--exclude-hidden=fetch" trace
+	'
+done
+
+test_expect_success 'prepare source branch' '
+	echo one >onebranch &&
+	git checkout --orphan onebranch &&
+	git rm --cached -r . &&
+	git add onebranch &&
+	git commit -m onebranch &&
+	git rev-list --objects onebranch -- >actual &&
+	# 3 objects should be created, at least ...
+	test 3 -le $(wc -l <actual)
 '
 
-test_expect_success 'fetch compact output' '
-	git clone . compact &&
-	test_commit extraaa &&
-	(
-		cd compact &&
-		git -c fetch.output=compact fetch origin >actual 2>&1 &&
-		grep -e "->" actual | cut -c 22- >../actual
-	) &&
-	cat >expect <<-\EOF &&
-	main       -> origin/*
-	extraaa    -> *
-	EOF
-	test_cmp expect actual
-'
+validate_store_type () {
+	git -C dest count-objects -v >actual &&
+	case "$store_type" in
+	packed)
+		grep "^count: 0$" actual ;;
+	loose)
+		grep "^packs: 0$" actual ;;
+	esac || {
+		echo "store_type is $store_type"
+		cat actual
+		false
+	}
+}
 
-test_expect_success '--no-show-forced-updates' '
-	mkdir forced-updates &&
-	(
-		cd forced-updates &&
-		git init &&
-		test_commit 1 &&
-		test_commit 2
-	) &&
-	git clone forced-updates forced-update-clone &&
-	git clone forced-updates no-forced-update-clone &&
-	git -C forced-updates reset --hard HEAD~1 &&
-	(
-		cd forced-update-clone &&
-		git fetch --show-forced-updates origin 2>output &&
-		test_i18ngrep "(forced update)" output
-	) &&
-	(
-		cd no-forced-update-clone &&
-		git fetch --no-show-forced-updates origin 2>output &&
-		test_i18ngrep ! "(forced update)" output
-	)
-'
+test_unpack_limit () {
+	store_type=$1
+
+	case "$store_type" in
+	packed) fetch_limit=1 transfer_limit=10000 ;;
+	loose) fetch_limit=10000 transfer_limit=1 ;;
+	esac
+
+	test_expect_success "fetch trumps transfer limit" '
+		rm -fr dest &&
+		git --bare init dest &&
+		git -C dest config fetch.unpacklimit $fetch_limit &&
+		git -C dest config transfer.unpacklimit $transfer_limit &&
+		git -C dest fetch .. onebranch &&
+		validate_store_type
+	'
+}
+
+test_unpack_limit packed
+test_unpack_limit loose
 
 setup_negotiation_tip () {
 	SERVER="$1"
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index 20d063f..5dbe107 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -15,6 +15,19 @@
 	done
 }
 
+test_expect_success 'set up fake upload-pack' '
+	# This can be used to simulate an upload-pack that just shows the
+	# contents of the "input" file (prepared with the test-tool pkt-line
+	# helper), and does not do any negotiation (since ls-remote does not
+	# need it).
+	write_script cat-input <<-\EOF
+	# send our initial advertisement/response
+	cat input
+	# soak up the flush packet from the client
+	cat
+	EOF
+'
+
 test_expect_success 'dies when no remote found' '
 	test_must_fail git ls-remote
 '
@@ -231,22 +244,25 @@
 
 test_expect_success 'ls-remote --symref' '
 	git fetch origin &&
-	echo "ref: refs/heads/main	HEAD" >expect &&
+	echo "ref: refs/heads/main	HEAD" >expect.v2 &&
 	generate_references \
 		HEAD \
-		refs/heads/main >>expect &&
+		refs/heads/main >>expect.v2 &&
+	echo "ref: refs/remotes/origin/main	refs/remotes/origin/HEAD" >>expect.v2 &&
 	oid=$(git rev-parse HEAD) &&
-	echo "$oid	refs/remotes/origin/HEAD" >>expect &&
+	echo "$oid	refs/remotes/origin/HEAD" >>expect.v2 &&
 	generate_references \
 		refs/remotes/origin/main \
 		refs/tags/mark \
 		refs/tags/mark1.1 \
 		refs/tags/mark1.10 \
-		refs/tags/mark1.2 >>expect &&
-	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
-	# protocol v0 here.
-	GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref >actual &&
-	test_cmp expect actual
+		refs/tags/mark1.2 >>expect.v2 &&
+	# v0 does not show non-HEAD symrefs
+	grep -v "ref: refs/remotes" <expect.v2 >expect.v0 &&
+	git -c protocol.version=0 ls-remote --symref >actual.v0 &&
+	test_cmp expect.v0 actual.v0 &&
+	git -c protocol.version=2 ls-remote --symref >actual.v2 &&
+	test_cmp expect.v2 actual.v2
 '
 
 test_expect_success 'ls-remote with filtered symref (refname)' '
@@ -255,76 +271,41 @@
 	ref: refs/heads/main	HEAD
 	$rev	HEAD
 	EOF
-	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
-	# protocol v0 here.
-	GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref . HEAD >actual &&
+	git ls-remote --symref . HEAD >actual &&
 	test_cmp expect actual
 '
 
-test_expect_failure 'ls-remote with filtered symref (--heads)' '
+test_expect_success 'ls-remote with filtered symref (--heads)' '
 	git symbolic-ref refs/heads/foo refs/tags/mark &&
-	cat >expect <<-EOF &&
+	cat >expect.v2 <<-EOF &&
 	ref: refs/tags/mark	refs/heads/foo
 	$rev	refs/heads/foo
 	$rev	refs/heads/main
 	EOF
-	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
-	# protocol v0 here.
-	GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&
-	test_cmp expect actual
+	grep -v "^ref: refs/tags/" <expect.v2 >expect.v0 &&
+	git -c protocol.version=0 ls-remote --symref --heads . >actual.v0 &&
+	test_cmp expect.v0 actual.v0 &&
+	git -c protocol.version=2 ls-remote --symref --heads . >actual.v2 &&
+	test_cmp expect.v2 actual.v2
 '
 
-test_expect_success 'ls-remote --symref omits filtered-out matches' '
-	cat >expect <<-EOF &&
-	$rev	refs/heads/foo
-	$rev	refs/heads/main
+test_expect_success 'indicate no refs in v0 standards-compliant empty remote' '
+	# Git does not produce an output like this, but it does match the
+	# standard and is produced by other implementations like JGit. So
+	# hard-code the case we care about.
+	#
+	# The actual capabilities do not matter; there are none that would
+	# change how ls-remote behaves.
+	oid=0000000000000000000000000000000000000000 &&
+	test-tool pkt-line pack >input.q <<-EOF &&
+	$oid capabilities^{}Qcaps-go-here
+	0000
 	EOF
-	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
-	# protocol v0 here.
-	GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&
-	test_cmp expect actual &&
-	GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref . "refs/heads/*" >actual &&
-	test_cmp expect actual
-'
+	q_to_nul <input.q >input &&
 
-test_lazy_prereq GIT_DAEMON '
-	test_bool_env GIT_TEST_GIT_DAEMON true
-'
-
-# This test spawns a daemon, so run it only if the user would be OK with
-# testing with git-daemon.
-test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-compliant empty remote' '
-	test_set_port JGIT_DAEMON_PORT &&
-	JGIT_DAEMON_PID= &&
-	git init --bare empty.git &&
-	>empty.git/git-daemon-export-ok &&
-	mkfifo jgit_daemon_output &&
-	{
-		jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
-		JGIT_DAEMON_PID=$!
-	} &&
-	test_when_finished kill "$JGIT_DAEMON_PID" &&
-	{
-		read line &&
-		case $line in
-		Exporting*)
-			;;
-		*)
-			echo "Expected: Exporting" &&
-			false;;
-		esac &&
-		read line &&
-		case $line in
-		"Listening on"*)
-			;;
-		*)
-			echo "Expected: Listening on" &&
-			false;;
-		esac
-	} <jgit_daemon_output &&
 	# --exit-code asks the command to exit with 2 when no
 	# matching refs are found.
-	test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
+	test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input .
 '
 
 test_expect_success 'ls-remote works outside repository' '
@@ -339,14 +320,14 @@
 test_expect_success 'ls-remote --sort fails gracefully outside repository' '
 	# Use a sort key that requires access to the referenced objects.
 	nongit test_must_fail git ls-remote --sort=authordate "$TRASH_DIRECTORY" 2>err &&
-	test_i18ngrep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err
+	test_grep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err
 '
 
 test_expect_success 'ls-remote patterns work with all protocol versions' '
 	git for-each-ref --format="%(objectname)	%(refname)" \
 		refs/heads/main refs/remotes/origin/main >expect &&
-	git -c protocol.version=1 ls-remote . main >actual.v1 &&
-	test_cmp expect actual.v1 &&
+	git -c protocol.version=0 ls-remote . main >actual.v0 &&
+	test_cmp expect actual.v0 &&
 	git -c protocol.version=2 ls-remote . main >actual.v2 &&
 	test_cmp expect actual.v2
 '
@@ -354,10 +335,49 @@
 test_expect_success 'ls-remote prefixes work with all protocol versions' '
 	git for-each-ref --format="%(objectname)	%(refname)" \
 		refs/heads/ refs/tags/ >expect &&
-	git -c protocol.version=1 ls-remote --heads --tags . >actual.v1 &&
-	test_cmp expect actual.v1 &&
+	git -c protocol.version=0 ls-remote --heads --tags . >actual.v0 &&
+	test_cmp expect actual.v0 &&
 	git -c protocol.version=2 ls-remote --heads --tags . >actual.v2 &&
 	test_cmp expect actual.v2
 '
 
+test_expect_success 'v0 clients can handle multiple symrefs' '
+	# Modern versions of Git will not return multiple symref capabilities
+	# for v0, so we have to hard-code the response. Note that we will
+	# always use both v0 and object-format=sha1 here, as the hard-coded
+	# response reflects a server that only supports those.
+	oid=1234567890123456789012345678901234567890 &&
+	symrefs="symref=refs/remotes/origin/HEAD:refs/remotes/origin/main" &&
+	symrefs="$symrefs symref=HEAD:refs/heads/main" &&
+
+	# Likewise we want to make sure our parser is not fooled by the string
+	# "symref" appearing as part of an earlier cap. But there is no way to
+	# do that via upload-pack, as arbitrary strings can appear only in a
+	# "symref" value itself (where we skip past the values as a whole)
+	# and "agent" (which always appears after "symref", so putting our
+	# parser in a confused state is less interesting).
+	caps="some other caps including a-fake-symref-cap" &&
+
+	test-tool pkt-line pack >input.q <<-EOF &&
+	$oid HEADQ$caps $symrefs
+	$oid refs/heads/main
+	$oid refs/remotes/origin/HEAD
+	$oid refs/remotes/origin/main
+	0000
+	EOF
+	q_to_nul <input.q >input &&
+
+	cat >expect <<-EOF &&
+	ref: refs/heads/main	HEAD
+	$oid	HEAD
+	$oid	refs/heads/main
+	ref: refs/remotes/origin/main	refs/remotes/origin/HEAD
+	$oid	refs/remotes/origin/HEAD
+	$oid	refs/remotes/origin/main
+	EOF
+
+	git ls-remote --symref --upload-pack=./cat-input . >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 54f422c..25772c8 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -24,6 +24,15 @@
 	)
 }
 
+setup_test_clone () {
+	test_dir="$1" &&
+	git clone one "$test_dir" &&
+	for r in one two three
+	do
+		git -C "$test_dir" remote add "$r" "../$r" || return 1
+	done
+}
+
 test_expect_success setup '
 	setup_repository one &&
 	setup_repository two &&
@@ -58,6 +67,13 @@
 	 test_cmp expect output)
 '
 
+test_expect_success 'git fetch --all --no-write-fetch-head' '
+	(cd test &&
+	rm -f .git/FETCH_HEAD &&
+	git fetch --all --no-write-fetch-head &&
+	test_path_is_missing .git/FETCH_HEAD)
+'
+
 test_expect_success 'git fetch --all should continue if a remote has errors' '
 	(git clone one test2 &&
 	 cd test2 &&
@@ -193,8 +209,8 @@
 	test_must_fail env GIT_TRACE="$PWD/trace" \
 		git fetch --jobs=2 --multiple one two 2>err &&
 	grep "preparing to run up to 2 tasks" trace &&
-	test_i18ngrep "could not fetch .one.*128" err &&
-	test_i18ngrep "could not fetch .two.*128" err
+	test_grep "could not fetch .one.*128" err &&
+	test_grep "could not fetch .two.*128" err
 '
 
 test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
@@ -202,4 +218,156 @@
 	 git fetch --multiple --jobs=0)
 '
 
+create_fetch_all_expect () {
+	cat >expect <<-\EOF
+	  one/main
+	  one/side
+	  origin/HEAD -> origin/main
+	  origin/main
+	  origin/side
+	  three/another
+	  three/main
+	  three/side
+	  two/another
+	  two/main
+	  two/side
+	EOF
+}
+
+for fetch_all in true false
+do
+	test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
+		test_dir="test_fetch_all_$fetch_all" &&
+		setup_test_clone "$test_dir" &&
+		(
+			cd "$test_dir" &&
+			git config fetch.all $fetch_all &&
+			git fetch --all &&
+			create_fetch_all_expect &&
+			git branch -r >actual &&
+			test_cmp expect actual
+		)
+	'
+done
+
+test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
+	setup_test_clone test9 &&
+	(
+		cd test9 &&
+		git config fetch.all true &&
+		git fetch &&
+		git branch -r >actual &&
+		create_fetch_all_expect &&
+		test_cmp expect actual
+	)
+'
+
+create_fetch_one_expect () {
+	cat >expect <<-\EOF
+	  one/main
+	  one/side
+	  origin/HEAD -> origin/main
+	  origin/main
+	  origin/side
+	EOF
+}
+
+test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
+	setup_test_clone test10 &&
+	(
+		cd test10 &&
+		git config fetch.all true &&
+		git fetch one &&
+		create_fetch_one_expect &&
+		git branch -r >actual &&
+		test_cmp expect actual
+	)
+'
+
+create_fetch_two_as_origin_expect () {
+	cat >expect <<-\EOF
+	  origin/HEAD -> origin/main
+	  origin/another
+	  origin/main
+	  origin/side
+	EOF
+}
+
+test_expect_success 'git config fetch.all false (fetch only default remote)' '
+	setup_test_clone test11 &&
+	(
+		cd test11 &&
+		git config fetch.all false &&
+		git remote set-url origin ../two &&
+		git fetch &&
+		create_fetch_two_as_origin_expect &&
+		git branch -r >actual &&
+		test_cmp expect actual
+	)
+'
+
+for fetch_all in true false
+do
+	test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
+		test_dir="test_no_all_fetch_all_$fetch_all" &&
+		setup_test_clone "$test_dir" &&
+		(
+			cd "$test_dir" &&
+			git config fetch.all $fetch_all &&
+			git remote set-url origin ../two &&
+			git fetch --no-all &&
+			create_fetch_two_as_origin_expect &&
+			git branch -r >actual &&
+			test_cmp expect actual
+		)
+	'
+done
+
+test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
+	setup_test_clone test12 &&
+	(
+		cd test12 &&
+		git config --unset-all fetch.all || true &&
+		git remote set-url origin ../two &&
+		git fetch --no-all &&
+		create_fetch_two_as_origin_expect &&
+		git branch -r >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
+	setup_test_clone test13 &&
+	(
+		cd test13 &&
+		git remote set-url origin ../two &&
+		git fetch --all --no-all &&
+		create_fetch_two_as_origin_expect &&
+		git branch -r >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
+	setup_test_clone test14 &&
+	(
+		cd test14 &&
+		git fetch --no-all one &&
+		create_fetch_one_expect &&
+		git branch -r >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
+	setup_test_clone test15 &&
+	(
+		cd test15 &&
+		git fetch --no-all --all &&
+		create_fetch_all_expect &&
+		git branch -r >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 98a27a29..2e7c0e1 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -120,6 +120,17 @@
 
 '
 
+for cmd in push fetch
+do
+	for opt in ipv4 ipv6
+	do
+		test_expect_success "reject 'git $cmd --no-$opt'" '
+			test_must_fail git $cmd --no-$opt 2>err &&
+			grep "unknown option .no-$opt" err
+		'
+	done
+done
+
 test_expect_success 'fetch without wildcard' '
 	mk_empty testrepo &&
 	(
@@ -216,7 +227,7 @@
 	GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" \
 		git -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
 	grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
-	test_i18ngrep "push negotiation failed" err
+	test_grep "push negotiation failed" err
 '
 
 test_expect_success 'push with negotiation does not attempt to fetch submodules' '
@@ -401,6 +412,11 @@
 
 '
 
+test_expect_success 'push with onelevel ref' '
+	mk_test testrepo heads/main &&
+	test_must_fail git push testrepo HEAD:refs/onelevel
+'
+
 test_expect_success 'push with colon-less refspec (1)' '
 
 	mk_test testrepo heads/frotz tags/frotz &&
@@ -898,6 +914,13 @@
 	test_must_fail git push testrepo --delete ""
 '
 
+test_expect_success 'push --delete onelevel refspecs' '
+	mk_test testrepo heads/main &&
+	git -C testrepo update-ref refs/onelevel refs/heads/main &&
+	git push testrepo --delete refs/onelevel &&
+	test_must_fail git -C testrepo rev-parse --verify refs/onelevel
+'
+
 test_expect_success 'warn on push to HEAD of non-bare repository' '
 	mk_test testrepo heads/main &&
 	(
@@ -1244,7 +1267,7 @@
 		# fetching the hidden object should fail by default
 		test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
 			git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
-		test_i18ngrep "Server does not allow request for unadvertised object" err &&
+		test_grep "Server does not allow request for unadvertised object" err &&
 		test_must_fail git rev-parse --verify refs/heads/copy &&
 
 		# the server side can allow it to succeed
@@ -1346,7 +1369,7 @@
 				git fetch ../testrepo/.git $SHA1_3 2>err &&
 			# ideally we would insist this be on a "remote error:"
 			# line, but it is racy; see the commit message
-			test_i18ngrep "not our ref.*$SHA1_3\$" err
+			test_grep "not our ref.*$SHA1_3\$" err
 		)
 	'
 done
@@ -1384,7 +1407,7 @@
 	oid=$(git -C testrepo rev-parse mytag^{commit}) &&
 	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
 		git fetch testrepo $oid 2>err &&
-	test_i18ngrep "Server does not allow request for unadvertised object" err
+	test_grep "Server does not allow request for unadvertised object" err
 '
 
 test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index a448e16..6d4944a 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 D=$(pwd)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 0b72112..47534f1 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -31,7 +31,7 @@
 	echo dirty >new_file &&
 	git add new_file &&
 	test_must_fail git pull "$@" . copy 2>err &&
-	test_i18ngrep -E "uncommitted changes.|overwritten by merge:" err
+	test_grep -E "uncommitted changes.|overwritten by merge:" err
 }
 
 test_expect_success setup '
@@ -151,7 +151,7 @@
 	echo file >expect &&
 	test_cmp expect file &&
 	test_must_fail git pull . "refs/nonexisting1/*:refs/nonexisting2/*" 2>err &&
-	test_i18ngrep "no candidates for merging" err &&
+	test_grep "no candidates for merging" err &&
 	test_cmp expect file
 '
 
@@ -164,7 +164,7 @@
 	test_cmp expect file &&
 	test_config branch.test.remote origin &&
 	test_must_fail git pull test_remote 2>err &&
-	test_i18ngrep "specify a branch on the command line" err &&
+	test_grep "specify a branch on the command line" err &&
 	test_cmp expect file
 '
 
@@ -176,7 +176,7 @@
 	echo file >expect &&
 	test_cmp expect file &&
 	test_must_fail git pull 2>err &&
-	test_i18ngrep "not currently on a branch" err &&
+	test_grep "not currently on a branch" err &&
 	test_cmp expect file
 '
 
@@ -189,7 +189,7 @@
 	echo file >expect &&
 	test_cmp expect file &&
 	test_must_fail git pull 2>err &&
-	test_i18ngrep "no tracking information" err &&
+	test_grep "no tracking information" err &&
 	test_cmp expect file
 '
 
@@ -202,7 +202,7 @@
 	echo file >expect &&
 	test_cmp expect file &&
 	test_must_fail git pull --all 2>err &&
-	test_i18ngrep "There is no tracking information" err &&
+	test_grep "There is no tracking information" err &&
 	test_cmp expect file
 '
 
@@ -214,7 +214,7 @@
 	echo file >expect &&
 	test_cmp expect file &&
 	test_must_fail git pull 2>err &&
-	test_i18ngrep "no such ref was fetched" err &&
+	test_grep "no such ref was fetched" err &&
 	test_cmp expect file
 '
 
@@ -248,13 +248,13 @@
 	test_file_not_empty unmerged &&
 	cp file expected &&
 	test_must_fail git pull . second 2>err &&
-	test_i18ngrep "Pulling is not possible because you have unmerged files." err &&
+	test_grep "Pulling is not possible because you have unmerged files." err &&
 	test_cmp expected file &&
 	git add file &&
 	git ls-files -u >unmerged &&
 	test_must_be_empty unmerged &&
 	test_must_fail git pull . second 2>err &&
-	test_i18ngrep "You have not concluded your merge" err &&
+	test_grep "You have not concluded your merge" err &&
 	test_cmp expected file
 '
 
@@ -264,7 +264,7 @@
 	echo file >expect &&
 	test_cmp expect file &&
 	git pull . second:third 2>err &&
-	test_i18ngrep "fetch updated the current branch head" err &&
+	test_grep "fetch updated the current branch head" err &&
 	echo modified >expect &&
 	test_cmp expect file &&
 	test_cmp_rev third second
@@ -277,7 +277,7 @@
 	test_cmp expect file &&
 	echo conflict >file &&
 	test_must_fail git pull . second:third 2>err &&
-	test_i18ngrep "Cannot fast-forward your working tree" err &&
+	test_grep "Cannot fast-forward your working tree" err &&
 	echo conflict >expect &&
 	test_cmp expect file &&
 	test_cmp_rev third second
@@ -375,7 +375,7 @@
 	test_tick &&
 	git commit -m "Create conflict" seq.txt &&
 	test_must_fail git pull --rebase . seq 2>err >out &&
-	test_i18ngrep "Resolve all conflicts manually" err
+	test_grep "Resolve all conflicts manually" err
 '
 
 test_expect_success 'failed --rebase shows advice' '
@@ -389,14 +389,14 @@
 	git checkout -f -b fails-to-rebase HEAD^ &&
 	test_commit v2-without-cr file "2" file2-lf &&
 	test_must_fail git pull --rebase . diverging 2>err >out &&
-	test_i18ngrep "Resolve all conflicts manually" err
+	test_grep "Resolve all conflicts manually" err
 '
 
 test_expect_success '--rebase fails with multiple branches' '
 	git reset --hard before-rebase &&
 	test_must_fail git pull --rebase . copy main 2>err &&
 	test_cmp_rev HEAD before-rebase &&
-	test_i18ngrep "Cannot rebase onto multiple branches" err &&
+	test_grep "Cannot rebase onto multiple branches" err &&
 	echo modified >expect &&
 	git show HEAD:file >actual &&
 	test_cmp expect actual
@@ -520,7 +520,7 @@
 	echo new >expect &&
 	git show HEAD:file2 >actual &&
 	test_cmp expect actual &&
-	test_i18ngrep "ignoring --verify-signatures for rebase" err
+	test_grep "ignoring --verify-signatures for rebase" err
 '
 
 test_expect_success 'pull --rebase does not warn on --no-verify-signatures' '
@@ -530,7 +530,7 @@
 	echo new >expect &&
 	git show HEAD:file2 >actual &&
 	test_cmp expect actual &&
-	test_i18ngrep ! "verify-signatures" err
+	test_grep ! "verify-signatures" err
 '
 
 # add a feature branch, keep-merge, that is merged into main, so the
@@ -740,7 +740,7 @@
 		test_cmp expect actual &&
 		git show :staged-file >actual &&
 		test_cmp expect actual &&
-		test_i18ngrep "unborn branch with changes added to the index" err
+		test_grep "unborn branch with changes added to the index" err
 	)
 '
 
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 264de29..db00c43 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -93,7 +94,7 @@
 	(cd clonedwfh && git init &&
 	test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err &&
 	test_must_be_empty out &&
-	test_i18ngrep "no-write-fetch-head" err)
+	test_grep "no-write-fetch-head" err)
 '
 
 test_expect_success 'git pull --force' '
@@ -142,7 +143,7 @@
 		cd clonedry &&
 		git pull --dry-run ../parent &&
 		test_path_is_missing .git/FETCH_HEAD &&
-		test_path_is_missing .git/refs/heads/main &&
+		test_ref_missing refs/heads/main &&
 		test_path_is_missing .git/index &&
 		test_path_is_missing file
 	)
@@ -156,7 +157,7 @@
 		git remote add origin ../parent &&
 		git pull --all --dry-run &&
 		test_path_is_missing .git/FETCH_HEAD &&
-		test_path_is_missing .git/refs/remotes/origin/main &&
+		test_ref_missing refs/remotes/origin/main &&
 		test_path_is_missing .git/index &&
 		test_path_is_missing file
 	)
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
index 394bc60..cc5496e 100755
--- a/t/t5522-pull-symlink.sh
+++ b/t/t5522-pull-symlink.sh
@@ -79,7 +79,9 @@
 		git commit -m push ./file &&
 		git push
 	) &&
-	test push = $(git show HEAD:subdir/file)
+	echo push >expect &&
+	git show HEAD:subdir/file >actual &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index c9acc07..1f859ad 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -61,12 +61,20 @@
 	check_config topic_2 upstream refs/heads/other2
 '
 
-test_expect_success 'push -u --all' '
+test_expect_success 'push -u --all(the same behavior with--branches)' '
 	git branch all1 &&
 	git branch all2 &&
 	git push -u --all &&
 	check_config all1 upstream refs/heads/all1 &&
-	check_config all2 upstream refs/heads/all2
+	check_config all2 upstream refs/heads/all2 &&
+	git config --get-regexp branch.all* > expect &&
+	git config --remove-section branch.all1 &&
+	git config --remove-section branch.all2 &&
+	git push -u --branches &&
+	check_config all1 upstream refs/heads/all1 &&
+	check_config all2 upstream refs/heads/all2 &&
+	git config --get-regexp branch.all* > actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'push -u HEAD' '
@@ -79,7 +87,7 @@
 	ensure_fresh_upstream &&
 
 	test_terminal git push -u upstream main >out 2>err &&
-	test_i18ngrep "Writing objects" err
+	test_grep "Writing objects" err
 '
 
 test_expect_success 'progress messages do not go to non-tty' '
@@ -87,7 +95,7 @@
 
 	# skip progress messages, since stderr is non-tty
 	git push -u upstream main >out 2>err &&
-	test_i18ngrep ! "Writing objects" err
+	test_grep ! "Writing objects" err
 '
 
 test_expect_success 'progress messages go to non-tty (forced)' '
@@ -95,22 +103,22 @@
 
 	# force progress messages to stderr, even though it is non-tty
 	git push -u --progress upstream main >out 2>err &&
-	test_i18ngrep "Writing objects" err
+	test_grep "Writing objects" err
 '
 
 test_expect_success TTY 'push -q suppresses progress' '
 	ensure_fresh_upstream &&
 
 	test_terminal git push -u -q upstream main >out 2>err &&
-	test_i18ngrep ! "Writing objects" err
+	test_grep ! "Writing objects" err
 '
 
 test_expect_success TTY 'push --no-progress suppresses progress' '
 	ensure_fresh_upstream &&
 
 	test_terminal git push -u --no-progress upstream main >out 2>err &&
-	test_i18ngrep ! "Unpacking objects" err &&
-	test_i18ngrep ! "Writing objects" err
+	test_grep ! "Unpacking objects" err &&
+	test_grep ! "Writing objects" err
 '
 
 test_expect_success TTY 'quiet push' '
diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh
index 45815f7..3a28f1d 100755
--- a/t/t5525-fetch-tagopt.sh
+++ b/t/t5525-fetch-tagopt.sh
@@ -2,6 +2,7 @@
 
 test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 setup_clone () {
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index b9546ef..5e56620 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -167,6 +167,19 @@
 	verify_fetch_result actual.err
 '
 
+test_expect_success "fetch --recurse-submodules honors --no-write-fetch-head" '
+	(
+		cd downstream &&
+		git submodule foreach --recursive \
+		sh -c "cd \"\$(git rev-parse --git-dir)\" && rm -f FETCH_HEAD" &&
+
+		git fetch --recurse-submodules --no-write-fetch-head &&
+
+		git submodule foreach --recursive \
+		sh -c "cd \"\$(git rev-parse --git-dir)\" && ! test -f FETCH_HEAD"
+	)
+'
+
 test_expect_success "submodule.recurse option triggers recursive fetch" '
 	add_submodule_commits &&
 	(
@@ -758,7 +771,7 @@
 	git -C dst fetch --recurse-submodules &&
 
 	# Break the receiving submodule
-	rm -f dst/sub/.git/HEAD &&
+	rm -r dst/sub/.git/objects &&
 
 	# NOTE: without the fix the following tests will recurse forever!
 	# They should terminate with an error.
@@ -1167,4 +1180,17 @@
 	test_line_count = 2 fetch-subs
 '
 
+test_expect_success "fetch --all with --no-recurse-submodules only fetches superproject" '
+	test_when_finished "rm -rf src_clone" &&
+
+	git clone --recurse-submodules src src_clone &&
+	(
+		cd src_clone &&
+		git remote add secondary ../src &&
+		git config submodule.recurse true &&
+		git fetch --all --no-recurse-submodules 2>../fetch-log
+	) &&
+	! grep "Fetching submodule" fetch-log
+'
+
 test_done
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index 284e20f..14f7ece 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -179,7 +179,7 @@
 test_expect_success '"matching" fails if none match' '
 	git init --bare empty &&
 	test_must_fail git push empty : 2>actual &&
-	test_i18ngrep "Perhaps you should specify a branch" actual
+	test_grep "Perhaps you should specify a branch" actual
 '
 
 test_expect_success 'push ambiguously named branch with upstream, matching and simple' '
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 7c1460e..7172780 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -2,6 +2,7 @@
 
 test_description='errors in upload-pack'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 D=$(pwd)
@@ -35,8 +36,8 @@
 	printf "%04xwant %s\n00000009done\n0000" \
 		$(($hexsz + 10)) $head >input &&
 	test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
-	test_i18ngrep "unable to read" output.err &&
-	test_i18ngrep "pack-objects died" output.err
+	test_grep "unable to read" output.err &&
+	test_grep "pack-objects died" output.err
 '
 
 test_expect_success 'corrupt repo differently' '
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 302e4cb..f3fff55 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -311,7 +311,7 @@
 	git -C work commit -m "bad commit" &&
 	test_when_finished "git -C work reset --hard HEAD^" &&
 	test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git main 2>err &&
-	test_i18ngrep "is a tag, not a commit" err
+	test_grep "is a tag, not a commit" err
 '
 
 test_expect_success 'push fails if recurse submodules option passed as yes' '
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index 7c0a148..c91a62b 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -68,13 +68,13 @@
 test_expect_success 'push --signed fails with a receiver without push certificate support' '
 	prepare_dst &&
 	test_must_fail git push --signed dst noop ff +noff 2>err &&
-	test_i18ngrep "the receiving end does not support" err
+	test_grep "the receiving end does not support" err
 '
 
 test_expect_success 'push --signed=1 is accepted' '
 	prepare_dst &&
 	test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
-	test_i18ngrep "the receiving end does not support" err
+	test_grep "the receiving end does not support" err
 '
 
 test_expect_success GPG 'no certificate for a signed push with no update' '
@@ -303,7 +303,7 @@
 		EOF
 		sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
 	) >expect.in &&
-	key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+	key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
 	sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
 
 	noop=$(git rev-parse noop) &&
@@ -378,7 +378,7 @@
 			--signed --atomic --porcelain \
 			dst noop ff noff >out 2>err &&
 
-	test_i18ngrep ! "gpg failed to sign" err &&
+	test_grep ! "gpg failed to sign" err &&
 	cat >expect <<-EOF &&
 	To dst
 	=	refs/heads/noop:refs/heads/noop	[up to date]
diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh
index 91f28c2..23bf696 100755
--- a/t/t5536-fetch-conflicts.sh
+++ b/t/t5536-fetch-conflicts.sh
@@ -40,7 +40,7 @@
 		"+refs/heads/branch2:refs/remotes/origin/branch1" && (
 		cd ccc &&
 		test_must_fail git fetch origin 2>error &&
-		test_i18ngrep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
+		test_grep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
 	)
 '
 
@@ -67,7 +67,7 @@
 		test_must_fail git fetch origin \
 			refs/heads/*:refs/remotes/origin/* \
 			refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
-		test_i18ngrep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
+		test_grep "fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1" error
 	)
 '
 
@@ -78,8 +78,8 @@
 		git fetch origin \
 			refs/heads/branch1:refs/remotes/origin/branch2 \
 			refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
-		test_i18ngrep "warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2" error &&
-		test_i18ngrep "warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1" error
+		test_grep "warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2" error &&
+		test_grep "warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1" error
 	)
 '
 
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index d0211cd..71428f3 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -153,7 +153,7 @@
 '
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
-	test_i18ngrep "Updates were rejected because" \
+	test_grep "Updates were rejected because" \
 		output
 '
 
@@ -232,8 +232,9 @@
 	test_config -C "$d" http.receivepack true &&
 	up="$HTTPD_URL"/smart/atomic-branches.git &&
 
-	# break ref updates for other on the remote site
-	mkdir "$d/refs/heads/other.lock" &&
+	# Create d/f conflict to break ref updates for other on the remote site.
+	git -C "$d" update-ref -d refs/heads/other &&
+	git -C "$d" update-ref refs/heads/other/conflict HEAD &&
 
 	# add the new commit to other
 	git branch -f other collateral &&
@@ -241,18 +242,9 @@
 	# --atomic should cause entire push to be rejected
 	test_must_fail git push --atomic "$up" atomic other 2>output  &&
 
-	# the new branch should not have been created upstream
+	# The atomic and other branches should not be created upstream.
 	test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
-
-	# upstream should still reflect atomic2, the last thing we pushed
-	# successfully
-	git rev-parse atomic2 >expected &&
-	# ...to other.
-	git -C "$d" rev-parse refs/heads/other >actual &&
-	test_cmp expected actual &&
-
-	# the new branch should not have been created upstream
-	test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
+	test_must_fail git -C "$d" show-ref --verify refs/heads/other &&
 
 	# the failed refs should be indicated to the user
 	grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
@@ -297,7 +289,7 @@
 	cd "$ROOT_PATH"/test_repo_clone &&
 	test_commit noisy &&
 	test_terminal git push >output 2>&1 &&
-	test_i18ngrep "^Writing objects" output
+	test_grep "^Writing objects" output
 '
 
 test_expect_success TTY 'push --quiet silences status and progress' '
@@ -311,16 +303,16 @@
 	cd "$ROOT_PATH"/test_repo_clone &&
 	test_commit no-progress &&
 	test_terminal git push --no-progress >output 2>&1 &&
-	test_i18ngrep "^To http" output &&
-	test_i18ngrep ! "^Writing objects" output
+	test_grep "^To http" output &&
+	test_grep ! "^Writing objects" output
 '
 
 test_expect_success 'push --progress shows progress to non-tty' '
 	cd "$ROOT_PATH"/test_repo_clone &&
 	test_commit progress &&
 	git push --progress >output 2>&1 &&
-	test_i18ngrep "^To http" output &&
-	test_i18ngrep "^Writing objects" output
+	test_grep "^To http" output &&
+	test_grep "^Writing objects" output
 '
 
 test_expect_success 'http push gives sane defaults to reflog' '
@@ -489,10 +481,10 @@
 		-c color.push=always \
 		push origin origin/main^:main 2>act &&
 	test_decode_color <act >decoded &&
-	test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
-	test_i18ngrep "<RED>error: failed to push some refs" decoded &&
-	test_i18ngrep "<YELLOW>hint: " decoded &&
-	test_i18ngrep ! "^hint: " decoded
+	test_grep "<RED>.*rejected.*<RESET>" decoded &&
+	test_grep "<RED>error: failed to push some refs" decoded &&
+	test_grep "<YELLOW>hint: " decoded &&
+	test_grep ! "^hint: " decoded
 '
 
 test_expect_success 'report error server does not provide ref status' '
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 7043112..04b47ad 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -117,7 +117,10 @@
 		test_commit five &&
 		git checkout main &&
 		test_commit six &&
-		test_must_fail git push --atomic --all up
+		test_must_fail git push --atomic --all up >output-all 2>&1 &&
+		# --all and --branches have the same behavior when be combined with --atomic
+		test_must_fail git push --atomic --branches up >output-branches 2>&1 &&
+		test_cmp output-all output-branches
 	) &&
 	test_refs main HEAD@{7} &&
 	test_refs second HEAD@{4}
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index a158e7d..fb13549 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -252,7 +252,7 @@
 	mk_http_pair false &&
 	test_commit -C test_http_clone one &&
 	test_must_fail git -C test_http_clone push --push-option=asdf origin main 2>actual &&
-	test_i18ngrep "the receiving end does not support push options" actual &&
+	test_grep "the receiving end does not support push options" actual &&
 	git -C test_http_clone push origin main
 '
 
diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh
index eed3c9d..9fc9ba5 100755
--- a/t/t5546-receive-limits.sh
+++ b/t/t5546-receive-limits.sh
@@ -9,10 +9,26 @@
 # When the limit is 1, `git receive-pack` will call `git index-pack`.
 # When the limit is 10000, `git receive-pack` will call `git unpack-objects`.
 
+validate_store_type () {
+	git -C dest count-objects -v >actual &&
+	case "$store_type" in
+	index)
+		grep "^count: 0$" actual ;;
+	unpack)
+		grep "^packs: 0$" actual ;;
+	esac || {
+		echo "store_type is $store_type"
+		cat actual
+		false;
+	}
+}
+
 test_pack_input_limit () {
-	case "$1" in
-	index) unpack_limit=1 ;;
-	unpack) unpack_limit=10000 ;;
+	store_type=$1
+
+	case "$store_type" in
+	index) unpack_limit=1 other_limit=10000 ;;
+	unpack) unpack_limit=10000 other_limit=1 ;;
 	esac
 
 	test_expect_success 'prepare destination repository' '
@@ -43,6 +59,19 @@
 		git --git-dir=dest config receive.maxInputSize 0 &&
 		git push dest HEAD
 	'
+
+	test_expect_success 'prepare destination repository (once more)' '
+		rm -fr dest &&
+		git --bare init dest
+	'
+
+	test_expect_success 'receive trumps transfer' '
+		git --git-dir=dest config receive.unpacklimit "$unpack_limit" &&
+		git --git-dir=dest config transfer.unpacklimit "$other_limit" &&
+		git push dest HEAD &&
+		validate_store_type
+	'
+
 }
 
 test_expect_success "create known-size (1024 bytes) commit" '
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 8f182a3..4c3b327 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -66,11 +66,11 @@
 	setup_post_update_server_info_hook "$HTTPD_DOCUMENT_ROOT_PATH/empty.git"
 '
 
-test_expect_success 'empty dumb HTTP repository has default hash algorithm' '
+test_expect_success 'empty dumb HTTP repository falls back to SHA1' '
 	test_when_finished "rm -fr clone-empty" &&
 	git clone $HTTPD_URL/dumb/empty.git clone-empty &&
 	git -C clone-empty rev-parse --show-object-format >empty-format &&
-	test "$(cat empty-format)" = "$(test_oid algo)"
+	test "$(cat empty-format)" = sha1
 '
 
 setup_askpass_helper
@@ -376,7 +376,7 @@
 
 test_expect_success 'remote-http complains cleanly about malformed urls' '
 	test_must_fail git remote-http http::/example.com/repo.git 2>stderr &&
-	test_i18ngrep "url has no scheme" stderr
+	test_grep "url has no scheme" stderr
 '
 
 # NEEDSWORK: Writing commands to git-remote-curl can race against the latter
@@ -385,7 +385,7 @@
 test_expect_success 'remote-http complains cleanly about empty scheme' '
 	test_must_fail ok=sigpipe git ls-remote \
 		http::${HTTPD_URL#http}/dumb/repo.git 2>stderr &&
-	test_i18ngrep "url has no scheme" stderr
+	test_grep "url has no scheme" stderr
 '
 
 test_expect_success 'redirects can be forbidden/allowed' '
@@ -397,7 +397,7 @@
 
 test_expect_success 'redirects are reported to stderr' '
 	# just look for a snippet of the redirected-to URL
-	test_i18ngrep /dumb/ stderr
+	test_grep /dumb/ stderr
 '
 
 test_expect_success 'non-initial redirects can be forbidden' '
@@ -466,7 +466,7 @@
 
 test_expect_success 'print HTTP error when any intermediate redirect throws error' '
 	test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr &&
-	test_i18ngrep "unable to access.*/redir-to/502" stderr
+	test_grep "unable to access.*/redir-to/502" stderr
 '
 
 test_expect_success 'fetching via http alternates works' '
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 0908534..a623a10 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -275,7 +275,7 @@
 
 test_expect_success 'invalid Content-Type rejected' '
 	test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
-	test_i18ngrep "not valid:" actual
+	test_grep "not valid:" actual
 '
 
 test_expect_success 'create namespaced refs' '
@@ -359,7 +359,9 @@
 
 	# now assign tags to all the dangling commits we created above
 	tag=$(perl -e "print \"bla\" x 30") &&
-	sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+	sed -e "s|^:\([^ ]*\) \(.*\)$|create refs/tags/$tag-\1 \2|" <marks >input &&
+	git update-ref --stdin <input &&
+	rm input
 }
 
 test_expect_success 'create 2,000 tags in the repo' '
@@ -558,7 +560,7 @@
 
 test_expect_success 'server-side error detected' '
 	test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
-	test_i18ngrep "server-side error" actual
+	test_grep "server-side error" actual
 '
 
 test_expect_success 'http auth remembers successful credentials' '
@@ -611,6 +613,33 @@
 	grep symref=HEAD:refs/heads/ trace
 '
 
+test_expect_success 'create empty http-accessible SHA-256 repository' '
+	mkdir "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+	(cd "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+	 git --bare init --object-format=sha256
+	)
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v2' '
+	rm -fr sha256 &&
+	echo sha256 >expected &&
+	git -c protocol.version=2 clone "$HTTPD_URL/smart/sha256.git" &&
+	git -C sha256 rev-parse --show-object-format >actual &&
+	test_cmp actual expected &&
+	git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v0' '
+	rm -fr sha256 &&
+	echo sha256 >expected &&
+	GIT_TRACE=1 GIT_TRACE_PACKET=1 git -c protocol.version=0 clone "$HTTPD_URL/smart/sha256.git" &&
+	git -C sha256 rev-parse --show-object-format >actual &&
+	test_cmp actual expected &&
+	git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+	test_must_be_empty actual
+'
+
 test_expect_success 'passing hostname resolution information works' '
 	BOGUS_HOST=gitbogusexamplehost.invalid &&
 	BOGUS_HTTPD_URL=$HTTPD_PROTO://$BOGUS_HOST:$LIB_HTTPD_PORT &&
@@ -704,4 +733,22 @@
 	! grep "//" log
 '
 
+test_expect_success 'tag following always works over v0 http' '
+	upstream=$HTTPD_DOCUMENT_ROOT_PATH/tags &&
+	git init "$upstream" &&
+	(
+		cd "$upstream" &&
+		git commit --allow-empty -m base &&
+		git tag not-annotated &&
+		git tag -m foo annotated
+	) &&
+	git init tags &&
+	git -C tags -c protocol.version=0 \
+		fetch --depth 1 $HTTPD_URL/smart/tags \
+		refs/tags/annotated:refs/tags/annotated &&
+	git -C "$upstream" for-each-ref refs/tags >expect &&
+	git -C tags for-each-ref >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh
index 165427d..b55a9f6 100755
--- a/t/t5552-skipping-fetch-negotiator.sh
+++ b/t/t5552-skipping-fetch-negotiator.sh
@@ -3,6 +3,22 @@
 test_description='test skipping fetch negotiator'
 . ./test-lib.sh
 
+test_expect_success 'fetch.negotiationalgorithm config' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	cat >repo/.git/config <<-\EOF &&
+	[fetch]
+	negotiationAlgorithm
+	EOF
+	cat >expect <<-\EOF &&
+	error: missing value for '\''fetch.negotiationalgorithm'\''
+	fatal: bad config variable '\''fetch.negotiationalgorithm'\'' in file '\''.git/config'\'' at line 2
+	EOF
+	test_expect_code 128 git -C repo fetch >out 2>actual &&
+	test_must_be_empty out &&
+	test_cmp expect actual
+'
+
 have_sent () {
 	while test "$#" -ne 0
 	do
diff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh
index b1cfe8b..3dcb334 100755
--- a/t/t5555-http-smart-common.sh
+++ b/t/t5555-http-smart-common.sh
@@ -131,7 +131,6 @@
 	fetch=shallow wait-for-done
 	server-option
 	object-format=$(test_oid algo)
-	object-info
 	0000
 	EOF
 
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index afd5692..1ca5f74 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -33,6 +33,15 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'clone with path bundle and non-default hash' '
+	test_when_finished "rm -rf clone-path-non-default-hash" &&
+	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
+		clone-from clone-path-non-default-hash &&
+	git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
+	git -C clone-from rev-parse topic >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'clone with file:// bundle' '
 	git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
 		clone-from clone-file &&
@@ -284,6 +293,15 @@
 	test_config -C clone-http log.excludedecoration refs/bundle/
 '
 
+test_expect_success 'clone HTTP bundle with non-default hash' '
+	test_when_finished "rm -rf clone-http-non-default-hash" &&
+	GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
+		"$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
+	git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
+	git -C clone-from rev-parse topic >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'clone bundle list (HTTP, no heuristic)' '
 	test_when_finished rm -f trace*.txt &&
 
@@ -1018,6 +1036,40 @@
 	test_cmp expect refs
 '
 
+test_expect_success 'bundles are downloaded once during fetch --all' '
+	test_when_finished rm -rf download-* trace*.txt fetch-mult &&
+
+	cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+	[bundle]
+		version = 1
+		mode = all
+		heuristic = creationToken
+
+	[bundle "bundle-1"]
+		uri = bundle-1.bundle
+		creationToken = 1
+
+	[bundle "bundle-2"]
+		uri = bundle-2.bundle
+		creationToken = 2
+
+	[bundle "bundle-3"]
+		uri = bundle-3.bundle
+		creationToken = 3
+	EOF
+
+	git clone --single-branch --branch=left \
+		--bundle-uri="$HTTPD_URL/bundle-list" \
+		"$HTTPD_URL/smart/fetch.git" fetch-mult &&
+	git -C fetch-mult remote add dup1 "$HTTPD_URL/smart/fetch.git" &&
+	git -C fetch-mult remote add dup2 "$HTTPD_URL/smart/fetch.git" &&
+
+	GIT_TRACE2_EVENT="$(pwd)/trace-mult.txt" \
+		git -C fetch-mult fetch --all &&
+	grep "\"child_start\".*\"git-remote-https\",\"$HTTPD_URL/bundle-list\"" \
+		trace-mult.txt >bundle-fetches &&
+	test_line_count = 1 bundle-fetches
+'
 # Do not add tests here unless they use the HTTP server, as they will
 # not run unless the HTTP dependencies exist.
 
diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
index 718dd9b..9babb9a 100644
--- a/t/t5562/invoke-with-content-length.pl
+++ b/t/t5562/invoke-with-content-length.pl
@@ -1,4 +1,4 @@
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
new file mode 100755
index 0000000..ab8a721
--- /dev/null
+++ b/t/t5563-simple-http-auth.sh
@@ -0,0 +1,329 @@
+#!/bin/sh
+
+test_description='test http auth header and credential helper interop'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+enable_cgipassauth
+if ! test_have_prereq CGIPASSAUTH
+then
+	skip_all="no CGIPassAuth support"
+	test_done
+fi
+start_httpd
+
+test_expect_success 'setup_credential_helper' '
+	mkdir "$TRASH_DIRECTORY/bin" &&
+	PATH=$PATH:"$TRASH_DIRECTORY/bin" &&
+	export PATH &&
+
+	CREDENTIAL_HELPER="$TRASH_DIRECTORY/bin/git-credential-test-helper" &&
+	write_script "$CREDENTIAL_HELPER" <<-\EOF
+	cmd=$1
+	teefile=$cmd-query.cred
+	catfile=$cmd-reply.cred
+	sed -n -e "/^$/q" -e "p" >>$teefile
+	if test "$cmd" = "get"
+	then
+		cat $catfile
+	fi
+	EOF
+'
+
+set_credential_reply () {
+	cat >"$TRASH_DIRECTORY/$1-reply.cred"
+}
+
+expect_credential_query () {
+	cat >"$TRASH_DIRECTORY/$1-expect.cred" &&
+	test_cmp "$TRASH_DIRECTORY/$1-expect.cred" \
+		 "$TRASH_DIRECTORY/$1-query.cred"
+}
+
+per_test_cleanup () {
+	rm -f *.cred &&
+	rm -f "$HTTPD_ROOT_PATH"/custom-auth.valid \
+	      "$HTTPD_ROOT_PATH"/custom-auth.challenge
+}
+
+test_expect_success 'setup repository' '
+	test_commit foo &&
+	git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+	git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
+'
+
+test_expect_success 'access using basic auth' '
+	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 &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=alice
+	password=secret-passwd
+	EOF
+'
+
+test_expect_success 'access using basic auth invalid credentials' '
+	test_when_finished "per_test_cleanup" &&
+
+	set_credential_reply get <<-EOF &&
+	username=baduser
+	password=wrong-passwd
+	EOF
+
+	# Basic base64(alice:secret-passwd)
+	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query erase <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=baduser
+	password=wrong-passwd
+	wwwauth[]=Basic realm="example.com"
+	EOF
+'
+
+test_expect_success 'access using basic auth with extra challenges' '
+	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 &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: FooBar param1="value1" param2="value2"
+	WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=alice
+	password=secret-passwd
+	EOF
+'
+
+test_expect_success 'access using basic auth mixed-case wwwauth header name' '
+	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 &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	www-authenticate: foobar param1="value1" param2="value2"
+	WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
+	WwW-aUtHeNtIcAtE: baSiC realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=foobar param1="value1" param2="value2"
+	wwwauth[]=BEARER authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=baSiC realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=alice
+	password=secret-passwd
+	EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header continuations' '
+	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 &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	# Note that leading and trailing whitespace is important to correctly
+	# simulate a continuation/folded header.
+	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
+	WWW-Authenticate: FooBar param1="value1"
+	 param2="value2"
+	WWW-Authenticate: Bearer authorize_uri="id.example.com"
+	 p=1
+	 q=0
+	WWW-Authenticate: Basic realm="example.com"
+	EOF
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=alice
+	password=secret-passwd
+	EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header empty continuations' '
+	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 &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+	# Note that leading and trailing whitespace is important to correctly
+	# simulate a continuation/folded header.
+	printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+	printf " \r\n" >>"$CHALLENGE" &&
+	printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+	printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+	printf " p=1\r\n" >>"$CHALLENGE" &&
+	printf " \r\n" >>"$CHALLENGE" &&
+	printf " q=0\r\n" >>"$CHALLENGE" &&
+	printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=alice
+	password=secret-passwd
+	EOF
+'
+
+test_expect_success 'access using basic auth with wwwauth header mixed line-endings' '
+	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 &&
+	Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
+	EOF
+
+	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
+
+	# Note that leading and trailing whitespace is important to correctly
+	# simulate a continuation/folded header.
+	printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+	printf " \r\n" >>"$CHALLENGE" &&
+	printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+	printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
+
+	test_config_global credential.helper test-helper &&
+	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
+
+	expect_credential_query get <<-EOF &&
+	protocol=http
+	host=$HTTPD_DEST
+	wwwauth[]=FooBar param1="value1" param2="value2"
+	wwwauth[]=Basic realm="example.com"
+	EOF
+
+	expect_credential_query store <<-EOF
+	protocol=http
+	host=$HTTPD_DEST
+	username=alice
+	password=secret-passwd
+	EOF
+'
+
+test_done
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
index 1131503..f9a9bf9 100755
--- a/t/t5570-git-daemon.sh
+++ b/t/t5570-git-daemon.sh
@@ -10,9 +10,9 @@
 start_git_daemon
 
 check_verbose_connect () {
-	test_i18ngrep -F "Looking up 127.0.0.1 ..." stderr &&
-	test_i18ngrep -F "Connecting to 127.0.0.1 (port " stderr &&
-	test_i18ngrep -F "done." stderr
+	test_grep -F "Looking up 127.0.0.1 ..." stderr &&
+	test_grep -F "Connecting to 127.0.0.1 (port " stderr &&
+	test_grep -F "done." stderr
 }
 
 test_expect_success 'setup repository' '
@@ -108,7 +108,7 @@
 
 test_expect_success 'client refuses to ask for repo with newline' '
 	test_must_fail git clone "$GIT_DAEMON_URL/repo$LF.git" dst 2>stderr &&
-	test_i18ngrep newline.is.forbidden stderr
+	test_grep newline.is.forbidden stderr
 '
 
 test_remote_error()
@@ -148,7 +148,7 @@
 	fi
 
 	test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
-	test_i18ngrep "fatal: remote error: $msg: /$repo" output &&
+	test_grep "fatal: remote error: $msg: /$repo" output &&
 	ret=$?
 	chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
 	(exit $ret)
diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh
index a11b20e..448134c 100755
--- a/t/t5571-pre-push-hook.sh
+++ b/t/t5571-pre-push-hook.sh
@@ -4,6 +4,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 09097ef..5174452 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -121,7 +121,7 @@
 	sub_oid=$(git -C child rev-parse HEAD) &&
 	git -C super/sub cat-file -e $sub_oid &&
 	# Check that the submodule worktree did not update
-	! test_path_is_file super/sub/merge_strategy_5.t
+	test_path_is_missing super/sub/merge_strategy_5.t
 '
 
 test_expect_success "fetch.recurseSubmodules takes precedence over submodule.recurse" '
@@ -134,7 +134,7 @@
 	sub_oid=$(git -C child rev-parse HEAD) &&
 	git -C super/sub cat-file -e $sub_oid &&
 	# Check that the submodule worktree did not update
-	! test_path_is_file super/sub/merge_strategy_6.t
+	test_path_is_missing super/sub/merge_strategy_6.t
 '
 
 test_expect_success 'pull --rebase --recurse-submodules (remote superproject submodule changes, local submodule changes)' '
@@ -177,7 +177,7 @@
 	# submodule itself, but the merge strategy in submodules
 	# does not support rebase:
 	test_must_fail git -C super pull --rebase --recurse-submodules 2>err &&
-	test_i18ngrep "locally recorded submodule modifications" err
+	test_grep "locally recorded submodule modifications" err
 '
 
 test_expect_success 'pull --rebase --recurse-submodules (no submodule changes, no fork-point)' '
diff --git a/t/t5573-pull-verify-signatures.sh b/t/t5573-pull-verify-signatures.sh
index 1221ac0..ab05f38 100755
--- a/t/t5573-pull-verify-signatures.sh
+++ b/t/t5573-pull-verify-signatures.sh
@@ -47,46 +47,46 @@
 test_expect_success GPG 'pull unsigned commit with --verify-signatures' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_must_fail git pull --ff-only --verify-signatures unsigned 2>pullerror &&
-	test_i18ngrep "does not have a GPG signature" pullerror
+	test_grep "does not have a GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with bad signature with --verify-signatures' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_must_fail git pull --ff-only --verify-signatures bad 2>pullerror &&
-	test_i18ngrep "has a bad GPG signature" pullerror
+	test_grep "has a bad GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
-	test_i18ngrep "has an untrusted GPG signature" pullerror
+	test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=ultimate' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config gpg.minTrustLevel ultimate &&
 	test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
-	test_i18ngrep "has an untrusted GPG signature" pullerror
+	test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=marginal' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config gpg.minTrustLevel marginal &&
 	test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
-	test_i18ngrep "has an untrusted GPG signature" pullerror
+	test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=undefined' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config gpg.minTrustLevel undefined &&
 	git pull --ff-only --verify-signatures untrusted >pulloutput &&
-	test_i18ngrep "has a good GPG signature" pulloutput
+	test_grep "has a good GPG signature" pulloutput
 '
 
 test_expect_success GPG 'pull signed commit with --verify-signatures' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	git pull --verify-signatures signed >pulloutput &&
-	test_i18ngrep "has a good GPG signature" pulloutput
+	test_grep "has a good GPG signature" pulloutput
 '
 
 test_expect_success GPG 'pull commit with bad signature without verification' '
@@ -106,7 +106,7 @@
 	git init empty-repo &&
 	test_must_fail \
 		git -C empty-repo pull --verify-signatures ..  2>pullerror &&
-	test_i18ngrep "does not have a GPG signature" pullerror
+	test_grep "does not have a GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with bad signature and --verify-signatures' '
@@ -114,7 +114,7 @@
 	git init empty-repo &&
 	test_must_fail \
 		git -C empty-repo pull --ff-only --verify-signatures ../bad 2>pullerror &&
-	test_i18ngrep "has a bad GPG signature" pullerror
+	test_grep "has a bad GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures' '
@@ -122,7 +122,7 @@
 	git init empty-repo &&
 	test_must_fail \
 		git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
-	test_i18ngrep "has an untrusted GPG signature" pullerror
+	test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=ultimate' '
@@ -131,7 +131,7 @@
 	test_config_global gpg.minTrustLevel ultimate &&
 	test_must_fail \
 		git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
-	test_i18ngrep "has an untrusted GPG signature" pullerror
+	test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=marginal' '
@@ -140,7 +140,7 @@
 	test_config_global gpg.minTrustLevel marginal &&
 	test_must_fail \
 		git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
-	test_i18ngrep "has an untrusted GPG signature" pullerror
+	test_grep "has an untrusted GPG signature" pullerror
 '
 
 test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=undefined' '
@@ -148,7 +148,7 @@
 	git init empty-repo &&
 	test_config_global gpg.minTrustLevel undefined &&
 	git -C empty-repo pull --ff-only --verify-signatures ../untrusted >pulloutput &&
-	test_i18ngrep "has a good GPG signature" pulloutput
+	test_grep "has a good GPG signature" pulloutput
 '
 
 test_done
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
new file mode 100755
index 0000000..5883839
--- /dev/null
+++ b/t/t5574-fetch-output.sh
@@ -0,0 +1,304 @@
+#!/bin/sh
+
+test_description='git fetch output format'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'fetch with invalid output format configuration' '
+	test_when_finished "rm -rf clone" &&
+	git clone . clone &&
+
+	test_must_fail git -C clone -c fetch.output fetch origin 2>actual.err &&
+	cat >expect <<-EOF &&
+	error: missing value for ${SQ}fetch.output${SQ}
+	fatal: unable to parse ${SQ}fetch.output${SQ} from command-line config
+	EOF
+	test_cmp expect actual.err &&
+
+	test_must_fail git -C clone -c fetch.output= fetch origin 2>actual.err &&
+	cat >expect <<-EOF &&
+	fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ}
+	EOF
+	test_cmp expect actual.err &&
+
+	test_must_fail git -C clone -c fetch.output=garbage fetch origin 2>actual.err &&
+	cat >expect <<-EOF &&
+	fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ}
+	EOF
+	test_cmp expect actual.err
+'
+
+test_expect_success 'fetch aligned output' '
+	git clone . full-output &&
+	test_commit looooooooooooong-tag &&
+	(
+		cd full-output &&
+		git -c fetch.output=full fetch origin >actual 2>&1 &&
+		grep -e "->" actual | cut -c 22- >../actual
+	) &&
+	cat >expect <<-\EOF &&
+	main                 -> origin/main
+	looooooooooooong-tag -> looooooooooooong-tag
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'fetch compact output' '
+	git clone . compact &&
+	test_commit extraaa &&
+	(
+		cd compact &&
+		git -c fetch.output=compact fetch origin >actual 2>&1 &&
+		grep -e "->" actual | cut -c 22- >../actual
+	) &&
+	cat >expect <<-\EOF &&
+	main       -> origin/*
+	extraaa    -> *
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'setup for fetch porcelain output' '
+	# Set up a bunch of references that we can use to demonstrate different
+	# kinds of flag symbols in the output format.
+	test_commit commit-for-porcelain-output &&
+	MAIN_OLD=$(git rev-parse HEAD) &&
+	git branch "fast-forward" &&
+	git branch "deleted-branch" &&
+	git checkout -b force-updated &&
+	test_commit --no-tag force-update-old &&
+	FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
+	git checkout main &&
+
+	# Backup to preseed.git
+	git clone --mirror . preseed.git &&
+
+	# Continue changing our local references.
+	git branch new-branch &&
+	git branch -d deleted-branch &&
+	git checkout fast-forward &&
+	test_commit --no-tag fast-forward-new &&
+	FAST_FORWARD_NEW=$(git rev-parse HEAD) &&
+	git checkout force-updated &&
+	git reset --hard HEAD~ &&
+	test_commit --no-tag force-update-new &&
+	FORCE_UPDATED_NEW=$(git rev-parse HEAD)
+'
+
+for opt in "" "--atomic"
+do
+	test_expect_success "fetch porcelain output ${opt:+(atomic)}" '
+		test_when_finished "rm -rf porcelain" &&
+
+		# Clone and pre-seed the repositories. We fetch references into two
+		# namespaces so that we can test that rejected and force-updated
+		# references are reported properly.
+		refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
+		git clone preseed.git porcelain &&
+		git -C porcelain fetch origin $opt $refspecs &&
+
+		cat >expect <<-EOF &&
+		- $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
+		- $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
+		  $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
+		! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
+		* $ZERO_OID $MAIN_OLD refs/unforced/new-branch
+		  $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
+		+ $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
+		* $ZERO_OID $MAIN_OLD refs/forced/new-branch
+		  $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
+		+ $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
+		* $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
+		EOF
+
+		# Change the URL of the repository to fetch different references.
+		git -C porcelain remote set-url origin .. &&
+
+		# Execute a dry-run fetch first. We do this to assert that the dry-run
+		# and non-dry-run fetches produces the same output. Execution of the
+		# fetch is expected to fail as we have a rejected reference update.
+		test_must_fail git -C porcelain fetch $opt \
+			--porcelain --dry-run --prune origin $refspecs >actual &&
+		test_cmp expect actual &&
+
+		# And now we perform a non-dry-run fetch.
+		test_must_fail git -C porcelain fetch $opt \
+			--porcelain --prune origin $refspecs >actual 2>stderr &&
+		test_cmp expect actual &&
+		test_must_be_empty stderr
+	'
+done
+
+test_expect_success 'fetch porcelain with multiple remotes' '
+	test_when_finished "rm -rf porcelain" &&
+
+	git switch --create multiple-remotes &&
+	git clone . porcelain &&
+	git -C porcelain remote add second-remote "$PWD" &&
+	git -C porcelain fetch second-remote &&
+
+	test_commit --no-tag multi-commit &&
+	old_commit=$(git rev-parse HEAD~) &&
+	new_commit=$(git rev-parse HEAD) &&
+
+	cat >expect <<-EOF &&
+	  $old_commit $new_commit refs/remotes/origin/multiple-remotes
+	  $old_commit $new_commit refs/remotes/second-remote/multiple-remotes
+	EOF
+
+	git -C porcelain fetch --porcelain --all >actual 2>stderr &&
+	test_cmp expect actual &&
+	test_must_be_empty stderr
+'
+
+test_expect_success 'fetch porcelain refuses to work with submodules' '
+	test_when_finished "rm -rf porcelain" &&
+
+	cat >expect <<-EOF &&
+	fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together
+	EOF
+
+	git init porcelain &&
+	test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr &&
+	test_cmp expect stderr &&
+
+	test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr &&
+	test_cmp expect stderr
+'
+
+test_expect_success 'fetch porcelain overrides fetch.output config' '
+	test_when_finished "rm -rf porcelain" &&
+
+	git switch --create config-override &&
+	git clone . porcelain &&
+	test_commit new-commit &&
+	old_commit=$(git rev-parse HEAD~) &&
+	new_commit=$(git rev-parse HEAD) &&
+
+	cat >expect <<-EOF &&
+	  $old_commit $new_commit refs/remotes/origin/config-override
+	* $ZERO_OID $new_commit refs/tags/new-commit
+	EOF
+
+	git -C porcelain -c fetch.output=compact fetch --porcelain >stdout 2>stderr &&
+	test_must_be_empty stderr &&
+	test_cmp expect stdout
+'
+
+test_expect_success 'fetch --no-porcelain overrides previous --porcelain' '
+	test_when_finished "rm -rf no-porcelain" &&
+
+	git switch --create no-porcelain &&
+	git clone . no-porcelain &&
+	test_commit --no-tag no-porcelain &&
+	old_commit=$(git rev-parse --short HEAD~) &&
+	new_commit=$(git rev-parse --short HEAD) &&
+
+	cat >expect <<-EOF &&
+	From $(test-tool path-utils real_path .)/.
+	   $old_commit..$new_commit  no-porcelain -> origin/no-porcelain
+	EOF
+
+	git -C no-porcelain fetch --porcelain --no-porcelain >stdout 2>stderr &&
+	test_cmp expect stderr &&
+	test_must_be_empty stdout
+'
+
+test_expect_success 'fetch output with HEAD' '
+	test_when_finished "rm -rf head" &&
+	git clone . head &&
+
+	git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err &&
+	cat >expect <<-EOF &&
+	From $(test-tool path-utils real_path .)/.
+	 * branch            HEAD       -> FETCH_HEAD
+	EOF
+	test_must_be_empty actual.out &&
+	test_cmp expect actual.err &&
+
+	git -C head fetch origin HEAD >actual.out 2>actual.err &&
+	test_must_be_empty actual.out &&
+	test_cmp expect actual.err &&
+
+	git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err &&
+	cat >expect <<-EOF &&
+	From $(test-tool path-utils real_path .)/.
+	 * [new ref]         HEAD       -> foo
+	EOF
+	test_must_be_empty actual.out &&
+	test_cmp expect actual.err &&
+
+	git -C head fetch origin HEAD:foo >actual.out 2>actual.err &&
+	test_must_be_empty actual.out &&
+	test_cmp expect actual.err
+'
+
+test_expect_success 'fetch porcelain output with HEAD' '
+	test_when_finished "rm -rf head" &&
+	git clone . head &&
+	COMMIT_ID=$(git rev-parse HEAD) &&
+
+	git -C head fetch --porcelain --dry-run origin HEAD >actual &&
+	cat >expect <<-EOF &&
+	* $ZERO_OID $COMMIT_ID FETCH_HEAD
+	EOF
+	test_cmp expect actual &&
+
+	git -C head fetch --porcelain origin HEAD >actual &&
+	test_cmp expect actual &&
+
+	git -C head fetch --porcelain --dry-run origin HEAD:foo >actual &&
+	cat >expect <<-EOF &&
+	* $ZERO_OID $COMMIT_ID refs/heads/foo
+	EOF
+	test_cmp expect actual &&
+
+	git -C head fetch --porcelain origin HEAD:foo >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'fetch output with object ID' '
+	test_when_finished "rm -rf object-id" &&
+	git clone . object-id &&
+	commit=$(git rev-parse HEAD) &&
+
+	git -C object-id fetch --dry-run origin $commit:object-id >actual.out 2>actual.err &&
+	cat >expect <<-EOF &&
+	From $(test-tool path-utils real_path .)/.
+	 * [new ref]         $commit -> object-id
+	EOF
+	test_must_be_empty actual.out &&
+	test_cmp expect actual.err &&
+
+	git -C object-id fetch origin $commit:object-id >actual.out 2>actual.err &&
+	test_must_be_empty actual.out &&
+	test_cmp expect actual.err
+'
+
+test_expect_success '--no-show-forced-updates' '
+	mkdir forced-updates &&
+	(
+		cd forced-updates &&
+		git init &&
+		test_commit 1 &&
+		test_commit 2
+	) &&
+	git clone forced-updates forced-update-clone &&
+	git clone forced-updates no-forced-update-clone &&
+	git -C forced-updates reset --hard HEAD~1 &&
+	(
+		cd forced-update-clone &&
+		git fetch --show-forced-updates origin 2>output &&
+		test_grep "(forced update)" output
+	) &&
+	(
+		cd no-forced-update-clone &&
+		git fetch --no-show-forced-updates origin 2>output &&
+		test_grep ! "(forced update)" output
+	)
+'
+
+test_done
diff --git a/t/t5580-unc-paths.sh b/t/t5580-unc-paths.sh
index cd7604f..d7537a1 100755
--- a/t/t5580-unc-paths.sh
+++ b/t/t5580-unc-paths.sh
@@ -75,7 +75,7 @@
 test_expect_success MINGW 'remote nick cannot contain backslashes' '
 	BACKSLASHED="$(winpwd | tr / \\\\)" &&
 	git ls-remote "$BACKSLASHED" 2>err &&
-	test_i18ngrep ! "unable to access" err
+	test_grep ! "unable to access" err
 '
 
 test_expect_success 'unc alternates' '
diff --git a/t/t5583-push-branches.sh b/t/t5583-push-branches.sh
new file mode 100755
index 0000000..320f49c
--- /dev/null
+++ b/t/t5583-push-branches.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+test_description='check the consisitency of behavior of --all and --branches'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+delete_refs() {
+	dir=$1
+	shift
+	rm -rf deletes
+	for arg in $*
+	do
+		echo "delete ${arg}" >>deletes
+	done
+	git -C $dir update-ref --stdin < deletes
+}
+
+test_expect_success 'setup bare remote' '
+	git init --bare remote-1 &&
+	git -C remote-1 config gc.auto 0 &&
+	test_commit one &&
+	git push remote-1 HEAD
+'
+
+test_expect_success 'setup different types of references' '
+	cat >refs <<-EOF &&
+	update refs/heads/branch-1 HEAD
+	update refs/heads/branch-2 HEAD
+	EOF
+
+	git tag -a -m "annotated" annotated-1 HEAD &&
+	git tag -a -m "annotated" annotated-2 HEAD &&
+	git update-ref --stdin < refs
+'
+
+test_expect_success '--all and --branches have the same behavior' '
+	test_when_finished "delete_refs remote-1 \
+			   refs/heads/branch-1 \
+			   refs/heads/branch-2" &&
+	git push remote-1 --all &&
+	commit=$(git rev-parse HEAD) &&
+	cat >expect <<-EOF &&
+	$commit refs/heads/branch-1
+	$commit refs/heads/branch-2
+	$commit refs/heads/main
+	EOF
+
+	git -C remote-1 show-ref --heads >actual.all &&
+	delete_refs remote-1 refs/heads/branch-1 refs/heads/branch-2 &&
+	git push remote-1 --branches &&
+	git -C remote-1 show-ref --heads >actual.branches &&
+	test_cmp actual.all actual.branches &&
+	test_cmp expect actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with refspecs' '
+	test_must_fail git push remote-1 --all main >actual.all 2>&1 &&
+	test_must_fail git push remote-1 --branches main >actual.branches 2>&1 &&
+	test_cmp actual.all actual.branches &&
+	grep "be combined with refspecs" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --mirror' '
+	test_must_fail git push remote-1 --all --mirror >actual.all 2>&1 &&
+	test_must_fail git push remote-1 --branches --mirror >actual.branches 2>&1 &&
+	test_cmp actual.all actual.branches &&
+	grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --tags' '
+	test_must_fail git push remote-1 --all --tags >actual.all 2>&1 &&
+	test_must_fail git push remote-1 --branches --tags >actual.branches 2>&1 &&
+	test_cmp actual.all actual.branches &&
+	grep "cannot be used together" actual.all
+'
+
+
+test_expect_success '--all or --branches can not be combined with --delete' '
+	test_must_fail git push remote-1 --all --delete >actual.all 2>&1 &&
+	test_must_fail git push remote-1 --branches --delete >actual.branches 2>&1 &&
+	test_cmp actual.all actual.branches &&
+	grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches combines with --follow-tags have same behavior' '
+	test_when_finished "delete_refs remote-1 \
+			   refs/heads/branch-1 \
+			   refs/heads/branch-2 \
+			   refs/tags/annotated-1 \
+			   refs/tags/annotated-2" &&
+	git push remote-1 --all --follow-tags &&
+	git -C remote-1 show-ref > actual.all &&
+	cat >expect <<-EOF &&
+	$commit refs/heads/branch-1
+	$commit refs/heads/branch-2
+	$commit refs/heads/main
+	$(git rev-parse annotated-1) refs/tags/annotated-1
+	$(git rev-parse annotated-2) refs/tags/annotated-2
+	EOF
+
+	delete_refs remote-1 \
+		    refs/heads/branch-1 \
+		    refs/heads/branch-2 \
+		    refs/tags/annotated-1 \
+		    refs/tags/annotated-2 &&
+	git push remote-1 --branches --follow-tags &&
+	git -C remote-1 show-ref >actual.branches &&
+	test_cmp actual.all actual.branches &&
+	test_cmp expect actual.all
+'
+
+test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index b7d5551..ca43185 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -157,6 +157,23 @@
 
 '
 
+test_expect_success 'clone with files ref format' '
+	test_when_finished "rm -rf ref-storage" &&
+	git clone --ref-format=files --mirror src ref-storage &&
+	echo files >expect &&
+	git -C ref-storage rev-parse --show-ref-format >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'clone with garbage ref format' '
+	cat >expect <<-EOF &&
+	fatal: unknown ref storage format ${SQ}garbage${SQ}
+	EOF
+	test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err &&
+	test_cmp expect err &&
+	test_path_is_missing ref-storage
+'
+
 test_expect_success 'clone to destination with trailing /' '
 
 	git clone src target-1/ &&
@@ -630,7 +647,7 @@
 test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
 	grep X icasefs/warning &&
 	grep x icasefs/warning &&
-	test_i18ngrep "the following paths have collided" icasefs/warning
+	test_grep "the following paths have collided" icasefs/warning
 '
 
 test_expect_success 'clone with GIT_DEFAULT_HASH' '
@@ -696,7 +713,7 @@
 
 	git clone --filter=blob:limit=0 "file://$(pwd)/server" client 2> err &&
 
-	test_i18ngrep "filtering not recognized by server" err
+	test_grep "filtering not recognized by server" err
 '
 
 test_expect_success 'batch missing blob request during checkout' '
@@ -759,6 +776,18 @@
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
+test_expect_success 'clone with includeIf' '
+	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" &&
+	cat >"$HOME"/.gitconfig <<-EOF &&
+	[includeIf "onbranch:something"]
+		path = /does/not/exist.inc
+	EOF
+	git clone $HTTPD_URL/smart/repo.git repo
+'
+
 test_expect_success 'partial clone using HTTP' '
 	partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 '
@@ -767,7 +796,7 @@
 	test_when_finished "rm -rf repo" &&
 	git clone --bare --no-local --depth=1 src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 	test_must_fail git -c protocol.version=2 clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&
-	test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+	test_grep -e "source repository is shallow, reject to clone." err &&
 
 	git clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo
 '
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 83e3c97..9b32db8 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -317,7 +317,7 @@
 	for option in --local --no-hardlinks --dissociate
 	do
 		test_must_fail git clone $option T T$option 2>err || return 1 &&
-		test_i18ngrep "symlink.*exists" err || return 1
+		test_grep "symlink.*exists" err || return 1
 	done &&
 
 	# But `--shared` clones should still work, even when specifying
@@ -358,7 +358,7 @@
 	test_must_fail git clone --local malicious clone 2>err &&
 
 	test_path_is_missing clone &&
-	grep "failed to start iterator over" err
+	grep "is a symlink, refusing to clone with --local" err
 '
 
 test_done
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 38b850c..a305586 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -15,8 +15,12 @@
 	: >file && git add . && git commit -m1 &&
 	git clone --bare . a.git &&
 	git clone --bare . x &&
-	test "$(cd a.git && git config --bool core.bare)" = true &&
-	test "$(cd x && git config --bool core.bare)" = true &&
+	echo true >expect &&
+	git -C a.git config --bool core.bare >actual &&
+	test_cmp expect actual &&
+	echo true >expect &&
+	git -C x config --bool core.bare >actual &&
+	test_cmp expect actual &&
 	git bundle create b1.bundle --all &&
 	git bundle create b2.bundle main &&
 	mkdir dir &&
@@ -29,7 +33,9 @@
 test_expect_success 'local clone without .git suffix' '
 	git clone -l -s a b &&
 	(cd b &&
-	test "$(git config --bool core.bare)" = false &&
+	echo false >expect &&
+	git config --bool core.bare >actual &&
+	test_cmp expect actual &&
 	git fetch)
 '
 
@@ -59,11 +65,11 @@
 '
 
 test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
-	echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
+	git -C a.git symbolic-ref HEAD refs/heads/nonexistent &&
 	git clone a d &&
 	(cd d &&
 	git fetch &&
-	test ! -e .git/refs/remotes/origin/HEAD)
+	test_ref_missing refs/remotes/origin/HEAD)
 '
 
 test_expect_success 'bundle clone without .bundle suffix' '
@@ -151,7 +157,7 @@
 	test_must_fail git clone --bare -u false a should_not_work.git
 '
 
-test_expect_success 'local clone from repo with corrupt refs fails gracefully' '
+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '
 	git init corrupt &&
 	test_commit -C corrupt one &&
 	echo a >corrupt/.git/refs/heads/topic &&
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index 27f9f77..e93e0d0 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -39,7 +39,7 @@
 test_expect_success 'rejects invalid -o/--origin' '
 
 	test_must_fail git clone -o "bad...name" parent clone-bad-name 2>err &&
-	test_i18ngrep "'\''bad...name'\'' is not a valid remote name" err
+	test_grep "'\''bad...name'\'' is not a valid remote name" err
 
 '
 
@@ -56,7 +56,7 @@
 
 	test_must_fail git clone --bare --separate-git-dir dot-git-destiation parent clone-bare-sgd 2>err &&
 	test_debug "cat err" &&
-	test_i18ngrep -e "options .--bare. and .--separate-git-dir. cannot be used together" err
+	test_grep -e "options .--bare. and .--separate-git-dir. cannot be used together" err
 
 '
 
@@ -64,14 +64,14 @@
 	for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD
 	do
 		test_must_fail git clone --bundle-uri=bundle $option from to 2>err &&
-		grep "bundle-uri is incompatible" err || return 1
+		grep "bundle-uri.* cannot be used together" err || return 1
 	done
 '
 
 test_expect_success 'reject cloning shallow repository' '
 	test_when_finished "rm -rf repo" &&
 	test_must_fail git clone --reject-shallow shallow-repo out 2>err &&
-	test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+	test_grep -e "source repository is shallow, reject to clone." err &&
 
 	git clone --no-reject-shallow shallow-repo repo
 '
@@ -79,7 +79,7 @@
 test_expect_success 'reject cloning non-local shallow repository' '
 	test_when_finished "rm -rf repo" &&
 	test_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&
-	test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+	test_grep -e "source repository is shallow, reject to clone." err &&
 
 	git clone --no-reject-shallow --no-local shallow-repo repo
 '
@@ -120,6 +120,16 @@
 
 '
 
+test_expect_success 'ignore --template config for core.bare' '
+
+	template="$TRASH_DIRECTORY/template-with-bare-config" &&
+	mkdir "$template" &&
+	git config --file "$template/config" core.bare true &&
+	git clone "--template=$template" parent clone-bare-config &&
+	test "$(git -C clone-bare-config config --local core.bare)" = "false" &&
+	test_path_is_missing clone-bare-config/HEAD
+'
+
 test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
 
 	test_config_global clone.defaultRemoteName from_config &&
@@ -139,7 +149,7 @@
 
 	git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
 	! grep % err &&
-	test_i18ngrep ! "Checking connectivity" err
+	test_grep ! "Checking connectivity" err
 
 '
 
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
index 51705aa..0d1e92d 100755
--- a/t/t5607-clone-bundle.sh
+++ b/t/t5607-clone-bundle.sh
@@ -24,7 +24,7 @@
 test_expect_success '"verify" needs a worktree' '
 	git bundle create tip.bundle -1 main &&
 	nongit test_must_fail git bundle verify ../tip.bundle 2>err &&
-	test_i18ngrep "need a repository" err
+	test_grep "need a repository" err
 '
 
 test_expect_success 'annotated tags can be excluded by rev-list options' '
@@ -166,7 +166,7 @@
 	@unknown=silly
 	EOF
 	test_must_fail git bundle verify new 2>output &&
-	test_i18ngrep "unknown capability .unknown=silly." output
+	test_grep "unknown capability .unknown=silly." output
 '
 
 test_done
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index 727caff..298d4be 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -103,7 +103,7 @@
 test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo' '
 	test_when_finished "rm -rf out" &&
 	test_must_fail git -c clone.rejectshallow=true clone --no-local shallow-repo out 2>err &&
-	test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+	test_grep -e "source repository is shallow, reject to clone." err &&
 
 	git -c clone.rejectshallow=false clone --no-local shallow-repo out
 '
@@ -111,7 +111,7 @@
 test_expect_success 'option --[no-]reject-shallow override clone.rejectshallow config' '
 	test_when_finished "rm -rf out" &&
 	test_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local shallow-repo out 2>err &&
-	test_i18ngrep -e "source repository is shallow, reject to clone." err &&
+	test_grep -e "source repository is shallow, reject to clone." err &&
 
 	git -c clone.rejectshallow=true clone --no-reject-shallow --no-local shallow-repo out
 '
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index f519d2a..2da7291 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -257,8 +257,8 @@
 	test_commit -C submodule mycommit &&
 
 	test_create_repo src_with_sub &&
-	test_config -C src_with_sub uploadpack.allowfilter 1 &&
-	test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
+	git -C src_with_sub config uploadpack.allowfilter 1 &&
+	git -C src_with_sub config uploadpack.allowanysha1inwant 1 &&
 
 	test_config_global protocol.file.allow always &&
 
@@ -270,6 +270,12 @@
 	test_when_finished rm -rf dst
 '
 
+test_expect_success 'lazily fetched .gitmodules works' '
+	git clone --filter="blob:none" --no-checkout "file://$(pwd)/src_with_sub" dst &&
+	git -C dst fetch &&
+	test_when_finished rm -rf dst
+'
+
 test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack --fsck-objects' '
 	git init src &&
 	test_commit -C src x &&
@@ -347,14 +353,14 @@
 	test_must_fail git \
 		-c uploadpackfilter.tree.maxdepth \
 		upload-pack . >/dev/null 2>err &&
-	test_i18ngrep "unable to parse.*tree.maxdepth" err
+	test_grep "unable to parse.*tree.maxdepth" err
 '
 
 test_expect_success 'upload-pack fails banned object filters' '
 	test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
 	test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
 		"file://$(pwd)/srv.bare" pc3 2>err &&
-	test_i18ngrep "filter '\''blob:none'\'' not supported" err
+	test_grep "filter '\''blob:none'\'' not supported" err
 '
 
 test_expect_success 'upload-pack fails banned combine object filters' '
@@ -364,14 +370,14 @@
 	test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
 	test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
 		--filter=blob:none "file://$(pwd)/srv.bare" pc3 2>err &&
-	test_i18ngrep "filter '\''blob:none'\'' not supported" err
+	test_grep "filter '\''blob:none'\'' not supported" err
 '
 
 test_expect_success 'upload-pack fails banned object filters with fallback' '
 	test_config -C srv.bare uploadpackfilter.allow false &&
 	test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
 		"file://$(pwd)/srv.bare" pc3 2>err &&
-	test_i18ngrep "filter '\''blob:none'\'' not supported" err
+	test_grep "filter '\''blob:none'\'' not supported" err
 '
 
 test_expect_success 'upload-pack limits tree depth filters' '
@@ -380,7 +386,7 @@
 	test_config -C srv.bare uploadpackfilter.tree.maxDepth 0 &&
 	test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
 		"file://$(pwd)/srv.bare" pc3 2>err &&
-	test_i18ngrep "tree filter allows max depth 0, but got 1" err &&
+	test_grep "tree filter allows max depth 0, but got 1" err &&
 
 	git clone --no-checkout --filter=tree:0 "file://$(pwd)/srv.bare" pc4 &&
 
@@ -388,7 +394,7 @@
 	git clone --no-checkout --filter=tree:5 "file://$(pwd)/srv.bare" pc5 &&
 	test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:6 \
 		"file://$(pwd)/srv.bare" pc6 2>err &&
-	test_i18ngrep "tree filter allows max depth 5, but got 6" err
+	test_grep "tree filter allows max depth 5, but got 6" err
 '
 
 test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
@@ -453,11 +459,11 @@
 	test_must_fail git clone --no-local --bare \
 				 --filter=sparse:oid=main:no-such-name \
 				 sparse-src dst.git 2>err &&
-	test_i18ngrep "unable to access sparse blob in .main:no-such-name" err &&
+	test_grep "unable to access sparse blob in .main:no-such-name" err &&
 	test_must_fail git clone --no-local --bare \
 				 --filter=sparse:oid=main \
 				 sparse-src dst.git 2>err &&
-	test_i18ngrep "unable to parse sparse filter data in" err
+	test_grep "unable to parse sparse filter data in" err
 '
 
 setup_triangle () {
@@ -487,8 +493,8 @@
 	TREE_HASH=$(git -C server rev-parse HEAD~1^{tree}) &&
 	git -C promisor-remote fetch --keep "file://$(pwd)/server" "$TREE_HASH" &&
 	git -C promisor-remote count-objects -v >object-count &&
-	test_i18ngrep "count: 0" object-count &&
-	test_i18ngrep "in-pack: 2" object-count &&
+	test_grep "count: 0" object-count &&
+	test_grep "in-pack: 2" object-count &&
 
 	# Set it as the promisor remote of client. Thus, whenever
 	# the client lazy fetches, the lazy fetch will succeed only if it is
@@ -742,7 +748,7 @@
 	test_must_fail git -c protocol.version=2 clone \
 		--filter=blob:none $HTTPD_URL/one_time_perl/server repo 2>err &&
 
-	test_i18ngrep "did not send all necessary objects" err &&
+	test_grep "did not send all necessary objects" err &&
 
 	# Ensure that the one-time-perl script was used.
 	! test -e "$HTTPD_ROOT_PATH/one-time-perl"
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
index 6c8d4c6..a73b4d4 100755
--- a/t/t5700-protocol-v1.sh
+++ b/t/t5700-protocol-v1.sh
@@ -244,15 +244,28 @@
 	grep "push< version 1" log
 '
 
+test_expect_success 'clone propagates object-format from empty repo' '
+	test_when_finished "rm -fr src256 dst256" &&
+
+	echo sha256 >expect &&
+	git init --object-format=sha256 src256 &&
+	git clone --no-local src256 dst256 &&
+	git -C dst256 rev-parse --show-object-format >actual &&
+
+	test_cmp expect actual
+'
+
 # Test protocol v1 with 'http://' transport
 #
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
-test_expect_success 'create repo to be served by http:// transport' '
+test_expect_success 'create repos to be served by http:// transport' '
 	git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
 	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
-	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
+	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one &&
+	git init --object-format=sha256 "$HTTPD_DOCUMENT_ROOT_PATH/sha256" &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH/sha256" config http.receivepack true
 '
 
 test_expect_success 'clone with http:// using protocol v1' '
@@ -269,6 +282,20 @@
 	grep "git< version 1" log
 '
 
+test_expect_success 'clone with http:// using protocol v1 with empty SHA-256 repo' '
+	GIT_TRACE_PACKET=1 GIT_TRACE_CURL=1 git -c protocol.version=1 \
+		clone "$HTTPD_URL/smart/sha256" sha256 2>log &&
+
+	echo sha256 >expect &&
+	git -C sha256 rev-parse --show-object-format >actual &&
+	test_cmp expect actual &&
+
+	# Client requested to use protocol v1
+	grep "Git-Protocol: version=1" log &&
+	# Server responded using protocol v1
+	grep "git< version 1" log
+'
+
 test_expect_success 'fetch with http:// using protocol v1' '
 	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
 
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index f21e5e9..c48830d 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -20,7 +20,6 @@
 	fetch=shallow wait-for-done
 	server-option
 	object-format=$(test_oid algo)
-	object-info
 	EOF
 	cat >expect.trailer <<-EOF &&
 	0000
@@ -52,7 +51,7 @@
 	0000
 	EOF
 	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-	test_i18ngrep "unknown capability" err
+	test_grep "unknown capability" err
 '
 
 test_expect_success 'request with no command' '
@@ -62,7 +61,7 @@
 	0000
 	EOF
 	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-	test_i18ngrep "no command requested" err
+	test_grep "no command requested" err
 '
 
 test_expect_success 'request invalid command' '
@@ -73,7 +72,7 @@
 	0000
 	EOF
 	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-	test_i18ngrep "invalid command" err
+	test_grep "invalid command" err
 '
 
 test_expect_success 'request capability as command' '
@@ -115,7 +114,7 @@
 	0000
 	EOF
 	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
-	test_i18ngrep "mismatched object format" err
+	test_grep "mismatched object format" err
 '
 
 # Test the basics of ls-refs
@@ -323,6 +322,8 @@
 # Test the basics of object-info
 #
 test_expect_success 'basics of object-info' '
+	test_config transfer.advertiseObjectInfo true &&
+
 	test-tool pkt-line pack >in <<-EOF &&
 	command=object-info
 	object-format=$(test_oid algo)
@@ -380,4 +381,25 @@
 	test_must_be_empty out
 '
 
+test_expect_success 'object-info missing from capabilities when disabled' '
+	test_config transfer.advertiseObjectInfo false &&
+
+	GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+		--advertise-capabilities >out &&
+	test-tool pkt-line unpack <out >actual &&
+
+	! grep object.info actual
+'
+
+test_expect_success 'object-info commands rejected when disabled' '
+	test_config transfer.advertiseObjectInfo false &&
+
+	test-tool pkt-line pack >in <<-EOF &&
+	command=object-info
+	EOF
+
+	test_must_fail test-tool serve-v2 --stateless-rpc <in 2>err &&
+	grep invalid.command err
+'
+
 test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index e4db751..1ef540f 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -189,8 +189,8 @@
 	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
 		ls-remote -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
 
-	test_i18ngrep "see protocol.version in" err &&
-	test_i18ngrep "server options require protocol version 2 or later" err
+	test_grep "see protocol.version in" err &&
+	test_grep "server options require protocol version 2 or later" err
 '
 
 test_expect_success 'clone with file:// using protocol v2' '
@@ -221,7 +221,9 @@
 	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
 	git -c init.defaultBranch=main -c protocol.version=2 \
 		clone "file://$(pwd)/file_empty_parent" file_empty_child &&
-	grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+	echo refs/heads/mydefaultbranch >expect &&
+	git -C file_empty_child symbolic-ref HEAD >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success '...but not if explicitly forbidden by config' '
@@ -234,7 +236,9 @@
 	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
 	git -c init.defaultBranch=main -c protocol.version=2 \
 		clone "file://$(pwd)/file_empty_parent" file_empty_child &&
-	! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+	echo refs/heads/main >expect &&
+	git -C file_empty_child symbolic-ref HEAD >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'bare clone propagates empty default branch' '
@@ -247,7 +251,9 @@
 	git -c init.defaultBranch=main -c protocol.version=2 \
 		clone --bare \
 		"file://$(pwd)/file_empty_parent" file_empty_child.git &&
-	grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
+	echo "refs/heads/mydefaultbranch" >expect &&
+	git -C file_empty_child.git symbolic-ref HEAD >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
@@ -265,10 +271,23 @@
 	git -c init.defaultBranch=main -c protocol.version=2 \
 		clone "file://$(pwd)/file_unborn_parent" \
 		file_unborn_child 2>stderr &&
-	grep "refs/heads/mydefaultbranch" file_unborn_child/.git/HEAD &&
+	echo "refs/heads/mydefaultbranch" >expect &&
+	git -C file_unborn_child symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
 	grep "warning: remote HEAD refers to nonexistent ref" stderr
 '
 
+test_expect_success 'clone propagates object-format from empty repo' '
+	test_when_finished "rm -fr src256 dst256" &&
+
+	echo sha256 >expect &&
+	git init --object-format=sha256 src256 &&
+	git clone src256 dst256 &&
+	git -C dst256 rev-parse --show-object-format >actual &&
+
+	test_cmp expect actual
+'
+
 test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
 	test_when_finished "rm -rf file_unborn_parent file_unborn_child.git" &&
 
@@ -284,7 +303,9 @@
 	git -c init.defaultBranch=main -c protocol.version=2 \
 		clone --bare "file://$(pwd)/file_unborn_parent" \
 		file_unborn_child.git 2>stderr &&
-	grep "refs/heads/mydefaultbranch" file_unborn_child.git/HEAD &&
+	echo "refs/heads/mydefaultbranch" >expect &&
+	git -C file_unborn_child.git symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
 	! grep "warning:" stderr
 '
 
@@ -304,7 +325,9 @@
 	git -c init.defaultBranch=branchwithstuff -c protocol.version=2 \
 		clone "file://$(pwd)/file_unborn_parent" \
 		file_unborn_child 2>stderr &&
-	grep "refs/heads/branchwithstuff" file_unborn_child/.git/HEAD &&
+	echo "refs/heads/branchwithstuff" >expect &&
+	git -C file_unborn_child symbolic-ref HEAD >actual &&
+	test_cmp expect actual &&
 	test_path_is_file file_unborn_child/stuff.t &&
 	! grep "warning:" stderr
 '
@@ -366,8 +389,8 @@
 	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C temp_child -c protocol.version=0 \
 		fetch -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
 
-	test_i18ngrep "see protocol.version in" err &&
-	test_i18ngrep "server options require protocol version 2 or later" err
+	test_grep "see protocol.version in" err &&
+	test_grep "server options require protocol version 2 or later" err
 '
 
 test_expect_success 'server-options are sent when cloning' '
@@ -388,8 +411,8 @@
 		clone --server-option=hello --server-option=world \
 		"file://$(pwd)/file_parent" myclone 2>err &&
 
-	test_i18ngrep "see protocol.version in" err &&
-	test_i18ngrep "server options require protocol version 2 or later" err
+	test_grep "see protocol.version in" err &&
+	test_grep "server options require protocol version 2 or later" err
 '
 
 test_expect_success 'upload-pack respects config using protocol v2' '
@@ -484,7 +507,7 @@
 	git -C server config uploadpack.allowfilter 0 &&
 	git -c protocol.version=2 \
 		clone --filter=blob:none "file://$(pwd)/server" client 2>err &&
-	test_i18ngrep "filtering not recognized by server, ignoring" err
+	test_grep "filtering not recognized by server, ignoring" err
 '
 
 test_expect_success 'even with handcrafted request, filter does not work if not advertised' '
@@ -725,7 +748,53 @@
 		--negotiate-only \
 		--negotiation-tip=$(git -C client rev-parse HEAD) \
 		origin 2>err &&
-	test_i18ngrep "negotiate-only requires protocol v2" err
+	test_grep "negotiate-only requires protocol v2" err
+'
+
+test_expect_success 'push with custom path does not request v2' '
+	rm -f env.trace &&
+	git -C client push \
+		--receive-pack="env >../env.trace; git-receive-pack" \
+		origin HEAD:refs/heads/custom-push-test &&
+	test_path_is_file env.trace &&
+	! grep ^GIT_PROTOCOL env.trace
+'
+
+test_expect_success 'fetch with custom path does request v2' '
+	rm -f env.trace &&
+	git -C client fetch \
+		--upload-pack="env >../env.trace; git-upload-pack" \
+		origin HEAD &&
+	grep ^GIT_PROTOCOL=version=2 env.trace
+'
+
+test_expect_success 'archive with custom path does not request v2' '
+	rm -f env.trace &&
+	git -C client archive \
+		--exec="env >../env.trace; git-upload-archive" \
+		--remote=origin \
+		HEAD >/dev/null &&
+	test_path_is_file env.trace &&
+	! grep ^GIT_PROTOCOL env.trace
+'
+
+test_expect_success 'reject client packfile-uris if not advertised' '
+	{
+		packetize command=fetch &&
+		packetize object-format=$(test_oid algo) &&
+		printf 0001 &&
+		packetize packfile-uris https &&
+		packetize done &&
+		printf 0000
+	} >input &&
+	test_must_fail env GIT_PROTOCOL=version=2 \
+		git upload-pack client <input &&
+	test_must_fail env GIT_PROTOCOL=version=2 \
+		git -c uploadpack.blobpackfileuri \
+		upload-pack client <input &&
+	GIT_PROTOCOL=version=2 \
+		git -c uploadpack.blobpackfileuri=anything \
+		upload-pack client <input
 '
 
 # Test protocol v2 with 'http://' transport
@@ -771,7 +840,7 @@
 	# Server responded using protocol v2
 	grep "git< version 2" log &&
 	# Client reported appropriate failure
-	test_i18ngrep "bytes of length header were received" err
+	test_grep "bytes of length header were received" err
 '
 
 test_expect_success 'clone repository with http:// using protocol v2 with incomplete pktline body' '
@@ -788,7 +857,7 @@
 	# Server responded using protocol v2
 	grep "git< version 2" log &&
 	# Client reported appropriate failure
-	test_i18ngrep "bytes of body are still expected" err
+	test_grep "bytes of body are still expected" err
 '
 
 test_expect_success 'clone with http:// using protocol v2 and invalid parameters' '
@@ -935,7 +1004,7 @@
 
 	test_must_fail git -C http_child -c protocol.version=2 \
 		fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
-	test_i18ngrep "expected packfile to be sent after .ready." err
+	test_grep "expected packfile to be sent after .ready." err
 '
 
 test_expect_success 'when server does not send "ready", expect FLUSH' '
@@ -963,7 +1032,7 @@
 		fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
 	grep "fetch< .*acknowledgments" log &&
 	! grep "fetch< .*ready" log &&
-	test_i18ngrep "expected no other sections to be sent after no .ready." err
+	test_grep "expected no other sections to be sent after no .ready." err
 '
 
 configure_exclusion () {
@@ -1073,7 +1142,7 @@
 		git -c protocol.version=2 \
 		-c fetch.uriprotocols=http,https \
 		clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
-	test_i18ngrep "pack downloaded from.*does not match expected hash" err
+	test_grep "pack downloaded from.*does not match expected hash" err
 '
 
 test_expect_success 'packfile-uri with transfer.fsckobjects' '
@@ -1127,7 +1196,7 @@
 	test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
 		-c fetch.uriprotocols=http,https \
 		clone "$HTTPD_URL/smart/http_parent" http_child 2>error &&
-	test_i18ngrep "invalid author/committer line - missing email" error
+	test_grep "invalid author/committer line - missing email" error
 '
 
 test_expect_success 'packfile-uri with transfer.fsckobjects succeeds when .gitmodules is separate from tree' '
@@ -1175,7 +1244,7 @@
 	test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
 		-c fetch.uriprotocols=http,https \
 		clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
-	test_i18ngrep "disallowed submodule name" err
+	test_grep "disallowed submodule name" err
 '
 
 test_expect_success 'packfile-uri path redacted in trace' '
@@ -1258,7 +1327,7 @@
 		--negotiate-only \
 		--negotiation-tip=$(git -C client rev-parse HEAD) \
 		origin 2>err &&
-	test_i18ngrep "server does not support wait-for-done" err
+	test_grep "server does not support wait-for-done" err
 '
 
 test_expect_success 'http:// --negotiate-only with protocol v0' '
@@ -1272,7 +1341,7 @@
 		--negotiate-only \
 		--negotiation-tip=$(git -C client rev-parse HEAD) \
 		origin 2>err &&
-	test_i18ngrep "negotiate-only requires protocol v2" err
+	test_grep "negotiate-only requires protocol v2" err
 '
 
 # DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index df74f80..1910971 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -484,7 +484,7 @@
 	cp -r "$LOCAL_PRISTINE" local &&
 	inconsistency main $(test_oid numeric) &&
 	test_must_fail git -C local fetch 2>err &&
-	test_i18ngrep "fatal: remote error: upload-pack: not our ref" err
+	test_grep "fatal: remote error: upload-pack: not our ref" err
 '
 
 test_expect_success 'server is initially ahead - ref in want' '
@@ -530,7 +530,7 @@
 	echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
 	test_must_fail git -C local fetch 2>err &&
 
-	test_i18ngrep "fatal: remote error: unknown ref refs/heads/rain" err
+	test_grep "fatal: remote error: unknown ref refs/heads/rain" err
 '
 
 # DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5704-protocol-violations.sh b/t/t5704-protocol-violations.sh
index ae1a00a..11be64f 100755
--- a/t/t5704-protocol-violations.sh
+++ b/t/t5704-protocol-violations.sh
@@ -18,7 +18,7 @@
 	} >input &&
 	test_must_fail env GIT_PROTOCOL=version=2 \
 		git upload-pack . <input 2>err &&
-	test_i18ngrep "expected flush after ls-refs arguments" err
+	test_grep "expected flush after ls-refs arguments" err
 '
 
 test_expect_success 'extra delim packet in v2 fetch args' '
@@ -31,7 +31,7 @@
 	} >input &&
 	test_must_fail env GIT_PROTOCOL=version=2 \
 		git upload-pack . <input 2>err &&
-	test_i18ngrep "expected flush after fetch arguments" err
+	test_grep "expected flush after fetch arguments" err
 '
 
 test_expect_success 'bogus symref in v0 capabilities' '
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index d386076..4e0a77f 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -137,7 +137,7 @@
 test_expect_success 'cloning without refspec' '
 	GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
 	git clone "testgit::${PWD}/server" local2 2>error &&
-	test_i18ngrep "this remote helper should implement refspec capability" error &&
+	test_grep "this remote helper should implement refspec capability" error &&
 	compare_refs local2 HEAD server HEAD
 '
 
@@ -145,7 +145,7 @@
 	(cd local2 &&
 	git reset --hard &&
 	GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
-	test_i18ngrep "this remote helper should implement refspec capability" error &&
+	test_grep "this remote helper should implement refspec capability" error &&
 	compare_refs local2 HEAD server HEAD
 '
 
@@ -157,7 +157,7 @@
 	GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
 	export GIT_REMOTE_TESTGIT_NOREFSPEC &&
 	test_must_fail git push 2>../error) &&
-	test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
+	test_grep "remote-helper doesn.t support push; refspec needed" error
 '
 
 test_expect_success 'pulling without marks' '
@@ -256,7 +256,7 @@
 test_expect_success 'proper failure checks for fetching' '
 	(cd local &&
 	test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
-	test_i18ngrep -q "error while running fast-import" error
+	test_grep -q "error while running fast-import" error
 	)
 '
 
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
index 1544d6d..c5b10f5 100755
--- a/t/t5801/git-remote-testgit
+++ b/t/t5801/git-remote-testgit
@@ -12,6 +12,11 @@
 
 dir="$GIT_DIR/testgit/$alias"
 
+if ! git rev-parse --is-inside-git-dir
+then
+	exit 1
+fi
+
 h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
 t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
 
@@ -25,6 +30,7 @@
 export GIT_DIR
 
 force=
+object_format=
 
 mkdir -p "$dir"
 
@@ -56,7 +62,8 @@
 		echo
 		;;
 	list)
-		echo ":object-format $(git rev-parse --show-object-format=storage)"
+		test -n "$object_format" &&
+			echo ":object-format $(git rev-parse --show-object-format=storage)"
 		git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
 		head=$(git symbolic-ref HEAD)
 		echo "@$head HEAD"
diff --git a/t/t5811-proto-disable-git.sh b/t/t5811-proto-disable-git.sh
index 8ac6b2a..ed773e7 100755
--- a/t/t5811-proto-disable-git.sh
+++ b/t/t5811-proto-disable-git.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test disabling of git-over-tcp in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-proto-disable.sh"
 . "$TEST_DIRECTORY/lib-git-daemon.sh"
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
index d8da5f5..769c717 100755
--- a/t/t5812-proto-disable-http.sh
+++ b/t/t5812-proto-disable-http.sh
@@ -20,7 +20,7 @@
 	test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
 			   GIT_SMART_HTTP=0 \
 		git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
-	test_i18ngrep -E "(ftp.*disabled|your curl version is too old)" stderr
+	test_grep -E "(ftp.*disabled|your curl version is too old)" stderr
 '
 
 test_expect_success 'curl limits redirects' '
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 12def7b..6289a2e 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -169,4 +169,17 @@
 	test_line_count = $count actual
 '
 
+test_expect_success 'rev-list --unpacked' '
+	git repack -ad &&
+	test_commit unpacked &&
+
+	git rev-list --objects --no-object-names unpacked^.. >expect.raw &&
+	sort expect.raw >expect &&
+
+	git rev-list --all --objects --unpacked --no-object-names >actual.raw &&
+	sort actual.raw >actual &&
+
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
index 16635ec..73a2465 100755
--- a/t/t6001-rev-list-graft.sh
+++ b/t/t6001-rev-list-graft.sh
@@ -118,10 +118,10 @@
 
 test_expect_success 'show advice that grafts are deprecated' '
 	git show HEAD 2>err &&
-	test_i18ngrep "git replace" err &&
+	test_grep "git replace" err &&
 	test_config advice.graftFileDeprecated false &&
 	git show HEAD 2>err &&
-	test_i18ngrep ! "git replace" err
+	test_grep ! "git replace" err
 '
 
 test_done
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index 0729f80..ee0306a 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -18,20 +18,34 @@
 '
 
 test_expect_success '--max-count' '
+	test_must_fail git rev-list --max-count=1q HEAD 2>error &&
+	grep "not an integer" error &&
+
 	test_stdout_line_count = 0 git rev-list HEAD --max-count=0 &&
 	test_stdout_line_count = 3 git rev-list HEAD --max-count=3 &&
 	test_stdout_line_count = 5 git rev-list HEAD --max-count=5 &&
-	test_stdout_line_count = 5 git rev-list HEAD --max-count=10
+	test_stdout_line_count = 5 git rev-list HEAD --max-count=10 &&
+	test_stdout_line_count = 5 git rev-list HEAD --max-count=-1
 '
 
 test_expect_success '--max-count all forms' '
+	test_must_fail git rev-list -1q HEAD 2>error &&
+	grep "not an integer" error &&
+	test_must_fail git rev-list --1 HEAD &&
+	test_must_fail git rev-list -n 1q HEAD 2>error &&
+	grep "not an integer" error &&
+
 	test_stdout_line_count = 1 git rev-list HEAD --max-count=1 &&
 	test_stdout_line_count = 1 git rev-list HEAD -1 &&
 	test_stdout_line_count = 1 git rev-list HEAD -n1 &&
-	test_stdout_line_count = 1 git rev-list HEAD -n 1
+	test_stdout_line_count = 1 git rev-list HEAD -n 1 &&
+	test_stdout_line_count = 5 git rev-list HEAD -n -1
 '
 
 test_expect_success '--skip' '
+	test_must_fail git rev-list --skip 1q HEAD 2>error &&
+	grep "not an integer" error &&
+
 	test_stdout_line_count = 5 git rev-list HEAD --skip=0 &&
 	test_stdout_line_count = 2 git rev-list HEAD --skip=3 &&
 	test_stdout_line_count = 0 git rev-list HEAD --skip=5 &&
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 41d0ca0..573eb97 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -493,7 +493,7 @@
 	test_tick &&
 	C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
 	A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
-	verbose test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
+	test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
 '
 
 test_expect_success 'del LF before empty (1)' '
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index 5a67bbc..91db8fa 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_revlist () {
@@ -62,6 +63,17 @@
 	git checkout main
 '
 
+test_expect_success 'parse --max-parents & --min-parents' '
+	test_must_fail git rev-list --max-parents=1q HEAD 2>error &&
+	grep "not an integer" error &&
+
+	test_must_fail git rev-list --min-parents=1q HEAD 2>error &&
+	grep "not an integer" error &&
+
+	git rev-list --max-parents=1 --min-parents=1 HEAD &&
+	git rev-list --max-parents=-1 --min-parents=-1 HEAD
+'
+
 test_expect_success 'rev-list roots' '
 
 	check_revlist "--max-parents=0" one five
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
index 0516251..4821b90 100755
--- a/t/t6017-rev-list-stdin.sh
+++ b/t/t6017-rev-list-stdin.sh
@@ -48,7 +48,9 @@
 			git add file-$i &&
 			test_tick &&
 			git commit -m side-$i || exit
-		done
+		done &&
+
+		git update-ref refs/heads/-dashed-branch HEAD
 	)
 '
 
@@ -60,6 +62,13 @@
 check side-3 ^side-4 -- file-3
 check side-3 ^side-2
 check side-3 ^side-2 -- file-1
+check --all
+check --all --not --branches
+check --glob=refs/heads
+check --glob=refs/heads --
+check --glob=refs/heads -- file-1
+check --end-of-options -dashed-branch
+check --all --not refs/heads/main
 
 test_expect_success 'not only --stdin' '
 	cat >expect <<-EOF &&
@@ -78,4 +87,65 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'pseudo-opt with missing value' '
+	cat >input <<-EOF &&
+	--glob
+	refs/heads
+	EOF
+
+	cat >expect <<-EOF &&
+	fatal: Option ${SQ}--glob${SQ} requires a value
+	EOF
+
+	test_must_fail git rev-list --stdin <input 2>error &&
+	test_cmp expect error
+'
+
+test_expect_success 'pseudo-opt with invalid value' '
+	cat >input <<-EOF &&
+	--no-walk=garbage
+	EOF
+
+	cat >expect <<-EOF &&
+	error: invalid argument to --no-walk
+	fatal: invalid option ${SQ}--no-walk=garbage${SQ} in --stdin mode
+	EOF
+
+	test_must_fail git rev-list --stdin <input 2>error &&
+	test_cmp expect error
+'
+
+test_expect_success 'unknown option without --end-of-options' '
+	cat >input <<-EOF &&
+	-dashed-branch
+	EOF
+
+	cat >expect <<-EOF &&
+	fatal: invalid option ${SQ}-dashed-branch${SQ} in --stdin mode
+	EOF
+
+	test_must_fail git rev-list --stdin <input 2>error &&
+	test_cmp expect error
+'
+
+test_expect_success '--not on command line does not influence revisions read via --stdin' '
+	cat >input <<-EOF &&
+	refs/heads/main
+	EOF
+	git rev-list refs/heads/main >expect &&
+
+	git rev-list refs/heads/main --not --stdin <input >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--not via stdin does not influence revisions from command line' '
+	cat >input <<-EOF &&
+	--not
+	EOF
+	git rev-list refs/heads/main >expect &&
+
+	git rev-list refs/heads/main --stdin refs/heads/main <input >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index aabf590..3b181f7 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -187,7 +187,7 @@
 	compare rev-parse "--exclude=upstream/x --remotes=upstream/*" "upstream/one upstream/two"
 '
 
-for section in receive uploadpack
+for section in fetch receive uploadpack
 do
 	test_expect_success "rev-parse --exclude-hidden=$section with --all" '
 		compare "-c transfer.hideRefs=refs/remotes/ rev-parse" "--branches --tags" "--exclude-hidden=$section --all"
@@ -214,15 +214,13 @@
 	for pseudoopt in branches tags remotes
 	do
 		test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
-			echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
 			test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
-			test_cmp expected err
+			test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
 		'
 
 		test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
-			echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
 			test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
-			test_cmp expected err
+			test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
 		'
 	done
 done
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index 7d40994..3e6bcbf 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -10,6 +10,7 @@
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bundle.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 for cmd in create verify list-heads unbundle
 do
@@ -606,4 +607,48 @@
 	)
 '
 
+test_expect_success 'bundle progress includes write phase' '
+	GIT_PROGRESS_DELAY=0 \
+		git bundle create --progress out.bundle --all 2>err &&
+	grep 'Writing' err
+'
+
+test_expect_success TTY 'create --quiet disables all bundle progress' '
+	test_terminal env GIT_PROGRESS_DELAY=0 \
+		git bundle create --quiet out.bundle --all 2>err &&
+	test_must_be_empty err
+'
+
+test_expect_success 'bundle progress with --no-quiet' '
+	GIT_PROGRESS_DELAY=0 \
+		git bundle create --no-quiet out.bundle --all 2>err &&
+	grep "%" err
+'
+
+test_expect_success 'read bundle over stdin' '
+	git bundle create some.bundle HEAD &&
+
+	git bundle verify - <some.bundle 2>err &&
+	grep "<stdin> is okay" err &&
+
+	git bundle list-heads some.bundle >expect &&
+	git bundle list-heads - <some.bundle >actual &&
+	test_cmp expect actual &&
+
+	git bundle unbundle some.bundle >expect &&
+	git bundle unbundle - <some.bundle >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'send a bundle to standard output' '
+	git bundle create - --all HEAD >bundle-one &&
+	mkdir -p down &&
+	git -C down bundle create - --all HEAD >bundle-two &&
+	git bundle verify bundle-one &&
+	git bundle verify bundle-two &&
+	git ls-remote bundle-one >expect &&
+	git ls-remote bundle-two >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
index 11c50b7..51df021 100755
--- a/t/t6021-rev-list-exclude-hidden.sh
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -22,7 +22,7 @@
 	test_cmp expected err
 '
 
-for section in receive uploadpack
+for section in fetch receive uploadpack
 do
 	test_expect_success "$section: passed multiple times" '
 		echo "fatal: --exclude-hidden= passed more than once" >expected &&
@@ -151,12 +151,12 @@
 	do
 		test_expect_success "$section: fails with --$pseudoopt" '
 			test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
-			test_i18ngrep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+			test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
 		'
 
 		test_expect_success "$section: fails with --$pseudoopt=pattern" '
 			test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
-			test_i18ngrep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+			test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
 		'
 	done
 done
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
new file mode 100755
index 0000000..127180e
--- /dev/null
+++ b/t/t6022-rev-list-missing.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+test_description='handling of missing objects in rev-list'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# We setup the repository with two commits, this way HEAD is always
+# available and we can hide commit 1.
+test_expect_success 'create repository and alternate directory' '
+	test_commit 1 &&
+	test_commit 2 &&
+	test_commit 3 &&
+	git tag -m "tag message" annot_tag HEAD~1 &&
+	git tag regul_tag HEAD~1 &&
+	git branch a_branch HEAD~1
+'
+
+# We manually corrupt the repository, which means that the commit-graph may
+# contain references to already-deleted objects. We thus need to enable
+# commit-graph paranoia to not returned these deleted commits from the graph.
+GIT_COMMIT_GRAPH_PARANOIA=true
+export GIT_COMMIT_GRAPH_PARANOIA
+
+for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+	test_expect_success "rev-list --missing=error fails with missing object $obj" '
+		oid="$(git rev-parse $obj)" &&
+		path=".git/objects/$(test_oid_to_path $oid)" &&
+
+		mv "$path" "$path.hidden" &&
+		test_when_finished "mv $path.hidden $path" &&
+
+		test_must_fail git rev-list --missing=error --objects \
+			--no-object-names HEAD
+	'
+done
+
+for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+	for action in "allow-any" "print"
+	do
+		test_expect_success "rev-list --missing=$action with missing $obj" '
+			oid="$(git rev-parse $obj)" &&
+			path=".git/objects/$(test_oid_to_path $oid)" &&
+
+			# Before the object is made missing, we use rev-list to
+			# get the expected oids.
+			git rev-list --objects --no-object-names \
+				HEAD ^$obj >expect.raw &&
+
+			# Blobs are shared by all commits, so even though a commit/tree
+			# might be skipped, its blob must be accounted for.
+			if test $obj != "HEAD:1.t"
+			then
+				echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+				echo $(git rev-parse HEAD:2.t) >>expect.raw
+			fi &&
+
+			mv "$path" "$path.hidden" &&
+			test_when_finished "mv $path.hidden $path" &&
+
+			git rev-list --missing=$action --objects --no-object-names \
+				HEAD >actual.raw &&
+
+			# When the action is to print, we should also add the missing
+			# oid to the expect list.
+			case $action in
+			allow-any)
+				;;
+			print)
+				grep ?$oid actual.raw &&
+				echo ?$oid >>expect.raw
+				;;
+			esac &&
+
+			sort actual.raw >actual &&
+			sort expect.raw >expect &&
+			test_cmp expect actual
+		'
+	done
+done
+
+for missing_tip in "annot_tag" "regul_tag" "a_branch" "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+	# We want to check that things work when both
+	#   - all the tips passed are missing (case existing_tip = ""), and
+	#   - there is one missing tip and one existing tip (case existing_tip = "HEAD")
+	for existing_tip in "" "HEAD"
+	do
+		for action in "allow-any" "print"
+		do
+			test_expect_success "--missing=$action with tip '$missing_tip' missing and tip '$existing_tip'" '
+				# Before the object is made missing, we use rev-list to
+				# get the expected oids.
+				if test "$existing_tip" = "HEAD"
+				then
+					git rev-list --objects --no-object-names \
+						HEAD ^$missing_tip >expect.raw
+				else
+					>expect.raw
+				fi &&
+
+				# Blobs are shared by all commits, so even though a commit/tree
+				# might be skipped, its blob must be accounted for.
+				if test "$existing_tip" = "HEAD" && test $missing_tip != "HEAD:1.t"
+				then
+					echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+					echo $(git rev-parse HEAD:2.t) >>expect.raw
+				fi &&
+
+				missing_oid="$(git rev-parse $missing_tip)" &&
+
+				if test "$missing_tip" = "annot_tag"
+				then
+					oid="$(git rev-parse $missing_tip^{commit})" &&
+					echo "$missing_oid" >>expect.raw
+				else
+					oid="$missing_oid"
+				fi &&
+
+				path=".git/objects/$(test_oid_to_path $oid)" &&
+
+				mv "$path" "$path.hidden" &&
+				test_when_finished "mv $path.hidden $path" &&
+
+				git rev-list --missing=$action --objects --no-object-names \
+				     $missing_oid $existing_tip >actual.raw &&
+
+				# When the action is to print, we should also add the missing
+				# oid to the expect list.
+				case $action in
+				allow-any)
+					;;
+				print)
+					grep ?$oid actual.raw &&
+					echo ?$oid >>expect.raw
+					;;
+				esac &&
+
+				sort actual.raw >actual &&
+				sort expect.raw >expect &&
+				test_cmp expect actual
+			'
+		done
+	done
+done
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 3ba4fdf..cdc0270 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -122,6 +122,29 @@
 	grep bar ".git/BISECT_NAMES"
 '
 
+test_expect_success 'bisect reset: back in a branch checked out also elsewhere' '
+	echo "shared" > branch.expect &&
+	test_bisect_reset() {
+		git -C $1 bisect start &&
+		git -C $1 bisect good $HASH1 &&
+		git -C $1 bisect bad $HASH3 &&
+		git -C $1 bisect reset &&
+		git -C $1 branch --show-current > branch.output &&
+		cmp branch.expect branch.output
+	} &&
+	test_when_finished "
+		git worktree remove wt1 &&
+		git worktree remove wt2 &&
+		git branch -d shared
+	" &&
+	git worktree add wt1 -b shared &&
+	git worktree add wt2 -f shared &&
+	# we test in both worktrees to ensure that works
+	# as expected with "first" and "next" worktrees
+	test_bisect_reset wt1 &&
+	test_bisect_reset wt2
+'
+
 test_expect_success 'bisect reset: back in the main branch' '
 	git bisect reset &&
 	echo "* main" > branch.expect &&
@@ -147,6 +170,12 @@
 	cmp branch.expect branch.output
 '
 
+test_expect_success 'bisect reset cleans up even when not bisecting' '
+	echo garbage >.git/BISECT_LOG &&
+	git bisect reset &&
+	test_path_is_missing .git/BISECT_LOG
+'
+
 test_expect_success 'bisect reset removes packed refs' '
 	git bisect reset &&
 	git bisect start &&
@@ -197,7 +226,7 @@
 	cp .git/BISECT_START saved &&
 	test_must_fail git bisect start $HASH4 foo -- &&
 	git branch > branch.output &&
-	test_i18ngrep "* (no branch, bisect started on other)" branch.output > /dev/null &&
+	test_grep "* (no branch, bisect started on other)" branch.output > /dev/null &&
 	test_cmp saved .git/BISECT_START
 '
 test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
@@ -565,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_i18ngrep "mistook good and bad" rev_list_error &&
+	test_grep "mistook good and bad" rev_list_error &&
 	git bisect reset
 '
 
@@ -607,7 +636,7 @@
 
 test_expect_success 'good merge base when good and bad are siblings' '
 	git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+	test_grep "merge base must be tested" my_bisect_log.txt &&
 	grep $HASH4 my_bisect_log.txt &&
 	git bisect good > my_bisect_log.txt &&
 	! grep "merge base must be tested" my_bisect_log.txt &&
@@ -616,7 +645,7 @@
 '
 test_expect_success 'skipped merge base when good and bad are siblings' '
 	git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+	test_grep "merge base must be tested" my_bisect_log.txt &&
 	grep $HASH4 my_bisect_log.txt &&
 	git bisect skip > my_bisect_log.txt 2>&1 &&
 	grep "warning" my_bisect_log.txt &&
@@ -626,11 +655,11 @@
 
 test_expect_success 'bad merge base when good and bad are siblings' '
 	git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+	test_grep "merge base must be tested" my_bisect_log.txt &&
 	grep $HASH4 my_bisect_log.txt &&
 	test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
-	test_i18ngrep "merge base $HASH4 is bad" my_bisect_log.txt &&
-	test_i18ngrep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
+	test_grep "merge base $HASH4 is bad" my_bisect_log.txt &&
+	test_grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
 	git bisect reset
 '
 
@@ -681,9 +710,9 @@
 
 test_expect_success 'good merge bases when good and bad are siblings' '
 	git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+	test_grep "merge base must be tested" my_bisect_log.txt &&
 	git bisect good > my_bisect_log2.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log2.txt &&
+	test_grep "merge base must be tested" my_bisect_log2.txt &&
 	{
 		{
 			grep "$SIDE_HASH5" my_bisect_log.txt &&
@@ -698,14 +727,14 @@
 
 test_expect_success 'optimized merge base checks' '
 	git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
+	test_grep "merge base must be tested" my_bisect_log.txt &&
 	grep "$HASH4" my_bisect_log.txt &&
 	git bisect good > my_bisect_log2.txt &&
 	test -f ".git/BISECT_ANCESTORS_OK" &&
 	test "$HASH6" = $(git rev-parse --verify HEAD) &&
 	git bisect bad &&
 	git bisect good "$A_HASH" > my_bisect_log4.txt &&
-	test_i18ngrep "merge base must be tested" my_bisect_log4.txt &&
+	test_grep "merge base must be tested" my_bisect_log4.txt &&
 	test_path_is_missing ".git/BISECT_ANCESTORS_OK"
 '
 
@@ -783,7 +812,7 @@
 
 test_expect_success 'erroring out when using bad path arguments' '
 	test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
-	test_i18ngrep "bad path arguments" error.txt
+	test_grep "bad path arguments" error.txt
 '
 
 test_expect_success 'test bisection on bare repo - --no-checkout specified' '
@@ -849,7 +878,7 @@
 
 echo "" > expected.ok
 cat > expected.missing-tree.default <<EOF
-fatal: unable to read tree $deleted
+fatal: unable to read tree ($deleted)
 EOF
 
 test_expect_success 'bisect fails if tree is broken on start commit' '
@@ -1153,7 +1182,7 @@
 	git bisect bad $HASH4 &&
 	git bisect reset &&
 	test -z "$(git for-each-ref "refs/bisect/*")" &&
-	test_path_is_missing ".git/BISECT_EXPECTED_REV" &&
+	test_ref_missing BISECT_EXPECTED_REV &&
 	test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
 	test_path_is_missing ".git/BISECT_LOG" &&
 	test_path_is_missing ".git/BISECT_RUN" &&
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index a313849..acc281c 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 advance () {
@@ -82,13 +83,13 @@
 	(
 		cd test && git checkout b1
 	) >actual &&
-	test_i18ngrep "have 1 and 1 different" actual
+	test_grep "have 1 and 1 different" actual
 '
 
 test_expect_success 'checkout with local tracked branch' '
 	git checkout main &&
 	git checkout follower >actual &&
-	test_i18ngrep "is ahead of" actual
+	test_grep "is ahead of" actual
 '
 
 test_expect_success 'checkout (upstream is gone)' '
@@ -96,14 +97,14 @@
 		cd test &&
 		git checkout b5
 	) >actual &&
-	test_i18ngrep "is based on .*, but the upstream is gone." actual
+	test_grep "is based on .*, but the upstream is gone." actual
 '
 
 test_expect_success 'checkout (up-to-date with upstream)' '
 	(
 		cd test && git checkout b6
 	) >actual &&
-	test_i18ngrep "Your branch is up to date with .origin/main" actual
+	test_grep "Your branch is up to date with .origin/main" actual
 '
 
 test_expect_success 'status (diverged from upstream)' '
@@ -113,7 +114,7 @@
 		# reports nothing to commit
 		test_must_fail git commit --dry-run
 	) >actual &&
-	test_i18ngrep "have 1 and 1 different" actual
+	test_grep "have 1 and 1 different" actual
 '
 
 test_expect_success 'status (upstream is gone)' '
@@ -123,7 +124,7 @@
 		# reports nothing to commit
 		test_must_fail git commit --dry-run
 	) >actual &&
-	test_i18ngrep "is based on .*, but the upstream is gone." actual
+	test_grep "is based on .*, but the upstream is gone." actual
 '
 
 test_expect_success 'status (up-to-date with upstream)' '
@@ -133,7 +134,7 @@
 		# reports nothing to commit
 		test_must_fail git commit --dry-run
 	) >actual &&
-	test_i18ngrep "Your branch is up to date with .origin/main" actual
+	test_grep "Your branch is up to date with .origin/main" actual
 '
 
 cat >expect <<\EOF
@@ -252,7 +253,7 @@
 	git checkout main &&
 	git tag light &&
 	test_must_fail git branch --track lighttrack light >actual &&
-	test_i18ngrep ! "set up to track" actual &&
+	test_grep ! "set up to track" actual &&
 	test_must_fail git checkout lighttrack
 '
 
@@ -260,7 +261,7 @@
 	git checkout main &&
 	git tag -m heavy heavy &&
 	test_must_fail git branch --track heavytrack heavy >actual &&
-	test_i18ngrep ! "set up to track" actual &&
+	test_grep ! "set up to track" actual &&
 	test_must_fail git checkout heavytrack
 '
 
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 2500acc..c6e9b33 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -44,7 +44,7 @@
 	_parent_number=$(( $_parent_number + 1 ))
     done &&
     test_must_fail git rev-parse --verify $_commit^$_parent_number 2>err &&
-    test_i18ngrep "Needed a single revision" err
+    test_grep "Needed a single revision" err
 }
 
 commit_has_parents ()
@@ -62,59 +62,59 @@
 HASH7=
 
 test_expect_success 'set up buggy branch' '
-     echo "line 1" >>hello &&
-     echo "line 2" >>hello &&
-     echo "line 3" >>hello &&
-     echo "line 4" >>hello &&
-     add_and_commit_file hello "4 lines" &&
-     HASH1=$(git rev-parse --verify HEAD) &&
-     echo "line BUG" >>hello &&
-     echo "line 6" >>hello &&
-     echo "line 7" >>hello &&
-     echo "line 8" >>hello &&
-     add_and_commit_file hello "4 more lines with a BUG" &&
-     HASH2=$(git rev-parse --verify HEAD) &&
-     echo "line 9" >>hello &&
-     echo "line 10" >>hello &&
-     add_and_commit_file hello "2 more lines" &&
-     HASH3=$(git rev-parse --verify HEAD) &&
-     echo "line 11" >>hello &&
-     add_and_commit_file hello "1 more line" &&
-     HASH4=$(git rev-parse --verify HEAD) &&
-     sed -e "s/BUG/5/" hello >hello.new &&
-     mv hello.new hello &&
-     add_and_commit_file hello "BUG fixed" &&
-     HASH5=$(git rev-parse --verify HEAD) &&
-     echo "line 12" >>hello &&
-     echo "line 13" >>hello &&
-     add_and_commit_file hello "2 more lines" &&
-     HASH6=$(git rev-parse --verify HEAD) &&
-     echo "line 14" >>hello &&
-     echo "line 15" >>hello &&
-     echo "line 16" >>hello &&
-     add_and_commit_file hello "again 3 more lines" &&
-     HASH7=$(git rev-parse --verify HEAD)
+	echo "line 1" >>hello &&
+	echo "line 2" >>hello &&
+	echo "line 3" >>hello &&
+	echo "line 4" >>hello &&
+	add_and_commit_file hello "4 lines" &&
+	HASH1=$(git rev-parse --verify HEAD) &&
+	echo "line BUG" >>hello &&
+	echo "line 6" >>hello &&
+	echo "line 7" >>hello &&
+	echo "line 8" >>hello &&
+	add_and_commit_file hello "4 more lines with a BUG" &&
+	HASH2=$(git rev-parse --verify HEAD) &&
+	echo "line 9" >>hello &&
+	echo "line 10" >>hello &&
+	add_and_commit_file hello "2 more lines" &&
+	HASH3=$(git rev-parse --verify HEAD) &&
+	echo "line 11" >>hello &&
+	add_and_commit_file hello "1 more line" &&
+	HASH4=$(git rev-parse --verify HEAD) &&
+	sed -e "s/BUG/5/" hello >hello.new &&
+	mv hello.new hello &&
+	add_and_commit_file hello "BUG fixed" &&
+	HASH5=$(git rev-parse --verify HEAD) &&
+	echo "line 12" >>hello &&
+	echo "line 13" >>hello &&
+	add_and_commit_file hello "2 more lines" &&
+	HASH6=$(git rev-parse --verify HEAD) &&
+	echo "line 14" >>hello &&
+	echo "line 15" >>hello &&
+	echo "line 16" >>hello &&
+	add_and_commit_file hello "again 3 more lines" &&
+	HASH7=$(git rev-parse --verify HEAD)
 '
 
 test_expect_success 'replace the author' '
-     git cat-file commit $HASH2 | grep "author A U Thor" &&
-     R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
-     git cat-file commit $R | grep "author O Thor" &&
-     git update-ref refs/replace/$HASH2 $R &&
-     git show HEAD~5 | grep "O Thor" &&
-     git show $HASH2 | grep "O Thor"
+	git cat-file commit $HASH2 | grep "author A U Thor" &&
+	R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+	git cat-file commit $R | grep "author O Thor" &&
+	git update-ref refs/replace/$HASH2 $R &&
+	git show HEAD~5 | grep "O Thor" &&
+	git show $HASH2 | grep "O Thor"
 '
 
 test_expect_success 'test --no-replace-objects option' '
-     git cat-file commit $HASH2 | grep "author O Thor" &&
-     git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
-     git show $HASH2 | grep "O Thor" &&
-     git --no-replace-objects show $HASH2 | grep "A U Thor"
+	git cat-file commit $HASH2 | grep "author O Thor" &&
+	git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
+	git show $HASH2 | grep "O Thor" &&
+	git --no-replace-objects show $HASH2 | grep "A U Thor"
 '
 
 test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
-     GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
-     GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
+	GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
+	GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
 '
 
 test_expect_success 'test core.usereplacerefs config option' '
@@ -132,64 +132,64 @@
 EOF
 
 test_expect_success 'tag replaced commit' '
-     git update-ref refs/tags/mytag $(git mktag <tag.sig)
+	git update-ref refs/tags/mytag $(git mktag <tag.sig)
 '
 
 test_expect_success '"git fsck" works' '
-     git fsck main >fsck_main.out &&
-     test_i18ngrep "dangling commit $R" fsck_main.out &&
-     test_i18ngrep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
-     test -z "$(git fsck)"
+	git fsck main >fsck_main.out &&
+	test_grep "dangling commit $R" fsck_main.out &&
+	test_grep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
+	test -z "$(git fsck)"
 '
 
 test_expect_success 'repack, clone and fetch work' '
-     git repack -a -d &&
-     git clone --no-hardlinks . clone_dir &&
-     (
-	  cd clone_dir &&
-	  git show HEAD~5 | grep "A U Thor" &&
-	  git show $HASH2 | grep "A U Thor" &&
-	  git cat-file commit $R &&
-	  git repack -a -d &&
-	  test_must_fail git cat-file commit $R &&
-	  git fetch ../ "refs/replace/*:refs/replace/*" &&
-	  git show HEAD~5 | grep "O Thor" &&
-	  git show $HASH2 | grep "O Thor" &&
-	  git cat-file commit $R
-     )
+	git repack -a -d &&
+	git clone --no-hardlinks . clone_dir &&
+	(
+		cd clone_dir &&
+		git show HEAD~5 | grep "A U Thor" &&
+		git show $HASH2 | grep "A U Thor" &&
+		git cat-file commit $R &&
+		git repack -a -d &&
+		test_must_fail git cat-file commit $R &&
+		git fetch ../ "refs/replace/*:refs/replace/*" &&
+		git show HEAD~5 | grep "O Thor" &&
+		git show $HASH2 | grep "O Thor" &&
+		git cat-file commit $R
+	)
 '
 
 test_expect_success '"git replace" listing and deleting' '
-     test "$HASH2" = "$(git replace -l)" &&
-     test "$HASH2" = "$(git replace)" &&
-     aa=${HASH2%??????????????????????????????????????} &&
-     test "$HASH2" = "$(git replace --list "$aa*")" &&
-     test_must_fail git replace -d $R &&
-     test_must_fail git replace --delete &&
-     test_must_fail git replace -l -d $HASH2 &&
-     git replace -d $HASH2 &&
-     git show $HASH2 | grep "A U Thor" &&
-     test -z "$(git replace -l)"
+	test "$HASH2" = "$(git replace -l)" &&
+	test "$HASH2" = "$(git replace)" &&
+	aa=${HASH2%??????????????????????????????????????} &&
+	test "$HASH2" = "$(git replace --list "$aa*")" &&
+	test_must_fail git replace -d $R &&
+	test_must_fail git replace --delete &&
+	test_must_fail git replace -l -d $HASH2 &&
+	git replace -d $HASH2 &&
+	git show $HASH2 | grep "A U Thor" &&
+	test -z "$(git replace -l)"
 '
 
 test_expect_success '"git replace" replacing' '
-     git replace $HASH2 $R &&
-     git show $HASH2 | grep "O Thor" &&
-     test_must_fail git replace $HASH2 $R &&
-     git replace -f $HASH2 $R &&
-     test_must_fail git replace -f &&
-     test "$HASH2" = "$(git replace)"
+	git replace $HASH2 $R &&
+	git show $HASH2 | grep "O Thor" &&
+	test_must_fail git replace $HASH2 $R &&
+	git replace -f $HASH2 $R &&
+	test_must_fail git replace -f &&
+	test "$HASH2" = "$(git replace)"
 '
 
 test_expect_success '"git replace" resolves sha1' '
-     SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
-     git replace -d $SHORTHASH2 &&
-     git replace $SHORTHASH2 $R &&
-     git show $HASH2 | grep "O Thor" &&
-     test_must_fail git replace $HASH2 $R &&
-     git replace -f $HASH2 $R &&
-     test_must_fail git replace --force &&
-     test "$HASH2" = "$(git replace)"
+	SHORTHASH2=$(git rev-parse --short=8 $HASH2) &&
+	git replace -d $SHORTHASH2 &&
+	git replace $SHORTHASH2 $R &&
+	git show $HASH2 | grep "O Thor" &&
+	test_must_fail git replace $HASH2 $R &&
+	git replace -f $HASH2 $R &&
+	test_must_fail git replace --force &&
+	test "$HASH2" = "$(git replace)"
 '
 
 # This creates a side branch where the bug in H2
@@ -207,79 +207,79 @@
 # Then we replace H6 with P6.
 #
 test_expect_success 'create parallel branch without the bug' '
-     git replace -d $HASH2 &&
-     git show $HASH2 | grep "A U Thor" &&
-     git checkout $HASH1 &&
-     git cherry-pick $HASH2 &&
-     git show $HASH5 | git apply &&
-     git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
-     PARA2=$(git rev-parse --verify HEAD) &&
-     git cherry-pick $HASH3 &&
-     PARA3=$(git rev-parse --verify HEAD) &&
-     git cherry-pick $HASH4 &&
-     PARA4=$(git rev-parse --verify HEAD) &&
-     git cherry-pick $HASH6 &&
-     PARA6=$(git rev-parse --verify HEAD) &&
-     git replace $HASH6 $PARA6 &&
-     git checkout main &&
-     cur=$(git rev-parse --verify HEAD) &&
-     test "$cur" = "$HASH7" &&
-     git log --pretty=oneline | grep $PARA2 &&
-     git remote add cloned ./clone_dir
+	git replace -d $HASH2 &&
+	git show $HASH2 | grep "A U Thor" &&
+	git checkout $HASH1 &&
+	git cherry-pick $HASH2 &&
+	git show $HASH5 | git apply &&
+	git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+	PARA2=$(git rev-parse --verify HEAD) &&
+	git cherry-pick $HASH3 &&
+	PARA3=$(git rev-parse --verify HEAD) &&
+	git cherry-pick $HASH4 &&
+	PARA4=$(git rev-parse --verify HEAD) &&
+	git cherry-pick $HASH6 &&
+	PARA6=$(git rev-parse --verify HEAD) &&
+	git replace $HASH6 $PARA6 &&
+	git checkout main &&
+	cur=$(git rev-parse --verify HEAD) &&
+	test "$cur" = "$HASH7" &&
+	git log --pretty=oneline | grep $PARA2 &&
+	git remote add cloned ./clone_dir
 '
 
 test_expect_success 'push to cloned repo' '
-     git push cloned $HASH6^:refs/heads/parallel &&
-     (
-	  cd clone_dir &&
-	  git checkout parallel &&
-	  git log --pretty=oneline | grep $PARA2
-     )
+	git push cloned $HASH6^:refs/heads/parallel &&
+	(
+		cd clone_dir &&
+		git checkout parallel &&
+		git log --pretty=oneline | grep $PARA2
+	)
 '
 
 test_expect_success 'push branch with replacement' '
-     git cat-file commit $PARA3 | grep "author A U Thor" &&
-     S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
-     git cat-file commit $S | grep "author O Thor" &&
-     git replace $PARA3 $S &&
-     git show $HASH6~2 | grep "O Thor" &&
-     git show $PARA3 | grep "O Thor" &&
-     git push cloned $HASH6^:refs/heads/parallel2 &&
-     (
-	  cd clone_dir &&
-	  git checkout parallel2 &&
-	  git log --pretty=oneline | grep $PARA3 &&
-	  git show $PARA3 | grep "A U Thor"
-     )
+	git cat-file commit $PARA3 | grep "author A U Thor" &&
+	S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+	git cat-file commit $S | grep "author O Thor" &&
+	git replace $PARA3 $S &&
+	git show $HASH6~2 | grep "O Thor" &&
+	git show $PARA3 | grep "O Thor" &&
+	git push cloned $HASH6^:refs/heads/parallel2 &&
+	(
+		cd clone_dir &&
+		git checkout parallel2 &&
+		git log --pretty=oneline | grep $PARA3 &&
+		git show $PARA3 | grep "A U Thor"
+	)
 '
 
 test_expect_success 'fetch branch with replacement' '
-     git branch tofetch $HASH6 &&
-     (
-	  cd clone_dir &&
-	  git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
-	  git log --pretty=oneline parallel3 >output.txt &&
-	  ! grep $PARA3 output.txt &&
-	  git show $PARA3 >para3.txt &&
-	  grep "A U Thor" para3.txt &&
-	  git fetch origin "refs/replace/*:refs/replace/*" &&
-	  git log --pretty=oneline parallel3 >output.txt &&
-	  grep $PARA3 output.txt &&
-	  git show $PARA3 >para3.txt &&
-	  grep "O Thor" para3.txt
-     )
+	git branch tofetch $HASH6 &&
+	(
+		cd clone_dir &&
+		git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
+		git log --pretty=oneline parallel3 >output.txt &&
+		! grep $PARA3 output.txt &&
+		git show $PARA3 >para3.txt &&
+		grep "A U Thor" para3.txt &&
+		git fetch origin "refs/replace/*:refs/replace/*" &&
+		git log --pretty=oneline parallel3 >output.txt &&
+		grep $PARA3 output.txt &&
+		git show $PARA3 >para3.txt &&
+		grep "O Thor" para3.txt
+	)
 '
 
 test_expect_success 'bisect and replacements' '
-     git bisect start $HASH7 $HASH1 &&
-     test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
-     git bisect reset &&
-     GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
-     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
-     git bisect reset &&
-     git --no-replace-objects bisect start $HASH7 $HASH1 &&
-     test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
-     git bisect reset
+	git bisect start $HASH7 $HASH1 &&
+	test "$PARA3" = "$(git rev-parse --verify HEAD)" &&
+	git bisect reset &&
+	GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
+	test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+	git bisect reset &&
+	git --no-replace-objects bisect start $HASH7 $HASH1 &&
+	test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+	git bisect reset
 '
 
 test_expect_success 'index-pack and replacements' '
@@ -490,9 +490,9 @@
 		$(git rev-parse HEAD^^ HEAD^ HEAD^^ HEAD^2) \
 		>.git/info/grafts &&
 	git status 2>stderr &&
-	test_i18ngrep "hint:.*grafts is deprecated" stderr &&
+	test_grep "hint:.*grafts is deprecated" stderr &&
 	git replace --convert-graft-file 2>stderr &&
-	test_i18ngrep ! "hint:.*grafts is deprecated" stderr &&
+	test_grep ! "hint:.*grafts is deprecated" stderr &&
 	test_path_is_missing .git/info/grafts &&
 
 	: verify that the history is now "grafted" &&
@@ -503,8 +503,8 @@
 	test_when_finished "rm -f .git/info/grafts" &&
 	echo $EMPTY_BLOB $EMPTY_TREE >.git/info/grafts &&
 	test_must_fail git replace --convert-graft-file 2>err &&
-	test_i18ngrep "$EMPTY_BLOB $EMPTY_TREE" err &&
-	test_i18ngrep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts
+	test_grep "$EMPTY_BLOB $EMPTY_TREE" err &&
+	test_grep "$EMPTY_BLOB $EMPTY_TREE" .git/info/grafts
 '
 
 test_done
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index 9350b5f..5d28507 100755
--- a/t/t6102-rev-list-unexpected-objects.sh
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -28,7 +28,7 @@
 
 test_expect_success 'traverse unexpected non-blob entry (seen)' '
 	test_must_fail git rev-list --objects $tree $broken_tree >output 2>&1 &&
-	test_i18ngrep "is not a blob" output
+	test_grep "is not a blob" output
 '
 
 test_expect_success 'setup unexpected non-tree entry' '
@@ -42,7 +42,7 @@
 
 test_expect_success 'traverse unexpected non-tree entry (seen)' '
 	test_must_fail git rev-list --objects $blob $broken_tree >output 2>&1 &&
-	test_i18ngrep "is not a tree" output
+	test_grep "is not a tree" output
 '
 
 test_expect_success 'setup unexpected non-commit parent' '
@@ -54,13 +54,13 @@
 
 test_expect_success 'traverse unexpected non-commit parent (lone)' '
 	test_must_fail git rev-list --objects $broken_commit >output 2>&1 &&
-	test_i18ngrep "not a commit" output
+	test_grep "not a commit" output
 '
 
 test_expect_success 'traverse unexpected non-commit parent (seen)' '
 	test_must_fail git rev-list --objects $blob $broken_commit \
 		>output 2>&1 &&
-	test_i18ngrep "not a commit" output
+	test_grep "not a commit" output
 '
 
 test_expect_success 'setup unexpected non-tree root' '
@@ -76,7 +76,7 @@
 test_expect_success 'traverse unexpected non-tree root (seen)' '
 	test_must_fail git rev-list --objects $blob $broken_commit \
 		>output 2>&1 &&
-	test_i18ngrep "not a tree" output
+	test_grep "not a tree" output
 '
 
 test_expect_success 'setup unexpected non-commit tag' '
@@ -93,7 +93,7 @@
 
 test_expect_success 'traverse unexpected non-commit tag (seen)' '
 	test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
-	test_i18ngrep "not a commit" output
+	test_grep "not a commit" output
 '
 
 test_expect_success 'setup unexpected non-tree tag' '
@@ -110,7 +110,7 @@
 
 test_expect_success 'traverse unexpected non-tree tag (seen)' '
 	test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
-	test_i18ngrep "not a tree" output
+	test_grep "not a tree" output
 '
 
 test_expect_success 'setup unexpected non-blob tag' '
@@ -127,7 +127,7 @@
 
 test_expect_success 'traverse unexpected non-blob tag (seen)' '
 	test_must_fail git rev-list --objects $commit $tag >output 2>&1 &&
-	test_i18ngrep "not a blob" output
+	test_grep "not a blob" output
 '
 
 test_done
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 8d9d660..43e1afd 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -457,7 +457,7 @@
 	test_must_fail git -C r3 rev-list --objects --filter="$spec" HEAD \
 		>actual 2>actual_stderr &&
 	test_must_be_empty actual &&
-	test_i18ngrep "$err" actual_stderr
+	test_grep "$err" actual_stderr
 }
 
 test_expect_success 'combine:... while URL-encoding things that should not be' '
@@ -670,7 +670,7 @@
 	awk -f print_2.awk ls_files_result |
 	sort >expected &&
 
-	for id in `cat expected | sed "s|..|&/|"`
+	for id in `sed "s|..|&/|" expected`
 	do
 		rm r1/.git/objects/$id || return 1
 	done &&
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 4d8e091..a9656a1 100755
--- a/t/t6113-rev-list-bitmap-filters.sh
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -1,9 +1,12 @@
 #!/bin/sh
 
 test_description='rev-list combining bitmaps and filters'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bitmap.sh
 
+
 test_expect_success 'set up bitmapped repo' '
 	# one commit will have bitmaps, the other will not
 	test_commit one &&
@@ -141,4 +144,17 @@
 	done <objects
 '
 
+test_expect_success 'bitmap traversal with --unpacked' '
+	git repack -adb &&
+	test_commit unpacked &&
+
+	git rev-list --objects --no-object-names unpacked^.. >expect.raw &&
+	sort expect.raw >expect &&
+
+	git rev-list --use-bitmap-index --objects --all --unpacked >actual.raw &&
+	sort actual.raw >actual &&
+
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
index d59111d..c0cfda6 100755
--- a/t/t6115-rev-list-du.sh
+++ b/t/t6115-rev-list-du.sh
@@ -48,6 +48,13 @@
 check_du --objects HEAD
 check_du --objects HEAD^..HEAD
 
+test_expect_success 'setup for --unpacked tests' '
+	git repack -adb &&
+	test_commit unpacked
+'
+
+check_du --all --objects --unpacked
+
 # As mentioned above, don't use hardcode sizes as actual size, but use the
 # output from git cat-file.
 test_expect_success 'rev-list --disk-usage=human' '
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index c9afcef..e78315d 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -85,6 +85,7 @@
 check_describe c-2-gHASH --tags HEAD^^2
 check_describe B --tags HEAD^^2^
 check_describe e --tags HEAD^^^
+check_describe e --tags --exact-match HEAD^^^
 
 check_describe heads/main --all HEAD
 check_describe tags/c-6-gHASH --all HEAD^
@@ -96,6 +97,13 @@
 check_describe c-7-gHASH --tags
 check_describe e-3-gHASH --first-parent --tags
 
+check_describe c-7-gHASH --tags --no-exact-match HEAD
+check_describe e-3-gHASH --first-parent --tags --no-exact-match HEAD
+
+test_expect_success '--exact-match failure' '
+	test_must_fail git describe --exact-match HEAD 2>err
+'
+
 test_expect_success 'describe --contains defaults to HEAD without commit-ish' '
 	echo "A^0" >expect &&
 	git checkout A &&
@@ -384,7 +392,7 @@
 test_expect_success 'describe tag object' '
 	git tag test-blob-1 -a -m msg unique-file:file &&
 	test_must_fail git describe test-blob-1 2>actual &&
-	test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
+	test_grep "fatal: test-blob-1 is neither a commit nor blob" actual
 '
 
 test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 3214d9d..16ce4cf 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -27,7 +27,7 @@
 
 test_expect_success 'error message for path inside submodule from within submodule' '
 	test_must_fail git -C sub add . 2>actual &&
-	test_i18ngrep "in unpopulated submodule" actual
+	test_grep "in unpopulated submodule" actual
 '
 
 test_done
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
index 457cc16..120dcd7 100755
--- a/t/t6135-pathspec-with-attrs.sh
+++ b/t/t6135-pathspec-with-attrs.sh
@@ -64,11 +64,24 @@
 	fileSetLabel label
 	fileValue label=foo
 	fileWrongLabel label☺
+	newFileA* labelA
+	newFileB* labelB
 	EOF
-	git add .gitattributes &&
+	echo fileSetLabel label1 >sub/.gitattributes &&
+	git add .gitattributes sub/.gitattributes &&
 	git commit -m "add attributes"
 '
 
+test_expect_success 'setup .gitignore' '
+	cat <<-\EOF >.gitignore &&
+	actual
+	expect
+	pathspec_file
+	EOF
+	git add .gitignore &&
+	git commit -m "add gitignore"
+'
+
 test_expect_success 'check specific set attr' '
 	cat <<-\EOF >expect &&
 	fileSetLabel
@@ -78,7 +91,17 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'check specific set attr (2)' '
+test_expect_success 'check set attr with pathspec pattern' '
+	echo sub/fileSetLabel >expect &&
+
+	git ls-files ":(attr:label)sub" >actual &&
+	test_cmp expect actual &&
+
+	git ls-files ":(attr:label)sub/" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check specific set attr in tree-ish' '
 	cat <<-\EOF >expect &&
 	HEAD:fileSetLabel
 	HEAD:sub/fileSetLabel
@@ -87,6 +110,16 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'check specific set attr with pathspec pattern in tree-ish' '
+	echo HEAD:sub/fileSetLabel >expect &&
+
+	git grep -l content HEAD ":(attr:label)sub" >actual &&
+	test_cmp expect actual &&
+
+	git grep -l content HEAD ":(attr:label)sub/" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'check specific unset attr' '
 	cat <<-\EOF >expect &&
 	fileUnsetLabel
@@ -129,6 +162,7 @@
 test_expect_success 'check unspecified attr' '
 	cat <<-\EOF >expect &&
 	.gitattributes
+	.gitignore
 	fileA
 	fileAB
 	fileAC
@@ -137,6 +171,7 @@
 	fileC
 	fileNoLabel
 	fileWrongLabel
+	sub/.gitattributes
 	sub/fileA
 	sub/fileAB
 	sub/fileAC
@@ -153,6 +188,7 @@
 test_expect_success 'check unspecified attr (2)' '
 	cat <<-\EOF >expect &&
 	HEAD:.gitattributes
+	HEAD:.gitignore
 	HEAD:fileA
 	HEAD:fileAB
 	HEAD:fileAC
@@ -161,6 +197,7 @@
 	HEAD:fileC
 	HEAD:fileNoLabel
 	HEAD:fileWrongLabel
+	HEAD:sub/.gitattributes
 	HEAD:sub/fileA
 	HEAD:sub/fileAB
 	HEAD:sub/fileAC
@@ -177,9 +214,11 @@
 test_expect_success 'check multiple unspecified attr' '
 	cat <<-\EOF >expect &&
 	.gitattributes
+	.gitignore
 	fileC
 	fileNoLabel
 	fileWrongLabel
+	sub/.gitattributes
 	sub/fileC
 	sub/fileNoLabel
 	sub/fileWrongLabel
@@ -212,17 +251,100 @@
 
 test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
 	test_must_fail git ls-files . ":(attr:labelB,attr:labelC)" 2>actual &&
-	test_i18ngrep "Only one" actual
+	test_grep "Only one" actual
 '
 
-test_expect_success 'fail if attr magic is used places not implemented' '
+test_expect_success 'fail if attr magic is used in places not implemented' '
 	# The main purpose of this test is to check that we actually fail
 	# when you attempt to use attr magic in commands that do not implement
-	# attr magic. This test does not advocate git-add to stay that way,
-	# though, but git-add is convenient as it has its own internal pathspec
-	# parsing.
-	test_must_fail git add ":(attr:labelB)" 2>actual &&
-	test_i18ngrep "magic not supported" actual
+	# attr magic. This test does not advocate check-ignore to stay that way.
+	# When you teach the command to grok the pathspec, you need to find
+	# another command to replace it for the test.
+	test_must_fail git check-ignore ":(attr:labelB)" 2>actual &&
+	test_grep "magic not supported" actual
+'
+
+test_expect_success 'check that attr magic works for git stash push' '
+	cat <<-\EOF >expect &&
+	A	sub/newFileA-foo
+	EOF
+	>sub/newFileA-foo &&
+	>sub/newFileB-foo &&
+	git stash push --include-untracked -- ":(exclude,attr:labelB)" &&
+	git stash show --include-untracked --name-status >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --all' '
+	cat <<-\EOF >expect &&
+	sub/newFileA-foo
+	EOF
+	>sub/newFileA-foo &&
+	>sub/newFileB-foo &&
+	git add --all ":(exclude,attr:labelB)" &&
+	git diff --name-only --cached >actual &&
+	git restore -W -S . &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add -u' '
+	cat <<-\EOF >expect &&
+	sub/fileA
+	EOF
+	>sub/newFileA-foo &&
+	>sub/newFileB-foo &&
+	>sub/fileA &&
+	>sub/fileB &&
+	git add -u ":(exclude,attr:labelB)" &&
+	git diff --name-only --cached  >actual &&
+	git restore -S -W . && rm sub/new* &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add <path>' '
+	cat <<-\EOF >expect &&
+	fileA
+	fileB
+	sub/fileA
+	EOF
+	>fileA &&
+	>fileB &&
+	>sub/fileA &&
+	>sub/fileB &&
+	git add ":(exclude,attr:labelB)sub/*" &&
+	git diff --name-only --cached >actual &&
+	git restore -S -W . &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git -add .' '
+	cat <<-\EOF >expect &&
+	sub/fileA
+	EOF
+	>fileA &&
+	>fileB &&
+	>sub/fileA &&
+	>sub/fileB &&
+	cd sub &&
+	git add . ":(exclude,attr:labelB)" &&
+	cd .. &&
+	git diff --name-only --cached >actual &&
+	git restore -S -W . &&
+	test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --pathspec-from-file' '
+	cat <<-\EOF >pathspec_file &&
+	:(exclude,attr:labelB)
+	EOF
+	cat <<-\EOF >expect &&
+	sub/newFileA-foo
+	EOF
+	>sub/newFileA-foo &&
+	>sub/newFileB-foo &&
+	git add --all --pathspec-from-file=pathspec_file &&
+	git diff --name-only --cached >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'abort on giving invalid label on the command line' '
@@ -245,12 +367,57 @@
 
 test_expect_success 'backslash cannot be the last character' '
 	test_must_fail git ls-files ":(attr:label=foo\\ labelA=bar)" 2>actual &&
-	test_i18ngrep "not allowed as last character in attr value" actual
+	test_grep "not allowed as last character in attr value" actual
 '
 
 test_expect_success 'backslash cannot be used as a value' '
 	test_must_fail git ls-files ":(attr:label=f\\\oo)" 2>actual &&
-	test_i18ngrep "for value matching" actual
+	test_grep "for value matching" actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (1)' '
+	git ls-files ":(attr:label1)" >actual &&
+	test_write_lines "sub/fileSetLabel" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (2)' '
+	git ls-files ":(attr:label1)sub" >actual &&
+	test_write_lines "sub/fileSetLabel" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
+	git ls-files ":(attr:label1)sub/" >actual &&
+	test_write_lines "sub/fileSetLabel" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
+	>mode_exec_file_1 &&
+
+	git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
+	echo ?? mode_exec_file_1 >expect &&
+	test_cmp expect actual &&
+
+	git add mode_exec_file_1 &&
+	chmod +x mode_exec_file_1 &&
+	git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
+	echo AM mode_exec_file_1 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
+	>mode_1_regular &&
+	>mode_1_exec  &&
+	chmod +x mode_1_exec &&
+	git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
+	echo ?? mode_1_exec >expect &&
+	test_cmp expect actual &&
+
+	git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
+	echo ?? mode_1_regular >expect &&
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t6136-pathspec-in-bare.sh b/t/t6136-pathspec-in-bare.sh
index ae8b537..2db37a6 100755
--- a/t/t6136-pathspec-in-bare.sh
+++ b/t/t6136-pathspec-in-bare.sh
@@ -15,11 +15,11 @@
 		cd bare &&
 		test_must_fail git log -- .. >out 2>err &&
 		test_must_be_empty out &&
-		test_i18ngrep "outside repository" err &&
+		test_grep "outside repository" err &&
 
 		test_must_fail git ls-files -- .. >out 2>err &&
 		test_must_be_empty out &&
-		test_i18ngrep "outside repository" err
+		test_grep "outside repository" err
 	)
 '
 
@@ -28,11 +28,11 @@
 		cd .git &&
 		test_must_fail git log -- .. >out 2>err &&
 		test_must_be_empty out &&
-		test_i18ngrep "outside repository" err &&
+		test_grep "outside repository" err &&
 
 		test_must_fail git ls-files -- .. >out 2>err &&
 		test_must_be_empty out &&
-		test_i18ngrep "outside repository" err
+		test_grep "outside repository" err
 	)
 '
 
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index c466fd9..eb6c820 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -6,6 +6,7 @@
 test_description='for-each-ref test'
 
 . ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
 . "$TEST_DIRECTORY"/lib-gpg.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -19,11 +20,19 @@
     export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
+test_object_file_size () {
+	oid=$(git rev-parse "$1")
+	path=".git/objects/$(test_oid_to_path $oid)"
+	test_file_size "$path"
+}
+
 test_expect_success setup '
-	test_oid_cache <<-EOF &&
-	disklen sha1:138
-	disklen sha256:154
+	# setup .mailmap
+	cat >.mailmap <<-EOF &&
+	A Thor <athor@example.com> A U Thor <author@example.com>
+	C Mitter <cmitter@example.com> C O Mitter <committer@example.com>
 	EOF
+
 	setdate_and_increment &&
 	echo "Using $datestamp" > one &&
 	git add one &&
@@ -40,25 +49,29 @@
 	git config push.default current
 '
 
-test_atom() {
+test_atom () {
 	case "$1" in
 		head) ref=refs/heads/main ;;
 		 tag) ref=refs/tags/testtag ;;
 		 sym) ref=refs/heads/sym ;;
 		   *) ref=$1 ;;
 	esac
+	format=$2
+	test_do=test_expect_${4:-success}
+
 	printf '%s\n' "$3" >expected
-	test_expect_${4:-success} $PREREQ "basic atom: $1 $2" "
-		git for-each-ref --format='%($2)' $ref >actual &&
+	$test_do $PREREQ "basic atom: $ref $format" '
+		git for-each-ref --format="%($format)" "$ref" >actual &&
 		sanitize_pgp <actual >actual.clean &&
 		test_cmp expected actual.clean
-	"
+	'
+
 	# Automatically test "contents:size" atom after testing "contents"
-	if test "$2" = "contents"
+	if test "$format" = "contents"
 	then
 		# for commit leg, $3 is changed there
 		expect=$(printf '%s' "$3" | wc -c)
-		test_expect_${4:-success} $PREREQ "basic atom: $1 contents:size" '
+		$test_do $PREREQ "basic atom: $ref contents:size" '
 			type=$(git cat-file -t "$ref") &&
 			case $type in
 			tag)
@@ -82,7 +95,6 @@
 }
 
 hexlen=$(test_oid hexsz)
-disklen=$(test_oid disklen)
 
 test_atom head refname refs/heads/main
 test_atom head refname: refs/heads/main
@@ -117,7 +129,7 @@
 test_atom head push:strip=-1 main
 test_atom head objecttype commit
 test_atom head objectsize $((131 + hexlen))
-test_atom head objectsize:disk $disklen
+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)
 test_atom head deltabase $ZERO_OID
 test_atom head objectname $(git rev-parse refs/heads/main)
 test_atom head objectname:short $(git rev-parse --short refs/heads/main)
@@ -140,15 +152,31 @@
 test_atom head '*objecttype' ''
 test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
 test_atom head authorname 'A U Thor'
+test_atom head authorname:mailmap 'A Thor'
 test_atom head authoremail '<author@example.com>'
 test_atom head authoremail:trim 'author@example.com'
 test_atom head authoremail:localpart 'author'
+test_atom head authoremail:trim,localpart 'author'
+test_atom head authoremail:mailmap '<athor@example.com>'
+test_atom head authoremail:mailmap,trim 'athor@example.com'
+test_atom head authoremail:trim,mailmap 'athor@example.com'
+test_atom head authoremail:mailmap,localpart 'athor'
+test_atom head authoremail:localpart,mailmap 'athor'
+test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor'
 test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
 test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
 test_atom head committername 'C O Mitter'
+test_atom head committername:mailmap 'C Mitter'
 test_atom head committeremail '<committer@example.com>'
 test_atom head committeremail:trim 'committer@example.com'
 test_atom head committeremail:localpart 'committer'
+test_atom head committeremail:localpart,trim 'committer'
+test_atom head committeremail:mailmap '<cmitter@example.com>'
+test_atom head committeremail:mailmap,trim 'cmitter@example.com'
+test_atom head committeremail:trim,mailmap 'cmitter@example.com'
+test_atom head committeremail:mailmap,localpart 'cmitter'
+test_atom head committeremail:localpart,mailmap 'cmitter'
+test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter'
 test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
 test_atom head tag ''
 test_atom head tagger ''
@@ -175,8 +203,8 @@
 test_atom tag push ''
 test_atom tag objecttype tag
 test_atom tag objectsize $((114 + hexlen))
-test_atom tag objectsize:disk $disklen
-test_atom tag '*objectsize:disk' $disklen
+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)
+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)
 test_atom tag deltabase $ZERO_OID
 test_atom tag '*deltabase' $ZERO_OID
 test_atom tag objectname $(git rev-parse refs/tags/testtag)
@@ -198,22 +226,46 @@
 test_atom tag '*objecttype' 'commit'
 test_atom tag author ''
 test_atom tag authorname ''
+test_atom tag authorname:mailmap ''
 test_atom tag authoremail ''
 test_atom tag authoremail:trim ''
 test_atom tag authoremail:localpart ''
+test_atom tag authoremail:trim,localpart ''
+test_atom tag authoremail:mailmap ''
+test_atom tag authoremail:mailmap,trim ''
+test_atom tag authoremail:trim,mailmap ''
+test_atom tag authoremail:mailmap,localpart ''
+test_atom tag authoremail:localpart,mailmap ''
+test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim ''
 test_atom tag authordate ''
 test_atom tag committer ''
 test_atom tag committername ''
+test_atom tag committername:mailmap ''
 test_atom tag committeremail ''
 test_atom tag committeremail:trim ''
 test_atom tag committeremail:localpart ''
+test_atom tag committeremail:localpart,trim ''
+test_atom tag committeremail:mailmap ''
+test_atom tag committeremail:mailmap,trim ''
+test_atom tag committeremail:trim,mailmap ''
+test_atom tag committeremail:mailmap,localpart ''
+test_atom tag committeremail:localpart,mailmap ''
+test_atom tag committeremail:trim,mailmap,trim,trim,localpart ''
 test_atom tag committerdate ''
 test_atom tag tag 'testtag'
 test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
 test_atom tag taggername 'C O Mitter'
+test_atom tag taggername:mailmap 'C Mitter'
 test_atom tag taggeremail '<committer@example.com>'
 test_atom tag taggeremail:trim 'committer@example.com'
 test_atom tag taggeremail:localpart 'committer'
+test_atom tag taggeremail:trim,localpart 'committer'
+test_atom tag taggeremail:mailmap '<cmitter@example.com>'
+test_atom tag taggeremail:mailmap,trim 'cmitter@example.com'
+test_atom tag taggeremail:trim,mailmap 'cmitter@example.com'
+test_atom tag taggeremail:mailmap,localpart 'cmitter'
+test_atom tag taggeremail:localpart,mailmap 'cmitter'
+test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter'
 test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
 test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
 test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
@@ -266,6 +318,66 @@
 	test_must_fail git for-each-ref --format="%(objectname:short=foo)"
 '
 
+test_bad_atom () {
+	case "$1" in
+	head) ref=refs/heads/main ;;
+	 tag) ref=refs/tags/testtag ;;
+	 sym) ref=refs/heads/sym ;;
+	   *) ref=$1 ;;
+	esac
+	format=$2
+	test_do=test_expect_${4:-success}
+
+	printf '%s\n' "$3" >expect
+	$test_do $PREREQ "err basic atom: $ref $format" '
+		test_must_fail git for-each-ref \
+			--format="%($format)" "$ref" 2>error &&
+		test_cmp expect error
+	'
+}
+
+test_bad_atom head 'authoremail:foo' \
+	'fatal: unrecognized %(authoremail) argument: foo'
+
+test_bad_atom head 'authoremail:mailmap,trim,bar' \
+	'fatal: unrecognized %(authoremail) argument: bar'
+
+test_bad_atom head 'authoremail:trim,' \
+	'fatal: unrecognized %(authoremail) argument: '
+
+test_bad_atom head 'authoremail:mailmaptrim' \
+	'fatal: unrecognized %(authoremail) argument: trim'
+
+test_bad_atom head 'committeremail: ' \
+	'fatal: unrecognized %(committeremail) argument:  '
+
+test_bad_atom head 'committeremail: trim,foo' \
+	'fatal: unrecognized %(committeremail) argument:  trim,foo'
+
+test_bad_atom head 'committeremail:mailmap,localpart ' \
+	'fatal: unrecognized %(committeremail) argument:  '
+
+test_bad_atom head 'committeremail:trim_localpart' \
+	'fatal: unrecognized %(committeremail) argument: _localpart'
+
+test_bad_atom head 'committeremail:localpart,,,trim' \
+	'fatal: unrecognized %(committeremail) argument: ,,trim'
+
+test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \
+	'fatal: unrecognized %(taggeremail) argument:  foo '
+
+test_bad_atom tag 'taggeremail:trim,localpart,' \
+	'fatal: unrecognized %(taggeremail) argument: '
+
+test_bad_atom tag 'taggeremail:mailmap;localpart trim' \
+	'fatal: unrecognized %(taggeremail) argument: ;localpart trim'
+
+test_bad_atom tag 'taggeremail:localpart trim' \
+	'fatal: unrecognized %(taggeremail) argument:  trim'
+
+test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \
+	'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim'
+
 test_date () {
 	f=$1 &&
 	committer_date=$2 &&
@@ -448,6 +560,41 @@
 '
 
 cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with prefix exclusions' '
+	for tag in foo/one foo/two foo/three bar baz
+	do
+		git tag "$tag" || return 1
+	done &&
+	test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+	git for-each-ref --format="%(refname)" \
+		refs/tags/ --exclude=refs/tags/foo >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/foo/one
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with pattern exclusions' '
+	for tag in foo/one foo/two foo/three bar baz
+	do
+		git tag "$tag" || return 1
+	done &&
+	test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+	git for-each-ref --format="%(refname)" \
+		refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
 'refs/heads/main'
 'refs/remotes/origin/main'
 'refs/tags/testtag'
@@ -561,6 +708,144 @@
 	test_cmp expected.bare actual
 '
 
+test_expect_success 'setup for describe atom tests' '
+	git init -b master describe-repo &&
+	(
+		cd describe-repo &&
+
+		test_commit --no-tag one &&
+		git tag tagone &&
+
+		test_commit --no-tag two &&
+		git tag -a -m "tag two" tagtwo
+	)
+'
+
+test_expect_success 'describe atom vs git describe' '
+	(
+		cd describe-repo &&
+
+		git for-each-ref --format="%(objectname)" \
+			refs/tags/ >obj &&
+		while read hash
+		do
+			if desc=$(git describe $hash)
+			then
+				: >expect-contains-good
+			else
+				: >expect-contains-bad
+			fi &&
+			echo "$hash $desc" || return 1
+		done <obj >expect &&
+		test_path_exists expect-contains-good &&
+		test_path_exists expect-contains-bad &&
+
+		git for-each-ref --format="%(objectname) %(describe)" \
+			refs/tags/ >actual 2>err &&
+		test_cmp expect actual &&
+		test_must_be_empty err
+	)
+'
+
+test_expect_success 'describe:tags vs describe --tags' '
+	(
+		cd describe-repo &&
+		git describe --tags >expect &&
+		git for-each-ref --format="%(describe:tags)" \
+				refs/heads/master >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'describe:abbrev=... vs describe --abbrev=...' '
+	(
+		cd describe-repo &&
+
+		# Case 1: We have commits between HEAD and the most
+		#	  recent tag reachable from it
+		test_commit --no-tag file &&
+		git describe --abbrev=14 >expect &&
+		git for-each-ref --format="%(describe:abbrev=14)" \
+			refs/heads/master >actual &&
+		test_cmp expect actual &&
+
+		# Make sure the hash used is atleast 14 digits long
+		sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart &&
+		test 15 -le $(wc -c <hexpart) &&
+
+		# Case 2: We have a tag at HEAD, describe directly gives
+		#	  the name of the tag
+		git tag -a -m tagged tagname &&
+		git describe --abbrev=14 >expect &&
+		git for-each-ref --format="%(describe:abbrev=14)" \
+			refs/heads/master >actual &&
+		test_cmp expect actual &&
+		test tagname = $(cat actual)
+	)
+'
+
+test_expect_success 'describe:match=... vs describe --match ...' '
+	(
+		cd describe-repo &&
+		git tag -a -m "tag foo" tag-foo &&
+		git describe --match "*-foo" >expect &&
+		git for-each-ref --format="%(describe:match="*-foo")" \
+			refs/heads/master >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'describe:exclude:... vs describe --exclude ...' '
+	(
+		cd describe-repo &&
+		git tag -a -m "tag bar" tag-bar &&
+		git describe --exclude "*-bar" >expect &&
+		git for-each-ref --format="%(describe:exclude="*-bar")" \
+			refs/heads/master >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'deref with describe atom' '
+	(
+		cd describe-repo &&
+		cat >expect <<-\EOF &&
+
+		tagname
+		tagname
+		tagname
+
+		tagtwo
+		EOF
+		git for-each-ref --format="%(*describe)" >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'err on bad describe atom arg' '
+	(
+		cd describe-repo &&
+
+		# The bad arg is the only arg passed to describe atom
+		cat >expect <<-\EOF &&
+		fatal: unrecognized %(describe) argument: baz
+		EOF
+		test_must_fail git for-each-ref --format="%(describe:baz)" \
+			refs/heads/master 2>actual &&
+		test_cmp expect actual &&
+
+		# The bad arg is in the middle of the option string
+		# passed to the describe atom
+		cat >expect <<-\EOF &&
+		fatal: unrecognized %(describe) argument: qux=1,abbrev=14
+		EOF
+		test_must_fail git for-each-ref \
+			--format="%(describe:tags,qux=1,abbrev=14)" \
+			ref/heads/master 2>actual &&
+		test_cmp expect actual
+	)
+'
+
 cat >expected <<\EOF
 heads/main
 tags/main
@@ -843,16 +1128,16 @@
 test_expect_success 'Verify sorts with raw:size' '
 	cat >expected <<-EOF &&
 	refs/myblobs/blob8
-	refs/myblobs/first
 	refs/myblobs/blob7
-	refs/heads/main
 	refs/myblobs/blob4
 	refs/myblobs/blob1
 	refs/myblobs/blob2
 	refs/myblobs/blob3
 	refs/myblobs/blob5
 	refs/myblobs/blob6
+	refs/myblobs/first
 	refs/mytrees/first
+	refs/heads/main
 	EOF
 	git for-each-ref --format="%(refname)" --sort=raw:size \
 		refs/heads/main refs/myblobs/ refs/mytrees/first >actual &&
@@ -964,6 +1249,17 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'verify sorts with contents:size' '
+	cat >expect <<-\EOF &&
+	refs/heads/main
+	refs/heads/newtag
+	refs/heads/ambiguous
+	EOF
+	git for-each-ref --format="%(refname)" \
+		--sort=contents:size refs/heads/ >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'set up multiple-sort tags' '
 	for when in 100000 200000
 	do
@@ -1039,6 +1335,73 @@
 	test_cmp expected actual
 '
 
+test_expect_success '--no-sort without subsequent --sort prints expected refs' '
+	cat >expected <<-\EOF &&
+	refs/tags/multi-ref1-100000-user1
+	refs/tags/multi-ref1-100000-user2
+	refs/tags/multi-ref1-200000-user1
+	refs/tags/multi-ref1-200000-user2
+	refs/tags/multi-ref2-100000-user1
+	refs/tags/multi-ref2-100000-user2
+	refs/tags/multi-ref2-200000-user1
+	refs/tags/multi-ref2-200000-user2
+	EOF
+
+	# Sort the results with `sort` for a consistent comparison against
+	# expected
+	git for-each-ref \
+		--format="%(refname)" \
+		--no-sort \
+		"refs/tags/multi-*" | sort >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'set up custom date sorting' '
+	# Dates:
+	# - Wed Feb 07 2024 21:34:20 +0000
+	# - Tue Dec 14 1999 00:05:22 +0000
+	# - Fri Jun 04 2021 11:26:51 +0000
+	# - Mon Jan 22 2007 16:44:01 GMT+0000
+	i=1 &&
+	for when in 1707341660 945129922 1622806011 1169484241
+	do
+		GIT_COMMITTER_DATE="@$when +0000" \
+		GIT_COMMITTER_EMAIL="user@example.com" \
+		git tag -m "tag $when" custom-dates-$i &&
+		i=$(($i+1)) || return 1
+	done
+'
+
+test_expect_success 'sort by date defaults to full timestamp' '
+	cat >expected <<-\EOF &&
+	945129922 refs/tags/custom-dates-2
+	1169484241 refs/tags/custom-dates-4
+	1622806011 refs/tags/custom-dates-3
+	1707341660 refs/tags/custom-dates-1
+	EOF
+
+	git for-each-ref \
+		--format="%(creatordate:unix) %(refname)" \
+		--sort=creatordate \
+		"refs/tags/custom-dates-*" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'sort by custom date format' '
+	cat >expected <<-\EOF &&
+	00:05:22 refs/tags/custom-dates-2
+	11:26:51 refs/tags/custom-dates-3
+	16:44:01 refs/tags/custom-dates-4
+	21:34:20 refs/tags/custom-dates-1
+	EOF
+
+	git for-each-ref \
+		--format="%(creatordate:format:%H:%M:%S) %(refname)" \
+		--sort="creatordate:format:%H:%M:%S" \
+		"refs/tags/custom-dates-*" >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
 	test_when_finished "git checkout main" &&
 	git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
@@ -1374,6 +1737,14 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'for-each-ref --omit-empty works' '
+	git for-each-ref --format="%(refname)" >actual &&
+	test_line_count -gt 1 actual &&
+	git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
+	echo refs/heads/main >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
 	# name refs numerically to avoid case-insensitive filesystem conflicts
 	nr=0 &&
@@ -1464,4 +1835,264 @@
 sig_crlf=${sig_crlf%dummy}
 test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
 
+test_expect_success 'git for-each-ref --stdin: empty' '
+	>in &&
+	git for-each-ref --format="%(refname)" --stdin <in >actual &&
+	git for-each-ref --format="%(refname)" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref --stdin: fails if extra args' '
+	>in &&
+	test_must_fail git for-each-ref --format="%(refname)" \
+		--stdin refs/heads/extra <in 2>err &&
+	grep "unknown arguments supplied with --stdin" err
+'
+
+test_expect_success 'git for-each-ref --stdin: matches' '
+	cat >in <<-EOF &&
+	refs/tags/multi*
+	refs/heads/amb*
+	EOF
+
+	cat >expect <<-EOF &&
+	refs/heads/ambiguous
+	refs/tags/multi-ref1-100000-user1
+	refs/tags/multi-ref1-100000-user2
+	refs/tags/multi-ref1-200000-user1
+	refs/tags/multi-ref1-200000-user2
+	refs/tags/multi-ref2-100000-user1
+	refs/tags/multi-ref2-100000-user2
+	refs/tags/multi-ref2-200000-user1
+	refs/tags/multi-ref2-200000-user2
+	refs/tags/multiline
+	EOF
+
+	git for-each-ref --format="%(refname)" --stdin <in >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref with non-existing refs' '
+	cat >in <<-EOF &&
+	refs/heads/this-ref-does-not-exist
+	refs/tags/bogus
+	EOF
+
+	git for-each-ref --format="%(refname)" --stdin <in >actual &&
+	test_must_be_empty actual &&
+
+	xargs git for-each-ref --format="%(refname)" <in >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'git for-each-ref with nested tags' '
+	git tag -am "Normal tag" nested/base HEAD &&
+	git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
+	git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
+
+	head_oid="$(git rev-parse HEAD)" &&
+	base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
+	nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
+	nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
+
+	cat >expect <<-EOF &&
+	refs/tags/nested/base $base_tag_oid tag $head_oid commit
+	refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
+	refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
+	EOF
+
+	git for-each-ref \
+		--format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+		refs/tags/nested/ >actual &&
+	test_cmp expect actual
+'
+
+GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
+
+test_expect_success GPG 'setup for signature atom using gpg' '
+	git checkout -b signed &&
+
+	test_when_finished "test_unconfig commit.gpgSign" &&
+
+	echo "1" >file &&
+	git add file &&
+	test_tick &&
+	git commit -S -m "file: 1" &&
+	git tag first-signed &&
+
+	echo "2" >file &&
+	test_tick &&
+	git commit -a -m "file: 2" &&
+	git tag second-unsigned &&
+
+	git config commit.gpgSign 1 &&
+	echo "3" >file &&
+	test_tick &&
+	git commit -a --no-gpg-sign -m "file: 3" &&
+	git tag third-unsigned &&
+
+	test_tick &&
+	git rebase -f HEAD^^ && git tag second-signed HEAD^ &&
+	git tag third-signed &&
+
+	echo "4" >file &&
+	test_tick &&
+	git commit -a -SB7227189 -m "file: 4" &&
+	git tag fourth-signed &&
+
+	echo "5" >file &&
+	test_tick &&
+	git commit -a --no-gpg-sign -m "file: 5" &&
+	git tag fifth-unsigned &&
+
+	echo "6" >file &&
+	test_tick &&
+	git commit -a --no-gpg-sign -m "file: 6" &&
+
+	test_tick &&
+	git rebase -f HEAD^^ &&
+	git tag fifth-signed HEAD^ &&
+	git tag sixth-signed &&
+
+	echo "7" >file &&
+	test_tick &&
+	git commit -a --no-gpg-sign -m "file: 7" &&
+	git tag seventh-unsigned
+'
+
+test_expect_success GPGSSH 'setup for signature atom using ssh' '
+	test_when_finished "test_unconfig gpg.format user.signingkey" &&
+
+	test_config gpg.format ssh &&
+	test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+	echo "8" >file &&
+	test_tick &&
+	git add file &&
+	git commit -S -m "file: 8" &&
+	git tag eighth-signed-ssh
+'
+
+test_expect_success GPG2 'bare signature atom' '
+	git verify-commit first-signed 2>expect &&
+	echo  >>expect &&
+	git for-each-ref refs/tags/first-signed \
+		--format="%(signature)" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show good signature with custom format' '
+	git verify-commit first-signed &&
+	cat >expect <<-\EOF &&
+	G
+	13B6F51ECDDE430D
+	C O Mitter <committer@example.com>
+	73D758744BE721698EC54E8713B6F51ECDDE430D
+	73D758744BE721698EC54E8713B6F51ECDDE430D
+	EOF
+	git for-each-ref refs/tags/first-signed \
+		--format="$GRADE_FORMAT" >actual &&
+	test_cmp expect actual
+'
+test_expect_success GPGSSH 'show good signature with custom format
+			    with ssh' '
+	test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+	FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+	cat >expect.tmpl <<-\EOF &&
+	G
+	FINGERPRINT
+	principal with number 1
+	FINGERPRINT
+
+	EOF
+	sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+	git for-each-ref refs/tags/eighth-signed-ssh \
+		--format="$GRADE_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'signature atom with grade option and bad signature' '
+	git cat-file commit third-signed >raw &&
+	sed -e "s/^file: 3/file: 3 forged/" raw >forged1 &&
+	FORGED1=$(git hash-object -w -t commit forged1) &&
+	git update-ref refs/tags/third-signed "$FORGED1" &&
+	test_must_fail git verify-commit "$FORGED1" &&
+
+	cat >expect <<-\EOF &&
+	B
+	13B6F51ECDDE430D
+	C O Mitter <committer@example.com>
+
+
+	EOF
+	git for-each-ref refs/tags/third-signed \
+		--format="$GRADE_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with custom format' '
+	cat >expect <<-\EOF &&
+	U
+	65A0EEA02E30CAD7
+	Eris Discordia <discord@example.net>
+	F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+	D4BE22311AD3131E5EDA29A461092E85B7227189
+	EOF
+	git for-each-ref refs/tags/fourth-signed \
+		--format="$GRADE_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+	cat >expect <<-\EOF &&
+	undefined
+	65A0EEA02E30CAD7
+	Eris Discordia <discord@example.net>
+	F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+	D4BE22311AD3131E5EDA29A461092E85B7227189
+	EOF
+	git for-each-ref refs/tags/fourth-signed \
+		--format="$TRUSTLEVEL_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+	cat >expect <<-\EOF &&
+	ultimate
+	13B6F51ECDDE430D
+	C O Mitter <committer@example.com>
+	73D758744BE721698EC54E8713B6F51ECDDE430D
+	73D758744BE721698EC54E8713B6F51ECDDE430D
+	EOF
+	git for-each-ref refs/tags/sixth-signed \
+		--format="$TRUSTLEVEL_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show unknown signature with custom format' '
+	cat >expect <<-\EOF &&
+	E
+	13B6F51ECDDE430D
+
+
+
+	EOF
+	GNUPGHOME="$GNUPGHOME_NOT_USED" git for-each-ref \
+		refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show lack of signature with custom format' '
+	cat >expect <<-\EOF &&
+	N
+
+
+
+
+	EOF
+	git for-each-ref refs/tags/seventh-unsigned \
+		--format="$GRADE_FORMAT" >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index bfda1f4..83b8a19 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -15,7 +15,7 @@
 	git for-each-ref --format="%(objectname) %(refname)" >brief-list
 '
 
-test_expect_success 'Broken refs are reported correctly' '
+test_expect_success REFFILES 'Broken refs are reported correctly' '
 	r=refs/heads/bogus &&
 	: >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
@@ -25,7 +25,7 @@
 	test_cmp broken-err err
 '
 
-test_expect_success 'NULL_SHA1 refs are reported correctly' '
+test_expect_success REFFILES 'NULL_SHA1 refs are reported correctly' '
 	r=refs/heads/zeros &&
 	echo $ZEROS >.git/$r &&
 	test_when_finished "rm -f .git/$r" &&
@@ -39,19 +39,32 @@
 '
 
 test_expect_success 'Missing objects are reported correctly' '
-	r=refs/heads/missing &&
-	echo $MISSING >.git/$r &&
-	test_when_finished "rm -f .git/$r" &&
-	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	test_when_finished "git update-ref -d refs/heads/missing" &&
+	test-tool ref-store main update-ref msg refs/heads/missing "$MISSING" "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+	echo "fatal: missing object $MISSING for refs/heads/missing" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
 	test_cmp missing-err err &&
 	(
 		cat brief-list &&
-		echo "$MISSING $r"
+		echo "$MISSING refs/heads/missing"
 	) | sort -k 2 >missing-brief-expected &&
 	git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
 	test_cmp missing-brief-expected brief-out &&
 	test_must_be_empty brief-err
 '
 
+test_expect_success 'ahead-behind requires an argument' '
+	test_must_fail git for-each-ref \
+		--format="%(ahead-behind)" 2>err &&
+	echo "fatal: expected format: %(ahead-behind:<committish>)" >expect &&
+	test_cmp expect err
+'
+
+test_expect_success 'missing ahead-behind base' '
+	test_must_fail git for-each-ref \
+		--format="%(ahead-behind:refs/heads/missing)" 2>err &&
+	echo "fatal: failed to find '\''refs/heads/missing'\''" >expect &&
+	test_cmp expect err
+'
+
 test_done
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 1ce5f49..948f1bb 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -31,6 +31,37 @@
 	git update-ref refs/odd/spot main
 '
 
+test_expect_success '--include-root-refs pattern prints pseudorefs' '
+	cat >expect <<-\EOF &&
+	HEAD
+	ORIG_HEAD
+	refs/heads/main
+	refs/heads/side
+	refs/odd/spot
+	refs/tags/annotated-tag
+	refs/tags/doubly-annotated-tag
+	refs/tags/doubly-signed-tag
+	refs/tags/four
+	refs/tags/one
+	refs/tags/signed-tag
+	refs/tags/three
+	refs/tags/two
+	EOF
+	git update-ref ORIG_HEAD main &&
+	git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--include-root-refs with other patterns' '
+	cat >expect <<-\EOF &&
+	HEAD
+	ORIG_HEAD
+	EOF
+	git update-ref ORIG_HEAD main &&
+	git for-each-ref --format="%(refname)" --include-root-refs "*HEAD" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'filtering with --points-at' '
 	cat >expect <<-\EOF &&
 	refs/heads/main
@@ -45,6 +76,8 @@
 	sed -e "s/Z$//" >expect <<-\EOF &&
 	refs/heads/side Z
 	refs/tags/annotated-tag four
+	refs/tags/doubly-annotated-tag four
+	refs/tags/doubly-signed-tag four
 	refs/tags/four Z
 	refs/tags/signed-tag four
 	EOF
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 772238e..2738b50 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -311,13 +311,13 @@
 	git checkout -q renamed-file-has-no-conflicts^0 &&
 	test_must_fail git merge --strategy=recursive dir-in-way >output &&
 
-	test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
-	test_i18ngrep "Auto-merging dir" output &&
+	test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+	test_grep "Auto-merging dir" output &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test_i18ngrep "moving it to dir~HEAD instead" output
+		test_grep "moving it to dir~HEAD instead" output
 	else
-		test_i18ngrep "Adding as dir~HEAD instead" output
+		test_grep "Adding as dir~HEAD instead" output
 	fi &&
 
 	test_stdout_line_count = 3 git ls-files -u &&
@@ -338,13 +338,13 @@
 	test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors &&
 
 	! grep "error: refusing to lose untracked file at" errors &&
-	test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
-	test_i18ngrep "Auto-merging dir" output &&
+	test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
+	test_grep "Auto-merging dir" output &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test_i18ngrep "moving it to dir~renamed-file-has-no-conflicts instead" output
+		test_grep "moving it to dir~renamed-file-has-no-conflicts instead" output
 	else
-		test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output
+		test_grep "Adding as dir~renamed-file-has-no-conflicts instead" output
 	fi &&
 
 	test_stdout_line_count = 3 git ls-files -u &&
diff --git a/t/t6403-merge-file.sh b/t/t6403-merge-file.sh
index 1a70823..fb872c5 100755
--- a/t/t6403-merge-file.sh
+++ b/t/t6403-merge-file.sh
@@ -56,7 +56,67 @@
 	deduxit me super semitas jusitiae,
 	EOF
 
-	printf "propter nomen suum." >>new4.txt
+	printf "propter nomen suum." >>new4.txt &&
+
+	cat >base.c <<-\EOF &&
+	int f(int x, int y)
+	{
+		if (x == 0)
+		{
+			return y;
+		}
+		return x;
+	}
+
+	int g(size_t u)
+	{
+		while (u < 30)
+		{
+			u++;
+		}
+		return u;
+	}
+	EOF
+
+	cat >ours.c <<-\EOF &&
+	int g(size_t u)
+	{
+		while (u < 30)
+		{
+			u++;
+		}
+		return u;
+	}
+
+	int h(int x, int y, int z)
+	{
+		if (z == 0)
+		{
+			return x;
+		}
+		return y;
+	}
+	EOF
+
+	cat >theirs.c <<-\EOF
+	int f(int x, int y)
+	{
+		if (x == 0)
+		{
+			return y;
+		}
+		return x;
+	}
+
+	int g(size_t u)
+	{
+		while (u > 34)
+		{
+			u--;
+		}
+		return u;
+	}
+	EOF
 '
 
 test_expect_success 'merge with no changes' '
@@ -65,11 +125,30 @@
 	test_cmp test.txt orig.txt
 '
 
+test_expect_success 'merge with no changes with --object-id' '
+	git add orig.txt &&
+	git merge-file -p --object-id :orig.txt :orig.txt :orig.txt >actual &&
+	test_cmp actual orig.txt
+'
+
 test_expect_success "merge without conflict" '
 	cp new1.txt test.txt &&
 	git merge-file test.txt orig.txt new2.txt
 '
 
+test_expect_success 'merge without conflict with --object-id' '
+	git add orig.txt new2.txt &&
+	git merge-file --object-id :orig.txt :orig.txt :new2.txt >actual &&
+	git rev-parse :new2.txt >expected &&
+	test_cmp actual expected
+'
+
+test_expect_success 'can accept object ID with --object-id' '
+	git merge-file --object-id $(test_oid empty_blob) $(test_oid empty_blob) :new2.txt >actual &&
+	git rev-parse :new2.txt >expected &&
+	test_cmp actual expected
+'
+
 test_expect_success 'works in subdirectory' '
 	mkdir dir &&
 	cp new1.txt dir/a.txt &&
@@ -138,6 +217,31 @@
 	test_cmp expect.txt test.txt
 '
 
+test_expect_success "merge with conflicts with --object-id" '
+	git add backup.txt orig.txt new3.txt &&
+	test_must_fail git merge-file -p --object-id :backup.txt :orig.txt :new3.txt >actual &&
+	sed -e "s/<< test.txt/<< :backup.txt/" \
+	    -e "s/>> new3.txt/>> :new3.txt/" \
+	    expect.txt >expect &&
+	test_cmp expect actual &&
+	test_must_fail git merge-file --object-id :backup.txt :orig.txt :new3.txt >oid &&
+	git cat-file blob "$(cat oid)" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success "merge with conflicts with --object-id with labels" '
+	git add backup.txt orig.txt new3.txt &&
+	test_must_fail git merge-file -p --object-id \
+		-L test.txt -L orig.txt -L new3.txt \
+		:backup.txt :orig.txt :new3.txt >actual &&
+	test_cmp expect.txt actual &&
+	test_must_fail git merge-file --object-id \
+		-L test.txt -L orig.txt -L new3.txt \
+		:backup.txt :orig.txt :new3.txt >oid &&
+	git cat-file blob "$(cat oid)" >actual &&
+	test_cmp expect.txt actual
+'
+
 test_expect_success "merge conflicting with --ours" '
 	cp backup.txt test.txt &&
 
@@ -256,6 +360,14 @@
 	grep "Cannot merge binary files" merge.err
 '
 
+test_expect_success 'binary files cannot be merged with --object-id' '
+	cp "$TEST_DIRECTORY"/test-binary-1.png . &&
+	git add orig.txt new1.txt test-binary-1.png &&
+	test_must_fail git merge-file --object-id \
+		:orig.txt :test-binary-1.png :new1.txt 2> merge.err &&
+	grep "Cannot merge binary files" merge.err
+'
+
 test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
 	sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" <new5.txt >new6.txt &&
 	sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" <new5.txt >new7.txt &&
@@ -389,4 +501,72 @@
 	test $(tr "\015" Q <nolf.txt | grep "^[<=>].*Q$" | wc -l) = 0
 '
 
+test_expect_success '--object-id fails without repository' '
+	empty="$(test_oid empty_blob)" &&
+	nongit test_must_fail git merge-file --object-id $empty $empty $empty 2>err &&
+	grep "not a git repository" err
+'
+
+test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' '
+	cat >expect.c <<-\EOF &&
+	int g(size_t u)
+	{
+		while (u < 30)
+		{
+			u++;
+		}
+		return u;
+	}
+
+	int h(int x, int y, int z)
+	{
+	<<<<<<< ours.c
+		if (z == 0)
+	||||||| base.c
+		while (u < 30)
+	=======
+		while (u > 34)
+	>>>>>>> theirs.c
+		{
+	<<<<<<< ours.c
+			return x;
+	||||||| base.c
+			u++;
+	=======
+			u--;
+	>>>>>>> theirs.c
+		}
+		return y;
+	}
+	EOF
+
+	test_must_fail git merge-file -p --diff3 --diff-algorithm myers ours.c base.c theirs.c >myers_output.c &&
+	test_cmp expect.c myers_output.c
+'
+
+test_expect_success 'merging C files with "histogram" diff algorithm avoids some spurious conflicts' '
+	cat >expect.c <<-\EOF &&
+	int g(size_t u)
+	{
+		while (u > 34)
+		{
+			u--;
+		}
+		return u;
+	}
+
+	int h(int x, int y, int z)
+	{
+		if (z == 0)
+		{
+			return x;
+		}
+		return y;
+	}
+	EOF
+
+	git merge-file -p --diff3 --diff-algorithm histogram ours.c base.c theirs.c >histogram_output.c &&
+	test_cmp expect.c histogram_output.c
+'
+
 test_done
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 5e4e4dd..156a1ef 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -42,11 +42,15 @@
 	#!/bin/sh
 
 	orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
+	orig_name="$6" our_name="$7" their_name="$8"
 	(
 		echo "orig is $orig"
 		echo "ours is $ours"
 		echo "theirs is $theirs"
 		echo "path is $path"
+		echo "orig_name is $orig_name"
+		echo "our_name is $our_name"
+		echo "their_name is $their_name"
 		echo "=== orig ==="
 		cat "$orig"
 		echo "=== ours ==="
@@ -56,6 +60,12 @@
 	) >"$ours+"
 	cat "$ours+" >"$ours"
 	rm -f "$ours+"
+
+	if test -f ./please-abort
+	then
+		echo >>./please-abort killing myself
+		kill -9 $$
+	fi
 	exit "$exit"
 	EOF
 	chmod +x ./custom-merge
@@ -115,7 +125,7 @@
 
 	git reset --hard anchor &&
 	git config --replace-all \
-	merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
+	merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
 	git config --replace-all \
 	merge.custom.name "custom merge driver for testing" &&
 
@@ -126,7 +136,8 @@
 	o=$(git unpack-file main^:text) &&
 	a=$(git unpack-file side^:text) &&
 	b=$(git unpack-file main:text) &&
-	sh -c "./custom-merge $o $a $b 0 text" &&
+	base_revid=$(git rev-parse --short main^) &&
+	sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
 	sed -e 1,3d $a >check-2 &&
 	cmp check-1 check-2 &&
 	rm -f $o $a $b
@@ -136,7 +147,7 @@
 
 	git reset --hard anchor &&
 	git config --replace-all \
-	merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
+	merge.custom.driver "./custom-merge %O %A %B 1 %P %S %X %Y" &&
 	git config --replace-all \
 	merge.custom.name "custom merge driver for testing" &&
 
@@ -153,7 +164,8 @@
 	o=$(git unpack-file main^:text) &&
 	a=$(git unpack-file anchor:text) &&
 	b=$(git unpack-file main:text) &&
-	sh -c "./custom-merge $o $a $b 0 text" &&
+	base_revid=$(git rev-parse --short main^) &&
+	sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
 	sed -e 1,3d $a >check-2 &&
 	cmp check-1 check-2 &&
 	sed -e 1,3d -e 4q $a >check-3 &&
@@ -162,6 +174,24 @@
 	rm -f $o $a $b
 '
 
+test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' '
+	test_when_finished "rm -f output please-abort" &&
+
+	git reset --hard anchor &&
+	git config --replace-all \
+	merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
+	git config --replace-all \
+	merge.custom.name "custom merge driver for testing" &&
+
+	>./please-abort &&
+	echo "* merge=custom" >.gitattributes &&
+	test_must_fail git merge main 2>err &&
+	grep "^error: failed to execute internal merge" err &&
+	git ls-files -u >output &&
+	git diff --name-only HEAD >>output &&
+	test_must_be_empty output
+'
+
 test_expect_success 'up-to-date merge without common ancestor' '
 	git init repo1 &&
 	git init repo2 &&
diff --git a/t/t6413-merge-crlf.sh b/t/t6413-merge-crlf.sh
index b4f4a31..647ea1e 100755
--- a/t/t6413-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
@@ -34,14 +34,14 @@
 test_expect_success 'Check "ours" is CRLF' '
 	git reset --hard initial &&
 	git merge side -s ours &&
-	cat file | remove_cr | append_cr >file.temp &&
+	remove_cr <file | append_cr >file.temp &&
 	test_cmp file file.temp
 '
 
 test_expect_success 'Check that conflict file is CRLF' '
 	git reset --hard a &&
 	test_must_fail git merge side &&
-	cat file | remove_cr | append_cr >file.temp &&
+	remove_cr <file | append_cr >file.temp &&
 	test_cmp file file.temp
 '
 
diff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh
index 17b54d6..5f414ab 100755
--- a/t/t6416-recursive-corner-cases.sh
+++ b/t/t6416-recursive-corner-cases.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
diff --git a/t/t6418-merge-text-auto.sh b/t/t6418-merge-text-auto.sh
index 41288a6..48a62cb 100755
--- a/t/t6418-merge-text-auto.sh
+++ b/t/t6418-merge-text-auto.sh
@@ -15,6 +15,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index 076b6a7..80d7b5e 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -476,7 +476,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (.*/add)" out &&
+		test_grep "CONFLICT (.*/add)" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 2 out &&
@@ -522,7 +522,7 @@
 		git checkout B^0 &&
 
 		test_must_fail git merge -s recursive A^0 >out &&
-		test_i18ngrep "CONFLICT (.*/add)" out &&
+		test_grep "CONFLICT (.*/add)" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 2 out &&
@@ -602,7 +602,7 @@
 		git checkout B^0 &&
 
 		test_must_fail git merge -s recursive C^0 >out &&
-		test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+		test_grep "CONFLICT (\(.*\)/\1)" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 2 out &&
@@ -914,8 +914,8 @@
 		# be flexible in the type of console output message(s) reported
 		# for this particular case; we will be more stringent about the
 		# contents of the index and working directory.
-		test_i18ngrep "CONFLICT (.*/add)" out &&
-		test_i18ngrep "CONFLICT (rename.*/delete)" out &&
+		test_grep "CONFLICT (.*/add)" out &&
+		test_grep "CONFLICT (rename.*/delete)" out &&
 		test_must_be_empty err &&
 
 		git ls-files -s >file_count &&
@@ -988,8 +988,8 @@
 		# be flexible in the type of console output message(s) reported
 		# for this particular case; we will be more stringent about the
 		# contents of the index and working directory.
-		test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
-		test_i18ngrep "CONFLICT (rename.*delete)" out &&
+		test_grep "CONFLICT (\(.*\)/\1)" out &&
+		test_grep "CONFLICT (rename.*delete)" out &&
 		test_must_be_empty err &&
 
 		git ls-files -s >file_count &&
@@ -1068,7 +1068,7 @@
 
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep "CONFLICT (rename/rename)" out &&
+		test_grep "CONFLICT (rename/rename)" out &&
 		test_must_be_empty err &&
 
 		git ls-files -s >file_count &&
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 944de75..88d1cf2 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -276,7 +276,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+		test_grep "CONFLICT (\(.*\)/\1)" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 8 out &&
@@ -515,7 +515,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT.*directory rename split" out &&
+		test_grep "CONFLICT.*directory rename split" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -591,7 +591,7 @@
 		git rev-parse >expect \
 			 O:z/b  O:z/c  B:x/d &&
 		test_cmp expect actual &&
-		test_i18ngrep ! "CONFLICT.*directory rename split" out
+		test_grep ! "CONFLICT.*directory rename split" out
 	)
 '
 
@@ -726,8 +726,8 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
-		test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
+		test_grep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
+		test_grep ! CONFLICT.*rename/rename.*y/d out &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -938,7 +938,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT.*implicit dir rename" out &&
+		test_grep "CONFLICT.*implicit dir rename" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -1013,7 +1013,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+		test_grep "CONFLICT (add/add).* y/d" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -1094,8 +1094,8 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
-		test_i18ngrep "CONFLICT (add/add).* y/d" out &&
+		test_grep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
+		test_grep "CONFLICT (add/add).* y/d" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 9 out &&
@@ -1179,7 +1179,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
+		test_grep "CONFLICT (file/directory).*y/d" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -1278,7 +1278,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
+		test_grep "CONFLICT (rename/delete).*z/c.*y/c" out &&
 
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
@@ -1740,8 +1740,8 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
-		test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
+		test_grep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
+		test_grep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 7 out &&
@@ -1813,7 +1813,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
+		test_grep "CONFLICT (\(.*\)/\1)" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -1900,7 +1900,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
+		test_grep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -1965,7 +1965,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
+		test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
@@ -2071,7 +2071,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
+		test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
@@ -2330,7 +2330,7 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
+		test_grep "CONFLICT (modify/delete).* z/d" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -2491,8 +2491,8 @@
 		git checkout A^0 &&
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-		test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
-		test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
+		test_grep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
+		test_grep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
 
 		git ls-files -s >out &&
 		test_line_count = 7 out &&
@@ -2741,7 +2741,7 @@
 		git checkout A^0 &&
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
+		test_grep "WARNING: Avoiding applying x -> z rename to x/f" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -2830,10 +2830,10 @@
 		git checkout A^0 &&
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
-		test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
-		test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
-		test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
-		test_i18ngrep "WARNING: Avoiding applying w -> v rename to w/c" out &&
+		test_grep "WARNING: Avoiding applying z -> y rename to z/t" out &&
+		test_grep "WARNING: Avoiding applying y -> x rename to y/a" out &&
+		test_grep "WARNING: Avoiding applying x -> w rename to x/b" out &&
+		test_grep "WARNING: Avoiding applying w -> v rename to w/c" out &&
 
 		git ls-files -s >out &&
 		test_line_count = 7 out &&
@@ -3215,7 +3215,7 @@
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_path_is_missing .git/MERGE_HEAD &&
-		test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
+		test_grep "The following untracked working tree files would be overwritten by merge" err &&
 
 		git ls-files -s >out &&
 		test_line_count = 1 out &&
@@ -3287,7 +3287,7 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+			test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
 			git ls-files -s >out &&
 			test_line_count = 1 out &&
@@ -3296,8 +3296,8 @@
 			git ls-files -o >out &&
 			test_line_count = 5 out
 		else
-			test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
-			test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
+			test_grep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
+			test_grep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 3 out &&
@@ -3377,7 +3377,7 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+			test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
 			git ls-files -s >out &&
 			test_line_count = 4 out &&
@@ -3386,8 +3386,8 @@
 			git ls-files -o >out &&
 			test_line_count = 3 out
 		else
-			test_i18ngrep "CONFLICT (rename/rename)" out &&
-			test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
+			test_grep "CONFLICT (rename/rename)" out &&
+			test_grep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 6 out &&
@@ -3428,7 +3428,7 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+			test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
 			git ls-files -s >out &&
 			test_line_count = 4 out &&
@@ -3437,8 +3437,8 @@
 			git ls-files -o >out &&
 			test_line_count = 3 out
 		else
-			test_i18ngrep "CONFLICT (rename/rename)" out &&
-			test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
+			test_grep "CONFLICT (rename/rename)" out &&
+			test_grep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 6 out &&
@@ -3517,7 +3517,7 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: The following untracked working tree files would be overwritten by merge" err &&
+			test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
 
 			git ls-files -s >out &&
 			test_line_count = 6 out &&
@@ -3526,8 +3526,8 @@
 			git ls-files -o >out &&
 			test_line_count = 3 out
 		else
-			test_i18ngrep "CONFLICT (rename/rename)" out &&
-			test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
+			test_grep "CONFLICT (rename/rename)" out &&
+			test_grep "Refusing to lose untracked file at y/wham" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 6 out &&
@@ -3606,7 +3606,7 @@
 		echo random >z/c &&
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-		test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
+		test_grep ! "following untracked working tree files would be overwritten by merge" err &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -3690,9 +3690,9 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+			test_grep "error: Your local changes to the following files would be overwritten by merge" err
 		else
-			test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+			test_grep "Refusing to lose dirty file at z/c" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 2 out &&
@@ -3770,10 +3770,10 @@
 		then
 			test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+			test_grep "error: Your local changes to the following files would be overwritten by merge" err
 		else
 			git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
-			test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+			test_grep "Refusing to lose dirty file at z/c" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 3 out &&
@@ -3853,9 +3853,9 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+			test_grep "error: Your local changes to the following files would be overwritten by merge" err
 		else
-			test_i18ngrep "following files would be overwritten by merge" err
+			test_grep "following files would be overwritten by merge" err
 		fi &&
 
 		grep -q stuff y/c &&
@@ -3927,9 +3927,9 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+			test_grep "error: Your local changes to the following files would be overwritten by merge" err
 		else
-			test_i18ngrep "Refusing to lose dirty file at z/c" out &&
+			test_grep "Refusing to lose dirty file at z/c" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 4 out &&
@@ -4013,10 +4013,10 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+			test_grep "error: Your local changes to the following files would be overwritten by merge" err
 		else
-			test_i18ngrep "CONFLICT (rename/rename)" out &&
-			test_i18ngrep "Refusing to lose dirty file at y/c" out &&
+			test_grep "CONFLICT (rename/rename)" out &&
+			test_grep "Refusing to lose dirty file at y/c" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 7 out &&
@@ -4102,10 +4102,10 @@
 		if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 		then
 			test_path_is_missing .git/MERGE_HEAD &&
-			test_i18ngrep "error: Your local changes to the following files would be overwritten by merge" err
+			test_grep "error: Your local changes to the following files would be overwritten by merge" err
 		else
-			test_i18ngrep "CONFLICT (rename/rename)" out &&
-			test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
+			test_grep "CONFLICT (rename/rename)" out &&
+			test_grep "Refusing to lose dirty file at y/wham" out &&
 
 			git ls-files -s >out &&
 			test_line_count = 4 out &&
@@ -5417,8 +5417,8 @@
 
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
-		test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
+		test_grep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
+		test_grep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
 
 		git ls-files >paths &&
 		! grep z/ paths &&
@@ -5441,8 +5441,8 @@
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
-		test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
+		test_grep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
+		test_grep Path.updated:.*z/d.added.in.B^0.*y/d out &&
 
 		git ls-files >paths &&
 		! grep z/ paths &&
@@ -5507,8 +5507,8 @@
 
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
-		test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+		test_grep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+		test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
 
 		git ls-files >paths &&
 		! grep z/ paths &&
@@ -5529,8 +5529,8 @@
 
 		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
-		test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+		test_grep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+		test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
 
 		git ls-files >paths &&
 		! grep z/ paths &&
@@ -5593,7 +5593,7 @@
 
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+		test_grep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
 
 		git ls-files >paths &&
 		! grep z/ paths &&
@@ -5614,7 +5614,7 @@
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+		test_grep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
 
 		git ls-files >paths &&
 		! grep z/ paths &&
@@ -5682,8 +5682,8 @@
 
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
-		test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
+		test_grep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
+		test_grep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
 
 		git ls-files >paths &&
 		! grep b/ paths &&
@@ -5706,8 +5706,8 @@
 
 		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
-		test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
+		test_grep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
+		test_grep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
 
 		git ls-files >paths &&
 		! grep b/ paths &&
@@ -5821,9 +5821,9 @@
 
 		git -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&
 
-		test_i18ngrep ! CONFLICT out &&
-		test_i18ngrep ! BUG: err &&
-		test_i18ngrep ! core.dumped err &&
+		test_grep ! CONFLICT out &&
+		test_grep ! BUG: err &&
+		test_grep ! core.dumped err &&
 		test_must_be_empty err &&
 
 		git ls-files >paths &&
diff --git a/t/t6424-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh
index a61f20c..7677c5f 100755
--- a/t/t6424-merge-unrelated-index-changes.sh
+++ b/t/t6424-merge-unrelated-index-changes.sh
@@ -178,7 +178,7 @@
 	test_when_finished "git clean -fd" &&  # Do not leave untracked around
 	# Merge B & F, with B as "head"
 	git merge-recursive A -- B F > out &&
-	test_i18ngrep "Already up to date" out
+	test_grep "Already up to date" out
 '
 
 test_expect_success 'recursive, when file has staged changes not matching HEAD nor what a merge would give' '
@@ -194,7 +194,7 @@
 	test_must_fail git merge -s recursive E^0 2>err &&
 	git rev-parse --verify :subdir/a >actual &&
 	test_cmp expect actual &&
-	test_i18ngrep "changes to the following files would be overwritten" err
+	test_grep "changes to the following files would be overwritten" err
 '
 
 test_expect_success 'recursive, when file has staged changes matching what a merge would give' '
@@ -210,7 +210,7 @@
 	test_must_fail git merge -s recursive E^0 2>err &&
 	git rev-parse --verify :subdir/a >actual &&
 	test_cmp expect actual &&
-	test_i18ngrep "changes to the following files would be overwritten" err
+	test_grep "changes to the following files would be overwritten" err
 '
 
 test_expect_success 'octopus, unrelated file touched' '
diff --git a/t/t6425-merge-rename-delete.sh b/t/t6425-merge-rename-delete.sh
index 93cd286..b95b064 100755
--- a/t/t6425-merge-rename-delete.sh
+++ b/t/t6425-merge-rename-delete.sh
@@ -21,8 +21,8 @@
 	git commit -m "delete" &&
 
 	test_must_fail git merge --strategy=recursive rename >output &&
-	test_i18ngrep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
-	test_i18ngrep "CONFLICT (rename/delete): A.*deleted in HEAD." output
+	test_grep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
+	test_grep "CONFLICT (rename/delete): A.*deleted in HEAD." output
 '
 
 test_done
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index fd21c1a..b059475 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -375,7 +375,7 @@
 		export GIT_MERGE_VERBOSITY &&
 		test_must_fail git merge -s recursive B^0 >out 2>err &&
 
-		test_i18ngrep "CONFLICT (.*/add):" out &&
+		test_grep "CONFLICT (.*/add):" out &&
 		test_must_be_empty err &&
 
 		git ls-files -s >index_files &&
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index d02fa16..0f39ed0 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -71,8 +71,9 @@
 
 		git switch upstream &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream~1..topic
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		git ls-files >tracked-files &&
 		test_line_count = 2 tracked-files &&
@@ -140,8 +141,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream~1..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		grep region_enter.*diffcore_rename trace.output >calls &&
 		test_line_count = 1 calls
@@ -199,8 +201,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream~1..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		git ls-files >tracked &&
 		test_line_count = 2 tracked &&
@@ -276,8 +279,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream~1..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		git ls-files >tracked &&
 		test_line_count = 4 tracked &&
@@ -353,10 +357,7 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output &&
-		#git cherry-pick upstream..topic &&
-
-		grep CONFLICT..rename/rename output &&
+		test_must_fail git replay --onto HEAD upstream~1..topic >output &&
 
 		grep region_enter.*diffcore_rename trace.output >calls &&
 		test_line_count = 2 calls
@@ -455,8 +456,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		grep region_enter.*diffcore_rename trace.output >calls &&
 		test_line_count = 2 calls &&
@@ -521,8 +523,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		grep region_enter.*diffcore_rename trace.output >calls &&
 		test_line_count = 3 calls &&
@@ -623,8 +626,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		grep region_enter.*diffcore_rename trace.output >calls &&
 		test_line_count = 1 calls &&
@@ -681,8 +685,9 @@
 		GIT_TRACE2_PERF="$(pwd)/trace.output" &&
 		export GIT_TRACE2_PERF &&
 
-		test-tool fast-rebase --onto HEAD upstream~1 topic &&
-		#git cherry-pick upstream..topic &&
+		git replay --onto HEAD upstream~1..topic >out &&
+		git update-ref --stdin <out &&
+		git checkout topic &&
 
 		grep region_enter.*diffcore_rename trace.output >calls &&
 		test_line_count = 2 calls &&
diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh
index 07067bb..ca15e6d 100755
--- a/t/t6430-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
@@ -308,13 +308,13 @@
 
 	test_must_fail git merge "$c5" &&
 	test_must_fail git merge "$c5" 2> out &&
-	test_i18ngrep "not possible because you have unmerged files" out &&
+	test_grep "not possible because you have unmerged files" out &&
 	git add -u &&
 	test_must_fail git merge "$c5" 2> out &&
-	test_i18ngrep "You have not concluded your merge" out &&
+	test_grep "You have not concluded your merge" out &&
 	rm -f .git/MERGE_HEAD &&
 	test_must_fail git merge "$c5" 2> out &&
-	test_i18ngrep "Your local changes to the following files would be overwritten by merge:" out
+	test_grep "Your local changes to the following files would be overwritten by merge:" out
 '
 
 test_expect_success 'merge-recursive remove conflict' '
@@ -713,7 +713,7 @@
 	test_must_fail git -c merge.verbosity=5 merge-recursive $(cat trees) -- $c1 $c3 >out &&
 
 	# ...but make sure it fails in the expected way
-	test_i18ngrep CONFLICT.*rename/rename out &&
+	test_grep CONFLICT.*rename/rename out &&
 
 	# merge-recursive prints in reverse order, but we do not care
 	sort <trees >expect &&
diff --git a/t/t6433-merge-toplevel.sh b/t/t6433-merge-toplevel.sh
index b160314..ed7866d 100755
--- a/t/t6433-merge-toplevel.sh
+++ b/t/t6433-merge-toplevel.sh
@@ -5,6 +5,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 t3033_reset () {
@@ -151,7 +152,7 @@
 	echo change >>one.t &&
 	git diff >expect &&
 	test_must_fail git merge --autostash five 2>err &&
-	test_i18ngrep ! "stash" err &&
+	test_grep ! "stash" err &&
 	git diff >actual &&
 	test_cmp expect actual
 '
@@ -169,7 +170,7 @@
 	echo change >>one.t &&
 	git diff one.t >expect &&
 	git merge --allow-unrelated-histories --autostash five 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git diff one.t >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index c0b7bd7..4f43764 100755
--- a/t/t6436-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
@@ -104,12 +104,12 @@
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
 		test_must_fail git merge c1a >out 2>err &&
-		test_i18ngrep "would be overwritten by merge" err &&
+		test_grep "would be overwritten by merge" err &&
 		test_cmp important other.c &&
 		test_path_is_missing .git/MERGE_HEAD
 	else
 		test_must_fail git merge c1a >out &&
-		test_i18ngrep "Refusing to lose dirty file at other.c" out &&
+		test_grep "Refusing to lose dirty file at other.c" out &&
 		test_path_is_file other.c~HEAD &&
 		test $(git hash-object other.c~HEAD) = $(git rev-parse c1a:c1.c) &&
 		test_cmp important other.c
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index c9a86f2..7a3f1cb 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -8,6 +8,7 @@
 GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
 export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
@@ -112,7 +113,7 @@
 	 git checkout -b test-nonforward-a b &&
 	  if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	  then
-		test_must_fail git merge c >actual &&
+		test_must_fail git merge c 2>actual &&
 		sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
 		grep "$sub_expect" actual
 	  else
@@ -153,9 +154,9 @@
 	  git rev-parse --short sub-d > ../expect) &&
 	  if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	  then
-		test_must_fail git merge c >actual &&
+		test_must_fail git merge c >actual 2>sub-actual &&
 		sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
-		grep "$sub_expect" actual
+		grep "$sub_expect" sub-actual
 	  else
 		test_must_fail git merge c 2> actual
 	  fi &&
@@ -180,9 +181,9 @@
 	 ) &&
 	 if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	 then
-		test_must_fail git merge c >actual &&
+		test_must_fail git merge c >actual 2>sub-actual &&
 		sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
-		grep "$sub_expect" actual
+		grep "$sub_expect" sub-actual
 	 else
 		test_must_fail git merge c 2> actual
 	 fi &&
@@ -226,7 +227,7 @@
 	git commit -a -m "f" &&
 
 	git checkout -b test-backward e &&
-	test_must_fail git merge f >actual &&
+	test_must_fail git merge f 2>actual &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
     then
 		sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
@@ -479,7 +480,7 @@
 		# We do not want files within the submodule to prevent the
 		# merge from starting; we should not be writing to such paths
 		# anyway.
-		test_i18ngrep ! "refusing to lose untracked file at" err
+		test_grep ! "refusing to lose untracked file at" err
 	)
 '
 
@@ -534,7 +535,7 @@
 	git checkout -b b init &&
 	git add sub &&
 	git commit -m "b" &&
-	test_must_fail git merge a >actual &&
+	test_must_fail git merge a 2>actual &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
     then
 		sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index d9acb63..43d4017 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -11,23 +11,7 @@
 	# behavior, make sure we always pack everything to one pack by
 	# default
 	git config gc.bigPackThreshold 2g &&
-
-	# These are simply values which, when hashed as a blob with a newline,
-	# produce a hash where the first byte is 0x17 in their respective
-	# algorithms.
-	test_oid_cache <<-EOF
-	obj1 sha1:263
-	obj1 sha256:34
-
-	obj2 sha1:410
-	obj2 sha256:174
-
-	obj3 sha1:523
-	obj3 sha256:313
-
-	obj4 sha1:790
-	obj4 sha256:481
-	EOF
+	test_oid_init
 '
 
 test_expect_success 'gc empty repository' '
@@ -41,7 +25,7 @@
 
 test_expect_success 'gc --gobbledegook' '
 	test_expect_code 129 git gc --nonsense 2>err &&
-	test_i18ngrep "[Uu]sage: git gc" err
+	test_grep "[Uu]sage: git gc" err
 '
 
 test_expect_success 'gc -h with invalid configuration' '
@@ -52,7 +36,7 @@
 		echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
 		test_expect_code 129 git gc -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage" broken/usage
+	test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'gc is not aborted due to a stale symref' '
@@ -114,8 +98,8 @@
 		# We need to create two object whose sha1s start with 17
 		# since this is what git gc counts.  As it happens, these
 		# two blobs will do so.
-		test_commit "$(test_oid obj1)" &&
-		test_commit "$(test_oid obj2)" &&
+		test_commit "$(test_oid blob17_1)" &&
+		test_commit "$(test_oid blob17_2)" &&
 
 		git gc --auto >../out.actual 2>../err.actual
 	) &&
@@ -146,16 +130,16 @@
 	# We need to create two object whose sha1s start with 17
 	# since this is what git gc counts.  As it happens, these
 	# two blobs will do so.
-	test_commit "$(test_oid obj1)" &&
-	test_commit "$(test_oid obj2)" &&
+	test_commit "$(test_oid blob17_1)" &&
+	test_commit "$(test_oid blob17_2)" &&
 	# Our first gc will create a pack; our second will create a second pack
 	git gc --auto &&
 	ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
-	test_commit "$(test_oid obj3)" &&
-	test_commit "$(test_oid obj4)" &&
+	test_commit "$(test_oid blob17_3)" &&
+	test_commit "$(test_oid blob17_4)" &&
 
 	git gc --auto 2>err &&
-	test_i18ngrep ! "^warning:" err &&
+	test_grep ! "^warning:" err &&
 	ls .git/objects/pack/pack-*.pack | sort >post_packs &&
 	comm -1 -3 existing_packs post_packs >new &&
 	comm -2 -3 existing_packs post_packs >del &&
@@ -166,15 +150,15 @@
 test_expect_success 'gc --no-quiet' '
 	GIT_PROGRESS_DELAY=0 git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
 	test_must_be_empty stdout &&
-	test_i18ngrep "Computing commit graph generation numbers" stderr
+	test_grep "Computing commit graph generation numbers" stderr
 '
 
 test_expect_success TTY 'with TTY: gc --no-quiet' '
 	test_terminal env GIT_PROGRESS_DELAY=0 \
 		git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
 	test_must_be_empty stdout &&
-	test_i18ngrep "Enumerating objects" stderr &&
-	test_i18ngrep "Computing commit graph generation numbers" stderr
+	test_grep "Enumerating objects" stderr &&
+	test_grep "Computing commit graph generation numbers" stderr
 '
 
 test_expect_success 'gc --quiet' '
@@ -202,6 +186,30 @@
 	grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
 '
 
+test_expect_success 'gc.repackFilter launches repack with a filter' '
+	git clone --no-local --bare . bare.git &&
+
+	git -C bare.git -c gc.cruftPacks=false gc &&
+	test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+	GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \
+		-c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+	test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+	grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out
+'
+
+test_expect_success 'gc.repackFilterTo store filtered out objects' '
+	test_when_finished "rm -rf bare.git filtered.git" &&
+
+	git init --bare filtered.git &&
+	git -C bare.git -c gc.repackFilter=blob:none \
+		-c gc.repackFilterTo=../filtered.git/objects/pack/pack \
+		-c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+
+	test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+	test_stdout_line_count = 1 ls filtered.git/objects/pack/*.pack
+'
+
 prepare_cruft_history () {
 	test_commit base &&
 
@@ -210,94 +218,126 @@
 	git reset HEAD^^
 }
 
-assert_cruft_packs () {
-	find .git/objects/pack -name "*.mtimes" >mtimes &&
-	sed -e 's/\.mtimes$/\.pack/g' mtimes >packs &&
-
-	test_file_not_empty packs &&
-	while read pack
-	do
-		test_path_is_file "$pack" || return 1
-	done <packs
-}
-
 assert_no_cruft_packs () {
 	find .git/objects/pack -name "*.mtimes" >mtimes &&
 	test_must_be_empty mtimes
 }
 
-test_expect_success 'gc --cruft generates a cruft pack' '
-	test_when_finished "rm -fr crufts" &&
-	git init crufts &&
-	(
-		cd crufts &&
+for argv in \
+	"gc" \
+	"-c gc.cruftPacks=true gc" \
+	"-c gc.cruftPacks=false gc --cruft"
+do
+	test_expect_success "git $argv generates a cruft pack" '
+		test_when_finished "rm -fr repo" &&
+		git init repo &&
+		(
+			cd repo &&
 
+			prepare_cruft_history &&
+			git $argv &&
+
+			find .git/objects/pack -name "*.mtimes" >mtimes &&
+			sed -e 's/\.mtimes$/\.pack/g' mtimes >packs &&
+
+			test_file_not_empty packs &&
+			while read pack
+			do
+				test_path_is_file "$pack" || return 1
+			done <packs
+		)
+	'
+done
+
+for argv in \
+	"gc --no-cruft" \
+	"-c gc.cruftPacks=false gc" \
+	"-c gc.cruftPacks=true gc --no-cruft"
+do
+	test_expect_success "git $argv does not generate a cruft pack" '
+		test_when_finished "rm -fr repo" &&
+		git init repo &&
+		(
+			cd repo &&
+
+			prepare_cruft_history &&
+			git $argv &&
+
+			assert_no_cruft_packs
+		)
+	'
+done
+
+test_expect_success '--keep-largest-pack ignores cruft packs' '
+	test_when_finished "rm -fr repo" &&
+	git init repo &&
+	(
+		cd repo &&
+
+		# Generate a pack for reachable objects (of which there
+		# are 3), and one for unreachable objects (of which
+		# there are 6).
 		prepare_cruft_history &&
 		git gc --cruft &&
-		assert_cruft_packs
-	)
-'
 
-test_expect_success 'gc.cruftPacks=true generates a cruft pack' '
-	test_when_finished "rm -fr crufts" &&
-	git init crufts &&
-	(
-		cd crufts &&
+		mtimes="$(find .git/objects/pack -type f -name "pack-*.mtimes")" &&
+		sz="$(test_file_size "${mtimes%.mtimes}.pack")" &&
 
-		prepare_cruft_history &&
-		git -c gc.cruftPacks=true gc &&
-		assert_cruft_packs
-	)
-'
+		# Ensure that the cruft pack gets removed (due to
+		# `--prune=now`) despite it being the largest pack.
+		git -c gc.bigPackThreshold=$sz gc --cruft --prune=now &&
 
-test_expect_success 'feature.experimental=true generates a cruft pack' '
-	git init crufts &&
-	test_when_finished "rm -fr crufts" &&
-	(
-		cd crufts &&
-
-		prepare_cruft_history &&
-		git -c feature.experimental=true gc &&
-		assert_cruft_packs
-	)
-'
-
-test_expect_success 'feature.experimental=false allows explicit cruft packs' '
-	git init crufts &&
-	test_when_finished "rm -fr crufts" &&
-	(
-		cd crufts &&
-
-		prepare_cruft_history &&
-		git -c gc.cruftPacks=true -c feature.experimental=false gc &&
-		assert_cruft_packs
-	)
-'
-
-test_expect_success 'feature.experimental=true can be overridden' '
-	git init crufts &&
-	test_when_finished "rm -fr crufts" &&
-	(
-		cd crufts &&
-
-		prepare_cruft_history &&
-		git -c feature.expiremental=true -c gc.cruftPacks=false gc &&
 		assert_no_cruft_packs
 	)
 '
 
-test_expect_success 'feature.experimental=false avoids cruft packs by default' '
-	git init crufts &&
-	test_when_finished "rm -fr crufts" &&
+test_expect_success 'gc.bigPackThreshold ignores cruft packs' '
+	test_when_finished "rm -fr repo" &&
+	git init repo &&
 	(
-		cd crufts &&
+		cd repo &&
 
+		# Generate a pack for reachable objects (of which there
+		# are 3), and one for unreachable objects (of which
+		# there are 6).
 		prepare_cruft_history &&
-		git -c feature.experimental=false gc &&
+		git gc --cruft &&
+
+		# Ensure that the cruft pack gets removed (due to
+		# `--prune=now`) despite it being the largest pack.
+		git gc --cruft --prune=now --keep-largest-pack &&
+
 		assert_no_cruft_packs
 	)
 '
 
+cruft_max_size_opts="git repack -d -l --cruft --cruft-expiration=2.weeks.ago"
+
+test_expect_success 'setup for --max-cruft-size tests' '
+	git init cruft--max-size &&
+	(
+		cd cruft--max-size &&
+		prepare_cruft_history
+	)
+'
+
+test_expect_success '--max-cruft-size sets appropriate repack options' '
+	GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size \
+		gc --cruft --max-cruft-size=1M &&
+	test_subcommand $cruft_max_size_opts --max-cruft-size=1048576 <trace2.txt
+'
+
+test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
+	GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+		git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft &&
+	test_subcommand $cruft_max_size_opts --max-cruft-size=2097152 <trace2.txt &&
+
+	GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+		git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft \
+		--max-cruft-size=3M &&
+	test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
+'
+
 run_and_wait_for_auto_gc () {
 	# We read stdout from gc for the side effect of waiting until the
 	# background gc process exits, closing its fd 9.  Furthermore, the
@@ -316,7 +356,7 @@
 	test_config gc.autodetach true &&
 	echo fleem >.git/gc.log &&
 	git gc --auto 2>err &&
-	test_i18ngrep "^warning:" err &&
+	test_grep "^warning:" err &&
 	test_config gc.logexpiry 5.days &&
 	test-tool chmtime =-345600 .git/gc.log &&
 	git gc --auto &&
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index 3968b47..4521508 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -101,7 +101,7 @@
 	'
 
 	test_expect_success "simultaneous gc ($title)" '
-		git gc --prune=12.hours.ago
+		git gc --no-cruft --prune=12.hours.ago
 	'
 
 	test_expect_success "finish writing out commit ($title)" '
@@ -131,7 +131,7 @@
 	'
 
 	test_expect_success "simultaneous gc ($title)" '
-		git gc --prune=12.hours.ago
+		git gc --no-cruft --prune=12.hours.ago
 	'
 
 	# tree should have been refreshed by write-tree
@@ -151,8 +151,8 @@
 	some message
 	EOF
 	commit=$(git hash-object -t commit -w broken-commit) &&
-	git gc -q 2>stderr &&
-	verbose git cat-file -e $commit &&
+	git gc --no-cruft -q 2>stderr &&
+	git cat-file -e $commit &&
 	test_must_be_empty stderr
 '
 
@@ -161,7 +161,7 @@
 	100644 blob $(test_oid 003)	foo
 	EOF
 	tree=$(git mktree --missing <broken-tree) &&
-	git gc -q 2>stderr &&
+	git gc --no-cruft -q 2>stderr &&
 	git cat-file -e $tree &&
 	test_must_be_empty stderr
 '
@@ -176,7 +176,7 @@
 	this is a broken tag
 	EOF
 	tag=$(git hash-object -t tag -w broken-tag) &&
-	git gc -q 2>stderr &&
+	git gc --no-cruft -q 2>stderr &&
 	git cat-file -e $tag &&
 	test_must_be_empty stderr
 '
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index 338a9c4..b330945 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -443,4 +443,173 @@
 	test_all_modes get_reachable_subset
 '
 
+test_expect_success 'for-each-ref ahead-behind:linear' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-1-3
+	refs/heads/commit-1-5
+	refs/heads/commit-1-8
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1 0 8
+	refs/heads/commit-1-3 0 6
+	refs/heads/commit-1-5 0 4
+	refs/heads/commit-1-8 0 1
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname) %(ahead-behind:commit-1-9)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:all' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-2-4
+	refs/heads/commit-4-2
+	refs/heads/commit-4-4
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1 0 24
+	refs/heads/commit-2-4 0 17
+	refs/heads/commit-4-2 0 17
+	refs/heads/commit-4-4 0 9
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname) %(ahead-behind:commit-5-5)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-5-3
+	refs/heads/commit-4-8
+	refs/heads/commit-9-9
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1 0 53
+	refs/heads/commit-4-8 8 30
+	refs/heads/commit-5-3 0 39
+	refs/heads/commit-9-9 27 0
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname) %(ahead-behind:commit-9-6)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some, multibase' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-5-3
+	refs/heads/commit-7-8
+	refs/heads/commit-4-8
+	refs/heads/commit-9-9
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1 0 53 0 53
+	refs/heads/commit-4-8 8 30 0 22
+	refs/heads/commit-5-3 0 39 0 39
+	refs/heads/commit-7-8 14 12 8 6
+	refs/heads/commit-9-9 27 0 27 0
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname) %(ahead-behind:commit-9-6) %(ahead-behind:commit-6-9)" \
+		--stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:none' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-7-5
+	refs/heads/commit-4-8
+	refs/heads/commit-9-9
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-4-8 16 16
+	refs/heads/commit-7-5 7 4
+	refs/heads/commit-9-9 49 0
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname) %(ahead-behind:commit-8-4)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:linear' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-1-3
+	refs/heads/commit-1-5
+	refs/heads/commit-1-8
+	refs/heads/commit-2-1
+	refs/heads/commit-5-1
+	refs/heads/commit-9-1
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-1-3
+	refs/heads/commit-1-5
+	refs/heads/commit-1-8
+	EOF
+	run_all_modes git for-each-ref --merged=commit-1-9 \
+		--format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:all' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-2-4
+	refs/heads/commit-4-2
+	refs/heads/commit-4-4
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-2-4
+	refs/heads/commit-4-2
+	refs/heads/commit-4-4
+	EOF
+	run_all_modes git for-each-ref --merged=commit-5-5 \
+		--format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-5-3
+	refs/heads/commit-4-8
+	refs/heads/commit-9-9
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-5-3
+	EOF
+	run_all_modes git for-each-ref --merged=commit-9-6 \
+		--format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:some, multibase' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-5-3
+	refs/heads/commit-7-8
+	refs/heads/commit-4-8
+	refs/heads/commit-9-9
+	EOF
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-4-8
+	refs/heads/commit-5-3
+	EOF
+	run_all_modes git for-each-ref \
+		--merged=commit-5-8 \
+		--merged=commit-8-5 \
+		--format="%(refname)" \
+		--stdin
+'
+
+test_expect_success 'for-each-ref merged:none' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-7-5
+	refs/heads/commit-4-8
+	refs/heads/commit-9-9
+	EOF
+	>expect &&
+	run_all_modes git for-each-ref --merged=commit-8-4 \
+		--format="%(refname)" --stdin
+'
+
 test_done
diff --git a/t/t6700-tree-depth.sh b/t/t6700-tree-depth.sh
new file mode 100755
index 0000000..9e70a7c
--- /dev/null
+++ b/t/t6700-tree-depth.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='handling of deep trees in various commands'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# We'll test against two depths here: a small one that will let us check the
+# behavior of the config setting easily, and a large one that should be
+# forbidden by default. Testing the default depth will let us know whether our
+# default is enough to prevent segfaults on systems that run the tests.
+small_depth=50
+big_depth=4100
+
+small_ok="-c core.maxtreedepth=$small_depth"
+small_no="-c core.maxtreedepth=$((small_depth-1))"
+
+# usage: mkdeep <name> <depth>
+#   Create a tag <name> containing a file whose path has depth <depth>.
+#
+# We'll use fast-import here for two reasons:
+#
+#   1. It's faster than creating $big_depth tree objects.
+#
+#   2. As we tighten tree limits, it's more likely to allow large sizes
+#      than trying to stuff a deep path into the index.
+mkdeep () {
+	{
+		echo "commit refs/tags/$1" &&
+		echo "committer foo <foo@example.com> 1234 -0000" &&
+		echo "data <<EOF" &&
+		echo "the commit message" &&
+		echo "EOF" &&
+
+		printf 'M 100644 inline ' &&
+		i=0 &&
+		while test $i -lt $2
+		do
+			printf 'a/'
+			i=$((i+1))
+		done &&
+		echo "file" &&
+
+		echo "data <<EOF" &&
+		echo "the file contents" &&
+		echo "EOF" &&
+		echo
+	} | git fast-import
+}
+
+test_expect_success 'create small tree' '
+	mkdeep small $small_depth
+'
+
+test_expect_success 'create big tree' '
+	mkdeep big $big_depth
+'
+
+test_expect_success 'limit recursion of git-archive' '
+	git $small_ok archive small >/dev/null &&
+	test_must_fail git $small_no archive small >/dev/null
+'
+
+test_expect_success 'default limit for git-archive fails gracefully' '
+	test_must_fail git archive big >/dev/null
+'
+
+test_expect_success 'limit recursion of ls-tree -r' '
+	git $small_ok ls-tree -r small &&
+	test_must_fail git $small_no ls-tree -r small
+'
+
+test_expect_success 'default limit for ls-tree fails gracefully' '
+	test_must_fail git ls-tree -r big >/dev/null
+'
+
+test_expect_success 'limit recursion of rev-list --objects' '
+	git $small_ok rev-list --objects small >/dev/null &&
+	test_must_fail git $small_no rev-list --objects small >/dev/null
+'
+
+test_expect_success 'default limit for rev-list fails gracefully' '
+	test_must_fail git rev-list --objects big >/dev/null
+'
+
+test_expect_success 'limit recursion of diff-tree -r' '
+	git $small_ok diff-tree -r $EMPTY_TREE small &&
+	test_must_fail git $small_no diff-tree -r $EMPTY_TREE small
+'
+
+test_expect_success 'default limit for diff-tree fails gracefully' '
+	test_must_fail git diff-tree -r $EMPTY_TREE big
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index d72cef8..879a6dc 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -4,6 +4,10 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff-data.sh
 
+index_at_path () {
+	git ls-files --format='%(objectmode) %(objectname) %(stage)' "$@"
+}
+
 test_expect_success 'mv -f refreshes updated index entry' '
 	echo test >bar &&
 	git add bar &&
@@ -170,6 +174,13 @@
 	test_must_fail git mv path2 path0
 '
 
+test_expect_success 'rename directory to non-existing directory' '
+	mkdir dir-a &&
+	>dir-a/f &&
+	git add dir-a &&
+	git mv dir-a non-existing-dir
+'
+
 test_expect_success 'move into "."' '
 	git mv path1/path2/ .
 '
@@ -187,7 +198,8 @@
 	git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
 
 	T=$(git write-tree) &&
-	git ls-tree -r $T | verbose grep partA/outline.txt
+	git ls-tree -r $T >out &&
+	grep partA/outline.txt out
 '
 
 rm -fr papers partA path?
@@ -260,12 +272,12 @@
 	git init &&
 	echo 1 >dirty &&
 	git add dirty &&
-	entry="$(git ls-files --stage dirty | cut -f 1)" &&
+	entry="$(index_at_path dirty)" &&
 	git mv dirty dirty2 &&
-	test "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" &&
+	test "$entry" = "$(index_at_path dirty2)" &&
 	echo 2 >dirty2 &&
 	git mv dirty2 dirty &&
-	test "$entry" = "$(git ls-files --stage dirty | cut -f 1)"
+	test "$entry" = "$(index_at_path dirty)"
 '
 
 rm -f dirty dirty2
@@ -284,7 +296,7 @@
 	EOF
 
 	test_must_fail git mv conflict newname 2>actual &&
-	test_i18ngrep "conflicted" actual
+	test_grep "conflicted" actual
 '
 
 test_expect_success 'git mv should overwrite symlink to a file' '
@@ -342,7 +354,7 @@
 '
 
 test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
-	entry="$(git ls-files --stage sub | cut -f 1)" &&
+	entry="$(index_at_path sub)" &&
 	git rm .gitmodules &&
 	(
 		cd sub &&
@@ -353,7 +365,7 @@
 	mkdir mod &&
 	git mv sub mod/sub &&
 	test_path_is_missing sub &&
-	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	test "$entry" = "$(index_at_path mod/sub)" &&
 	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
@@ -363,7 +375,7 @@
 	rm -rf mod &&
 	git reset --hard &&
 	git submodule update &&
-	entry="$(git ls-files --stage sub | cut -f 1)" &&
+	entry="$(index_at_path sub)" &&
 	(
 		cd sub &&
 		rm -f .git &&
@@ -373,7 +385,7 @@
 	mkdir mod &&
 	git mv sub mod/sub &&
 	test_path_is_missing sub &&
-	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	test "$entry" = "$(index_at_path mod/sub)" &&
 	git -C mod/sub status &&
 	echo mod/sub >expected &&
 	git config -f .gitmodules submodule.sub.path >actual &&
@@ -386,11 +398,11 @@
 	rm -rf mod &&
 	git reset --hard &&
 	git submodule update &&
-	entry="$(git ls-files --stage sub | cut -f 1)" &&
+	entry="$(index_at_path sub)" &&
 	mkdir mod &&
 	git -C mod mv ../sub/ . &&
 	test_path_is_missing sub &&
-	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	test "$entry" = "$(index_at_path mod/sub)" &&
 	git -C mod/sub status &&
 	echo mod/sub >expected &&
 	git config -f .gitmodules submodule.sub.path >actual &&
@@ -404,12 +416,12 @@
 	git reset --hard &&
 	git submodule update &&
 	git rm .gitmodules &&
-	entry="$(git ls-files --stage sub | cut -f 1)" &&
+	entry="$(index_at_path sub)" &&
 	mkdir mod &&
 	git mv sub mod/sub 2>actual.err &&
 	test_must_be_empty actual.err &&
 	test_path_is_missing sub &&
-	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	test "$entry" = "$(index_at_path mod/sub)" &&
 	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
@@ -420,7 +432,7 @@
 	git reset --hard &&
 	git submodule update &&
 	git config -f .gitmodules foo.bar true &&
-	entry="$(git ls-files --stage sub | cut -f 1)" &&
+	entry="$(index_at_path sub)" &&
 	mkdir mod &&
 	test_must_fail git mv sub mod/sub 2>actual.err &&
 	test_file_not_empty actual.err &&
@@ -430,7 +442,7 @@
 	git mv sub mod/sub 2>actual.err &&
 	test_must_be_empty actual.err &&
 	test_path_is_missing sub &&
-	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	test "$entry" = "$(index_at_path mod/sub)" &&
 	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
@@ -442,13 +454,13 @@
 	git submodule update &&
 	git config -f .gitmodules --remove-section submodule.sub &&
 	git add .gitmodules &&
-	entry="$(git ls-files --stage sub | cut -f 1)" &&
+	entry="$(index_at_path sub)" &&
 	echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
 	mkdir mod &&
 	git mv sub mod/sub 2>actual.err &&
 	test_cmp expect.err actual.err &&
 	test_path_is_missing sub &&
-	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	test "$entry" = "$(index_at_path mod/sub)" &&
 	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
@@ -470,7 +482,7 @@
 	git mv sub sub2 &&
 	git commit -m "moved sub to sub2" &&
 	git checkout -q HEAD^ 2>actual &&
-	test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
+	test_grep "^warning: unable to rmdir '\''sub2'\'':" actual &&
 	git status -s sub2 >actual &&
 	echo "?? sub2/" >expected &&
 	test_cmp expected actual &&
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index f6aebe9..5ab4d41 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -396,10 +396,7 @@
 	git branch prune-entire B &&
 	git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire &&
 	test_must_fail git rev-parse refs/heads/prune-entire &&
-	if test_have_prereq REFFILES
-	then
-		test_must_fail git reflog exists refs/heads/prune-entire
-	fi
+	test_must_fail git reflog exists refs/heads/prune-entire
 '
 
 test_expect_success '--remap-to-ancestor with filename filters' '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 9aa1660..696866d 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -792,6 +792,34 @@
 	test_cmp expect actual
 '
 
+# Run this before doing any signing, so the test has the same results
+# regardless of the GPG prereq.
+test_expect_success 'git tag --format with ahead-behind' '
+	test_when_finished git reset --hard tag-one-line &&
+	git commit --allow-empty -m "left" &&
+	git tag -a -m left tag-left &&
+	git reset --hard HEAD~1 &&
+	git commit --allow-empty -m "right" &&
+	git tag -a -m left tag-right &&
+
+	# Use " !" at the end to demonstrate whitespace
+	# around empty ahead-behind token for tag-blob.
+	cat >expect <<-EOF &&
+	refs/tags/tag-blob  !
+	refs/tags/tag-left 1 1 !
+	refs/tags/tag-lines 0 1 !
+	refs/tags/tag-one-line 0 1 !
+	refs/tags/tag-right 0 0 !
+	refs/tags/tag-zero-lines 0 1 !
+	EOF
+	git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+	grep "refs/tags/tag" actual >actual.focus &&
+	test_cmp expect actual.focus &&
+
+	# Error reported for tags that point to non-commits.
+	grep "error: object [0-9a-f]* is a blob, not a commit" err
+'
+
 # trying to verify annotated non-signed tags:
 
 test_expect_success GPG \
@@ -1749,10 +1777,10 @@
 '
 
 test_expect_success 'recursive tagging should give advice' '
-	sed -e "s/|$//" <<-EOF >expect &&
+	cat >expect <<-EOF &&
 	hint: You have created a nested tag. The object referred to by your new tag is
 	hint: already a tag. If you meant to tag the object that it points to, use:
-	hint: |
+	hint:
 	hint: 	git tag -f nested annotated-v4.0^{}
 	hint: Disable this message with "git config advice.nestedTag false"
 	EOF
@@ -1834,6 +1862,51 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--no-sort cancels config sort keys' '
+	test_config tag.sort "-refname" &&
+
+	# objecttype is identical for all of them, so sort falls back on
+	# default (ascending refname)
+	git tag -l \
+		--no-sort \
+		--sort="objecttype" \
+		"foo*" >actual &&
+	cat >expect <<-\EOF &&
+	foo1.10
+	foo1.3
+	foo1.6
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+	# objecttype is identical for all of them, so sort falls back on
+	# default (ascending refname)
+	git tag -l \
+		--sort="-refname" \
+		--no-sort \
+		--sort="objecttype" \
+		"foo*" >actual &&
+	cat >expect <<-\EOF &&
+	foo1.10
+	foo1.3
+	foo1.6
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected tags' '
+	# Sort the results with `sort` for a consistent comparison against
+	# expected
+	git tag -l --no-sort "foo*" | sort >actual &&
+	cat >expect <<-\EOF &&
+	foo1.10
+	foo1.3
+	foo1.6
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'invalid sort parameter on command line' '
 	test_must_fail git tag -l --sort=notvalid "foo*" >actual
 '
@@ -1843,6 +1916,23 @@
 	test_must_fail git tag -l "foo*"
 '
 
+test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
+	cp .git/config .git/config.orig &&
+	test_when_finished mv .git/config.orig .git/config &&
+
+	cat >>.git/config <<-\EOF &&
+	[versionsort]
+		prereleaseSuffix
+		suffix
+	EOF
+	cat >expect <<-\EOF &&
+	error: missing value for '\''versionsort.suffix'\''
+	error: missing value for '\''versionsort.prereleasesuffix'\''
+	EOF
+	git tag -l --sort=version:refname 2>actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'version sort with prerelease reordering' '
 	test_config versionsort.prereleaseSuffix -rc &&
 	git tag foo1.6-rc1 &&
@@ -2001,6 +2091,22 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--format --omit-empty works' '
+	cat >expect <<-\EOF &&
+	refname : refs/tags/v1.0
+
+	refname : refs/tags/v1.1.3
+	EOF
+	git tag -l --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF &&
+	refname : refs/tags/v1.0
+	refname : refs/tags/v1.1.3
+	EOF
+	git tag -l --omit-empty --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git tag -l with --format="%(rest)" must fail' '
 	test_must_fail git tag -l --format="%(rest)" "v1*"
 '
@@ -2127,4 +2233,23 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'If tag is created then tag message file is unlinked' '
+	test_when_finished "git tag -d foo" &&
+	write_script fakeeditor <<-\EOF &&
+	echo Message >.git/TAG_EDITMSG
+	EOF
+	GIT_EDITOR=./fakeeditor git tag -a foo &&
+	test_path_is_missing .git/TAG_EDITMSG
+'
+
+test_expect_success 'If tag cannot be created then tag message file is not unlinked' '
+	test_when_finished "git tag -d foo/bar && rm .git/TAG_EDITMSG" &&
+	write_script fakeeditor <<-\EOF &&
+	echo Message >.git/TAG_EDITMSG
+	EOF
+	git tag foo/bar &&
+	test_must_fail env GIT_EDITOR=./fakeeditor git tag -a foo &&
+	test_path_exists .git/TAG_EDITMSG
+'
+
 test_done
diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh
index 36eb86a..20913b3 100755
--- a/t/t7031-verify-tag-signed-ssh.sh
+++ b/t/t7031-verify-tag-signed-ssh.sh
@@ -200,4 +200,14 @@
 	test_must_be_empty actual-forged
 '
 
+test_expect_success GPGSSH 'rev-list --format=%G' '
+	test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+	git rev-list -1 --format="%G? %H" sixth-signed >actual &&
+	cat >expect <<-EOF &&
+	commit $(git rev-parse sixth-signed^0)
+	G $(git rev-parse sixth-signed^0)
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7101-reset-empty-subdirs.sh b/t/t7101-reset-empty-subdirs.sh
index 638bb04..89cf98b 100755
--- a/t/t7101-reset-empty-subdirs.sh
+++ b/t/t7101-reset-empty-subdirs.sh
@@ -10,57 +10,57 @@
 . "$TEST_DIRECTORY"/lib-diff-data.sh
 
 test_expect_success 'creating initial files' '
-     mkdir path0 &&
-     COPYING_test_data >path0/COPYING &&
-     git add path0/COPYING &&
-     git commit -m add -a
+	mkdir path0 &&
+	COPYING_test_data >path0/COPYING &&
+	git add path0/COPYING &&
+	git commit -m add -a
 '
 
 test_expect_success 'creating second files' '
-     mkdir path1 &&
-     mkdir path1/path2 &&
-     COPYING_test_data >path1/path2/COPYING &&
-     COPYING_test_data >path1/COPYING &&
-     COPYING_test_data >COPYING &&
-     COPYING_test_data >path0/COPYING-TOO &&
-     git add path1/path2/COPYING &&
-     git add path1/COPYING &&
-     git add COPYING &&
-     git add path0/COPYING-TOO &&
-     git commit -m change -a
+	mkdir path1 &&
+	mkdir path1/path2 &&
+	COPYING_test_data >path1/path2/COPYING &&
+	COPYING_test_data >path1/COPYING &&
+	COPYING_test_data >COPYING &&
+	COPYING_test_data >path0/COPYING-TOO &&
+	git add path1/path2/COPYING &&
+	git add path1/COPYING &&
+	git add COPYING &&
+	git add path0/COPYING-TOO &&
+	git commit -m change -a
 '
 
 test_expect_success 'resetting tree HEAD^' '
-     git reset --hard HEAD^
+	git reset --hard HEAD^
 '
 
 test_expect_success 'checking initial files exist after rewind' '
-     test -d path0 &&
-     test -f path0/COPYING
+	test -d path0 &&
+	test -f path0/COPYING
 '
 
 test_expect_success 'checking lack of path1/path2/COPYING' '
-    ! test -f path1/path2/COPYING
+	! test -f path1/path2/COPYING
 '
 
 test_expect_success 'checking lack of path1/COPYING' '
-    ! test -f path1/COPYING
+	! test -f path1/COPYING
 '
 
 test_expect_success 'checking lack of COPYING' '
-     ! test -f COPYING
+	! test -f COPYING
 '
 
 test_expect_success 'checking checking lack of path1/COPYING-TOO' '
-     ! test -f path0/COPYING-TOO
+	! test -f path0/COPYING-TOO
 '
 
 test_expect_success 'checking lack of path1/path2' '
-     ! test -d path1/path2
+	! test -d path1/path2
 '
 
 test_expect_success 'checking lack of path1' '
-     ! test -d path1
+	! test -d path1
 '
 
 test_done
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 22477f3..62d9f84 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -71,6 +71,16 @@
 	done | test_cmp .cat_expect -
 }
 
+# no negated form for various type of resets
+for opt in soft mixed hard merge keep
+do
+	test_expect_success "no 'git reset --no-$opt'" '
+		test_when_finished "rm -f err" &&
+		test_must_fail git reset --no-$opt 2>err &&
+		grep "error: unknown option .no-$opt." err
+	'
+done
+
 test_expect_success 'reset --hard message' '
 	hex=$(git log -1 --format="%h") &&
 	git reset --hard >.actual &&
@@ -606,4 +616,12 @@
 	test_must_be_empty actual
 '
 
+test_expect_success 'reset handles --end-of-options' '
+	git update-ref refs/heads/--foo HEAD^ &&
+	git log -1 --format=%s refs/heads/--foo >expect &&
+	git reset --hard --end-of-options --foo &&
+	git log -1 --format=%s HEAD >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index 9b46da7..f4f3b7a 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -5,7 +5,7 @@
 TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
 	mkdir dir &&
 	echo parent > dir/foo &&
 	echo dummy > bar &&
@@ -19,42 +19,46 @@
 
 # note: bar sorts before foo, so the first 'n' is always to skip 'bar'
 
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
 	set_and_save_state dir/foo work work &&
 	test_write_lines n n | git reset -p &&
 	verify_saved_state dir/foo &&
 	verify_saved_state bar
 '
 
-test_expect_success PERL 'git reset -p' '
-	test_write_lines n y | git reset -p >output &&
-	verify_state dir/foo work head &&
-	verify_saved_state bar &&
-	test_i18ngrep "Unstage" output
-'
+for opt in "HEAD" "@" ""
+do
+	test_expect_success "git reset -p $opt" '
+		set_and_save_state dir/foo work work &&
+		test_write_lines n y | git reset -p $opt >output &&
+		verify_state dir/foo work head &&
+		verify_saved_state bar &&
+		test_grep "Unstage" output
+	'
+done
 
-test_expect_success PERL 'git reset -p HEAD^' '
+test_expect_success 'git reset -p HEAD^' '
 	test_write_lines n y | git reset -p HEAD^ >output &&
 	verify_state dir/foo work parent &&
 	verify_saved_state bar &&
-	test_i18ngrep "Apply" output
+	test_grep "Apply" output
 '
 
-test_expect_success PERL 'git reset -p HEAD^^{tree}' '
+test_expect_success 'git reset -p HEAD^^{tree}' '
 	test_write_lines n y | git reset -p HEAD^^{tree} >output &&
 	verify_state dir/foo work parent &&
 	verify_saved_state bar &&
-	test_i18ngrep "Apply" output
+	test_grep "Apply" output
 '
 
-test_expect_success PERL 'git reset -p HEAD^:dir/foo (blob fails)' '
+test_expect_success 'git reset -p HEAD^:dir/foo (blob fails)' '
 	set_and_save_state dir/foo work work &&
 	test_must_fail git reset -p HEAD^:dir/foo &&
 	verify_saved_state dir/foo &&
 	verify_saved_state bar
 '
 
-test_expect_success PERL 'git reset -p aaaaaaaa (unknown fails)' '
+test_expect_success 'git reset -p aaaaaaaa (unknown fails)' '
 	set_and_save_state dir/foo work work &&
 	test_must_fail git reset -p aaaaaaaa &&
 	verify_saved_state dir/foo &&
@@ -66,27 +70,27 @@
 # dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
 # the failure case (and thus get out of the loop).
 
-test_expect_success PERL 'git reset -p dir' '
+test_expect_success 'git reset -p dir' '
 	set_state dir/foo work work &&
 	test_write_lines y n | git reset -p dir &&
 	verify_state dir/foo work head &&
 	verify_saved_state bar
 '
 
-test_expect_success PERL 'git reset -p -- foo (inside dir)' '
+test_expect_success 'git reset -p -- foo (inside dir)' '
 	set_state dir/foo work work &&
 	test_write_lines y n | (cd dir && git reset -p -- foo) &&
 	verify_state dir/foo work head &&
 	verify_saved_state bar
 '
 
-test_expect_success PERL 'git reset -p HEAD^ -- dir' '
+test_expect_success 'git reset -p HEAD^ -- dir' '
 	test_write_lines y n | git reset -p HEAD^ -- dir &&
 	verify_state dir/foo work parent &&
 	verify_saved_state bar
 '
 
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
 	verify_saved_head
 '
 
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
index a0b67a0..88d1c8a 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -34,7 +34,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success PERL 'reset -p' '
+test_expect_success 'reset -p' '
 	rm .git/index &&
 	git add a &&
 	echo y >yes &&
@@ -42,7 +42,7 @@
 
 	git ls-files >actual &&
 	test_must_be_empty actual &&
-	test_i18ngrep "Unstage" output
+	test_grep "Unstage" output
 '
 
 test_expect_success 'reset --soft is a no-op' '
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
index af5ea40..020db20 100755
--- a/t/t7107-reset-pathspec-file.sh
+++ b/t/t7107-reset-pathspec-file.sh
@@ -161,19 +161,19 @@
 	git rm fileA.t &&
 
 	test_must_fail git reset --pathspec-from-file=list --patch 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
 	test_must_fail git reset --pathspec-from-file=list -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git reset --pathspec-file-nul 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
 	test_must_fail git reset --soft --pathspec-from-file=list 2>err &&
-	test_i18ngrep -e "fatal: Cannot do soft reset with paths" err &&
+	test_grep -e "fatal: Cannot do soft reset with paths" err &&
 
 	test_must_fail git reset --hard --pathspec-from-file=list 2>err &&
-	test_i18ngrep -e "fatal: Cannot do hard reset with paths" err
+	test_grep -e "fatal: Cannot do hard reset with paths" err
 '
 
 test_done
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
index eb881be..7ee180f 100755
--- a/t/t7110-reset-merge.sh
+++ b/t/t7110-reset-merge.sh
@@ -9,17 +9,17 @@
 . ./test-lib.sh
 
 test_expect_success setup '
-    printf "line %d\n" 1 2 3 >file1 &&
-    cat file1 >file2 &&
-    git add file1 file2 &&
-    test_tick &&
-    git commit -m "Initial commit" &&
-    git tag initial &&
-    echo line 4 >>file1 &&
-    cat file1 >file2 &&
-    test_tick &&
-    git commit -m "add line 4 to file1" file1 &&
-    git tag second
+	printf "line %d\n" 1 2 3 >file1 &&
+	cat file1 >file2 &&
+	git add file1 file2 &&
+	test_tick &&
+	git commit -m "Initial commit" &&
+	git tag initial &&
+	echo line 4 >>file1 &&
+	cat file1 >file2 &&
+	test_tick &&
+	git commit -m "add line 4 to file1" file1 &&
+	git tag second
 '
 
 # The next test will test the following:
@@ -29,19 +29,19 @@
 # file1:     C       C     C    D     --merge  D       D     D
 # file2:     C       D     D    D     --merge  C       D     D
 test_expect_success 'reset --merge is ok with changes in file it does not touch' '
-    git reset --merge HEAD^ &&
-    ! grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+	git reset --merge HEAD^ &&
+	! grep 4 file1 &&
+	grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+	test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --merge is ok when switching back' '
-    git reset --merge second &&
-    grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+	git reset --merge second &&
+	grep 4 file1 &&
+	grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+	test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -51,21 +51,21 @@
 # file1:     C       C     C    D     --keep   D       D     D
 # file2:     C       D     D    D     --keep   C       D     D
 test_expect_success 'reset --keep is ok with changes in file it does not touch' '
-    git reset --hard second &&
-    cat file1 >file2 &&
-    git reset --keep HEAD^ &&
-    ! grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+	git reset --hard second &&
+	cat file1 >file2 &&
+	git reset --keep HEAD^ &&
+	! grep 4 file1 &&
+	grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+	test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --keep is ok when switching back' '
-    git reset --keep second &&
-    grep 4 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+	git reset --keep second &&
+	grep 4 file1 &&
+	grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+	test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -75,28 +75,28 @@
 # file1:     B       B     C    D     --merge  D       D     D
 # file2:     C       D     D    D     --merge  C       D     D
 test_expect_success 'reset --merge discards changes added to index (1)' '
-    git reset --hard second &&
-    cat file1 >file2 &&
-    echo "line 5" >> file1 &&
-    git add file1 &&
-    git reset --merge HEAD^ &&
-    ! grep 4 file1 &&
-    ! grep 5 file1 &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+	git reset --hard second &&
+	cat file1 >file2 &&
+	echo "line 5" >> file1 &&
+	git add file1 &&
+	git reset --merge HEAD^ &&
+	! grep 4 file1 &&
+	! grep 5 file1 &&
+	grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+	test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --merge is ok again when switching back (1)' '
-    git reset --hard initial &&
-    echo "line 5" >> file2 &&
-    git add file2 &&
-    git reset --merge second &&
-    ! grep 4 file2 &&
-    ! grep 5 file1 &&
-    grep 4 file1 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+	git reset --hard initial &&
+	echo "line 5" >> file2 &&
+	git add file2 &&
+	git reset --merge second &&
+	! grep 4 file2 &&
+	! grep 5 file1 &&
+	grep 4 file1 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+	test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -105,10 +105,10 @@
 #           ----------------------------------------------------
 # file1:     B       B     C    D     --keep   (disallowed)
 test_expect_success 'reset --keep fails with changes in index in files it touches' '
-    git reset --hard second &&
-    echo "line 5" >> file1 &&
-    git add file1 &&
-    test_must_fail git reset --keep HEAD^
+	git reset --hard second &&
+	echo "line 5" >> file1 &&
+	git add file1 &&
+	test_must_fail git reset --keep HEAD^
 '
 
 # The next test will test the following:
@@ -118,23 +118,23 @@
 # file1:     C       C     C    D     --merge  D       D     D
 # file2:     C       C     D    D     --merge  D       D     D
 test_expect_success 'reset --merge discards changes added to index (2)' '
-    git reset --hard second &&
-    echo "line 4" >> file2 &&
-    git add file2 &&
-    git reset --merge HEAD^ &&
-    ! grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff)" &&
-    test -z "$(git diff --cached)"
+	git reset --hard second &&
+	echo "line 4" >> file2 &&
+	git add file2 &&
+	git reset --merge HEAD^ &&
+	! grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+	test -z "$(git diff)" &&
+	test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --merge is ok again when switching back (2)' '
-    git reset --hard initial &&
-    git reset --merge second &&
-    ! grep 4 file2 &&
-    grep 4 file1 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+	git reset --hard initial &&
+	git reset --merge second &&
+	! grep 4 file2 &&
+	grep 4 file1 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+	test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -144,21 +144,21 @@
 # file1:     C       C     C    D     --keep   D       D     D
 # file2:     C       C     D    D     --keep   C       D     D
 test_expect_success 'reset --keep keeps changes it does not touch' '
-    git reset --hard second &&
-    echo "line 4" >> file2 &&
-    git add file2 &&
-    git reset --keep HEAD^ &&
-    grep 4 file2 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
-    test -z "$(git diff --cached)"
+	git reset --hard second &&
+	echo "line 4" >> file2 &&
+	git add file2 &&
+	git reset --keep HEAD^ &&
+	grep 4 file2 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+	test -z "$(git diff --cached)"
 '
 
 test_expect_success 'reset --keep keeps changes when switching back' '
-    git reset --keep second &&
-    grep 4 file2 &&
-    grep 4 file1 &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)"
+	git reset --keep second &&
+	grep 4 file2 &&
+	grep 4 file1 &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+	test -z "$(git diff --cached)"
 '
 
 # The next test will test the following:
@@ -167,14 +167,14 @@
 #           ----------------------------------------------------
 # file1:     A       B     B    C     --merge  (disallowed)
 test_expect_success 'reset --merge fails with changes in file it touches' '
-    git reset --hard second &&
-    echo "line 5" >> file1 &&
-    test_tick &&
-    git commit -m "add line 5" file1 &&
-    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
-    mv file3 file1 &&
-    test_must_fail git reset --merge HEAD^ 2>err.log &&
-    grep file1 err.log | grep "not uptodate"
+	git reset --hard second &&
+	echo "line 5" >> file1 &&
+	test_tick &&
+	git commit -m "add line 5" file1 &&
+	sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+	mv file3 file1 &&
+	test_must_fail git reset --merge HEAD^ 2>err.log &&
+	grep file1 err.log | grep "not uptodate"
 '
 
 # The next test will test the following:
@@ -183,36 +183,36 @@
 #           ----------------------------------------------------
 # file1:     A       B     B    C     --keep   (disallowed)
 test_expect_success 'reset --keep fails with changes in file it touches' '
-    git reset --hard second &&
-    echo "line 5" >> file1 &&
-    test_tick &&
-    git commit -m "add line 5" file1 &&
-    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
-    mv file3 file1 &&
-    test_must_fail git reset --keep HEAD^ 2>err.log &&
-    grep file1 err.log | grep "not uptodate"
+	git reset --hard second &&
+	echo "line 5" >> file1 &&
+	test_tick &&
+	git commit -m "add line 5" file1 &&
+	sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+	mv file3 file1 &&
+	test_must_fail git reset --keep HEAD^ 2>err.log &&
+	grep file1 err.log | grep "not uptodate"
 '
 
 test_expect_success 'setup 3 different branches' '
-    git reset --hard second &&
-    git branch branch1 &&
-    git branch branch2 &&
-    git branch branch3 &&
-    git checkout branch1 &&
-    echo "line 5 in branch1" >> file1 &&
-    test_tick &&
-    git commit -a -m "change in branch1" &&
-    git checkout branch2 &&
-    echo "line 5 in branch2" >> file1 &&
-    test_tick &&
-    git commit -a -m "change in branch2" &&
-    git tag third &&
-    git checkout branch3 &&
-    echo a new file >file3 &&
-    rm -f file1 &&
-    git add file3 &&
-    test_tick &&
-    git commit -a -m "change in branch3"
+	git reset --hard second &&
+	git branch branch1 &&
+	git branch branch2 &&
+	git branch branch3 &&
+	git checkout branch1 &&
+	echo "line 5 in branch1" >> file1 &&
+	test_tick &&
+	git commit -a -m "change in branch1" &&
+	git checkout branch2 &&
+	echo "line 5 in branch2" >> file1 &&
+	test_tick &&
+	git commit -a -m "change in branch2" &&
+	git tag third &&
+	git checkout branch3 &&
+	echo a new file >file3 &&
+	rm -f file1 &&
+	git add file3 &&
+	test_tick &&
+	git commit -a -m "change in branch3"
 '
 
 # The next test will test the following:
@@ -221,12 +221,12 @@
 #           ----------------------------------------------------
 # file1:     X       U     B    C     --merge  C       C     C
 test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
-    git checkout third &&
-    test_must_fail git merge branch1 &&
-    git reset --merge HEAD^ &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
-    test -z "$(git diff --cached)" &&
-    test -z "$(git diff)"
+	git checkout third &&
+	test_must_fail git merge branch1 &&
+	git reset --merge HEAD^ &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+	test -z "$(git diff --cached)" &&
+	test -z "$(git diff)"
 '
 
 # The next test will test the following:
@@ -235,10 +235,10 @@
 #           ----------------------------------------------------
 # file1:     X       U     B    C     --keep   (disallowed)
 test_expect_success '"reset --keep HEAD^" fails with pending merge' '
-    git reset --hard third &&
-    test_must_fail git merge branch1 &&
-    test_must_fail git reset --keep HEAD^ 2>err.log &&
-    test_i18ngrep "middle of a merge" err.log
+	git reset --hard third &&
+	test_must_fail git merge branch1 &&
+	test_must_fail git reset --keep HEAD^ 2>err.log &&
+	test_grep "middle of a merge" err.log
 '
 
 # The next test will test the following:
@@ -247,12 +247,12 @@
 #           ----------------------------------------------------
 # file1:     X       U     B    B     --merge  B       B     B
 test_expect_success '"reset --merge HEAD" is ok with pending merge' '
-    git reset --hard third &&
-    test_must_fail git merge branch1 &&
-    git reset --merge HEAD &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
-    test -z "$(git diff --cached)" &&
-    test -z "$(git diff)"
+	git reset --hard third &&
+	test_must_fail git merge branch1 &&
+	git reset --merge HEAD &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
+	test -z "$(git diff --cached)" &&
+	test -z "$(git diff)"
 '
 
 # The next test will test the following:
@@ -261,36 +261,36 @@
 #           ----------------------------------------------------
 # file1:     X       U     B    B     --keep   (disallowed)
 test_expect_success '"reset --keep HEAD" fails with pending merge' '
-    git reset --hard third &&
-    test_must_fail git merge branch1 &&
-    test_must_fail git reset --keep HEAD 2>err.log &&
-    test_i18ngrep "middle of a merge" err.log
+	git reset --hard third &&
+	test_must_fail git merge branch1 &&
+	test_must_fail git reset --keep HEAD 2>err.log &&
+	test_grep "middle of a merge" err.log
 '
 
 test_expect_success '--merge is ok with added/deleted merge' '
-    git reset --hard third &&
-    rm -f file2 &&
-    test_must_fail git merge branch3 &&
-    ! test -f file2 &&
-    test -f file3 &&
-    git diff --exit-code file3 &&
-    git diff --exit-code branch3 file3 &&
-    git reset --merge HEAD &&
-    ! test -f file3 &&
-    ! test -f file2 &&
-    git diff --exit-code --cached
+	git reset --hard third &&
+	rm -f file2 &&
+	test_must_fail git merge branch3 &&
+	! test -f file2 &&
+	test -f file3 &&
+	git diff --exit-code file3 &&
+	git diff --exit-code branch3 file3 &&
+	git reset --merge HEAD &&
+	! test -f file3 &&
+	! test -f file2 &&
+	git diff --exit-code --cached
 '
 
 test_expect_success '--keep fails with added/deleted merge' '
-    git reset --hard third &&
-    rm -f file2 &&
-    test_must_fail git merge branch3 &&
-    ! test -f file2 &&
-    test -f file3 &&
-    git diff --exit-code file3 &&
-    git diff --exit-code branch3 file3 &&
-    test_must_fail git reset --keep HEAD 2>err.log &&
-    test_i18ngrep "middle of a merge" err.log
+	git reset --hard third &&
+	rm -f file2 &&
+	test_must_fail git merge branch3 &&
+	! test -f file2 &&
+	test -f file3 &&
+	git diff --exit-code file3 &&
+	git diff --exit-code branch3 file3 &&
+	test_must_fail git reset --keep HEAD 2>err.log &&
+	test_grep "middle of a merge" err.log
 '
 
 test_done
diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh
index 78f25c1..01b7c35 100755
--- a/t/t7111-reset-table.sh
+++ b/t/t7111-reset-table.sh
@@ -10,9 +10,9 @@
 
 
 test_expect_success 'creating initial commits' '
-    test_commit E file1 &&
-    test_commit D file1 &&
-    test_commit C file1
+	test_commit E file1 &&
+	test_commit D file1 &&
+	test_commit C file1
 '
 
 while read W1 I1 H1 T opt W2 I2 H2
@@ -74,13 +74,13 @@
 EOF
 
 test_expect_success 'setting up branches to test with unmerged entries' '
-    git reset --hard C &&
-    git branch branch1 &&
-    git branch branch2 &&
-    git checkout branch1 &&
-    test_commit B1 file1 &&
-    git checkout branch2 &&
-    test_commit B file1
+	git reset --hard C &&
+	git branch branch1 &&
+	git branch branch2 &&
+	git checkout branch1 &&
+	test_commit B1 file1 &&
+	git checkout branch2 &&
+	test_commit B file1
 '
 
 while read W1 I1 H1 T opt W2 I2 H2
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 61ad47b..42352dc 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -217,7 +217,7 @@
 	git rm two &&
 
 	test_must_fail git checkout simple 2>errs &&
-	test_i18ngrep overwritten errs &&
+	test_grep overwritten errs &&
 
 	test_must_fail git read-tree --quiet -m -u HEAD simple 2>errs &&
 	test_must_be_empty errs
@@ -229,7 +229,7 @@
 	git checkout -f renamer &&
 	git clean -f &&
 	git checkout renamer^ 2>messages &&
-	test_i18ngrep "HEAD is now at $rev" messages &&
+	test_grep "HEAD is now at $rev" messages &&
 	test_line_count = 1 messages &&
 	H=$(git rev-parse --verify HEAD) &&
 	M=$(git show-ref -s --verify refs/heads/main) &&
@@ -372,75 +372,75 @@
 '
 
 test_expect_success 'checkout w/--track sets up tracking' '
-    git config branch.autosetupmerge false &&
-    git checkout main &&
-    git checkout --track -b track1 &&
-    test "$(git config branch.track1.remote)" &&
-    test "$(git config branch.track1.merge)"
+	git config branch.autosetupmerge false &&
+	git checkout main &&
+	git checkout --track -b track1 &&
+	test "$(git config branch.track1.remote)" &&
+	test "$(git config branch.track1.merge)"
 '
 
 test_expect_success 'checkout w/autosetupmerge=always sets up tracking' '
-    test_when_finished git config branch.autosetupmerge false &&
-    git config branch.autosetupmerge always &&
-    git checkout main &&
-    git checkout -b track2 &&
-    test "$(git config branch.track2.remote)" &&
-    test "$(git config branch.track2.merge)"
+	test_when_finished git config branch.autosetupmerge false &&
+	git config branch.autosetupmerge always &&
+	git checkout main &&
+	git checkout -b track2 &&
+	test "$(git config branch.track2.remote)" &&
+	test "$(git config branch.track2.merge)"
 '
 
 test_expect_success 'checkout w/--track from non-branch HEAD fails' '
-    git checkout main^0 &&
-    test_must_fail git symbolic-ref HEAD &&
-    test_must_fail git checkout --track -b track &&
-    test_must_fail git rev-parse --verify track &&
-    test_must_fail git symbolic-ref HEAD &&
-    test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
+	git checkout main^0 &&
+	test_must_fail git symbolic-ref HEAD &&
+	test_must_fail git checkout --track -b track &&
+	test_must_fail git rev-parse --verify track &&
+	test_must_fail git symbolic-ref HEAD &&
+	test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
 '
 
 test_expect_success 'checkout w/--track from tag fails' '
-    git checkout main^0 &&
-    test_must_fail git symbolic-ref HEAD &&
-    test_must_fail git checkout --track -b track frotz &&
-    test_must_fail git rev-parse --verify track &&
-    test_must_fail git symbolic-ref HEAD &&
-    test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
+	git checkout main^0 &&
+	test_must_fail git symbolic-ref HEAD &&
+	test_must_fail git checkout --track -b track frotz &&
+	test_must_fail git rev-parse --verify track &&
+	test_must_fail git symbolic-ref HEAD &&
+	test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
 '
 
 test_expect_success 'detach a symbolic link HEAD' '
-    git checkout main &&
-    git config --bool core.prefersymlinkrefs yes &&
-    git checkout side &&
-    git checkout main &&
-    it=$(git symbolic-ref HEAD) &&
-    test "z$it" = zrefs/heads/main &&
-    here=$(git rev-parse --verify refs/heads/main) &&
-    git checkout side^ &&
-    test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
+	git checkout main &&
+	git config --bool core.prefersymlinkrefs yes &&
+	git checkout side &&
+	git checkout main &&
+	it=$(git symbolic-ref HEAD) &&
+	test "z$it" = zrefs/heads/main &&
+	here=$(git rev-parse --verify refs/heads/main) &&
+	git checkout side^ &&
+	test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
 '
 
 test_expect_success 'checkout with --track fakes a sensible -b <name>' '
-    git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
-    git update-ref refs/remotes/origin/koala/bear renamer &&
+	git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
+	git update-ref refs/remotes/origin/koala/bear renamer &&
 
-    git checkout --track origin/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+	git checkout --track origin/koala/bear &&
+	test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 
-    git checkout main && git branch -D koala/bear &&
+	git checkout main && git branch -D koala/bear &&
 
-    git checkout --track refs/remotes/origin/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+	git checkout --track refs/remotes/origin/koala/bear &&
+	test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 
-    git checkout main && git branch -D koala/bear &&
+	git checkout main && git branch -D koala/bear &&
 
-    git checkout --track remotes/origin/koala/bear &&
-    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
-    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+	git checkout --track remotes/origin/koala/bear &&
+	test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 '
 
 test_expect_success 'checkout with --track, but without -b, fails with too short tracked name' '
-    test_must_fail git checkout --track renamer
+	test_must_fail git checkout --track renamer
 '
 
 setup_conflicting_index () {
@@ -497,6 +497,11 @@
 	test ztheirside = "z$(cat file)"
 '
 
+test_expect_success 'checkout path with --merge from tree-ish is a no-no' '
+	setup_conflicting_index &&
+	test_must_fail git checkout -m HEAD -- file
+'
+
 test_expect_success 'checkout with --merge' '
 	setup_conflicting_index &&
 	echo "none of the above" >sample &&
@@ -517,6 +522,48 @@
 	test_cmp merged file
 '
 
+test_expect_success 'checkout -m works after (mistaken) resolution' '
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	# resolve to something
+	git add file &&
+	git checkout --merge -- fild file filf &&
+	{
+		echo "<<<<<<< ours" &&
+		echo ourside &&
+		echo "=======" &&
+		echo theirside &&
+		echo ">>>>>>> theirs"
+	} >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
+test_expect_success 'checkout -m works after (mistaken) resolution to remove' '
+	setup_conflicting_index &&
+	echo "none of the above" >sample &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	# resolve to remove
+	git rm file &&
+	git checkout --merge -- fild file filf &&
+	{
+		echo "<<<<<<< ours" &&
+		echo ourside &&
+		echo "=======" &&
+		echo theirside &&
+		echo ">>>>>>> theirs"
+	} >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
 test_expect_success 'checkout with --merge, in diff3 -m style' '
 	git config merge.conflictstyle diff3 &&
 	setup_conflicting_index &&
@@ -584,6 +631,72 @@
 	test_cmp merged file
 '
 
+test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' '
+	setup_conflicting_index &&
+	echo "none of the above" >expect &&
+	cat expect >fild &&
+	cat expect >file &&
+	test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err &&
+	test_cmp expect file &&
+	test_cmp expect fild &&
+	echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+	test_cmp expect err
+'
+
+test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' '
+	setup_conflicting_index &&
+	echo "none of the above" >expect &&
+	cat expect >fild &&
+	cat expect >file &&
+	test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err &&
+	test_cmp expect file &&
+	test_cmp expect fild &&
+	echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+	test_cmp expect err
+'
+
+test_expect_success 'checkout --no-merge --conflict=diff3 does merge' '
+	setup_conflicting_index &&
+	echo "none of the above" >fild &&
+	echo "none of the above" >file &&
+	git checkout --no-merge --conflict=diff3 -- fild file &&
+	echo "ourside" >expect &&
+	test_cmp expect fild &&
+	cat >expect <<-\EOF &&
+	<<<<<<< ours
+	ourside
+	||||||| base
+	original
+	=======
+	theirside
+	>>>>>>> theirs
+	EOF
+	test_cmp expect file
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' '
+	setup_conflicting_index &&
+	echo "none of the above" >fild &&
+	echo "none of the above" >file &&
+	git checkout --merge --conflict=diff3 --no-conflict -- fild file &&
+	echo "ourside" >expect &&
+	test_cmp expect fild &&
+	cat >expect <<-\EOF &&
+	<<<<<<< ours
+	ourside
+	=======
+	theirside
+	>>>>>>> theirs
+	EOF
+	test_cmp expect file
+'
+
+test_expect_success 'checkout with invalid conflict style' '
+	test_must_fail git checkout --conflict=bad 2>actual -- file &&
+	echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success 'failing checkout -b should not break working tree' '
 	git clean -fd &&  # Remove untracked files in the way
 	git reset --hard main &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 529fc53..0aae0de 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -121,7 +121,7 @@
 		grep part3 |
 		sed -n -e "s|^Would remove ||p"
 	) &&
-	verbose test "$would_clean" = ../src/part3.c
+	test "$would_clean" = ../src/part3.c
 '
 
 test_expect_success 'git clean with absolute path' '
@@ -134,7 +134,7 @@
 		grep part3 |
 		sed -n -e "s|^Would remove ||p"
 	) &&
-	verbose test "$would_clean" = ../src/part3.c
+	test "$would_clean" = ../src/part3.c
 '
 
 test_expect_success 'git clean with out of work tree relative path' '
@@ -408,6 +408,12 @@
 
 '
 
+test_expect_success 'clean.requireForce and --interactive' '
+	git clean --interactive </dev/null >output 2>error &&
+	test_grep ! "requireForce is true and" error &&
+	test_grep "\*\*\* Commands \*\*\*" output
+'
+
 test_expect_success 'core.excludesfile' '
 
 	echo excludes >excludes &&
@@ -518,8 +524,12 @@
 	git init empty_repo &&
 	mkdir to_clean &&
 	>to_clean/should_clean.this &&
+	# Note that we put the expect file in the .git directory so that it
+	# does not get cleaned.
+	find empty_repo | sort >.git/expect &&
 	git clean -f -d &&
-	test_path_is_file empty_repo/.git/HEAD &&
+	find empty_repo | sort >actual &&
+	test_cmp .git/expect actual &&
 	test_path_is_missing to_clean
 '
 
@@ -560,10 +570,10 @@
 		mkdir -p bar/baz &&
 		test_commit msg bar/baz/hello.world
 	) &&
+	find repo | sort >expect &&
 	git clean -f -d repo/bar/baz &&
-	test_path_is_file repo/.git/HEAD &&
-	test_path_is_dir repo/bar/ &&
-	test_path_is_file repo/bar/baz/hello.world
+	find repo | sort >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'giving path to nested .git will not remove it' '
@@ -574,10 +584,10 @@
 		git init &&
 		test_commit msg hello.world
 	) &&
+	find repo | sort >expect &&
 	git clean -f -d repo/.git &&
-	test_path_is_file repo/.git/HEAD &&
-	test_path_is_dir repo/.git/refs &&
-	test_path_is_dir repo/.git/objects &&
+	find repo | sort >actual &&
+	test_cmp expect actual &&
 	test_path_is_dir untracked/
 '
 
@@ -589,9 +599,10 @@
 		git init &&
 		test_commit msg hello.world
 	) &&
+	find repo | sort >expect &&
 	git clean -f -d repo/.git/ &&
-	test_path_is_dir repo/.git &&
-	test_path_is_file repo/.git/HEAD &&
+	find repo | sort >actual &&
+	test_cmp expect actual &&
 	test_path_is_dir untracked/
 '
 
@@ -736,7 +747,7 @@
 	test_must_fail git clean -xdf 2>.git/err &&
 	# grepping for a strerror string is unportable but it is OK here with
 	# MINGW prereq
-	test_i18ngrep "too long" .git/err
+	test_grep "too long" .git/err
 '
 
 test_expect_success 'clean untracked paths by pathspec' '
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index d82a321..4afe53c 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -25,18 +25,18 @@
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
 	echo c | git clean -i &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test ! -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_missing src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -46,18 +46,18 @@
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
 	echo cl | git clean -i &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test ! -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_missing src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -67,18 +67,18 @@
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
 	echo quit | git clean -i &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -88,18 +88,18 @@
 	touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
 	docs/manual.txt obj.o build/lib.so &&
 	echo "\04" | git clean -i &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -110,18 +110,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines f "*" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -132,18 +132,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines f "part3.* *.out" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test ! -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test ! -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_missing src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -154,18 +154,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines f "* !*.out" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -176,18 +176,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "*" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test ! -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test ! -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_missing src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -198,18 +198,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -220,18 +220,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s 3 "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -242,18 +242,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "2 3" 5 "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test ! -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -264,18 +264,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "3,4 5" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -285,11 +285,11 @@
 	touch a.out foo.txt bar.txt baz.txt &&
 	test_write_lines s "a.out fo ba bar" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test ! -f a.out &&
-	test ! -f foo.txt &&
-	test ! -f bar.txt &&
-	test -f baz.txt &&
+	test_path_is_file Makefile &&
+	test_path_is_missing a.out &&
+	test_path_is_missing foo.txt &&
+	test_path_is_missing bar.txt &&
+	test_path_is_file baz.txt &&
 	rm baz.txt
 
 '
@@ -301,18 +301,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "1,3-4" 2 "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test ! -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test ! -f docs/manual.txt &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -323,18 +323,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "4- 1" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test ! -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_missing src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -345,18 +345,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines s "*" "-5- 1 -2" "" c |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -367,18 +367,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines a Y y no yes bad "" |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test ! -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -389,18 +389,18 @@
 	docs/manual.txt obj.o build/lib.so &&
 	test_write_lines a Y no yes "\04" |
 	git clean -id &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -412,18 +412,18 @@
 	(cd build/ &&
 	 test_write_lines f docs "*.h" "" c |
 	 git clean -id ..) &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_file docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -435,18 +435,18 @@
 	(cd build/ &&
 	 test_write_lines s ../docs/ ../src/part3.c ../src/part4.c "" c |
 	 git clean -id ..) &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test -f a.out &&
-	test ! -f docs/manual.txt &&
-	test ! -f src/part3.c &&
-	test -f src/part3.h &&
-	test ! -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_file a.out &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_missing src/part3.c &&
+	test_path_is_file src/part3.h &&
+	test_path_is_missing src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
@@ -458,18 +458,18 @@
 	(cd build/ &&
 	 test_write_lines a Y y no yes bad "" |
 	 git clean -id ..) &&
-	test -f Makefile &&
-	test -f README &&
-	test -f src/part1.c &&
-	test -f src/part2.c &&
-	test ! -f a.out &&
-	test ! -f docs/manual.txt &&
-	test -f src/part3.c &&
-	test ! -f src/part3.h &&
-	test -f src/part4.c &&
-	test -f src/part4.h &&
-	test -f obj.o &&
-	test -f build/lib.so
+	test_path_is_file Makefile &&
+	test_path_is_file README &&
+	test_path_is_file src/part1.c &&
+	test_path_is_file src/part2.c &&
+	test_path_is_missing a.out &&
+	test_path_is_missing docs/manual.txt &&
+	test_path_is_file src/part3.c &&
+	test_path_is_missing src/part3.h &&
+	test_path_is_file src/part4.c &&
+	test_path_is_file src/part4.h &&
+	test_path_is_file obj.o &&
+	test_path_is_file build/lib.so
 
 '
 
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index eae6a46..5c4a89d 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -60,7 +60,7 @@
 	git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
 	# missing the .gitmodules file here
 	test_must_fail git submodule init 2>actual &&
-	test_i18ngrep "No url found for submodule path" actual
+	test_grep "No url found for submodule path" actual
 '
 
 test_expect_success 'submodule update aborts on missing .gitmodules file' '
@@ -68,7 +68,7 @@
 	git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
 	# missing the .gitmodules file here
 	git submodule update sub 2>actual &&
-	test_i18ngrep "Submodule path .sub. not initialized" actual
+	test_grep "Submodule path .sub. not initialized" actual
 '
 
 test_expect_success 'submodule update aborts on missing gitmodules url' '
@@ -100,7 +100,7 @@
 	) &&
 	test_must_fail git submodule status inner 2>output.err &&
 	rm -fr inner &&
-	test_i18ngrep "^error: .*did not match any file(s) known to git" output.err
+	test_grep "^error: .*did not match any file(s) known to git" output.err
 '
 
 test_expect_success 'setup - repository in init subdirectory' '
@@ -196,7 +196,7 @@
 	git -C addtest submodule add "file://$submodurl/parent" submod-redirected \
 		2>err &&
 	! grep % err &&
-	test_i18ngrep ! "Checking connectivity" err
+	test_grep ! "Checking connectivity" err
 '
 
 test_expect_success 'redirected submodule add --progress does show progress' '
@@ -212,8 +212,7 @@
 		The following paths are ignored by one of your .gitignore files:
 		submod
 		hint: Use -f if you really want to add them.
-		hint: Turn this message off by running
-		hint: "git config advice.addIgnoredFile false"
+		hint: Disable this message with "git config advice.addIgnoredFile false"
 		EOF
 		# Does not use test_commit due to the ignore
 		echo "*" > .gitignore &&
@@ -263,7 +262,7 @@
 		cd addtest &&
 		: >.git/index.lock &&
 		! git submodule add "$submodurl" sub-while-locked 2>output.err &&
-		test_i18ngrep "^fatal: .*index\.lock" output.err &&
+		test_grep "^fatal: .*index\.lock" output.err &&
 		test_path_is_missing sub-while-locked
 	)
 '
@@ -405,7 +404,7 @@
 		cd addtest/sub &&
 		test_must_fail git submodule add ../../ submod3 2>../../output.err
 	) &&
-	test_i18ngrep toplevel output.err
+	test_grep toplevel output.err
 '
 
 test_expect_success 'setup - add an example entry to .gitmodules' '
@@ -486,7 +485,7 @@
 
 test_failure_with_unknown_submodule () {
 	test_must_fail git submodule $1 no-such-submodule 2>output.err &&
-	test_i18ngrep "^error: .*no-such-submodule" output.err
+	test_grep "^error: .*no-such-submodule" output.err
 }
 
 test_expect_success 'init should fail with unknown submodule' '
@@ -644,7 +643,7 @@
 	test_must_fail git config submodule.example.url &&
 
 	git submodule update init 2> update.out &&
-	test_i18ngrep "not initialized" update.out &&
+	test_grep "not initialized" update.out &&
 	test_must_fail git rev-parse --resolve-git-dir init/.git &&
 
 	git submodule update --init init &&
@@ -661,7 +660,7 @@
 	(
 		cd sub &&
 		git submodule update ../init 2>update.out &&
-		test_i18ngrep "not initialized" update.out &&
+		test_grep "not initialized" update.out &&
 		test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
 
 		git submodule update --init ../init
@@ -1121,7 +1120,7 @@
 		cd sub &&
 		git submodule deinit ../init >../output
 	) &&
-	test_i18ngrep "\\.\\./init" output &&
+	test_grep "\\.\\./init" output &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
 	test -n "$(git config --get-regexp "submodule\.example2\.")" &&
 	test -f example2/.git &&
@@ -1136,8 +1135,8 @@
 	git submodule deinit . >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
 	test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-	test_i18ngrep "Cleared directory .init" actual &&
-	test_i18ngrep "Cleared directory .example2" actual &&
+	test_grep "Cleared directory .init" actual &&
+	test_grep "Cleared directory .example2" actual &&
 	rmdir init example2
 '
 
@@ -1149,8 +1148,8 @@
 	git submodule deinit --all >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
 	test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-	test_i18ngrep "Cleared directory .init" actual &&
-	test_i18ngrep "Cleared directory .example2" actual &&
+	test_grep "Cleared directory .init" actual &&
+	test_grep "Cleared directory .example2" actual &&
 	rmdir init example2
 '
 
@@ -1160,8 +1159,8 @@
 	git submodule deinit init example2 >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
 	test -z "$(git config --get-regexp "submodule\.example2\.")" &&
-	test_i18ngrep ! "Cleared directory .init" actual &&
-	test_i18ngrep "Cleared directory .example2" actual &&
+	test_grep ! "Cleared directory .init" actual &&
+	test_grep "Cleared directory .example2" actual &&
 	rmdir init
 '
 
@@ -1173,7 +1172,7 @@
 	test -f example2/.git &&
 	git submodule deinit -f init >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep "Cleared directory .init" actual &&
 	rmdir init
 '
 
@@ -1185,7 +1184,7 @@
 	test -f example2/.git &&
 	git submodule deinit -f init >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep "Cleared directory .init" actual &&
 	rmdir init
 '
 
@@ -1200,30 +1199,30 @@
 	test -f example2/.git &&
 	git submodule deinit -f init >actual &&
 	test -z "$(git config --get-regexp "submodule\.example\.")" &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep "Cleared directory .init" actual &&
 	rmdir init
 '
 
 test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' '
 	git submodule update --init &&
 	git submodule deinit init >actual &&
-	test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_grep "Cleared directory .init" actual &&
 	git submodule deinit init >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_grep "Cleared directory .init" actual &&
 	git submodule deinit . >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_grep "Submodule .example2. (.*) unregistered for path .example2" actual &&
+	test_grep "Cleared directory .init" actual &&
 	git submodule deinit . >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_grep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+	test_grep "Cleared directory .init" actual &&
 	git submodule deinit --all >actual &&
-	test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
-	test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
-	test_i18ngrep "Cleared directory .init" actual &&
+	test_grep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+	test_grep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+	test_grep "Cleared directory .init" actual &&
 	rmdir init example2
 '
 
@@ -1351,6 +1350,22 @@
 	)
 '
 
+test_expect_success 'update submodules without url set in .gitconfig' '
+	test_when_finished "rm -rf multisuper_clone" &&
+	git clone file://"$pwd"/multisuper multisuper_clone &&
+
+	git -C multisuper_clone submodule init &&
+	for s in sub0 sub1 sub2 sub3
+	do
+		key=submodule.$s.url &&
+		git -C multisuper_clone config --local --unset $key &&
+		git -C multisuper_clone config --file .gitmodules --unset $key || return 1
+	done &&
+
+	test_must_fail git -C multisuper_clone submodule update 2>err &&
+	grep "cannot clone submodule .sub[0-3]. without a URL" err
+'
+
 test_expect_success 'clone --recurse-submodules with a pathspec works' '
 	test_when_finished "rm -rf multisuper_clone" &&
 	cat >expected <<-\EOF &&
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index b19792b..aa2fdc3 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -56,12 +56,15 @@
 
 test_expect_success 'interactive rebase with a dirty submodule' '
 
-	test submodule = $(git diff --name-only) &&
+	echo submodule >expect &&
+	git diff --name-only >actual &&
+	test_cmp expect actual &&
 	HEAD=$(git rev-parse HEAD) &&
 	GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \
 		git rebase -i HEAD^ &&
-	test submodule = $(git diff --name-only)
-
+	echo submodule >expect &&
+	git diff --name-only >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'rebase with dirty file and submodule fails' '
@@ -83,11 +86,19 @@
 	CURRENT=$(cd submodule && git rev-parse HEAD) &&
 	git stash &&
 	test new != $(cat file) &&
-	test submodule = $(git diff --name-only) &&
-	test $CURRENT = $(cd submodule && git rev-parse HEAD) &&
+	echo submodule >expect &&
+	git diff --name-only >actual &&
+	test_cmp expect actual &&
+
+	echo "$CURRENT" >expect &&
+	git -C submodule rev-parse HEAD >actual &&
+	test_cmp expect actual &&
+
 	git stash apply &&
 	test new = $(cat file) &&
-	test $CURRENT = $(cd submodule && git rev-parse HEAD)
+	echo "$CURRENT" >expect &&
+	git -C submodule rev-parse HEAD >actual &&
+	test_cmp expect actual
 
 '
 
@@ -105,7 +116,7 @@
 	test_tick &&
 	git commit -m fourth &&
 
-	test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 >actual_output &&
+	test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 2>actual_output &&
 	git ls-files -s submodule >actual &&
 	(
 		cd submodule &&
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index ff09443..19b6135 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -163,7 +163,7 @@
 		cd sub &&
 		git submodule sync >../../output
 	) &&
-	test_i18ngrep "\\.\\./submodule" output &&
+	test_grep "\\.\\./submodule" output &&
 	test -d "$(
 		cd super-clone/submodule &&
 		git config remote.origin.url
@@ -194,7 +194,7 @@
 		cd sub &&
 		git submodule sync --recursive >../../output
 	) &&
-	test_i18ngrep "\\.\\./submodule/sub-submodule" output &&
+	test_grep "\\.\\./submodule/sub-submodule" output &&
 	test -d "$(
 		cd super-clone/submodule &&
 		git config remote.origin.url
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index f094e3d..8491b8c 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -945,7 +945,7 @@
 	git clone super_update_r super_update_r2 &&
 	(cd super_update_r2 &&
 	 git submodule update --init --recursive >actual &&
-	 test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual &&
+	 test_grep "Submodule path .submodule/subsubmodule.: checked out" actual &&
 	 (cd submodule/subsubmodule &&
 	  git log > ../../expected
 	 ) &&
@@ -1025,7 +1025,7 @@
 		# unadvertised objects, so restrict this test to v0.
 		test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 \
 			git submodule update --init --depth=1 2>actual &&
-		test_i18ngrep "Direct fetching of that commit failed." actual &&
+		test_grep "Direct fetching of that commit failed." actual &&
 		git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
 		git submodule update --init --depth=1 >actual &&
 		git -C submodule log --oneline >out &&
@@ -1039,7 +1039,7 @@
 	  git checkout HEAD^
 	 ) &&
 	 git submodule update --recursive deeper/submodule >actual &&
-	 test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
+	 test_grep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual
 	)
 '
 
@@ -1179,4 +1179,27 @@
 	test_cmp expect.err actual.err
 '
 
+add_submodule_commit_and_validate () {
+	HASH=$(git rev-parse HEAD) &&
+	git update-index --add --cacheinfo 160000,$HASH,sub &&
+	git commit -m "create submodule" &&
+	echo "160000 commit $HASH	sub" >expect &&
+	git ls-tree HEAD -- sub >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'commit with staged submodule change' '
+	add_submodule_commit_and_validate
+'
+
+test_expect_success 'commit with staged submodule change with ignoreSubmodules dirty' '
+	test_config diff.ignoreSubmodules dirty &&
+	add_submodule_commit_and_validate
+'
+
+test_expect_success 'commit with staged submodule change with ignoreSubmodules all' '
+	test_config diff.ignoreSubmodules all &&
+	add_submodule_commit_and_validate
+'
+
 test_done
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index c016794..31271f8 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -45,7 +45,7 @@
 	(
 		cd repo &&
 		test_must_fail test-tool submodule-config "" s 2>actual &&
-		test_i18ngrep "bad config" actual
+		test_grep "bad config" actual
 	)
 '
 
@@ -101,7 +101,7 @@
 				>actual \
 				2>actual_stderr &&
 		test_cmp expect_error actual &&
-		test_i18ngrep "submodule-blob $sha1:.gitmodules" actual_stderr >/dev/null
+		test_grep "submodule-blob $sha1:.gitmodules" actual_stderr >/dev/null
 	)
 '
 
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index 7cdc263..887d181 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -51,6 +51,22 @@
 	test-tool -C super submodule is-active sub1
 '
 
+test_expect_success 'is-active handles submodule.active config missing a value' '
+	cp super/.git/config super/.git/config.orig &&
+	test_when_finished mv super/.git/config.orig super/.git/config &&
+
+	cat >>super/.git/config <<-\EOF &&
+	[submodule]
+		active
+	EOF
+
+	cat >expect <<-\EOF &&
+	error: missing value for '\''submodule.active'\''
+	EOF
+	test-tool -C super submodule is-active sub1 2>actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'is-active works with basic submodule.active config' '
 	test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
 	test_when_finished "git -C super config --unset-all submodule.active" &&
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index 101afff..24f30e3 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -13,13 +13,13 @@
 test_expect_success 'git-add on embedded repository warns' '
 	test_when_finished "git rm --cached -f embed" &&
 	git add embed 2>stderr &&
-	test_i18ngrep warning stderr
+	test_grep warning stderr
 '
 
 test_expect_success '--no-warn-embedded-repo suppresses warning' '
 	test_when_finished "git rm --cached -f embed" &&
 	git add --no-warn-embedded-repo embed 2>stderr &&
-	test_i18ngrep ! warning stderr
+	test_grep ! warning stderr
 '
 
 test_expect_success 'no warning when updating entry' '
@@ -27,14 +27,14 @@
 	git add embed &&
 	git -C embed commit --allow-empty -m two &&
 	git add embed 2>stderr &&
-	test_i18ngrep ! warning stderr
+	test_grep ! warning stderr
 '
 
 test_expect_success 'submodule add does not warn' '
 	test_when_finished "git rm -rf submodule .gitmodules" &&
 	git -c protocol.file.allow=always \
 		submodule add ./embed submodule 2>stderr &&
-	test_i18ngrep ! warning stderr
+	test_grep ! warning stderr
 '
 
 test_done
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
index 7cf72b9..2ab566e 100755
--- a/t/t7416-submodule-dash-url.sh
+++ b/t/t7416-submodule-dash-url.sh
@@ -41,7 +41,7 @@
 test_expect_success 'clone rejects unprotected dash' '
 	test_when_finished "rm -rf dst" &&
 	test_must_fail git clone --recurse-submodules . dst 2>err &&
-	test_i18ngrep ignoring err
+	test_grep ignoring err
 '
 
 test_expect_success 'fsck rejects unprotected dash' '
@@ -63,7 +63,7 @@
 	mv .new .gitmodules &&
 	git commit -am "Add testmodule" &&
 	test_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&
-	test_i18ngrep ! "unknown option" err
+	test_grep ! "unknown option" err
 '
 
 test_expect_success 'fsck rejects missing URL scheme' '
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index 2f4b25d..5e3051d 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -21,7 +21,7 @@
 test_expect_success 'clone rejects unprotected dash' '
 	test_when_finished "rm -rf dst" &&
 	git clone --recurse-submodules . dst 2>err &&
-	test_i18ngrep ignoring err
+	test_grep ignoring err
 '
 
 test_expect_success 'fsck rejects unprotected dash' '
@@ -46,7 +46,7 @@
 	git -C super update-ref refs/heads/main $commit &&
 
 	test_must_fail git clone --recurse-submodules super dst 2>err &&
-	test_i18ngrep "sub " err
+	test_grep "sub " err
 '
 
 test_done
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
index 2320655..a5d1bc5 100755
--- a/t/t7419-submodule-set-branch.sh
+++ b/t/t7419-submodule-set-branch.sh
@@ -11,6 +11,10 @@
 
 TEST_PASSES_SANITIZE_LEAK=true
 TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -27,26 +31,28 @@
 		git checkout -b topic &&
 		echo b >a &&
 		git add . &&
-		git commit -mb
+		git commit -mb &&
+		git checkout main
 	) &&
 	mkdir super &&
 	(cd super &&
 		git init &&
 		git submodule add ../submodule &&
-		git commit -m "add submodule"
+		git submodule add --name thename ../submodule thepath &&
+		git commit -m "add submodules"
 	)
 '
 
 test_expect_success 'ensure submodule branch is unset' '
 	(cd super &&
-		! grep branch .gitmodules
+		test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch
 	)
 '
 
 test_expect_success 'test submodule set-branch --branch' '
 	(cd super &&
 		git submodule set-branch --branch topic submodule &&
-		grep "branch = topic" .gitmodules &&
+		test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
 		git submodule update --remote &&
 		cat <<-\EOF >expect &&
 		b
@@ -57,13 +63,12 @@
 '
 
 test_expect_success 'test submodule set-branch --default' '
-	test_commit -C submodule c &&
 	(cd super &&
 		git submodule set-branch --default submodule &&
-		! grep branch .gitmodules &&
+		test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
 		git submodule update --remote &&
 		cat <<-\EOF >expect &&
-		c
+		a
 		EOF
 		git -C submodule show -s --pretty=%s >actual &&
 		test_cmp expect actual
@@ -71,10 +76,9 @@
 '
 
 test_expect_success 'test submodule set-branch -b' '
-	test_commit -C submodule b &&
 	(cd super &&
 		git submodule set-branch -b topic submodule &&
-		grep "branch = topic" .gitmodules &&
+		test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
 		git submodule update --remote &&
 		cat <<-\EOF >expect &&
 		b
@@ -85,17 +89,43 @@
 '
 
 test_expect_success 'test submodule set-branch -d' '
-	test_commit -C submodule d &&
 	(cd super &&
 		git submodule set-branch -d submodule &&
-		! grep branch .gitmodules &&
+		test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
 		git submodule update --remote &&
 		cat <<-\EOF >expect &&
-		d
+		a
 		EOF
 		git -C submodule show -s --pretty=%s >actual &&
 		test_cmp expect actual
 	)
 '
 
+test_expect_success 'test submodule set-branch --branch with named submodule' '
+	(cd super &&
+		git submodule set-branch --branch topic thepath &&
+		test_cmp_config topic -f .gitmodules submodule.thename.branch &&
+		test_cmp_config "" -f .gitmodules --default "" submodule.thepath.branch &&
+		git submodule update --remote &&
+		cat <<-\EOF >expect &&
+		b
+		EOF
+		git -C thepath show -s --pretty=%s >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'test submodule set-branch --default with named submodule' '
+	(cd super &&
+		git submodule set-branch --default thepath &&
+		test_cmp_config "" -f .gitmodules --default "" submodule.thename.branch &&
+		git submodule update --remote &&
+		cat <<-\EOF >expect &&
+		a
+		EOF
+		git -C thepath show -s --pretty=%s >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t7420-submodule-set-url.sh b/t/t7420-submodule-set-url.sh
index d6bf62b..bf7f15e 100755
--- a/t/t7420-submodule-set-url.sh
+++ b/t/t7420-submodule-set-url.sh
@@ -25,17 +25,26 @@
 		git add file &&
 		git commit -ma
 	) &&
+	mkdir namedsubmodule &&
+	(
+		cd namedsubmodule &&
+		git init &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m1
+	) &&
 	mkdir super &&
 	(
 		cd super &&
 		git init &&
 		git submodule add ../submodule &&
-		git commit -m "add submodule"
+		git submodule add --name thename ../namedsubmodule thepath &&
+		git commit -m "add submodules"
 	)
 '
 
 test_expect_success 'test submodule set-url' '
-	# add a commit and move the submodule (change the url)
+	# add commits and move the submodules (change the urls)
 	(
 		cd submodule &&
 		echo b >>file &&
@@ -44,15 +53,28 @@
 	) &&
 	mv submodule newsubmodule &&
 
+	(
+		cd namedsubmodule &&
+		echo 2 >>file &&
+		git add file &&
+		git commit -m2
+	) &&
+	mv namedsubmodule newnamedsubmodule &&
+
 	git -C newsubmodule show >expect &&
+	git -C newnamedsubmodule show >>expect &&
 	(
 		cd super &&
 		test_must_fail git submodule update --remote &&
 		git submodule set-url submodule ../newsubmodule &&
-		grep -F "url = ../newsubmodule" .gitmodules &&
+		test_cmp_config ../newsubmodule -f .gitmodules submodule.submodule.url &&
+		git submodule set-url thepath ../newnamedsubmodule &&
+		test_cmp_config ../newnamedsubmodule -f .gitmodules submodule.thename.url &&
+		test_cmp_config "" -f .gitmodules --default "" submodule.thepath.url &&
 		git submodule update --remote
 	) &&
 	git -C super/submodule show >actual &&
+	git -C super/thepath show >>actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index 0d0c3f2..46d4fb0 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -45,6 +45,32 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'check urls' '
+	cat >expect <<-\EOF &&
+	./bar/baz/foo.git
+	https://example.com/foo.git
+	http://example.com:80/deeper/foo.git
+	EOF
+
+	test-tool submodule check-url >actual <<-\EOF &&
+	./bar/baz/foo.git
+	https://example.com/foo.git
+	http://example.com:80/deeper/foo.git
+	-a./foo
+	../../..//test/foo.git
+	../../../../../:localhost:8080/foo.git
+	..\../.\../:example.com/foo.git
+	./%0ahost=example.com/foo.git
+	https://one.example.com/evil?%0ahost=two.example.com
+	https:///example.com/foo.git
+	http://example.com:test/foo.git
+	https::example.com/foo.git
+	http:::example.com/foo.git
+	EOF
+
+	test_cmp expect actual
+'
+
 test_expect_success 'create innocent subrepo' '
 	git init innocent &&
 	git -C innocent commit --allow-empty -m foo
@@ -238,7 +264,7 @@
 		git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree &&
 
 		test_must_fail git fsck 2>output &&
-		test_i18ngrep gitmodulesBlob output
+		test_grep gitmodulesBlob output
 	)
 '
 
@@ -252,8 +278,8 @@
 		git commit -m "broken gitmodules" &&
 
 		git fsck 2>output &&
-		test_i18ngrep gitmodulesParse output &&
-		test_i18ngrep ! "bad config" output
+		test_grep gitmodulesParse output &&
+		test_grep ! "bad config" output
 	)
 '
 
@@ -275,7 +301,7 @@
 		hash="$(echo x | git hash-object -w --stdin)" &&
 		test_must_fail git update-index --add \
 			--cacheinfo 160000,$rev,d\\a 2>err &&
-		test_i18ngrep "Invalid path" err &&
+		test_grep "Invalid path" err &&
 		git -c core.protectNTFS=false update-index --add \
 			--cacheinfo 100644,$modules,.gitmodules \
 			--cacheinfo 160000,$rev,c \
@@ -289,7 +315,7 @@
 	then
 		test_must_fail git -c core.protectNTFS=false \
 			clone --recurse-submodules squatting squatting-clone 2>err &&
-		test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
+		test_grep -e "directory not empty" -e "not an empty directory" err &&
 		! grep gitdir squatting-clone/d/a/git~2
 	fi
 '
@@ -314,7 +340,7 @@
 		git commit -m nested
 	) &&
 	test_must_fail git clone --recurse-submodules nested clone 2>err &&
-	test_i18ngrep "is inside git dir" err
+	test_grep "is inside git dir" err
 '
 
 test_done
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 5fcaa0b..4dca8d9 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -555,7 +555,7 @@
 	git commit -m initial &&
 	echo "changes" >>file &&
 	test_must_fail git commit -m update >actual &&
-	test_i18ngrep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
+	test_grep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
 '
 
 test_done
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index fb5417d..cc12f99 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -3,8 +3,7 @@
 # Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
 #
 
-# FIXME: Test the various index usages, -i and -o, test reflog,
-# signoff
+# FIXME: Test the various index usages, test reflog
 
 test_description='git commit'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
@@ -21,7 +20,7 @@
 	echo bongo bongo >file &&
 	git add file &&
 	git status >actual &&
-	test_i18ngrep "No commits yet" actual
+	test_grep "No commits yet" actual
 '
 
 test_expect_success 'fail initial amend' '
@@ -92,6 +91,20 @@
 	test_must_fail git commit -m initial --long
 '
 
+test_expect_success 'fail to commit untracked file (even with --include/--only)' '
+	echo content >baz &&
+	error="error: pathspec .baz. did not match any file(s) known to git" &&
+
+	test_must_fail git commit -m "baz" baz 2>err &&
+	test_grep -e "$error" err &&
+
+	test_must_fail git commit --only -m "baz" baz 2>err &&
+	test_grep -e "$error" err &&
+
+	test_must_fail git commit --include -m "baz" baz 2>err &&
+	test_grep -e "$error" err
+'
+
 test_expect_success 'setup: non-initial commit' '
 	echo bongo bongo bongo >file &&
 	git commit -m next -a
@@ -117,6 +130,51 @@
 	git commit -m next -a --long
 '
 
+for opt in "" "-o" "--only"
+do
+	test_expect_success 'exclude additional staged changes when given pathspec' '
+		echo content >>file &&
+		echo content >>baz &&
+		git add baz &&
+		git commit $opt -m "file" file &&
+
+		git diff --name-only >actual &&
+		test_must_be_empty actual &&
+
+		test_write_lines baz >expect &&
+		git diff --name-only --cached >actual &&
+		test_cmp expect actual &&
+
+		test_write_lines file >expect &&
+		git diff --name-only HEAD^ HEAD >actual &&
+		test_cmp expect actual
+	'
+done
+
+test_expect_success '-i/--include includes staged changes' '
+	echo content >>file &&
+	echo content >>baz &&
+	git add file &&
+
+	# baz is in the index, therefore, it will be committed
+	git commit --include -m "file and baz" baz  &&
+
+	git diff --name-only HEAD >remaining &&
+	test_must_be_empty remaining &&
+
+	test_write_lines baz file >expect &&
+	git diff --name-only HEAD^ HEAD >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--include and --only do not mix' '
+	test_when_finished "git reset --hard" &&
+	echo content >>file &&
+	echo content >>baz &&
+	test_must_fail git commit --include --only -m "file baz" file baz 2>actual &&
+	test_grep -e "fatal: options .-i/--include. and .-o/--only. cannot be used together" actual
+'
+
 test_expect_success 'commit message from non-existing file' '
 	echo more bongo: bongo bongo bongo bongo >file &&
 	test_must_fail git commit -F gah -a
@@ -141,7 +199,7 @@
 test_expect_success 'template "emptyness" check' '
 	git checkout HEAD file && echo >>file && git add file &&
 	test_must_fail git commit -t file 2>err &&
-	test_i18ngrep "did not edit" err
+	test_grep "did not edit" err
 '
 
 test_expect_success 'setup: commit message from file' '
@@ -389,6 +447,28 @@
 
 '
 
+test_expect_success 'amend commit to add signoff' '
+
+	test_commit "msg" file content &&
+	git commit --amend --signoff &&
+	test_commit_message HEAD <<-EOF
+	msg
+
+	Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+	EOF
+'
+
+test_expect_success 'amend does not add signoff if it already exists' '
+
+	test_commit --signoff "tenor" file newcontent &&
+	git commit --amend --signoff &&
+	test_commit_message HEAD <<-EOF
+	tenor
+
+	Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+	EOF
+'
+
 test_expect_success 'commit mentions forced date in output' '
 	git commit --amend --date=2010-01-02T03:04:05 >output &&
 	grep "Date: *Sat Jan 2 03:04:05 2010" output
@@ -671,7 +751,7 @@
 	git add ./- &&
 	test_tick &&
 	git commit -m "add dash" >output </dev/null &&
-	test_i18ngrep " changed, 5 insertions" output
+	test_grep " changed, 5 insertions" output
 '
 
 test_expect_success '--only works on to-be-born branch' '
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 38a532d..b37e201 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -466,6 +466,43 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'commit --trailer not confused by --- separator' '
+	cat >msg <<-\EOF &&
+	subject
+
+	body with dashes
+	---
+	in it
+	EOF
+	git commit --allow-empty --trailer="my-trailer: value" -F msg &&
+	{
+		cat msg &&
+		echo &&
+		echo "my-trailer: value"
+	} >expected &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'commit --trailer with --verbose' '
+	cat >msg <<-\EOF &&
+	subject
+
+	body
+	EOF
+	GIT_EDITOR=: git commit --edit -F msg --allow-empty \
+		--trailer="my-trailer: value" --verbose &&
+	{
+		cat msg &&
+		echo &&
+		echo "my-trailer: value"
+	} >expected &&
+	git cat-file commit HEAD >commit.msg &&
+	sed -e "1,/^\$/d" commit.msg >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
 	>negative &&
@@ -687,18 +724,23 @@
 test_expect_success 'message shows author when it is not equal to committer' '
 	echo >>negative &&
 	git commit -e -m "sample" -a &&
-	test_i18ngrep \
+	test_grep \
 	  "^# Author: *A U Thor <author@example.com>\$" \
 	  .git/COMMIT_EDITMSG
 '
 
 test_expect_success 'message shows date when it is explicitly set' '
 	git commit --allow-empty -e -m foo --date="2010-01-02T03:04:05" &&
-	test_i18ngrep \
+	test_grep \
 	  "^# Date: *Sat Jan 2 03:04:05 2010 +0000" \
 	  .git/COMMIT_EDITMSG
 '
 
+test_expect_success 'message does not have multiple scissors lines' '
+	git commit --cleanup=scissors -v --allow-empty -e -m foo &&
+	test $(grep -c -e "--- >8 ---" .git/COMMIT_EDITMSG) -eq 1
+'
+
 test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
 
 	echo >>negative &&
@@ -709,7 +751,7 @@
 	) &&
 	# the ident is calculated from the system, so we cannot
 	# check the actual value, only that it is there
-	test_i18ngrep "^# Committer: " .git/COMMIT_EDITMSG
+	test_grep "^# Committer: " .git/COMMIT_EDITMSG
 '
 
 write_script .git/FAKE_EDITOR <<EOF
@@ -841,9 +883,9 @@
 	GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
 	case "$use_template" in
 	'')
-		test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
+		test_grep ! "^## Custom template" .git/COMMIT_EDITMSG ;;
 	*)
-		test_i18ngrep "^## Custom template" .git/COMMIT_EDITMSG ;;
+		test_grep "^## Custom template" .git/COMMIT_EDITMSG ;;
 	esac
 }
 
@@ -851,53 +893,53 @@
 
 	test_expect_success 'commit' '
 		try_commit "" &&
-		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --status' '
 		try_commit --status &&
-		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --no-status' '
 		try_commit --no-status &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit with commit.status = yes' '
 		test_config commit.status yes &&
 		try_commit "" &&
-		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit with commit.status = no' '
 		test_config commit.status no &&
 		try_commit "" &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --status with commit.status = yes' '
 		test_config commit.status yes &&
 		try_commit --status &&
-		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --no-status with commit.status = yes' '
 		test_config commit.status yes &&
 		try_commit --no-status &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --status with commit.status = no' '
 		test_config commit.status no &&
 		try_commit --status &&
-		test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 	test_expect_success 'commit --no-status with commit.status = no' '
 		test_config commit.status no &&
 		try_commit --no-status &&
-		test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
+		test_grep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
 	'
 
 }
@@ -911,13 +953,13 @@
 test_expect_success 'commit --status with custom comment character' '
 	test_config core.commentchar ";" &&
 	try_commit --status &&
-	test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+	test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 '
 
 test_expect_success 'switch core.commentchar' '
 	test_commit "#foo" foo &&
 	GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
-	test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+	test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 '
 
 test_expect_success 'switch core.commentchar but out of options' '
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 07ca46f..d125522 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -102,7 +102,9 @@
 '
 
 commit_msg_is () {
-	test "$(git log --pretty=format:%s%b -1)" = "$1"
+	printf "%s" "$1" >expect &&
+	git log --pretty=format:%s%b -1 >actual &&
+	test_cmp expect actual
 }
 
 test_expect_success 'with failing hook' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index d050091..46566d5 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -37,19 +37,19 @@
 
 test_expect_success 'status clean' '
 	git status >output &&
-	test_i18ngrep "nothing to commit" output
+	test_grep "nothing to commit" output
 '
 
 test_expect_success 'commit --dry-run -a clean' '
 	test_must_fail git commit --dry-run -a >output &&
-	test_i18ngrep "nothing to commit" output
+	test_grep "nothing to commit" output
 '
 
 test_expect_success 'status with modified file in submodule' '
 	(cd sub && git reset --hard) &&
 	echo "changed" >sub/foo &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (modified content)" output
+	test_grep "modified:   sub (modified content)" output
 '
 
 test_expect_success 'status with modified file in submodule (porcelain)' '
@@ -73,7 +73,7 @@
 test_expect_success 'status with added file in submodule' '
 	(cd sub && git reset --hard && echo >foo && git add foo) &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (modified content)" output
+	test_grep "modified:   sub (modified content)" output
 '
 
 test_expect_success 'status with added file in submodule (porcelain)' '
@@ -96,12 +96,12 @@
 	(cd sub && git reset --hard) &&
 	echo "content" >sub/new-file &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (untracked content)" output
+	test_grep "modified:   sub (untracked content)" output
 '
 
 test_expect_success 'status -uno with untracked file in submodule' '
 	git status -uno >output &&
-	test_i18ngrep "^nothing to commit" output
+	test_grep "^nothing to commit" output
 '
 
 test_expect_success 'status with untracked file in submodule (porcelain)' '
@@ -122,7 +122,7 @@
 	(cd sub && git reset --hard && echo >foo && git add foo) &&
 	echo "content" >sub/new-file &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (modified content, untracked content)" output
+	test_grep "modified:   sub (modified content, untracked content)" output
 '
 
 test_expect_success 'status with added and untracked file in submodule (porcelain)' '
@@ -140,7 +140,7 @@
 	(cd sub && echo "next change" >foo && git commit -m "next change" foo) &&
 	echo "changed" >sub/foo &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (new commits, modified content)" output
+	test_grep "modified:   sub (new commits, modified content)" output
 '
 
 test_expect_success 'status with modified file in modified submodule (porcelain)' '
@@ -155,7 +155,7 @@
 test_expect_success 'status with added file in modified submodule' '
 	(cd sub && git reset --hard && echo >foo && git add foo) &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (new commits, modified content)" output
+	test_grep "modified:   sub (new commits, modified content)" output
 '
 
 test_expect_success 'status with added file in modified submodule (porcelain)' '
@@ -170,7 +170,7 @@
 	(cd sub && git reset --hard) &&
 	echo "content" >sub/new-file &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (new commits, untracked content)" output
+	test_grep "modified:   sub (new commits, untracked content)" output
 '
 
 test_expect_success 'status with untracked file in modified submodule (porcelain)' '
@@ -184,7 +184,7 @@
 	(cd sub && git reset --hard && echo >foo && git add foo) &&
 	echo "content" >sub/new-file &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (new commits, modified content, untracked content)" output
+	test_grep "modified:   sub (new commits, modified content, untracked content)" output
 '
 
 test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
@@ -209,7 +209,7 @@
 test_expect_success 'status with added file in modified submodule with .git file' '
 	(cd sub && git reset --hard && echo >foo && git add foo) &&
 	git status >output &&
-	test_i18ngrep "modified:   sub (new commits, modified content)" output
+	test_grep "modified:   sub (new commits, modified content)" output
 '
 
 test_expect_success 'status with a lot of untracked files in the submodule' '
@@ -234,12 +234,12 @@
 
 test_expect_success 'status clean (empty submodule dir)' '
 	git status >output &&
-	test_i18ngrep "nothing to commit" output
+	test_grep "nothing to commit" output
 '
 
 test_expect_success 'status -a clean (empty submodule dir)' '
 	test_must_fail git commit --dry-run -a >output &&
-	test_i18ngrep "nothing to commit" output
+	test_grep "nothing to commit" output
 '
 
 cat >status_expect <<\EOF
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index 916470c..4c7db19 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -89,7 +89,7 @@
 		export GIT_EDITOR &&
 		test_must_fail git commit -a -v 2>err
 	) &&
-	test_i18ngrep "Aborting commit due to empty commit message." err
+	test_grep "Aborting commit due to empty commit message." err
 '
 
 test_expect_success 'verbose diff is stripped out with set core.commentChar' '
@@ -98,7 +98,17 @@
 		export GIT_EDITOR &&
 		test_must_fail git -c core.commentchar=";" commit -a -v 2>err
 	) &&
-	test_i18ngrep "Aborting commit due to empty commit message." err
+	test_grep "Aborting commit due to empty commit message." err
+'
+
+test_expect_success 'verbose diff is stripped with multi-byte comment char' '
+	(
+		GIT_EDITOR=cat &&
+		export GIT_EDITOR &&
+		test_must_fail git -c core.commentchar="foo>" commit -a -v >out 2>err
+	) &&
+	grep "^foo> " out &&
+	test_grep "Aborting commit due to empty commit message." err
 '
 
 test_expect_success 'status does not verbose without --verbose' '
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index aed07c5..773383f 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -5,6 +5,7 @@
 
 test_description='git status'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -18,7 +19,7 @@
 		echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
 		test_expect_code 129 git status -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage" broken/usage
+	test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'commit -h in broken repository' '
@@ -30,7 +31,7 @@
 		echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
 		test_expect_code 129 git commit -h >usage 2>&1
 	) &&
-	test_i18ngrep "[Uu]sage" broken/usage
+	test_grep "[Uu]sage" broken/usage
 '
 
 test_expect_success 'create upstream branch' '
@@ -71,7 +72,7 @@
 '
 
 test_expect_success 'status (1)' '
-	test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+	test_grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 '
 
 strip_comments () {
@@ -91,7 +92,7 @@
 # On branch main
 # Your branch and '\''upstream'\'' have diverged,
 # and have 1 and 2 different commits each, respectively.
-#   (use "git pull" to merge the remote branch into yours)
+#   (use "git pull" if you want to integrate the remote branch with yours)
 #
 # Changes to be committed:
 #   (use "git restore --staged <file>..." to unstage)
@@ -122,7 +123,7 @@
 # On branch main
 # Your branch and 'upstream' have diverged,
 # and have 1 and 2 different commits each, respectively.
-#   (use "git pull" to merge the remote branch into yours)
+#   (use "git pull" if you want to integrate the remote branch with yours)
 #
 # Changes to be committed:
 #   (use "git restore --staged <file>..." to unstage)
@@ -269,7 +270,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -334,7 +335,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -404,7 +405,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -418,14 +419,19 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status -uno >output &&
+	test_cmp expect output &&
+	git status -ufalse >output &&
 	test_cmp expect output
 '
 
-test_expect_success 'status (status.showUntrackedFiles no)' '
-	test_config status.showuntrackedfiles no &&
-	git status >output &&
-	test_cmp expect output
-'
+for no in no false 0
+do
+	test_expect_success "status (status.showUntrackedFiles $no)" '
+		test_config status.showuntrackedfiles "$no" &&
+		git status >output &&
+		test_cmp expect output
+	'
+done
 
 test_expect_success 'status -uno (advice.statusHints false)' '
 	cat >expect <<EOF &&
@@ -466,7 +472,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -487,14 +493,21 @@
 
 EOF
 	git status -unormal >output &&
+	test_cmp expect output &&
+	git status -utrue >output &&
+	test_cmp expect output &&
+	git status -uyes >output &&
 	test_cmp expect output
 '
 
-test_expect_success 'status (status.showUntrackedFiles normal)' '
-	test_config status.showuntrackedfiles normal &&
-	git status >output &&
-	test_cmp expect output
-'
+for normal in normal true 1
+do
+	test_expect_success "status (status.showUntrackedFiles $normal)" '
+		test_config status.showuntrackedfiles $normal &&
+		git status >output &&
+		test_cmp expect output
+	'
+done
 
 cat >expect <<EOF
  M dir1/modified
@@ -521,7 +534,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -581,7 +594,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -649,7 +662,7 @@
 On branch <GREEN>main<RESET>
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -772,7 +785,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -846,7 +859,6 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -900,7 +912,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -957,7 +969,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1012,11 +1024,11 @@
 '
 
 test_expect_success 'status submodule summary (clean submodule): commit' '
-	cat >expect <<EOF &&
+	cat >expect-status <<EOF &&
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
@@ -1032,12 +1044,13 @@
 
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
+	sed "/git pull/d" expect-status > expect-commit &&
 	git commit -m "commit submodule" &&
 	git config status.submodulesummary 10 &&
 	test_must_fail git commit --dry-run >output &&
-	test_cmp expect output &&
+	test_cmp expect-commit output &&
 	git status >output &&
-	test_cmp expect output
+	test_cmp expect-status output
 '
 
 cat >expect <<EOF
@@ -1064,7 +1077,6 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
   (use "git restore --source=HEAD^1 --staged <file>..." to unstage)
@@ -1116,7 +1128,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1225,7 +1237,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1282,7 +1294,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1363,7 +1375,7 @@
 ; On branch main
 ; Your branch and 'upstream' have diverged,
 ; and have 2 and 2 different commits each, respectively.
-;   (use "git pull" to merge the remote branch into yours)
+;   (use "git pull" if you want to integrate the remote branch with yours)
 ;
 ; Changes to be committed:
 ;   (use "git restore --staged <file>..." to unstage)
@@ -1403,7 +1415,9 @@
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
 	test_config core.commentchar ";;" &&
-	test_must_fail git -c status.displayCommentPrefix=true status
+	sed "s/^/;/" <expect >expect.double &&
+	git -c status.displayCommentPrefix=true status >output &&
+	test_cmp expect.double output
 '
 
 test_expect_success "--ignore-submodules=all suppresses submodule summary" '
@@ -1411,7 +1425,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes not staged for commit:
   (use "git add <file>..." to update what will be committed)
@@ -1437,7 +1451,7 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
+  (use "git pull" if you want to integrate the remote branch with yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1519,8 +1533,8 @@
 '
 
 test_expect_success '"status.branch=true" weaker than "--porcelain"' '
-       git -c status.branch=true status --porcelain >actual &&
-       test_cmp expected_nobranch actual
+	git -c status.branch=true status --porcelain >actual &&
+	test_cmp expected_nobranch actual
 '
 
 test_expect_success '"status.branch=false" same as "--no-branch"' '
@@ -1542,12 +1556,12 @@
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git config --add submodule.subname.ignore all &&
 	git status -s --ignore-submodules=dirty >output &&
-	test_i18ngrep "^M. sm" output &&
+	test_grep "^M. sm" output &&
 	GIT_EDITOR="echo hello >>\"\$1\"" &&
 	export GIT_EDITOR &&
 	git commit -uno &&
 	git status -s --ignore-submodules=dirty >output &&
-	test_i18ngrep ! "^M. sm" output
+	test_grep ! "^M. sm" output
 '
 
 test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '
@@ -1557,7 +1571,6 @@
 On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
-  (use "git pull" to merge the remote branch into yours)
 
 Changes to be committed:
   (use "git restore --staged <file>..." to unstage)
@@ -1573,13 +1586,13 @@
 	git commit -uno --dry-run >output &&
 	test_cmp expect output &&
 	git status -s --ignore-submodules=dirty >output &&
-	test_i18ngrep "^M. sm" output
+	test_grep "^M. sm" output
 '
 
 test_expect_success 'git commit -m will commit a staged but ignored submodule' '
 	git commit -uno -m message &&
 	git status -s --ignore-submodules=dirty >output &&
-	test_i18ngrep ! "^M. sm" output &&
+	test_grep ! "^M. sm" output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1592,7 +1605,7 @@
 	git stash &&
 	git status >expected_default &&
 	git status --show-stash >expected_with_stash &&
-	test_i18ngrep "^Your stash currently has 1 entry$" expected_with_stash
+	test_grep "^Your stash currently has 1 entry$" expected_with_stash
 '
 
 test_expect_success 'no stash info with "--show-stash --no-show-stash"' '
@@ -1619,14 +1632,14 @@
 test_expect_success '"No commits yet" should be noted in status output' '
 	git checkout --orphan empty-branch-1 &&
 	git status >output &&
-	test_i18ngrep "No commits yet" output
+	test_grep "No commits yet" output
 '
 
 test_expect_success '"No commits yet" should not be noted in status output' '
 	git checkout --orphan empty-branch-2 &&
 	test_commit test-commit-1 &&
 	git status >output &&
-	test_i18ngrep ! "No commits yet" output
+	test_grep ! "No commits yet" output
 '
 
 test_expect_success '"Initial commit" should be noted in commit template' '
@@ -1634,7 +1647,7 @@
 	touch to_be_committed_1 &&
 	git add to_be_committed_1 &&
 	git commit --dry-run >output &&
-	test_i18ngrep "Initial commit" output
+	test_grep "Initial commit" output
 '
 
 test_expect_success '"Initial commit" should not be noted in commit template' '
@@ -1643,7 +1656,7 @@
 	touch to_be_committed_2 &&
 	git add to_be_committed_2 &&
 	git commit --dry-run >output &&
-	test_i18ngrep ! "Initial commit" output
+	test_grep ! "Initial commit" output
 '
 
 test_expect_success '--no-optional-locks prevents index update' '
@@ -1746,4 +1759,20 @@
 	)
 '
 
+test_expect_success EXPENSIVE 'status does not re-read unchanged 4 or 8 GiB file' '
+	(
+		mkdir large-file &&
+		cd large-file &&
+		# Files are 2 GiB, 4 GiB, and 8 GiB sparse files.
+		test-tool truncate file-a 0x080000000 &&
+		test-tool truncate file-b 0x100000000 &&
+		test-tool truncate file-c 0x200000000 &&
+		# This will be slow.
+		git add file-a file-b file-c &&
+		git commit -m "add large files" &&
+		git diff-index HEAD file-a file-b file-c >actual &&
+		test_must_be_empty actual
+	)
+'
+
 test_done
diff --git a/t/t7509-commit-authorship.sh b/t/t7509-commit-authorship.sh
index 5d89094..fd8c8f8 100755
--- a/t/t7509-commit-authorship.sh
+++ b/t/t7509-commit-authorship.sh
@@ -99,7 +99,7 @@
 	echo "Empty author test" >>foo &&
 	test_tick &&
 	test_must_fail git commit -a -m "empty author" --amend 2>err &&
-	test_i18ngrep "empty ident" err
+	test_grep "empty ident" err
 '
 
 test_expect_success '--amend option with missing author' '
@@ -112,7 +112,7 @@
 	echo "Missing author test" >>foo &&
 	test_tick &&
 	test_must_fail git commit -a -m "malformed author" --amend 2>err &&
-	test_i18ngrep "empty ident" err
+	test_grep "empty ident" err
 '
 
 test_expect_success '--reset-author makes the commit ours even with --amend option' '
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 48f86cb..0d2dd29 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -218,87 +218,101 @@
 	! grep "BAD signature from" actual
 '
 
+test_expect_success GPG2 'bare signature' '
+	git verify-commit fifth-signed 2>expect &&
+	echo >>expect &&
+	git log -1 --format="%GG" fifth-signed >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success GPG 'show good signature with custom format' '
 	cat >expect <<-\EOF &&
 	G
-	13B6F51ECDDE430D
-	C O Mitter <committer@example.com>
-	73D758744BE721698EC54E8713B6F51ECDDE430D
-	73D758744BE721698EC54E8713B6F51ECDDE430D
-	EOF
-	git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
-	test_cmp expect actual
-'
-
-test_expect_success GPG 'show bad signature with custom format' '
-	cat >expect <<-\EOF &&
-	B
-	13B6F51ECDDE430D
-	C O Mitter <committer@example.com>
-
-
-	EOF
-	git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
-	test_cmp expect actual
-'
-
-test_expect_success GPG 'show untrusted signature with custom format' '
-	cat >expect <<-\EOF &&
-	U
-	65A0EEA02E30CAD7
-	Eris Discordia <discord@example.net>
-	F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
-	D4BE22311AD3131E5EDA29A461092E85B7227189
-	EOF
-	git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
-	test_cmp expect actual
-'
-
-test_expect_success GPG 'show untrusted signature with undefined trust level' '
-	cat >expect <<-\EOF &&
-	undefined
-	65A0EEA02E30CAD7
-	Eris Discordia <discord@example.net>
-	F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
-	D4BE22311AD3131E5EDA29A461092E85B7227189
-	EOF
-	git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
-	test_cmp expect actual
-'
-
-test_expect_success GPG 'show untrusted signature with ultimate trust level' '
-	cat >expect <<-\EOF &&
 	ultimate
 	13B6F51ECDDE430D
 	C O Mitter <committer@example.com>
 	73D758744BE721698EC54E8713B6F51ECDDE430D
 	73D758744BE721698EC54E8713B6F51ECDDE430D
 	EOF
-	git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+	git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show bad signature with custom format' '
+	cat >expect <<-\EOF &&
+	B
+	undefined
+	13B6F51ECDDE430D
+	C O Mitter <committer@example.com>
+
+
+	EOF
+	git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with custom format' '
+	cat >expect <<-\EOF &&
+	U
+	undefined
+	65A0EEA02E30CAD7
+	Eris Discordia <discord@example.net>
+	F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+	D4BE22311AD3131E5EDA29A461092E85B7227189
+	EOF
+	git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+	cat >expect <<-\EOF &&
+	U
+	undefined
+	65A0EEA02E30CAD7
+	Eris Discordia <discord@example.net>
+	F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+	D4BE22311AD3131E5EDA29A461092E85B7227189
+	EOF
+	git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+	cat >expect <<-\EOF &&
+	G
+	ultimate
+	13B6F51ECDDE430D
+	C O Mitter <committer@example.com>
+	73D758744BE721698EC54E8713B6F51ECDDE430D
+	73D758744BE721698EC54E8713B6F51ECDDE430D
+	EOF
+	git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success GPG 'show unknown signature with custom format' '
 	cat >expect <<-\EOF &&
 	E
+	undefined
 	65A0EEA02E30CAD7
 
 
 
 	EOF
-	GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+	GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success GPG 'show lack of signature with custom format' '
 	cat >expect <<-\EOF &&
 	N
+	undefined
 
 
 
 
 	EOF
-	git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
+	git log -1 --format="%G?%n%GT%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 2f16d57..802f8f7 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -692,6 +692,34 @@
 '
 
 
+test_expect_success 'status when bisecting while rebasing' '
+	git reset --hard main &&
+	test_when_finished "git rebase --abort" &&
+	ONTO=$(git rev-parse --short HEAD^) &&
+	FAKE_LINES="break" git rebase -i HEAD^ &&
+	test_when_finished "git checkout -" &&
+	git checkout -b bisect_while_rebasing &&
+	test_when_finished "git bisect reset" &&
+	git bisect start &&
+	cat >expected <<EOF &&
+On branch bisect_while_rebasing
+Last command done (1 command done):
+   break
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''bisect'\'' on '\''$ONTO'\''.
+  (use "git commit --amend" to amend the current commit)
+  (use "git rebase --continue" once you are satisfied with your changes)
+
+You are currently bisecting, started from branch '\''bisect_while_rebasing'\''.
+  (use "git bisect reset" to get back to the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+	git status --untracked-files=no >actual &&
+	test_cmp expected actual
+'
+
+
 test_expect_success 'status when rebase --apply conflicts with statushints disabled' '
 	git reset --hard main &&
 	git checkout -b statushints_disabled &&
@@ -774,6 +802,28 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'status when cherry-picking multiple commits' '
+	git reset --hard cherry_branch &&
+	test_when_finished "git cherry-pick --abort" &&
+	test_must_fail git cherry-pick cherry_branch_second one_cherry &&
+	TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) &&
+	cat >expected <<EOF &&
+On branch cherry_branch
+You are currently cherry-picking commit $TO_CHERRY_PICK.
+  (fix conflicts and run "git cherry-pick --continue")
+  (use "git cherry-pick --skip" to skip this patch)
+  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+Unmerged paths:
+  (use "git add <file>..." to mark resolution)
+	both modified:   main.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+	git status --untracked-files=no >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'status when cherry-picking after committing conflict resolution' '
 	git reset --hard cherry_branch &&
 	test_when_finished "git cherry-pick --abort" &&
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 97f1090..3d3e13c 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -489,7 +489,7 @@
 '
 
 test_expect_success 'with config setup' '
-	git config trailer.ack.key "Acked-by: " &&
+	test_config trailer.ack.key "Acked-by: " &&
 	cat >expected <<-\EOF &&
 
 		Acked-by: Peff
@@ -503,8 +503,8 @@
 '
 
 test_expect_success 'with config setup and ":=" as separators' '
-	git config trailer.separators ":=" &&
-	git config trailer.ack.key "Acked-by= " &&
+	test_config trailer.separators ":=" &&
+	test_config trailer.ack.key "Acked-by= " &&
 	cat >expected <<-\EOF &&
 
 		Acked-by= Peff
@@ -518,7 +518,7 @@
 '
 
 test_expect_success 'with config setup and "%" as separators' '
-	git config trailer.separators "%" &&
+	test_config trailer.separators "%" &&
 	cat >expected <<-\EOF &&
 
 		bug% 42
@@ -532,6 +532,7 @@
 '
 
 test_expect_success 'with "%" as separators and a message with trailers' '
+	test_config trailer.separators "%" &&
 	cat >special_message <<-\EOF &&
 		Special Message
 
@@ -553,8 +554,8 @@
 '
 
 test_expect_success 'with config setup and ":=#" as separators' '
-	git config trailer.separators ":=#" &&
-	git config trailer.bug.key "Bug #" &&
+	test_config trailer.separators ":=#" &&
+	test_config trailer.bug.key "Bug #" &&
 	cat >expected <<-\EOF &&
 
 		Bug #42
@@ -581,6 +582,8 @@
 '
 
 test_expect_success 'with commit complex message as argument' '
+	test_config trailer.separators ":=" &&
+	test_config trailer.ack.key "Acked-by= " &&
 	cat complex_message_body complex_message_trailers >complex_message &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
@@ -594,6 +597,8 @@
 '
 
 test_expect_success 'with 2 files arguments' '
+	test_config trailer.separators ":=" &&
+	test_config trailer.ack.key "Acked-by= " &&
 	cat basic_message >>expected &&
 	echo >>expected &&
 	cat basic_patch >>expected &&
@@ -677,6 +682,9 @@
 '
 
 test_expect_success 'with commit complex message and trailer args' '
+	test_config trailer.separators ":=#" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.bug.key "Bug #" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -692,6 +700,9 @@
 '
 
 test_expect_success 'with complex patch, args and --trim-empty' '
+	test_config trailer.separators ":=#" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.bug.key "Bug #" &&
 	cat complex_message >complex_patch &&
 	cat basic_patch >>complex_patch &&
 	cat complex_message_body >expected &&
@@ -746,7 +757,10 @@
 '
 
 test_expect_success 'using "where = before"' '
-	git config trailer.bug.where "before" &&
+	test_config trailer.separators ":=#" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -762,7 +776,9 @@
 '
 
 test_expect_success 'overriding configuration with "--where after"' '
-	git config trailer.ack.where "before" &&
+	test_config trailer.separators ":=" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "before" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -776,7 +792,12 @@
 	test_cmp expected actual
 '
 
-test_expect_success 'using "where = before" with "--no-where"' '
+test_expect_success 'using "--where after" with "--no-where"' '
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "before" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -791,8 +812,59 @@
 	test_cmp expected actual
 '
 
+# Check whether using "--no-where" clears out only the "--where after", such
+# that we still use the configuration in trailer.where (which is different from
+# the hardcoded default (in WHERE_END) assuming the absence of .gitconfig).
+# Here, the "start" setting of trailer.where is respected, so the new "Acked-by"
+# and "Bug" trailers are placed at the beginning, and not at the end which is
+# the harcoded default.
+test_expect_success 'using "--where after" with "--no-where" defaults to configuration' '
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.separators ":=#" &&
+	test_config trailer.where "start" &&
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+		Bug #42
+		Acked-by= Peff
+		Fixes: Z
+		Acked-by= Z
+		Reviewed-by: Z
+		Signed-off-by: Z
+	EOF
+	git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+		--trailer "bug: 42" complex_message >actual &&
+	test_cmp expected actual
+'
+
+# The "--where after" will only get respected for the trailer that came
+# immediately after it. For the next trailer (Bug #42), we default to using the
+# hardcoded WHERE_END because we don't have any "trailer.where" or
+# "trailer.bug.where" configured.
+test_expect_success 'using "--no-where" defaults to harcoded default if nothing configured' '
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.separators ":=#" &&
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+		Fixes: Z
+		Acked-by= Z
+		Acked-by= Peff
+		Reviewed-by: Z
+		Signed-off-by: Z
+		Bug #42
+	EOF
+	git interpret-trailers --where after --trailer "ack: Peff" --no-where \
+		--trailer "bug: 42" complex_message >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'using "where = after"' '
-	git config trailer.ack.where "after" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -808,8 +880,11 @@
 '
 
 test_expect_success 'using "where = end"' '
-	git config trailer.review.key "Reviewed-by" &&
-	git config trailer.review.where "end" &&
+	test_config trailer.review.key "Reviewed-by" &&
+	test_config trailer.review.where "end" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -827,8 +902,11 @@
 '
 
 test_expect_success 'using "where = start"' '
-	git config trailer.review.key "Reviewed-by" &&
-	git config trailer.review.where "start" &&
+	test_config trailer.review.key "Reviewed-by" &&
+	test_config trailer.review.where "start" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Reviewed-by: Johannes
@@ -846,8 +924,13 @@
 '
 
 test_expect_success 'using "where = before" for a token in the middle of the message' '
-	git config trailer.review.key "Reviewed-by:" &&
-	git config trailer.review.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.review.where "before" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -864,6 +947,12 @@
 '
 
 test_expect_success 'using "where = before" and --trim-empty' '
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	cat >>expected <<-\EOF &&
 		Bug #46
@@ -878,6 +967,13 @@
 '
 
 test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.review.where "before" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -896,7 +992,13 @@
 '
 
 test_expect_success 'default "ifExists" is now "addIfDifferent"' '
-	git config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -914,8 +1016,14 @@
 '
 
 test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
-	git config trailer.ack.ifExists "addIfDifferent" &&
-	git config trailer.ack.where "end" &&
+	test_config trailer.ack.ifExists "addIfDifferent" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "end" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -932,8 +1040,14 @@
 '
 
 test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
-	git config trailer.ack.ifExists "addIfDifferent" &&
-	git config trailer.ack.where "before" &&
+	test_config trailer.ack.ifExists "addIfDifferent" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "before" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -950,8 +1064,14 @@
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' '
-	git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
-	git config trailer.ack.where "end" &&
+	test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "end" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -973,8 +1093,14 @@
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor"  with "where = after"' '
-	git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
-	git config trailer.ack.where "after" &&
+	test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -995,7 +1121,11 @@
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
-	git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+	test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	cat >>expected <<-\EOF &&
 		Bug #42
@@ -1011,8 +1141,14 @@
 '
 
 test_expect_success 'using "ifExists = add" with "where = end"' '
-	git config trailer.ack.ifExists "add" &&
-	git config trailer.ack.where "end" &&
+	test_config trailer.ack.ifExists "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "end" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1036,8 +1172,14 @@
 '
 
 test_expect_success 'using "ifExists = add" with "where = after"' '
-	git config trailer.ack.ifExists "add" &&
-	git config trailer.ack.where "after" &&
+	test_config trailer.ack.ifExists "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1058,8 +1200,15 @@
 '
 
 test_expect_success 'overriding configuration with "--if-exists replace"' '
-	git config trailer.fix.key "Fixes: " &&
-	git config trailer.fix.ifExists "add" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.review.where "before" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1074,9 +1223,66 @@
 	test_cmp expected actual
 '
 
+# "trailer.ifexists" is set to "doNothing", so using "--no-if-exists" defaults
+# to this "doNothing" behavior. So the "Fixes: 53" trailer does not get added.
+test_expect_success 'using "--if-exists replace" with "--no-if-exists" defaults to configuration' '
+	test_config trailer.ifexists "doNothing" &&
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+		Fixes: Z
+		Acked-by: Z
+		Reviewed-by: Z
+		Signed-off-by: Z
+	EOF
+	git interpret-trailers --if-exists replace --no-if-exists --trailer "Fixes: 53" \
+		<complex_message >actual &&
+	test_cmp expected actual
+'
+
+# No "ifexists" configuration is set, so using "--no-if-exists" makes it default
+# to addIfDifferentNeighbor. Because we do have a different neighbor "Fixes: 53"
+# (because it got added by overriding with "--if-exists replace" earlier in the
+# arguments list), we add "Signed-off-by: addme".
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured' '
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+		Acked-by: Z
+		Reviewed-by: Z
+		Signed-off-by: Z
+		Fixes: 53
+		Signed-off-by: addme
+	EOF
+	git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+		--trailer "Signed-off-by: addme" <complex_message >actual &&
+	test_cmp expected actual
+'
+
+# The second "Fixes: 53" trailer is discarded, because the "--no-if-exists" here
+# makes us default to addIfDifferentNeighbor, and we already added the "Fixes:
+# 53" trailer earlier in the argument list.
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured (no addition)' '
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+		Acked-by: Z
+		Reviewed-by: Z
+		Signed-off-by: Z
+		Fixes: 53
+	EOF
+	git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+		--trailer "Fixes: 53" <complex_message >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'using "ifExists = replace"' '
-	git config trailer.fix.key "Fixes: " &&
-	git config trailer.fix.ifExists "replace" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "replace" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1095,7 +1301,16 @@
 '
 
 test_expect_success 'using "ifExists = replace" with "where = after"' '
-	git config trailer.fix.where "after" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "replace" &&
+	test_config trailer.fix.where "after" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1114,7 +1329,15 @@
 '
 
 test_expect_success 'using "ifExists = doNothing"' '
-	git config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1133,8 +1356,17 @@
 '
 
 test_expect_success 'the default is "ifMissing = add"' '
-	git config trailer.cc.key "Cc: " &&
-	git config trailer.cc.where "before" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.cc.key "Cc: " &&
+	test_config trailer.cc.where "before" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1154,7 +1386,14 @@
 '
 
 test_expect_success 'overriding configuration with "--if-missing doNothing"' '
-	git config trailer.ifmissing "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.ifmissing "add" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -1173,7 +1412,13 @@
 '
 
 test_expect_success 'when default "ifMissing" is "doNothing"' '
-	git config trailer.ifmissing "doNothing" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.ifmissing "doNothing" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -1187,14 +1432,21 @@
 		--trailer "cc=Linus" --trailer "ack: Junio" \
 		--trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
 		<complex_message >actual &&
-	test_cmp expected actual &&
-	git config trailer.ifmissing "add"
+	test_cmp expected actual
 '
 
 test_expect_success 'using "ifMissing = add" with "where = end"' '
-	git config trailer.cc.key "Cc: " &&
-	git config trailer.cc.where "end" &&
-	git config trailer.cc.ifMissing "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.cc.key "Cc: " &&
+	test_config trailer.cc.ifMissing "add" &&
+	test_config trailer.cc.where "end" &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1214,9 +1466,17 @@
 '
 
 test_expect_success 'using "ifMissing = add" with "where = before"' '
-	git config trailer.cc.key "Cc: " &&
-	git config trailer.cc.where "before" &&
-	git config trailer.cc.ifMissing "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.cc.key "Cc: " &&
+	test_config trailer.cc.ifMissing "add" &&
+	test_config trailer.cc.where "before" &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Cc: Linus
@@ -1236,7 +1496,15 @@
 '
 
 test_expect_success 'using "ifMissing = doNothing"' '
-	git config trailer.cc.ifMissing "doNothing" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.cc.ifMissing "doNothing" &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1254,9 +1522,50 @@
 	test_cmp expected actual
 '
 
+# Ignore the "IgnoredTrailer" because of "--if-missing doNothing", but also
+# ignore the "StillIgnoredTrailer" because we set "trailer.ifMissing" to
+# "doNothing" in configuration.
+test_expect_success 'using "--no-if-missing" defaults to configuration' '
+	test_config trailer.ifMissing "doNothing" &&
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+			Fixes: Z
+			Acked-by: Z
+			Reviewed-by: Z
+			Signed-off-by: Z
+	EOF
+	git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+			--trailer "StillIgnoredTrailer: ignoreme" <complex_message >actual &&
+	test_cmp expected actual
+'
+
+# Add the "AddedTrailer" because the "--no-if-missing" clears the "--if-missing
+# doNothing" from earlier in the argument list.
+test_expect_success 'using "--no-if-missing" defaults to hardcoded default if nothing configured' '
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+			Fixes: Z
+			Acked-by: Z
+			Reviewed-by: Z
+			Signed-off-by: Z
+			AddedTrailer: addme
+	EOF
+	git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+			--trailer "AddedTrailer: addme" <complex_message >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'default "where" is now "after"' '
 	git config trailer.where "after" &&
-	git config --unset trailer.ack.where &&
+	test_config trailer.ack.ifExists "add" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.ack.where "after" &&
+	test_config trailer.bug.key "Bug #" &&
+	test_config trailer.bug.where "before" &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=#" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Bug #42
@@ -1280,10 +1589,15 @@
 '
 
 test_expect_success 'with simple command' '
-	git config trailer.sign.key "Signed-off-by: " &&
-	git config trailer.sign.where "after" &&
-	git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
-	git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+	test_config trailer.sign.where "after" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -1298,8 +1612,14 @@
 '
 
 test_expect_success 'with command using committer information' '
-	git config trailer.sign.ifExists "addIfDifferent" &&
-	git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.sign.ifExists "addIfDifferent" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -1314,10 +1634,15 @@
 '
 
 test_expect_success 'with command using author information' '
-	git config trailer.sign.key "Signed-off-by: " &&
-	git config trailer.sign.where "after" &&
-	git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
-	git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.fix.ifExists "doNothing" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+	test_config trailer.sign.where "after" &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
 		Fixes: Z
@@ -1338,12 +1663,19 @@
 '
 
 test_expect_success 'cmd takes precedence over command' '
-	test_when_finished "git config --unset trailer.fix.cmd" &&
-	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
-	--abbrev-commit --abbrev=14 \"\$1\" || true" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
 		--abbrev-commit --abbrev=14 \$ARG" &&
+	test_config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+	--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "replace" &&
+	test_config trailer.fix.where "after" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=" &&
 	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected2 &&
 	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
@@ -1359,8 +1691,16 @@
 '
 
 test_expect_success 'with command using $ARG' '
-	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "replace" &&
+	test_config trailer.fix.where "after" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
@@ -1376,8 +1716,16 @@
 '
 
 test_expect_success 'with failing command using $ARG' '
-	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "false \$ARG" &&
+	test_config trailer.ack.key "Acked-by= " &&
+	test_config trailer.fix.command "false \$ARG" &&
+	test_config trailer.fix.key "Fixes: " &&
+	test_config trailer.fix.ifExists "replace" &&
+	test_config trailer.fix.where "after" &&
+	test_config trailer.review.key "Reviewed-by:" &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.ifexists "addIfDifferent" &&
+	test_config trailer.separators ":=" &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
 		Fixes: Z
@@ -1392,7 +1740,9 @@
 '
 
 test_expect_success 'with empty tokens' '
-	git config --unset trailer.fix.command &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.sign.key "Signed-off-by: " &&
+	test_config trailer.ifexists "addIfDifferent" &&
 	cat >expected <<-EOF &&
 
 		Signed-off-by: A U Thor <author@example.com>
@@ -1403,7 +1753,8 @@
 '
 
 test_expect_success 'with command but no key' '
-	git config --unset trailer.sign.key &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.ifexists "addIfDifferent" &&
 	cat >expected <<-EOF &&
 
 		sign: A U Thor <author@example.com>
@@ -1414,7 +1765,9 @@
 '
 
 test_expect_success 'with no command and no key' '
-	git config --unset trailer.review.key &&
+	test_config trailer.review.where "before" &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+	test_config trailer.ifexists "addIfDifferent" &&
 	cat >expected <<-EOF &&
 
 		review: Junio
@@ -1426,6 +1779,8 @@
 '
 
 test_expect_success 'with cut line' '
+	test_config trailer.review.where "before" &&
+	test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
 	cat >expected <<-\EOF &&
 		my subject
 
@@ -1443,7 +1798,8 @@
 '
 
 test_expect_success 'only trailers' '
-	git config trailer.sign.command "echo config-value" &&
+	test_config trailer.sign.command "echo config-value" &&
+	test_config trailer.ifexists "addIfDifferent" &&
 	cat >expected <<-\EOF &&
 		existing: existing-value
 		sign: config-value
@@ -1462,7 +1818,7 @@
 '
 
 test_expect_success 'only-trailers omits non-trailer in middle of block' '
-	git config trailer.sign.command "echo config-value" &&
+	test_config trailer.sign.command "echo config-value" &&
 	cat >expected <<-\EOF &&
 		Signed-off-by: nobody <nobody@nowhere>
 		Signed-off-by: somebody <somebody@somewhere>
@@ -1482,7 +1838,7 @@
 '
 
 test_expect_success 'only input' '
-	git config trailer.sign.command "echo config-value" &&
+	test_config trailer.sign.command "echo config-value" &&
 	cat >expected <<-\EOF &&
 		existing: existing-value
 	EOF
@@ -1560,4 +1916,37 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'suppressing --- does not disable cut-line handling' '
+	echo "real-trailer: before the cut" >expected &&
+
+	git interpret-trailers --parse --no-divider >actual <<-\EOF &&
+	subject
+
+	This input has a cut-line in it; we should stop parsing when we see it
+	and consider only trailers before that line.
+
+	real-trailer: before the cut
+
+	# ------------------------ >8 ------------------------
+	# Nothing below this line counts as part of the commit message.
+	not-a-trailer: too late
+	EOF
+
+	test_cmp expected actual
+'
+
+test_expect_success 'handling of --- lines in conjunction with cut-lines' '
+	echo "my-trailer: here" >expected &&
+
+	git interpret-trailers --parse >actual <<-\EOF &&
+	subject
+
+	my-trailer: here
+	---
+	# ------------------------ >8 ------------------------
+	EOF
+
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t7514-commit-patch.sh b/t/t7514-commit-patch.sh
index 998a210..b4de10a 100755
--- a/t/t7514-commit-patch.sh
+++ b/t/t7514-commit-patch.sh
@@ -3,12 +3,6 @@
 test_description='hunk edit with "commit -p -m"'
 . ./test-lib.sh
 
-if ! test_have_prereq PERL
-then
-	skip_all="skipping '$test_description' tests, perl not available"
-	test_done
-fi
-
 test_expect_success 'setup (initial)' '
 	echo line1 >file &&
 	git add file &&
diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh
index f2ce14e..bb95f09 100755
--- a/t/t7516-commit-races.sh
+++ b/t/t7516-commit-races.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git commit races'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'race to create orphan commit' '
@@ -10,7 +12,8 @@
 	test_must_fail env EDITOR=./hare-editor git commit --allow-empty -m tortoise -e &&
 	git show -s --pretty=format:%s >subject &&
 	grep hare subject &&
-	test -z "$(git show -s --pretty=format:%P)"
+	git show -s --pretty=format:%P >out &&
+	test_must_be_empty out
 '
 
 test_expect_success 'race to create non-orphan commit' '
diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh
index fffdb6f..b37de0a 100755
--- a/t/t7518-ident-corner-cases.sh
+++ b/t/t7518-ident-corner-cases.sh
@@ -15,15 +15,24 @@
 		sane_unset GIT_AUTHOR_EMAIL &&
 		GIT_AUTHOR_NAME= &&
 		test_must_fail git commit --allow-empty -m foo 2>err &&
-		test_i18ngrep ! "(null)" err
+		test_grep ! "(null)" err
 	)
 '
 
 test_expect_success 'commit rejects all-crud name' '
-	test_must_fail env GIT_AUTHOR_NAME=" .;<>" \
+	test_must_fail env GIT_AUTHOR_NAME=" ,;<>" \
 		git commit --allow-empty -m foo
 '
 
+test_expect_success 'commit does not strip trailing dot' '
+	author_name="Pat Doe Jr." &&
+	env GIT_AUTHOR_NAME="$author_name" \
+		git commit --allow-empty -m foo &&
+	git log -1 --format=%an >actual &&
+	echo "$author_name" >expected &&
+	test_cmp actual expected
+'
+
 # We must test the actual error message here, as an unwanted
 # auto-detection could fail for other reasons.
 test_expect_success 'empty configured name does not auto-detect' '
@@ -31,8 +40,8 @@
 		sane_unset GIT_AUTHOR_NAME &&
 		test_must_fail \
 			git -c user.name= commit --allow-empty -m foo 2>err &&
-		test_i18ngrep "empty ident name" err &&
-		test_i18ngrep "Author identity unknown" err
+		test_grep "empty ident name" err &&
+		test_grep "Author identity unknown" err
 	)
 '
 
@@ -41,8 +50,8 @@
 		sane_unset GIT_COMMITTER_NAME &&
 		test_must_fail \
 			git -c user.name= commit --allow-empty -m foo 2>err &&
-		test_i18ngrep "empty ident name" err &&
-		test_i18ngrep "Committer identity unknown" err
+		test_grep "empty ident name" err &&
+		test_grep "Committer identity unknown" err
 	)
 '
 
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index 8348e3a..7ee69ec 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -322,14 +322,14 @@
 			rm -f marker &&
 			git status >actual &&
 			test_path_is_file marker &&
-			test_i18ngrep ! "Changes not staged for commit:" actual &&
+			test_grep ! "Changes not staged for commit:" actual &&
 			if test $uc_val = true
 			then
-				test_i18ngrep ! "Untracked files:" actual
+				test_grep ! "Untracked files:" actual
 			fi &&
 			if test $uc_val = false
 			then
-				test_i18ngrep "Untracked files:" actual
+				test_grep "Untracked files:" actual
 			fi &&
 			rm -f marker
 		'
diff --git a/t/t7520-ignored-hook-warning.sh b/t/t7520-ignored-hook-warning.sh
index 184b258..3b63c34 100755
--- a/t/t7520-ignored-hook-warning.sh
+++ b/t/t7520-ignored-hook-warning.sh
@@ -13,27 +13,27 @@
 
 test_expect_success 'no warning if hook is not ignored' '
 	git commit --allow-empty -m "more" 2>message &&
-	test_i18ngrep ! -e "hook was ignored" message
+	test_grep ! -e "hook was ignored" message
 '
 
 test_expect_success POSIXPERM 'warning if hook is ignored' '
 	test_hook --disable pre-commit &&
 	git commit --allow-empty -m "even more" 2>message &&
-	test_i18ngrep -e "hook was ignored" message
+	test_grep -e "hook was ignored" message
 '
 
 test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
 	test_config advice.ignoredHook false &&
 	test_hook --disable pre-commit &&
 	git commit --allow-empty -m "even more" 2>message &&
-	test_i18ngrep ! -e "hook was ignored" message
+	test_grep ! -e "hook was ignored" message
 '
 
 test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
 	test_hook --remove pre-commit &&
 	test_unconfig advice.ignoredHook &&
 	git commit --allow-empty -m "even more" 2>message &&
-	test_i18ngrep ! -e "hook was ignored" message
+	test_grep ! -e "hook was ignored" message
 '
 
 test_done
diff --git a/t/t7525-status-rename.sh b/t/t7525-status-rename.sh
index 22bf5c7..a9210d3 100755
--- a/t/t7525-status-rename.sh
+++ b/t/t7525-status-rename.sh
@@ -21,81 +21,81 @@
 
 test_expect_success 'status no-options' '
 	git status >actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "renamed:" actual
 '
 
 test_expect_success 'status --no-renames' '
 	git status --no-renames >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'status.renames inherits from diff.renames false' '
 	git -c diff.renames=false status >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'status.renames inherits from diff.renames true' '
 	git -c diff.renames=true status >actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "renamed:" actual
 '
 
 test_expect_success 'status.renames overrides diff.renames false' '
 	git -c diff.renames=true -c status.renames=false status >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'status.renames overrides from diff.renames true' '
 	git -c diff.renames=false -c status.renames=true status >actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "renamed:" actual
 '
 
 test_expect_success 'status status.renames=false' '
 	git -c status.renames=false status >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'status status.renames=true' '
 	git -c status.renames=true status >actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "renamed:" actual
 '
 
 test_expect_success 'commit honors status.renames=false' '
 	git -c status.renames=false commit --dry-run >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'commit honors status.renames=true' '
 	git -c status.renames=true commit --dry-run >actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "renamed:" actual
 '
 
 test_expect_success 'status config overridden' '
 	git -c status.renames=true status --no-renames >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'status score=100%' '
 	git status -M=100% >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual &&
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual &&
 
 	git status --find-renames=100% >actual &&
-	test_i18ngrep "deleted:" actual &&
-	test_i18ngrep "new file:" actual
+	test_grep "deleted:" actual &&
+	test_grep "new file:" actual
 '
 
 test_expect_success 'status score=01%' '
 	git status -M=01% >actual &&
-	test_i18ngrep "renamed:" actual &&
+	test_grep "renamed:" actual &&
 
 	git status --find-renames=01% >actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "renamed:" actual
 '
 
 test_expect_success 'copies not overridden by find-renames' '
@@ -103,12 +103,12 @@
 	git add copy &&
 
 	git -c status.renames=copies status -M=01% >actual &&
-	test_i18ngrep "copied:" actual &&
-	test_i18ngrep "renamed:" actual &&
+	test_grep "copied:" actual &&
+	test_grep "renamed:" actual &&
 
 	git -c status.renames=copies status --find-renames=01% >actual &&
-	test_i18ngrep "copied:" actual &&
-	test_i18ngrep "renamed:" actual
+	test_grep "copied:" actual &&
+	test_grep "renamed:" actual
 '
 
 test_done
diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh
index ad011bb..c97c550 100755
--- a/t/t7526-commit-pathspec-file.sh
+++ b/t/t7526-commit-pathspec-file.sh
@@ -141,25 +141,25 @@
 	>empty_list &&
 
 	test_must_fail git commit --pathspec-from-file=list --interactive -m "Commit" 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
 	test_must_fail git commit --pathspec-from-file=list --patch -m "Commit" 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
 	test_must_fail git commit --pathspec-from-file=list --all -m "Commit" 2>err &&
-	test_i18ngrep -e "options .--pathspec-from-file. and .-a. cannot be used together" err &&
+	test_grep -e "options .--pathspec-from-file. and .-a. cannot be used together" err &&
 
 	test_must_fail git commit --pathspec-from-file=list -m "Commit" -- fileA.t 2>err &&
-	test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
+	test_grep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
 	test_must_fail git commit --pathspec-file-nul -m "Commit" 2>err &&
-	test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
+	test_grep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
 	test_must_fail git commit --pathspec-from-file=empty_list --include -m "Commit" 2>err &&
-	test_i18ngrep -e "No paths with --include/--only does not make sense." err &&
+	test_grep -e "No paths with --include/--only does not make sense." err &&
 
 	test_must_fail git commit --pathspec-from-file=empty_list --only -m "Commit" 2>err &&
-	test_i18ngrep -e "No paths with --include/--only does not make sense." err
+	test_grep -e "No paths with --include/--only does not make sense." err
 '
 
 test_done
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 4c0327b..730f3c7 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -809,6 +809,11 @@
 		status --porcelain=v2 >actual.without &&
 	test_cmp actual.with actual.without &&
 
+	git -C super --no-optional-locks diff-index --name-status HEAD >actual.with &&
+	git -C super --no-optional-locks -c core.fsmonitor=false \
+		diff-index --name-status HEAD >actual.without &&
+	test_cmp actual.with actual.without &&
+
 	git -C super/dir_1/dir_2/sub reset --hard &&
 	git -C super/dir_1/dir_2/sub clean -d -f
 }
@@ -973,7 +978,7 @@
 	mkdir test_unicode/nfd &&
 	mkdir test_unicode/nfd/d_${utf8_nfd} &&
 
-	git -C test_unicode fsmonitor--daemon stop &&
+	test-tool -C test_unicode fsmonitor-client query --token 0 &&
 
 	if test_have_prereq UNICODE_NFC_PRESERVED
 	then
@@ -995,4 +1000,264 @@
 	grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
 '
 
+test_expect_success 'split-index and FSMonitor work well together' '
+	git init split-index &&
+	test_when_finished "git -C \"$PWD/split-index\" \
+		fsmonitor--daemon stop" &&
+	(
+		cd split-index &&
+		git config core.splitIndex true &&
+		# force split-index in most cases
+		git config splitIndex.maxPercentChange 99 &&
+		git config core.fsmonitor true &&
+
+		# Create the following commit topology:
+		#
+		# *   merge three
+		# |\
+		# | * three
+		# * | merge two
+		# |\|
+		# | * two
+		# * | one
+		# |/
+		# * 5a5efd7 initial
+
+		test_commit initial &&
+		test_commit two &&
+		test_commit three &&
+		git reset --hard initial &&
+		test_commit one &&
+		test_tick &&
+		git merge two &&
+		test_tick &&
+		git merge three &&
+
+		git rebase --force-rebase -r one
+	)
+'
+
+# The FSMonitor daemon reports the OBSERVED pathname of modified files
+# and thus contains the OBSERVED spelling on case-insensitive file
+# systems.  The daemon does not (and should not) load the .git/index
+# file and therefore does not know the expected case-spelling.  Since
+# it is possible for the user to create files/subdirectories with the
+# incorrect case, a modified file event for a tracked will not have
+# the EXPECTED case. This can cause `index_name_pos()` to incorrectly
+# report that the file is untracked. This causes the client to fail to
+# mark the file as possibly dirty (keeping the CE_FSMONITOR_VALID bit
+# set) so that `git status` will avoid inspecting it and thus not
+# present in the status output.
+#
+# The setup is a little contrived.
+#
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
+	test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
+
+	git init subdir_case_wrong &&
+	(
+		cd subdir_case_wrong &&
+		echo x >AAA &&
+		echo x >BBB &&
+
+		mkdir dir1 &&
+		echo x >dir1/file1 &&
+		mkdir dir1/dir2 &&
+		echo x >dir1/dir2/file2 &&
+		mkdir dir1/dir2/dir3 &&
+		echo x >dir1/dir2/dir3/file3 &&
+
+		echo x >yyy &&
+		echo x >zzz &&
+		git add . &&
+		git commit -m "data" &&
+
+		# This will cause "dir1/" and everything under it
+		# to be deleted.
+		git sparse-checkout set --cone --sparse-index &&
+
+		# Create dir2 with the wrong case and then let Git
+		# repopulate dir3 -- it will not correct the spelling
+		# of dir2.
+		mkdir dir1 &&
+		mkdir dir1/DIR2 &&
+		git sparse-checkout add dir1/dir2/dir3
+	) &&
+
+	start_daemon -C subdir_case_wrong --tf "$PWD/subdir_case_wrong.trace" &&
+
+	# Enable FSMonitor in the client. Run enough commands for
+	# the .git/index to sync up with the daemon with everything
+	# marked clean.
+	git -C subdir_case_wrong config core.fsmonitor true &&
+	git -C subdir_case_wrong update-index --fsmonitor &&
+	git -C subdir_case_wrong status &&
+
+	# Make some files dirty so that FSMonitor gets FSEvents for
+	# each of them.
+	echo xx >>subdir_case_wrong/AAA &&
+	echo xx >>subdir_case_wrong/dir1/DIR2/dir3/file3 &&
+	echo xx >>subdir_case_wrong/zzz &&
+
+	GIT_TRACE_FSMONITOR="$PWD/subdir_case_wrong.log" \
+		git -C subdir_case_wrong --no-optional-locks status --short \
+			>"$PWD/subdir_case_wrong.out" &&
+
+	# "git status" should have gotten file events for each of
+	# the 3 files.
+	#
+	# "dir2" should be in the observed case on disk.
+	grep "fsmonitor_refresh_callback" \
+		<"$PWD/subdir_case_wrong.log" \
+		>"$PWD/subdir_case_wrong.log1" &&
+
+	grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" &&
+	grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" &&
+
+	grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
+
+	# Verify that we get a mapping event to correct the case.
+	grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
+		"$PWD/subdir_case_wrong.log1" &&
+
+	# The refresh-callbacks should have caused "git status" to clear
+	# the CE_FSMONITOR_VALID bit on each of those files and caused
+	# the worktree scan to visit them and mark them as modified.
+	grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
+	grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
+	grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
+'
+
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
+	test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
+
+	git init file_case_wrong &&
+	(
+		cd file_case_wrong &&
+		echo x >AAA &&
+		echo x >BBB &&
+
+		mkdir dir1 &&
+		mkdir dir1/dir2 &&
+		mkdir dir1/dir2/dir3 &&
+		echo x >dir1/dir2/dir3/FILE-3-B &&
+		echo x >dir1/dir2/dir3/XXXX-3-X &&
+		echo x >dir1/dir2/dir3/file-3-a &&
+		echo x >dir1/dir2/dir3/yyyy-3-y &&
+		mkdir dir1/dir2/dir4 &&
+		echo x >dir1/dir2/dir4/FILE-4-A &&
+		echo x >dir1/dir2/dir4/XXXX-4-X &&
+		echo x >dir1/dir2/dir4/file-4-b &&
+		echo x >dir1/dir2/dir4/yyyy-4-y &&
+
+		echo x >yyy &&
+		echo x >zzz &&
+		git add . &&
+		git commit -m "data"
+	) &&
+
+	start_daemon -C file_case_wrong --tf "$PWD/file_case_wrong.trace" &&
+
+	# Enable FSMonitor in the client. Run enough commands for
+	# the .git/index to sync up with the daemon with everything
+	# marked clean.
+	git -C file_case_wrong config core.fsmonitor true &&
+	git -C file_case_wrong update-index --fsmonitor &&
+	git -C file_case_wrong status &&
+
+	# Make some files dirty so that FSMonitor gets FSEvents for
+	# each of them.
+	echo xx >>file_case_wrong/AAA &&
+	echo xx >>file_case_wrong/zzz &&
+
+	# Rename some files so that FSMonitor sees a create and delete
+	# FSEvent for each.  (A simple "mv foo FOO" is not portable
+	# between macOS and Windows. It works on both platforms, but makes
+	# the test messy, since (1) one platform updates "ctime" on the
+	# moved file and one does not and (2) it causes a directory event
+	# on one platform and not on the other which causes additional
+	# scanning during "git status" which causes a "H" vs "h" discrepancy
+	# in "git ls-files -f".)  So old-school it and move it out of the
+	# way and copy it to the case-incorrect name so that we get fresh
+	# "ctime" and "mtime" values.
+
+	mv file_case_wrong/dir1/dir2/dir3/file-3-a file_case_wrong/dir1/dir2/dir3/ORIG &&
+	cp file_case_wrong/dir1/dir2/dir3/ORIG     file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+	rm file_case_wrong/dir1/dir2/dir3/ORIG &&
+	mv file_case_wrong/dir1/dir2/dir4/FILE-4-A file_case_wrong/dir1/dir2/dir4/ORIG &&
+	cp file_case_wrong/dir1/dir2/dir4/ORIG     file_case_wrong/dir1/dir2/dir4/file-4-a &&
+	rm file_case_wrong/dir1/dir2/dir4/ORIG &&
+
+	# Run status enough times to fully sync.
+	#
+	# The first instance should get the create and delete FSEvents
+	# for each pair.  Status should update the index with a new FSM
+	# token (so the next invocation will not see data for these
+	# events).
+
+	GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \
+		git -C file_case_wrong status --short \
+			>"$PWD/file_case_wrong-try1.out" &&
+	grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" &&
+	grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4"  "$PWD/file_case_wrong-try1.log" &&
+	grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6"  "$PWD/file_case_wrong-try1.log" &&
+	grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" &&
+
+	# FSM refresh will have invalidated the FSM bit and cause a regular
+	# (real) scan of these tracked files, so they should have "H" status.
+	# (We will not see a "h" status until the next refresh (on the next
+	# command).)
+
+	git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" &&
+	grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" &&
+	grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" &&
+
+
+	# Try the status again. We assume that the above status command
+	# advanced the token so that the next one will not see those events.
+
+	GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \
+		git -C file_case_wrong status --short \
+			>"$PWD/file_case_wrong-try2.out" &&
+	! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+	! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+	! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+	! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+
+	# FSM refresh saw nothing, so it will mark all files as valid,
+	# so they should now have "h" status.
+
+	git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" &&
+	grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" &&
+	grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" &&
+
+
+	# We now have files with clean content, but with case-incorrect
+	# file names.  Modify them to see if status properly reports
+	# them.
+
+	echo xx >>file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+	echo xx >>file_case_wrong/dir1/dir2/dir4/file-4-a &&
+
+	GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
+		git -C file_case_wrong --no-optional-locks status --short \
+			>"$PWD/file_case_wrong-try3.out" &&
+
+	# Verify that we get a mapping event to correct the case.
+	grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
+		"$PWD/file_case_wrong-try3.log" &&
+	grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
+		"$PWD/file_case_wrong-try3.log" &&
+
+	# FSEvents are in observed case.
+	grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
+	grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
+
+	# The refresh-callbacks should have caused "git status" to clear
+	# the CE_FSMONITOR_VALID bit on each of those files and caused
+	# the worktree scan to visit them and mark them as modified.
+	grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
+	grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
+'
+
 test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 060e145..e5ff073 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -175,7 +175,7 @@
 		>.git/index &&
 		test_expect_code 129 git merge -h 2>usage
 	) &&
-	test_i18ngrep "[Uu]sage: git merge" broken/usage
+	test_grep "[Uu]sage: git merge" broken/usage
 '
 
 test_expect_success 'reject non-strategy with a git-merge-foo name' '
@@ -639,41 +639,41 @@
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
-       git reset --hard c1 &&
-       test_tick &&
-       git merge c0 c2 c0 c1 &&
-       verify_merge file result.1-5 &&
-       verify_parents $c1 $c2
+	git reset --hard c1 &&
+	test_tick &&
+	git merge c0 c2 c0 c1 &&
+	verify_merge file result.1-5 &&
+	verify_parents $c1 $c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
-       git reset --hard c1 &&
-       test_tick &&
-       git merge c0 c2 c0 c1 &&
-       verify_merge file result.1-5 &&
-       verify_parents $c1 $c2
+	git reset --hard c1 &&
+	test_tick &&
+	git merge c0 c2 c0 c1 &&
+	verify_merge file result.1-5 &&
+	verify_parents $c1 $c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c1 and c2' '
-       git reset --hard c1 &&
-       test_tick &&
-       git merge c1 c2 &&
-       verify_merge file result.1-5 &&
-       verify_parents $c1 $c2
+	git reset --hard c1 &&
+	test_tick &&
+	git merge c1 c2 &&
+	verify_merge file result.1-5 &&
+	verify_parents $c1 $c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge fast-forward in a dirty tree' '
-       git reset --hard c0 &&
-       mv file file1 &&
-       cat file1 >file &&
-       rm -f file1 &&
-       git merge c2
+	git reset --hard c0 &&
+	mv file file1 &&
+	cat file1 >file &&
+	rm -f file1 &&
+	git merge c2
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
@@ -681,7 +681,7 @@
 test_expect_success 'in-index merge' '
 	git reset --hard c0 &&
 	git merge --no-ff -s resolve c1 >out &&
-	test_i18ngrep "Wonderful." out &&
+	test_grep "Wonderful." out &&
 	verify_parents $c0 $c1
 '
 
@@ -697,7 +697,7 @@
 	git reset --hard c1 &&
 	git merge-file file file.orig file.9 &&
 	git merge --autostash c2 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git show HEAD:file >merge-result &&
 	test_cmp result.1-5 merge-result &&
 	test_cmp result.1-5-9 file
@@ -708,7 +708,7 @@
 	git reset --hard c1 &&
 	git merge-file file file.orig file.9 &&
 	git merge c2 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git show HEAD:file >merge-result &&
 	test_cmp result.1-5 merge-result &&
 	test_cmp result.1-5-9 file
@@ -718,7 +718,7 @@
 	git reset --hard c0 &&
 	git merge-file file file.orig file.5 &&
 	git merge --autostash c1 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	test_cmp result.1-5 file
 '
 
@@ -728,7 +728,7 @@
 	cp file.5 other &&
 	test_when_finished "rm other" &&
 	test_must_fail git merge --autostash c1 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	test_cmp file.5 file
 '
 
@@ -736,7 +736,7 @@
 	git reset --hard c1 &&
 	git merge-file file file.orig file.3 &&
 	git merge --autostash c2 c3 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git show HEAD:file >merge-result &&
 	test_cmp result.1-5-9 merge-result &&
 	test_cmp result.1-3-5-9 file
@@ -746,7 +746,7 @@
 	git reset --hard c1 &&
 	git merge-file file file.orig file.5 &&
 	test_must_fail git merge -s recursive --autostash c2 c3 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	test_cmp result.1-5 file
 '
 
@@ -755,7 +755,7 @@
 	cp file.1 file &&
 	test_must_fail git merge --autostash c7 &&
 	git merge --abort 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	test_cmp file.1 file
 '
 
@@ -767,7 +767,7 @@
 	git stash show -p MERGE_AUTOSTASH >actual &&
 	test_cmp expect actual &&
 	git commit 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git show HEAD:file >merge-result &&
 	test_cmp result.1-5 merge-result &&
 	test_cmp result.1-5-9 file
@@ -781,7 +781,7 @@
 	git stash show -p MERGE_AUTOSTASH >actual &&
 	test_cmp expect actual &&
 	git merge --continue 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git show HEAD:file >merge-result &&
 	test_cmp result.1-5 merge-result &&
 	test_cmp result.1-5-9 file
@@ -795,7 +795,7 @@
 	git stash show -p MERGE_AUTOSTASH >actual &&
 	test_cmp expect actual &&
 	git merge --abort 2>err &&
-	test_i18ngrep "Applied autostash." err &&
+	test_grep "Applied autostash." err &&
 	git diff >actual &&
 	test_cmp expect actual
 '
@@ -808,7 +808,7 @@
 	git stash show -p MERGE_AUTOSTASH >actual &&
 	test_cmp expect actual &&
 	git reset --hard 2>err &&
-	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+	test_grep "Autostash exists; creating a new stash entry." err &&
 	git diff --exit-code
 '
 
@@ -821,7 +821,7 @@
 	test_cmp expect actual &&
 	git diff HEAD >expect &&
 	git merge --quit 2>err &&
-	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+	test_grep "Autostash exists; creating a new stash entry." err &&
 	git diff HEAD >actual &&
 	test_cmp expect actual
 '
@@ -832,7 +832,7 @@
 	git diff >expect &&
 	test_when_finished "test_might_fail git stash drop" &&
 	git merge --autostash c3 2>err &&
-	test_i18ngrep "Applying autostash resulted in conflicts." err &&
+	test_grep "Applying autostash 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/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index bd238d8..a94387a 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -30,117 +30,117 @@
 test_expect_success 'pull.rebase not set, ff possible' '
 	git reset --hard c0 &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=true' '
 	git reset --hard c0 &&
 	test_config pull.ff true &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=false' '
 	git reset --hard c0 &&
 	test_config pull.ff false &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=only' '
 	git reset --hard c0 &&
 	test_config pull.ff only &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --rebase given' '
 	git reset --hard c0 &&
 	git pull --rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-rebase given' '
 	git reset --hard c0 &&
 	git pull --no-rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff given' '
 	git reset --hard c0 &&
 	git pull --ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-ff given' '
 	git reset --hard c0 &&
 	git pull --no-ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff-only given' '
 	git reset --hard c0 &&
 	git pull --ff-only . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_must_fail git -c color.advice=always pull . c1 2>err &&
 	test_decode_color <err >decoded &&
-	test_i18ngrep "<YELLOW>hint: " decoded &&
-	test_i18ngrep "You have divergent branches" decoded
+	test_grep "<YELLOW>hint: " decoded &&
+	test_grep "You have divergent branches" decoded
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_config pull.ff true &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_config pull.ff false &&
 	git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_config pull.ff only &&
 	test_must_fail git pull . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --no-rebase . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
 	git reset --hard c2 &&
 	git pull --no-ff . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
 	git reset --hard c2 &&
 	test_must_fail git pull --ff-only . c1 2>err &&
-	test_i18ngrep ! "You have divergent branches" err
+	test_grep ! "You have divergent branches" err
 '
 
 test_does_rebase () {
@@ -202,7 +202,7 @@
 test_attempts_fast_forward () {
 	git reset --hard c2 &&
 	test_must_fail git "$@" . c1 2>err &&
-	test_i18ngrep "Not possible to fast-forward, aborting" err
+	test_grep "Not possible to fast-forward, aborting" err
 }
 
 #
@@ -328,34 +328,34 @@
 test_expect_success 'Multiple heads warns about inability to fast forward' '
 	git reset --hard c1 &&
 	test_must_fail git pull . c2 c3 2>err &&
-	test_i18ngrep "You have divergent branches" err
+	test_grep "You have divergent branches" err
 '
 
 test_expect_success 'Multiple can never be fast forwarded' '
 	git reset --hard c0 &&
 	test_must_fail git -c pull.ff=only pull . c1 c2 c3 2>err &&
-	test_i18ngrep ! "You have divergent branches" err &&
+	test_grep ! "You have divergent branches" err &&
 	# In addition to calling out "cannot fast-forward", we very much
 	# want the "multiple branches" piece to be called out to users.
-	test_i18ngrep "Cannot fast-forward to multiple branches" err
+	test_grep "Cannot fast-forward to multiple branches" err
 '
 
 test_expect_success 'Cannot rebase with multiple heads' '
 	git reset --hard c0 &&
 	test_must_fail git -c pull.rebase=true pull . c1 c2 c3 2>err &&
-	test_i18ngrep ! "You have divergent branches" err &&
-	test_i18ngrep "Cannot rebase onto multiple branches." err
+	test_grep ! "You have divergent branches" err &&
+	test_grep "Cannot rebase onto multiple branches." err
 '
 
 test_expect_success 'merge c1 with c2' '
 	git reset --hard c1 &&
-	test -f c0.c &&
-	test -f c1.c &&
-	test ! -f c2.c &&
-	test ! -f c3.c &&
+	test_path_is_file c0.c &&
+	test_path_is_file c1.c &&
+	test_path_is_missing c2.c &&
+	test_path_is_missing c3.c &&
 	git merge c2 &&
-	test -f c1.c &&
-	test -f c2.c
+	test_path_is_file c1.c &&
+	test_path_is_file c2.c
 '
 
 test_expect_success 'fast-forward pull succeeds with "true" in pull.ff' '
@@ -411,8 +411,8 @@
 	git reset --hard c1 &&
 	git config pull.twohead ours &&
 	git merge c2 &&
-	test -f c1.c &&
-	! test -f c2.c
+	test_path_is_file c1.c &&
+	test_path_is_missing c2.c
 '
 
 test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '
@@ -431,10 +431,10 @@
 	test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
 	test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
 	git diff --exit-code &&
-	test -f c0.c &&
-	test -f c1.c &&
-	test -f c2.c &&
-	test -f c3.c
+	test_path_is_file c0.c &&
+	test_path_is_file c1.c &&
+	test_path_is_file c2.c &&
+	test_path_is_file c3.c
 '
 
 conflict_count()
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index ff085b0..3669d33 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -4,6 +4,7 @@
 
 Testing octopus merge with more than 25 refs.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index 4887ca7..0e85b21 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -4,6 +4,7 @@
 
 Testing octopus merge when reducing parents to independent branches.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # 0 - 1
diff --git a/t/t7607-merge-state.sh b/t/t7607-merge-state.sh
index 89a62ac..9001674 100755
--- a/t/t7607-merge-state.sh
+++ b/t/t7607-merge-state.sh
@@ -4,6 +4,7 @@
 
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'Ensure we restore original state if no merge strategy handles it' '
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
index 0b908ab..2179938 100755
--- a/t/t7608-merge-messages.sh
+++ b/t/t7608-merge-messages.sh
@@ -4,6 +4,7 @@
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_oneline() {
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 7b95702..22b3a85 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -860,4 +860,42 @@
 	git commit -m "test resolved with mergetool"
 '
 
+test_expect_success 'mergetool with guiDefault' '
+	test_config merge.guitool myguitool &&
+	test_config mergetool.myguitool.cmd "(printf \"gui \" && cat \"\$REMOTE\") >\"\$MERGED\"" &&
+	test_config mergetool.myguitool.trustExitCode true &&
+	test_when_finished "git reset --hard" &&
+	git checkout -b test$test_count branch1 &&
+	git submodule update -N &&
+	test_must_fail git merge main &&
+
+	test_config mergetool.guiDefault auto &&
+	DISPLAY=SOMETHING && export DISPLAY &&
+	yes "" | git mergetool both &&
+	yes "" | git mergetool file1 file1 &&
+
+	DISPLAY= && export DISPLAY &&
+	yes "" | git mergetool file2 "spaced name" &&
+
+	test_config mergetool.guiDefault true &&
+	yes "" | git mergetool subdir/file3 &&
+
+	yes "d" | git mergetool file11 &&
+	yes "d" | git mergetool file12 &&
+	yes "l" | git mergetool submod &&
+
+	echo "gui main updated" >expect &&
+	test_cmp expect file1 &&
+
+	echo "main new" >expect &&
+	test_cmp expect file2 &&
+
+	echo "gui main new sub" >expect &&
+	test_cmp expect subdir/file3 &&
+
+	echo "branch1 submodule" >expect &&
+	test_cmp expect submod/bar &&
+	git commit -m "branch1 resolved with mergetool"
+'
+
 test_done
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
index c0e9425..d6975ca 100755
--- a/t/t7611-merge-abort.sh
+++ b/t/t7611-merge-abort.sh
@@ -50,7 +50,7 @@
 
 test_expect_success 'fails without MERGE_HEAD (unstarted merge)' '
 	test_must_fail git merge --abort 2>output &&
-	test_i18ngrep MERGE_HEAD output
+	test_grep MERGE_HEAD output
 '
 
 test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' '
@@ -64,7 +64,7 @@
 	# Merge successfully completed
 	post_merge_head="$(git rev-parse HEAD)" &&
 	test_must_fail git merge --abort 2>output &&
-	test_i18ngrep MERGE_HEAD output
+	test_grep MERGE_HEAD output
 '
 
 test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' '
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
index f5c90cc..84ddb56 100755
--- a/t/t7612-merge-verify-signatures.sh
+++ b/t/t7612-merge-verify-signatures.sh
@@ -41,54 +41,54 @@
 test_expect_success GPG 'merge unsigned commit with verification' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
-	test_i18ngrep "does not have a GPG signature" mergeerror
+	test_grep "does not have a GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge unsigned commit with merge.verifySignatures=true' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config merge.verifySignatures true &&
 	test_must_fail git merge --ff-only side-unsigned 2>mergeerror &&
-	test_i18ngrep "does not have a GPG signature" mergeerror
+	test_grep "does not have a GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with bad signature with verification' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
-	test_i18ngrep "has a bad GPG signature" mergeerror
+	test_grep "has a bad GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config merge.verifySignatures true &&
 	test_must_fail git merge --ff-only $(cat forged.commit) 2>mergeerror &&
-	test_i18ngrep "has a bad GPG signature" mergeerror
+	test_grep "has a bad GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with verification' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
-	test_i18ngrep "has an untrusted GPG signature" mergeerror
+	test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with verification and high minTrustLevel' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config gpg.minTrustLevel marginal &&
 	test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
-	test_i18ngrep "has an untrusted GPG signature" mergeerror
+	test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with verification and low minTrustLevel' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config gpg.minTrustLevel undefined &&
 	git merge --ff-only --verify-signatures side-untrusted >mergeoutput &&
-	test_i18ngrep "has a good GPG signature" mergeoutput
+	test_grep "has a good GPG signature" mergeoutput
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config merge.verifySignatures true &&
 	test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
-	test_i18ngrep "has an untrusted GPG signature" mergeerror
+	test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true and minTrustLevel' '
@@ -96,20 +96,20 @@
 	test_config merge.verifySignatures true &&
 	test_config gpg.minTrustLevel marginal &&
 	test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
-	test_i18ngrep "has an untrusted GPG signature" mergeerror
+	test_grep "has an untrusted GPG signature" mergeerror
 '
 
 test_expect_success GPG 'merge signed commit with verification' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
-	test_i18ngrep "has a good GPG signature" mergeoutput
+	test_grep "has a good GPG signature" mergeoutput
 '
 
 test_expect_success GPG 'merge signed commit with merge.verifySignatures=true' '
 	test_when_finished "git reset --hard && git checkout initial" &&
 	test_config merge.verifySignatures true &&
 	git merge --verbose --ff-only side-signed >mergeoutput &&
-	test_i18ngrep "has a good GPG signature" mergeoutput
+	test_grep "has a good GPG signature" mergeoutput
 '
 
 test_expect_success GPG 'merge commit with bad signature without verification' '
@@ -133,7 +133,7 @@
 	test_when_finished "git checkout initial" &&
 	git checkout --orphan unborn &&
 	test_must_fail git merge --verify-signatures side-unsigned 2>mergeerror &&
-	test_i18ngrep "does not have a GPG signature" mergeerror
+	test_grep "does not have a GPG signature" mergeerror
 '
 
 test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 4aabe98..94f9f4a 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -10,6 +10,10 @@
 commit_and_pack () {
 	test_commit "$@" 1>&2 &&
 	incrpackid=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) &&
+	# Remove any loose object(s) created by test_commit, since they have
+	# already been packed. Leaving these around can create subtly different
+	# packs with `pack-objects`'s `--unpacked` option.
+	git prune-packed 1>&2 &&
 	echo pack-${incrpackid}.pack
 }
 
@@ -106,6 +110,23 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--local disables writing bitmaps when connected to alternate ODB' '
+	test_when_finished "rm -rf shared member" &&
+
+	git init shared &&
+	git clone --shared shared member &&
+	(
+		cd member &&
+		test_commit "object" &&
+		GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adl --write-bitmap-index 2>err &&
+		cat >expect <<-EOF &&
+		warning: disabling bitmap writing, as some objects are not being packed
+		EOF
+		test_cmp expect err &&
+		test_path_is_missing .git/objects/pack-*.bitmap
+	)
+'
+
 test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
 	mkdir alt_objects/pack &&
 	mv .git/objects/pack/* alt_objects/pack &&
@@ -192,6 +213,8 @@
 	test_create_repo keep-pack &&
 	(
 		cd keep-pack &&
+		# avoid producing different packs due to delta/base choices
+		git config pack.window 0 &&
 		P1=$(commit_and_pack 1) &&
 		P2=$(commit_and_pack 2) &&
 		P3=$(commit_and_pack 3) &&
@@ -203,10 +226,61 @@
 		grep -q $P1 new-counts &&
 		grep -q $P4 new-counts &&
 		test_line_count = 3 new-counts &&
+		git fsck &&
+
+		P5=$(commit_and_pack --no-tag 5) &&
+		git reset --hard HEAD^ &&
+		git reflog expire --all --expire=all &&
+		rm -f ".git/objects/pack/${P5%.pack}.idx" &&
+		rm -f ".git/objects/info/commit-graph" &&
+		for from in $(find .git/objects/pack -type f -name "${P5%.pack}.*")
+		do
+			to="$(dirname "$from")/.tmp-1234-$(basename "$from")" &&
+			mv "$from" "$to" || return 1
+		done &&
+
+		# A .idx file without a .pack should not stop us from
+		# repacking what we can.
+		touch .git/objects/pack/pack-does-not-exist.idx &&
+
+		git repack --cruft -d --keep-pack $P1 --keep-pack $P4 &&
+
+		ls .git/objects/pack/*.pack >newer-counts &&
+		test_cmp new-counts newer-counts &&
 		git fsck
 	)
 '
 
+test_expect_success 'repacking fails when missing .pack actually means missing objects' '
+	test_create_repo idx-without-pack &&
+	(
+		cd idx-without-pack &&
+
+		# Avoid producing different packs due to delta/base choices
+		git config pack.window 0 &&
+		P1=$(commit_and_pack 1) &&
+		P2=$(commit_and_pack 2) &&
+		P3=$(commit_and_pack 3) &&
+		P4=$(commit_and_pack 4) &&
+		ls .git/objects/pack/*.pack >old-counts &&
+		test_line_count = 4 old-counts &&
+
+		# Remove one .pack file
+		rm .git/objects/pack/$P2 &&
+
+		ls .git/objects/pack/*.pack >before-pack-dir &&
+
+		test_must_fail git fsck &&
+		test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git repack --cruft -d 2>err &&
+		grep "bad object" err &&
+
+		# Before failing, the repack did not modify the
+		# pack directory.
+		ls .git/objects/pack/*.pack >after-pack-dir &&
+		test_cmp before-pack-dir after-pack-dir
+	)
+'
+
 test_expect_success 'bitmaps are created by default in bare repos' '
 	git clone --bare .git bare.git &&
 	rm -f bare.git/objects/pack/*.bitmap &&
@@ -253,6 +327,203 @@
 	test_must_be_empty actual
 '
 
+test_expect_success 'repacking with a filter works' '
+	git -C bare.git repack -a -d &&
+	test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+	git -C bare.git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+	test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+	commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+	blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+	test "$commit_pack" != "$blob_pack" &&
+	tree_pack=$(test-tool -C bare.git find-pack -c 1 HEAD^{tree}) &&
+	test "$tree_pack" = "$commit_pack" &&
+	blob_pack2=$(test-tool -C bare.git find-pack -c 1 HEAD:file2) &&
+	test "$blob_pack2" = "$blob_pack"
+'
+
+test_expect_success '--filter fails with --write-bitmap-index' '
+	test_must_fail \
+		env GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+		git -C bare.git repack -a -d --write-bitmap-index --filter=blob:none
+'
+
+test_expect_success 'repacking with two filters works' '
+	git init two-filters &&
+	(
+		cd two-filters &&
+		mkdir subdir &&
+		test_commit foo &&
+		test_commit subdir_bar subdir/bar &&
+		test_commit subdir_baz subdir/baz
+	) &&
+	git clone --no-local --bare two-filters two-filters.git &&
+	(
+		cd two-filters.git &&
+		test_stdout_line_count = 1 ls objects/pack/*.pack &&
+		git -c repack.writebitmaps=false repack -a -d \
+			--filter=blob:none --filter=tree:1 &&
+		test_stdout_line_count = 2 ls objects/pack/*.pack &&
+		commit_pack=$(test-tool find-pack -c 1 HEAD) &&
+		blob_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+		root_tree_pack=$(test-tool find-pack -c 1 HEAD^{tree}) &&
+		subdir_tree_hash=$(git ls-tree --object-only HEAD -- subdir) &&
+		subdir_tree_pack=$(test-tool find-pack -c 1 "$subdir_tree_hash") &&
+
+		# Root tree and subdir tree are not in the same packfiles
+		test "$commit_pack" != "$blob_pack" &&
+		test "$commit_pack" = "$root_tree_pack" &&
+		test "$blob_pack" = "$subdir_tree_pack"
+	)
+'
+
+prepare_for_keep_packs () {
+	git init keep-packs &&
+	(
+		cd keep-packs &&
+		test_commit foo &&
+		test_commit bar
+	) &&
+	git clone --no-local --bare keep-packs keep-packs.git &&
+	(
+		cd keep-packs.git &&
+
+		# Create two packs
+		# The first pack will contain all of the objects except one blob
+		git rev-list --objects --all >objs &&
+		grep -v "bar.t" objs | git pack-objects pack &&
+		# The second pack will contain the excluded object and be kept
+		packid=$(grep "bar.t" objs | git pack-objects pack) &&
+		>pack-$packid.keep &&
+
+		# Replace the existing pack with the 2 new ones
+		rm -f objects/pack/pack* &&
+		mv pack-* objects/pack/
+	)
+}
+
+test_expect_success '--filter works with .keep packs' '
+	prepare_for_keep_packs &&
+	(
+		cd keep-packs.git &&
+
+		foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+		bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+		head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+		test "$foo_pack" != "$bar_pack" &&
+		test "$foo_pack" = "$head_pack" &&
+
+		git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+
+		foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+		bar_pack_1=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+		head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+		# Object bar is still only in the old .keep pack
+		test "$foo_pack_1" != "$foo_pack" &&
+		test "$bar_pack_1" = "$bar_pack" &&
+		test "$head_pack_1" != "$head_pack" &&
+
+		test "$foo_pack_1" != "$bar_pack_1" &&
+		test "$foo_pack_1" != "$head_pack_1" &&
+		test "$bar_pack_1" != "$head_pack_1"
+	)
+'
+
+test_expect_success '--filter works with --pack-kept-objects and .keep packs' '
+	rm -rf keep-packs keep-packs.git &&
+	prepare_for_keep_packs &&
+	(
+		cd keep-packs.git &&
+
+		foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+		bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+		head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+		test "$foo_pack" != "$bar_pack" &&
+		test "$foo_pack" = "$head_pack" &&
+
+		git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+			--pack-kept-objects &&
+
+		foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+		test-tool find-pack -c 2 HEAD:bar.t >bar_pack_1 &&
+		head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+		test "$foo_pack_1" != "$foo_pack" &&
+		test "$foo_pack_1" != "$bar_pack" &&
+		test "$head_pack_1" != "$head_pack" &&
+
+		# Object bar is in both the old .keep pack and the new
+		# pack that contained the filtered out objects
+		grep "$bar_pack" bar_pack_1 &&
+		grep "$foo_pack_1" bar_pack_1 &&
+		test "$foo_pack_1" != "$head_pack_1"
+	)
+'
+
+test_expect_success '--filter-to stores filtered out objects' '
+	git -C bare.git repack -a -d &&
+	test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+	git init --bare filtered.git &&
+	git -C bare.git -c repack.writebitmaps=false repack -a -d \
+		--filter=blob:none \
+		--filter-to=../filtered.git/objects/pack/pack &&
+	test_stdout_line_count = 1 ls bare.git/objects/pack/pack-*.pack &&
+	test_stdout_line_count = 1 ls filtered.git/objects/pack/pack-*.pack &&
+
+	commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+	blob_pack=$(test-tool -C bare.git find-pack -c 0 HEAD:file1) &&
+	blob_hash=$(git -C bare.git rev-parse HEAD:file1) &&
+	test -n "$blob_hash" &&
+	blob_pack=$(test-tool -C filtered.git find-pack -c 1 $blob_hash) &&
+
+	echo $(pwd)/filtered.git/objects >bare.git/objects/info/alternates &&
+	blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+	blob_content=$(git -C bare.git show $blob_hash) &&
+	test "$blob_content" = "content1"
+'
+
+test_expect_success '--filter works with --max-pack-size' '
+	rm -rf filtered.git &&
+	git init --bare filtered.git &&
+	git init max-pack-size &&
+	(
+		cd max-pack-size &&
+		test_commit base &&
+		# two blobs which exceed the maximum pack size
+		test-tool genrandom foo 1048576 >foo &&
+		git hash-object -w foo &&
+		test-tool genrandom bar 1048576 >bar &&
+		git hash-object -w bar &&
+		git add foo bar &&
+		git commit -m "adding foo and bar"
+	) &&
+	git clone --no-local --bare max-pack-size max-pack-size.git &&
+	(
+		cd max-pack-size.git &&
+		git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+			--max-pack-size=1M \
+			--filter-to=../filtered.git/objects/pack/pack &&
+		echo $(cd .. && pwd)/filtered.git/objects >objects/info/alternates &&
+
+		# Check that the 3 blobs are in different packfiles in filtered.git
+		test_stdout_line_count = 3 ls ../filtered.git/objects/pack/pack-*.pack &&
+		test_stdout_line_count = 1 ls objects/pack/pack-*.pack &&
+		foo_pack=$(test-tool find-pack -c 1 HEAD:foo) &&
+		bar_pack=$(test-tool find-pack -c 1 HEAD:bar) &&
+		base_pack=$(test-tool find-pack -c 1 HEAD:base.t) &&
+		test "$foo_pack" != "$bar_pack" &&
+		test "$foo_pack" != "$base_pack" &&
+		test "$bar_pack" != "$base_pack" &&
+		for pack in "$foo_pack" "$bar_pack" "$base_pack"
+		do
+			case "$foo_pack" in */filtered.git/objects/pack/*) true ;; *) return 1 ;; esac
+		done
+	)
+'
+
 objdir=.git/objects
 midx=$objdir/pack/multi-pack-index
 
@@ -443,10 +714,10 @@
 '
 
 test_expect_success '--write-midx removes stale pack-based bitmaps' '
-       rm -fr repo &&
-       git init repo &&
-       test_when_finished "rm -fr repo" &&
-       (
+	rm -fr repo &&
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
 		cd repo &&
 		test_commit base &&
 		GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ab &&
@@ -460,7 +731,7 @@
 		test_path_is_file $midx &&
 		test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
 		test_path_is_missing $pack_bitmap
-       )
+	)
 '
 
 test_expect_success '--write-midx with --pack-kept-objects' '
@@ -559,125 +830,4 @@
 	test_server_info_missing
 '
 
-test_expect_success '--expire-to stores pruned objects (now)' '
-	git init expire-to-now &&
-	(
-		cd expire-to-now &&
-
-		git branch -M main &&
-
-		test_commit base &&
-
-		git checkout -b cruft &&
-		test_commit --no-tag cruft &&
-
-		git rev-list --objects --no-object-names main..cruft >moved.raw &&
-		sort moved.raw >moved.want &&
-
-		git rev-list --all --objects --no-object-names >expect.raw &&
-		sort expect.raw >expect &&
-
-		git checkout main &&
-		git branch -D cruft &&
-		git reflog expire --all --expire=all &&
-
-		git init --bare expired.git &&
-		git repack -d \
-			--cruft --cruft-expiration="now" \
-			--expire-to="expired.git/objects/pack/pack" &&
-
-		expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
-		test_path_is_file "${expired%.idx}.mtimes" &&
-
-		# Since the `--cruft-expiration` is "now", the effective
-		# behavior is to move _all_ unreachable objects out to
-		# the location in `--expire-to`.
-		git show-index <$expired >expired.raw &&
-		cut -d" " -f2 expired.raw | sort >expired.objects &&
-		git rev-list --all --objects --no-object-names \
-			>remaining.objects &&
-
-		# ...in other words, the combined contents of this
-		# repository and expired.git should be the same as the
-		# set of objects we started with.
-		cat expired.objects remaining.objects | sort >actual &&
-		test_cmp expect actual &&
-
-		# The "moved" objects (i.e., those in expired.git)
-		# should be the same as the cruft objects which were
-		# expired in the previous step.
-		test_cmp moved.want expired.objects
-	)
-'
-
-test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
-	git init expire-to-5.minutes.ago &&
-	(
-		cd expire-to-5.minutes.ago &&
-
-		git branch -M main &&
-
-		test_commit base &&
-
-		# Create two classes of unreachable objects, one which
-		# is older than 5 minutes (stale), and another which is
-		# newer (recent).
-		for kind in stale recent
-		do
-			git checkout -b $kind main &&
-			test_commit --no-tag $kind || return 1
-		done &&
-
-		git rev-list --objects --no-object-names main..stale >in &&
-		stale="$(git pack-objects $objdir/pack/pack <in)" &&
-		mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
-
-		# expect holds the set of objects we expect to find in
-		# this repository after repacking
-		git rev-list --objects --no-object-names recent >expect.raw &&
-		sort expect.raw >expect &&
-
-		# moved.want holds the set of objects we expect to find
-		# in expired.git
-		git rev-list --objects --no-object-names main..stale >out &&
-		sort out >moved.want &&
-
-		git checkout main &&
-		git branch -D stale recent &&
-		git reflog expire --all --expire=all &&
-		git prune-packed &&
-
-		git init --bare expired.git &&
-		git repack -d \
-			--cruft --cruft-expiration=5.minutes.ago \
-			--expire-to="expired.git/objects/pack/pack" &&
-
-		# Some of the remaining objects in this repository are
-		# unreachable, so use `cat-file --batch-all-objects`
-		# instead of `rev-list` to get their names
-		git cat-file --batch-all-objects --batch-check="%(objectname)" \
-			>remaining.objects &&
-		sort remaining.objects >actual &&
-		test_cmp expect actual &&
-
-		(
-			cd expired.git &&
-
-			expired="$(ls objects/pack/pack-*.mtimes)" &&
-			test-tool pack-mtimes $(basename $expired) >out &&
-			cut -d" " -f1 out | sort >../moved.got &&
-
-			# Ensure that there are as many objects with the
-			# expected mtime as were moved to expired.git.
-			#
-			# In other words, ensure that the recorded
-			# mtimes of any moved objects was written
-			# correctly.
-			grep " $mtime$" out >matching &&
-			test_line_count = $(wc -l <../moved.want) matching
-		) &&
-		test_cmp moved.want moved.got
-	)
-'
-
 test_done
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index ebb2678..fe6c3e7 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -113,6 +113,48 @@
 	test_must_fail git cat-file -p $obj2
 '
 
+test_expect_success 'gc.recentObjectsHook' '
+	obj1=$(echo one | git hash-object -w --stdin) &&
+	obj2=$(echo two | git hash-object -w --stdin) &&
+	obj3=$(echo three | git hash-object -w --stdin) &&
+	pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+	pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+	pack3=$(echo $obj3 | git pack-objects .git/objects/pack/pack) &&
+	git prune-packed &&
+
+	git cat-file -p $obj1 &&
+	git cat-file -p $obj2 &&
+	git cat-file -p $obj3 &&
+
+	# make an unreachable annotated tag object to ensure we rescue objects
+	# which are reachable from non-pruned unreachable objects
+	obj2_tag="$(git mktag <<-EOF
+	object $obj2
+	type blob
+	tag obj2-tag
+	tagger T A Gger <tagger@example.com> 1234567890 -0000
+	EOF
+	)" &&
+
+	obj2_tag_pack="$(echo $obj2_tag | git pack-objects .git/objects/pack/pack)" &&
+	git prune-packed &&
+
+	write_script precious-objects <<-EOF &&
+	echo $obj2_tag
+	EOF
+	git config gc.recentObjectsHook ./precious-objects &&
+
+	test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+	test-tool chmtime =-86400 .git/objects/pack/pack-$pack3.pack &&
+	test-tool chmtime =-86400 .git/objects/pack/pack-$obj2_tag_pack.pack &&
+	git repack -A -d --unpack-unreachable=1.hour.ago &&
+
+	git cat-file -p $obj1 &&
+	git cat-file -p $obj2 &&
+	git cat-file -p $obj2_tag &&
+	test_must_fail git cat-file -p $obj3
+'
+
 test_expect_success 'keep packed objects found only in index' '
 	echo my-unique-content >file &&
 	git add file &&
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh
index 8821fbd..9fc1626 100755
--- a/t/t7703-repack-geometric.sh
+++ b/t/t7703-repack-geometric.sh
@@ -10,6 +10,12 @@
 packdir=$objdir/pack
 midx=$objdir/pack/multi-pack-index
 
+packed_objects () {
+	git show-index <"$1" >tmp-object-list &&
+	cut -d' ' -f2 tmp-object-list | sort &&
+	rm tmp-object-list
+ }
+
 test_expect_success '--geometric with no packs' '
 	git init geometric &&
 	test_when_finished "rm -fr geometric" &&
@@ -17,7 +23,7 @@
 		cd geometric &&
 
 		git repack --write-midx --geometric 2 >out &&
-		test_i18ngrep "Nothing new to pack" out
+		test_grep "Nothing new to pack" out
 	)
 '
 
@@ -32,7 +38,7 @@
 
 		git repack --geometric 2 >out &&
 
-		test_i18ngrep "Nothing new to pack" out
+		test_grep "Nothing new to pack" out
 	)
 '
 
@@ -281,4 +287,162 @@
 	)
 '
 
+test_expect_success '--geometric --write-midx with packfiles in main and alternate ODB' '
+	test_when_finished "rm -fr shared member" &&
+
+	# Create a shared repository that will serve as the alternate object
+	# database for the member linked to it. It has got some objects on its
+	# own that are packed into a single packfile.
+	git init shared &&
+	test_commit -C shared common-object &&
+	git -C shared repack -ad &&
+
+	# We create member so that its alternates file points to the shared
+	# repository. We then create a commit in it so that git-repack(1) has
+	# something to repack.
+	# of the shared object database.
+	git clone --shared shared member &&
+	test_commit -C member unique-object &&
+	git -C member repack --geometric=2 --write-midx 2>err &&
+	test_must_be_empty err &&
+
+	# We should see that a new packfile was generated.
+	find shared/.git/objects/pack -type f -name "*.pack" >packs &&
+	test_line_count = 1 packs &&
+
+	# We should also see a multi-pack-index. This multi-pack-index should
+	# never refer to any packfiles in the alternate object database.
+	test_path_is_file member/.git/objects/pack/multi-pack-index &&
+	test-tool read-midx member/.git/objects >packs.midx &&
+	grep "^pack-.*\.idx$" packs.midx | sort >actual &&
+	basename member/.git/objects/pack/pack-*.idx >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '--geometric --with-midx with no local objects' '
+	test_when_finished "rm -fr shared member" &&
+
+	# Create a repository with a single packfile that acts as alternate
+	# object database.
+	git init shared &&
+	test_commit -C shared "shared-objects" &&
+	git -C shared repack -ad &&
+
+	# Create a second repository linked to the first one and perform a
+	# geometric repack on it.
+	git clone --shared shared member &&
+	git -C member repack --geometric 2 --write-midx 2>err &&
+	test_must_be_empty err &&
+
+	# Assert that we wrote neither a new packfile nor a multi-pack-index.
+	# We should not have a packfile because the single packfile in the
+	# alternate object database does not invalidate the geometric sequence.
+	# And we should not have a multi-pack-index because these only index
+	# local packfiles, and there are none.
+	test_dir_is_empty member/$packdir
+'
+
+test_expect_success '--geometric with same pack in main and alternate ODB' '
+	test_when_finished "rm -fr shared member" &&
+
+	# Create a repository with a single packfile that acts as alternate
+	# object database.
+	git init shared &&
+	test_commit -C shared "shared-objects" &&
+	git -C shared repack -ad &&
+
+	# We create the member repository as an exact copy so that it has the
+	# same packfile.
+	cp -r shared member &&
+	test-tool path-utils real_path shared/.git/objects >member/.git/objects/info/alternates &&
+	find shared/.git/objects -type f >expected-files &&
+
+	# Verify that we can repack objects as expected without observing any
+	# error. Having the same packfile in both ODBs used to cause an error
+	# in git-pack-objects(1).
+	git -C member repack --geometric 2 2>err &&
+	test_must_be_empty err &&
+	# Nothing should have changed.
+	find shared/.git/objects -type f >actual-files &&
+	test_cmp expected-files actual-files
+'
+
+test_expect_success '--geometric -l with non-intact geometric sequence across ODBs' '
+	test_when_finished "rm -fr shared member" &&
+
+	git init shared &&
+	test_commit_bulk -C shared --start=1 1 &&
+
+	git clone --shared shared member &&
+	test_commit_bulk -C member --start=2 1 &&
+
+	# Verify that our assumptions actually hold: both generated packfiles
+	# should have three objects and should be non-equal.
+	packed_objects shared/.git/objects/pack/pack-*.idx >shared-objects &&
+	packed_objects member/.git/objects/pack/pack-*.idx >member-objects &&
+	test_line_count = 3 shared-objects &&
+	test_line_count = 3 member-objects &&
+	! test_cmp shared-objects member-objects &&
+
+	# Perform the geometric repack. With `-l`, we should only see the local
+	# packfile and thus arrive at the conclusion that the geometric
+	# sequence is intact. We thus expect no changes.
+	#
+	# Note that we are tweaking mtimes of the packfiles so that we can
+	# verify they did not change. This is done in order to detect the case
+	# where we do repack objects, but the resulting packfile is the same.
+	test-tool chmtime --verbose =0 member/.git/objects/pack/* >expected-member-packs &&
+	git -C member repack --geometric=2 -l -d &&
+	test-tool chmtime --verbose member/.git/objects/pack/* >actual-member-packs &&
+	test_cmp expected-member-packs actual-member-packs &&
+
+	{
+		packed_objects shared/.git/objects/pack/pack-*.idx &&
+		packed_objects member/.git/objects/pack/pack-*.idx
+	} | sort >expected-objects &&
+
+	# On the other hand, when doing a non-local geometric repack we should
+	# see both packfiles and thus repack them. We expect that the shared
+	# object database was not changed.
+	test-tool chmtime --verbose =0 shared/.git/objects/pack/* >expected-shared-packs &&
+	git -C member repack --geometric=2 -d &&
+	test-tool chmtime --verbose shared/.git/objects/pack/* >actual-shared-packs &&
+	test_cmp expected-shared-packs actual-shared-packs &&
+
+	# Furthermore, we expect that the member repository now has a single
+	# packfile that contains the combined shared and non-shared objects.
+	ls member/.git/objects/pack/pack-*.idx >actual &&
+	test_line_count = 1 actual &&
+	packed_objects member/.git/objects/pack/pack-*.idx >actual-objects &&
+	test_line_count = 6 actual-objects &&
+	test_cmp expected-objects actual-objects
+'
+
+test_expect_success '--geometric -l disables writing bitmaps with non-local packfiles' '
+	test_when_finished "rm -fr shared member" &&
+
+	git init shared &&
+	test_commit_bulk -C shared --start=1 1 &&
+
+	git clone --shared shared member &&
+	test_commit_bulk -C member --start=2 1 &&
+
+	# When performing a geometric repack with `-l` while connected to an
+	# alternate object database that has a packfile we do not have full
+	# coverage of objects. As a result, we expect that writing the bitmap
+	# will be disabled.
+	git -C member repack -l --geometric=2 --write-midx --write-bitmap-index 2>err &&
+	cat >expect <<-EOF &&
+	warning: disabling bitmap writing, as some objects are not being packed
+	EOF
+	test_cmp expect err &&
+	test_path_is_missing member/.git/objects/pack/multi-pack-index-*.bitmap &&
+
+	# On the other hand, when we repack without `-l`, we should see that
+	# the bitmap gets created.
+	git -C member repack --geometric=2 --write-midx --write-bitmap-index 2>err &&
+	test_must_be_empty err &&
+	test_path_is_file member/.git/objects/pack/multi-pack-index-*.bitmap
+'
+
 test_done
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
new file mode 100755
index 0000000..71e1ef3
--- /dev/null
+++ b/t/t7704-repack-cruft.sh
@@ -0,0 +1,414 @@
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_expect_success '--expire-to stores pruned objects (now)' '
+	git init expire-to-now &&
+	(
+		cd expire-to-now &&
+
+		git branch -M main &&
+
+		test_commit base &&
+
+		git checkout -b cruft &&
+		test_commit --no-tag cruft &&
+
+		git rev-list --objects --no-object-names main..cruft >moved.raw &&
+		sort moved.raw >moved.want &&
+
+		git rev-list --all --objects --no-object-names >expect.raw &&
+		sort expect.raw >expect &&
+
+		git checkout main &&
+		git branch -D cruft &&
+		git reflog expire --all --expire=all &&
+
+		git init --bare expired.git &&
+		git repack -d \
+			--cruft --cruft-expiration="now" \
+			--expire-to="expired.git/objects/pack/pack" &&
+
+		expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
+		test_path_is_file "${expired%.idx}.mtimes" &&
+
+		# Since the `--cruft-expiration` is "now", the effective
+		# behavior is to move _all_ unreachable objects out to
+		# the location in `--expire-to`.
+		git show-index <$expired >expired.raw &&
+		cut -d" " -f2 expired.raw | sort >expired.objects &&
+		git rev-list --all --objects --no-object-names \
+			>remaining.objects &&
+
+		# ...in other words, the combined contents of this
+		# repository and expired.git should be the same as the
+		# set of objects we started with.
+		sort expired.objects remaining.objects >actual &&
+		test_cmp expect actual &&
+
+		# The "moved" objects (i.e., those in expired.git)
+		# should be the same as the cruft objects which were
+		# expired in the previous step.
+		test_cmp moved.want expired.objects
+	)
+'
+
+test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
+	git init expire-to-5.minutes.ago &&
+	(
+		cd expire-to-5.minutes.ago &&
+
+		git branch -M main &&
+
+		test_commit base &&
+
+		# Create two classes of unreachable objects, one which
+		# is older than 5 minutes (stale), and another which is
+		# newer (recent).
+		for kind in stale recent
+		do
+			git checkout -b $kind main &&
+			test_commit --no-tag $kind || return 1
+		done &&
+
+		git rev-list --objects --no-object-names main..stale >in &&
+		stale="$(git pack-objects $objdir/pack/pack <in)" &&
+		mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
+
+		# expect holds the set of objects we expect to find in
+		# this repository after repacking
+		git rev-list --objects --no-object-names recent >expect.raw &&
+		sort expect.raw >expect &&
+
+		# moved.want holds the set of objects we expect to find
+		# in expired.git
+		git rev-list --objects --no-object-names main..stale >out &&
+		sort out >moved.want &&
+
+		git checkout main &&
+		git branch -D stale recent &&
+		git reflog expire --all --expire=all &&
+		git prune-packed &&
+
+		git init --bare expired.git &&
+		git repack -d \
+			--cruft --cruft-expiration=5.minutes.ago \
+			--expire-to="expired.git/objects/pack/pack" &&
+
+		# Some of the remaining objects in this repository are
+		# unreachable, so use `cat-file --batch-all-objects`
+		# instead of `rev-list` to get their names
+		git cat-file --batch-all-objects --batch-check="%(objectname)" \
+			>remaining.objects &&
+		sort remaining.objects >actual &&
+		test_cmp expect actual &&
+
+		(
+			cd expired.git &&
+
+			expired="$(ls objects/pack/pack-*.mtimes)" &&
+			test-tool pack-mtimes $(basename $expired) >out &&
+			cut -d" " -f1 out | sort >../moved.got &&
+
+			# Ensure that there are as many objects with the
+			# expected mtime as were moved to expired.git.
+			#
+			# In other words, ensure that the recorded
+			# mtimes of any moved objects was written
+			# correctly.
+			grep " $mtime$" out >matching &&
+			test_line_count = $(wc -l <../moved.want) matching
+		) &&
+		test_cmp moved.want moved.got
+	)
+'
+
+generate_random_blob() {
+	test-tool genrandom "$@" >blob &&
+	git hash-object -w -t blob blob &&
+	rm blob
+}
+
+pack_random_blob () {
+	generate_random_blob "$@" &&
+	git repack -d -q >/dev/null
+}
+
+generate_cruft_pack () {
+	pack_random_blob "$@" >/dev/null &&
+
+	ls $packdir/pack-*.pack | xargs -n 1 basename >in &&
+	pack="$(git pack-objects --cruft $packdir/pack <in)" &&
+	git prune-packed &&
+
+	echo "$packdir/pack-$pack.mtimes"
+}
+
+test_expect_success '--max-cruft-size creates new packs when above threshold' '
+	git init max-cruft-size-large &&
+	(
+		cd max-cruft-size-large &&
+		test_commit base &&
+
+		foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+		git repack --cruft -d &&
+		cruft_foo="$(ls $packdir/pack-*.mtimes)" &&
+
+		bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+		git repack --cruft -d --max-cruft-size=1M &&
+		cruft_bar="$(ls $packdir/pack-*.mtimes | grep -v $cruft_foo)" &&
+
+		test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects &&
+		test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects &&
+
+		grep "^$foo" foo.objects &&
+		test_line_count = 1 foo.objects &&
+		grep "^$bar" bar.objects &&
+		test_line_count = 1 bar.objects
+	)
+'
+
+test_expect_success '--max-cruft-size combines existing packs when below threshold' '
+	git init max-cruft-size-small &&
+	(
+		cd max-cruft-size-small &&
+		test_commit base &&
+
+		foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+		git repack --cruft -d &&
+
+		bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+		git repack --cruft -d --max-cruft-size=10M &&
+
+		cruft=$(ls $packdir/pack-*.mtimes) &&
+		test-tool pack-mtimes $(basename "$cruft") >cruft.objects &&
+
+		grep "^$foo" cruft.objects &&
+		grep "^$bar" cruft.objects &&
+		test_line_count = 2 cruft.objects
+	)
+'
+
+test_expect_success '--max-cruft-size combines smaller packs first' '
+	git init max-cruft-size-consume-small &&
+	(
+		cd max-cruft-size-consume-small &&
+
+		test_commit base &&
+		git repack -ad &&
+
+		cruft_foo="$(generate_cruft_pack foo 524288)" &&    # 0.5 MiB
+		cruft_bar="$(generate_cruft_pack bar 524288)" &&    # 0.5 MiB
+		cruft_baz="$(generate_cruft_pack baz 1048576)" &&   # 1.0 MiB
+		cruft_quux="$(generate_cruft_pack quux 1572864)" && # 1.5 MiB
+
+		test-tool pack-mtimes "$(basename $cruft_foo)" >expect.raw &&
+		test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
+		sort expect.raw >expect.objects &&
+
+		# repacking with `--max-cruft-size=2M` should combine
+		# both 0.5 MiB packs together, instead of, say, one of
+		# the 0.5 MiB packs with the 1.0 MiB pack
+		ls $packdir/pack-*.mtimes | sort >cruft.before &&
+		git repack -d --cruft --max-cruft-size=2M &&
+		ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+		comm -13 cruft.before cruft.after >cruft.new &&
+		comm -23 cruft.before cruft.after >cruft.removed &&
+
+		test_line_count = 1 cruft.new &&
+		test_line_count = 2 cruft.removed &&
+
+		# the two smaller packs should be rolled up first
+		printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
+		test_cmp expect.removed cruft.removed &&
+
+		# ...and contain the set of objects rolled up
+		test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
+		sort actual.raw >actual.objects &&
+
+		test_cmp expect.objects actual.objects
+	)
+'
+
+test_expect_success 'setup --max-cruft-size with freshened objects' '
+	git init max-cruft-size-freshen &&
+	(
+		cd max-cruft-size-freshen &&
+
+		test_commit base &&
+		git repack -ad &&
+
+		foo="$(generate_random_blob foo 64)" &&
+		test-tool chmtime --get -10000 \
+			"$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+		git repack --cruft -d &&
+
+		cruft="$(ls $packdir/pack-*.mtimes)" &&
+		test-tool pack-mtimes "$(basename $cruft)" >actual &&
+		echo "$foo $(cat foo.mtime)" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--max-cruft-size with freshened objects (loose)' '
+	(
+		cd max-cruft-size-freshen &&
+
+		# regenerate the object, setting its mtime to be more recent
+		foo="$(generate_random_blob foo 64)" &&
+		test-tool chmtime --get -100 \
+			"$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+		git repack --cruft -d &&
+
+		cruft="$(ls $packdir/pack-*.mtimes)" &&
+		test-tool pack-mtimes "$(basename $cruft)" >actual &&
+		echo "$foo $(cat foo.mtime)" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--max-cruft-size with freshened objects (packed)' '
+	(
+		cd max-cruft-size-freshen &&
+
+		# regenerate the object and store it in a packfile,
+		# setting its mtime to be more recent
+		#
+		# store it alongside another cruft object so that we
+		# do not create an identical copy of the existing
+		# cruft pack (which contains $foo).
+		foo="$(generate_random_blob foo 64)" &&
+		bar="$(generate_random_blob bar 64)" &&
+		foo_pack="$(printf "%s\n" $foo $bar | git pack-objects $packdir/pack)" &&
+		git prune-packed &&
+
+		test-tool chmtime --get -10 \
+			"$packdir/pack-$foo_pack.pack" >foo.mtime &&
+
+		git repack --cruft -d &&
+
+		cruft="$(ls $packdir/pack-*.mtimes)" &&
+		test-tool pack-mtimes "$(basename $cruft)" >actual &&
+		echo "$foo $(cat foo.mtime)" >expect.raw &&
+		echo "$bar $(cat foo.mtime)" >>expect.raw &&
+		sort expect.raw >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '--max-cruft-size with pruning' '
+	git init max-cruft-size-prune &&
+	(
+		cd max-cruft-size-prune &&
+
+		test_commit base &&
+		foo="$(generate_random_blob foo $((1024*1024)))" &&
+		bar="$(generate_random_blob bar $((1024*1024)))" &&
+		baz="$(generate_random_blob baz $((1024*1024)))" &&
+
+		test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
+
+		git repack -d --cruft --max-cruft-size=1M &&
+
+		# backdate the mtimes of all cruft packs to validate
+		# that they were rewritten as a result of pruning
+		ls $packdir/pack-*.mtimes | sort >cruft.before &&
+		for cruft in $(cat cruft.before)
+		do
+			mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
+			echo $cruft $mtime >>mtimes || return 1
+		done &&
+
+		# repack (and prune) with a --max-cruft-size to ensure
+		# that we appropriately split the resulting set of packs
+		git repack -d --cruft --max-cruft-size=1M \
+			--cruft-expiration=10.seconds.ago &&
+		ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+		for cruft in $(cat cruft.after)
+		do
+			old_mtime="$(grep $cruft mtimes | cut -d" " -f2)" &&
+			new_mtime="$(test-tool chmtime --get $cruft)" &&
+			test $old_mtime -lt $new_mtime || return 1
+		done &&
+
+		test_line_count = 3 cruft.before &&
+		test_line_count = 2 cruft.after &&
+		test_must_fail git cat-file -e $foo &&
+		git cat-file -e $bar &&
+		git cat-file -e $baz
+	)
+'
+
+test_expect_success '--max-cruft-size ignores non-local packs' '
+	repo="max-cruft-size-non-local" &&
+	git init $repo &&
+	(
+		cd $repo &&
+		test_commit base &&
+		generate_random_blob foo 64 &&
+		git repack --cruft -d
+	) &&
+
+	git clone --reference=$repo $repo $repo-alt &&
+	(
+		cd $repo-alt &&
+
+		test_commit other &&
+		generate_random_blob bar 64 &&
+
+		# ensure that we do not attempt to pick up packs from
+		# the non-alternated repository, which would result in a
+		# crash
+		git repack --cruft --max-cruft-size=1M -d
+	)
+'
+
+test_expect_success 'reachable packs are preferred over cruft ones' '
+	repo="cruft-preferred-packs" &&
+	git init "$repo" &&
+	(
+		cd "$repo" &&
+
+		# This test needs to exercise careful control over when a MIDX
+		# is and is not written. Unset the corresponding TEST variable
+		# accordingly.
+		sane_unset GIT_TEST_MULTI_PACK_INDEX &&
+
+		test_commit base &&
+		test_commit --no-tag cruft &&
+
+		non_cruft="$(echo base | git pack-objects --revs $packdir/pack)" &&
+		# Write a cruft pack which both (a) sorts ahead of the non-cruft
+		# pack in lexical order, and (b) has an older mtime to appease
+		# the MIDX preferred pack selection routine.
+		cruft="$(echo pack-$non_cruft.pack | git pack-objects --cruft $packdir/pack-A)" &&
+		test-tool chmtime -1000 $packdir/pack-A-$cruft.pack &&
+
+		test_commit other &&
+		git repack -d &&
+
+		git repack --geometric 2 -d --write-midx --write-bitmap-index &&
+
+		# After repacking, there are two packs left: one reachable one
+		# (which is the result of combining both of the existing two
+		# non-cruft packs), and one cruft pack.
+		find .git/objects/pack -type f -name "*.pack" >packs &&
+		test_line_count = 2 packs &&
+
+		# Make sure that the pack we just wrote is marked as preferred,
+		# not the cruft one.
+		pack="$(test-tool read-midx --preferred-pack $objdir)" &&
+		test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"
+	)
+'
+
+test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 24297e2..cc917b2 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -28,14 +28,14 @@
 
 test_expect_success 'basic usage requires no repo' '
 	test_expect_code 129 git difftool -h >output &&
-	test_i18ngrep ^usage: output &&
+	test_grep ^usage: output &&
 	# create a ceiling directory to prevent Git from finding a repo
 	mkdir -p not/repo &&
 	test_when_finished rm -r not &&
 	test_expect_code 129 \
 	env GIT_CEILING_DIRECTORIES="$(pwd)/not" \
 	git -C not/repo difftool -h >output &&
-	test_i18ngrep ^usage: output
+	test_grep ^usage: output
 '
 
 # Create a file on main and change it on branch
@@ -91,58 +91,67 @@
 	rm for-diff
 '
 
-test_expect_success 'difftool ignores exit code' '
-	test_config difftool.error.cmd false &&
-	git difftool -y -t error branch
-'
+for opt in '' '--dir-diff'
+do
+	test_expect_success "difftool ${opt:-without options} ignores exit code" '
+		test_config difftool.error.cmd false &&
+		git difftool ${opt} -y -t error branch
+	'
 
-test_expect_success 'difftool forwards exit code with --trust-exit-code' '
-	test_config difftool.error.cmd false &&
-	test_must_fail git difftool -y --trust-exit-code -t error branch
-'
+	test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code" '
+		test_config difftool.error.cmd false &&
+		test_must_fail git difftool ${opt} -y --trust-exit-code -t error branch
+	'
 
-test_expect_success 'difftool forwards exit code with --trust-exit-code for built-ins' '
-	test_config difftool.vimdiff.path false &&
-	test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
-'
+	test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code for built-ins" '
+		test_config difftool.vimdiff.path false &&
+		test_must_fail git difftool ${opt} -y --trust-exit-code -t vimdiff branch
+	'
 
-test_expect_success 'difftool honors difftool.trustExitCode = true' '
-	test_config difftool.error.cmd false &&
-	test_config difftool.trustExitCode true &&
-	test_must_fail git difftool -y -t error branch
-'
+	test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = true" '
+		test_config difftool.error.cmd false &&
+		test_config difftool.trustExitCode true &&
+		test_must_fail git difftool ${opt} -y -t error branch
+	'
 
-test_expect_success 'difftool honors difftool.trustExitCode = false' '
-	test_config difftool.error.cmd false &&
-	test_config difftool.trustExitCode false &&
-	git difftool -y -t error branch
-'
+	test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = false" '
+		test_config difftool.error.cmd false &&
+		test_config difftool.trustExitCode false &&
+		git difftool ${opt} -y -t error branch
+	'
 
-test_expect_success 'difftool ignores exit code with --no-trust-exit-code' '
-	test_config difftool.error.cmd false &&
-	test_config difftool.trustExitCode true &&
-	git difftool -y --no-trust-exit-code -t error branch
-'
+	test_expect_success "difftool ${opt:-without options} ignores exit code with --no-trust-exit-code" '
+		test_config difftool.error.cmd false &&
+		test_config difftool.trustExitCode true &&
+		git difftool ${opt} -y --no-trust-exit-code -t error branch
+	'
 
-test_expect_success 'difftool stops on error with --trust-exit-code' '
-	test_when_finished "rm -f for-diff .git/fail-right-file" &&
-	test_when_finished "git reset -- for-diff" &&
-	write_script .git/fail-right-file <<-\EOF &&
-	echo failed
-	exit 1
-	EOF
-	>for-diff &&
-	git add for-diff &&
-	test_must_fail git difftool -y --trust-exit-code \
-		--extcmd .git/fail-right-file branch >actual &&
-	test_line_count = 1 actual
-'
+	test_expect_success "difftool ${opt:-without options} stops on error with --trust-exit-code" '
+		test_when_finished "rm -f for-diff .git/fail-right-file" &&
+		test_when_finished "git reset -- for-diff" &&
+		write_script .git/fail-right-file <<-\EOF &&
+		echo failed
+		exit 1
+		EOF
+		>for-diff &&
+		git add for-diff &&
+		test_must_fail git difftool ${opt} -y --trust-exit-code \
+			--extcmd .git/fail-right-file branch >actual &&
+		test_line_count = 1 actual
+	'
 
-test_expect_success 'difftool honors exit status if command not found' '
-	test_config difftool.nonexistent.cmd i-dont-exist &&
-	test_config difftool.trustExitCode false &&
-	test_must_fail git difftool -y -t nonexistent branch
-'
+	test_expect_success "difftool ${opt:-without options} honors exit status if command not found" '
+		test_config difftool.nonexistent.cmd i-dont-exist &&
+		test_config difftool.trustExitCode false &&
+		if test "${opt}" = --dir-diff
+		then
+			expected_code=127
+		else
+			expected_code=128
+		fi &&
+		test_expect_code ${expected_code} git difftool ${opt} -y -t nonexistent branch
+	'
+done
 
 test_expect_success 'difftool honors --gui' '
 	difftool_test_setup &&
@@ -155,6 +164,58 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'difftool with guiDefault auto selects gui tool when there is DISPLAY' '
+	difftool_test_setup &&
+	test_config merge.tool bogus-tool &&
+	test_config diff.tool bogus-tool &&
+	test_config diff.guitool test-tool &&
+	test_config difftool.guiDefault auto &&
+	DISPLAY=SOMETHING && export DISPLAY &&
+
+	echo branch >expect &&
+	git difftool --no-prompt branch >actual &&
+	test_cmp expect actual
+'
+test_expect_success 'difftool with guiDefault auto selects regular tool when no DISPLAY' '
+	difftool_test_setup &&
+	test_config diff.guitool bogus-tool &&
+	test_config diff.tool test-tool &&
+	test_config difftool.guiDefault Auto &&
+	DISPLAY= && export DISPLAY &&
+
+	echo branch >expect &&
+	git difftool --no-prompt branch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'difftool with guiDefault true selects gui tool' '
+	difftool_test_setup &&
+	test_config diff.tool bogus-tool &&
+	test_config diff.guitool test-tool &&
+	test_config difftool.guiDefault true &&
+
+	DISPLAY= && export DISPLAY &&
+	echo branch >expect &&
+	git difftool --no-prompt branch >actual &&
+	test_cmp expect actual &&
+
+	DISPLAY=Something && export DISPLAY &&
+	echo branch >expect &&
+	git difftool --no-prompt branch >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'difftool --no-gui trumps config guiDefault' '
+	difftool_test_setup &&
+	test_config diff.guitool bogus-tool &&
+	test_config diff.tool test-tool &&
+	test_config difftool.guiDefault true &&
+
+	echo branch >expect &&
+	git difftool --no-prompt --no-gui branch >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'difftool --gui last setting wins' '
 	difftool_test_setup &&
 	: >expect &&
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 8eded6a..875dcfd 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -808,6 +808,19 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'grep -f, use cwd relative file' '
+	test_when_finished "git rm -f sub/dir/file" &&
+	mkdir -p sub/dir &&
+	echo hit >sub/dir/file &&
+	git add sub/dir/file &&
+	echo hit >sub/dir/pattern &&
+	echo miss >pattern &&
+	(
+		cd sub/dir && git grep -f pattern file
+	) &&
+	git -C sub/dir grep -f pattern file
+'
+
 cat >expected <<EOF
 y:y yy
 --
@@ -1001,7 +1014,9 @@
 test_expect_success 'grep with CE_VALID file' '
 	git update-index --assume-unchanged t/t &&
 	rm t/t &&
-	test "$(git grep test)" = "t/t:test" &&
+	echo "t/t:test" >expect &&
+	git grep test >actual &&
+	test_cmp expect actual &&
 	git update-index --no-assume-unchanged t/t &&
 	git checkout t/t
 '
@@ -1232,6 +1247,33 @@
 	)
 '
 
+test_expect_success 'no repository with path outside $cwd' '
+	test_when_finished rm -fr non &&
+	rm -fr non &&
+	mkdir -p non/git/sub non/tig &&
+	(
+		GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd non/git &&
+		test_expect_code 128 git grep --no-index search .. 2>error &&
+		grep "is outside the directory tree" error
+	) &&
+	(
+		GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd non/git &&
+		test_expect_code 128 git grep --no-index search ../tig 2>error &&
+		grep "is outside the directory tree" error
+	) &&
+	(
+		GIT_CEILING_DIRECTORIES="$(pwd)/non" &&
+		export GIT_CEILING_DIRECTORIES &&
+		cd non/git &&
+		test_expect_code 128 git grep --no-index search ../non 2>error &&
+		grep "no such path in the working tree" error
+	)
+'
+
 test_expect_success 'inside git repository but with --no-index' '
 	rm -fr is &&
 	mkdir -p is/git/sub &&
@@ -1384,7 +1426,7 @@
 
 test_expect_success 'grep --no-index complains of revs' '
 	test_must_fail git grep --no-index o main -- 2>err &&
-	test_i18ngrep "cannot be used with revs" err
+	test_grep "cannot be used with revs" err
 '
 
 test_expect_success 'grep --no-index prefers paths to revs' '
@@ -1397,7 +1439,7 @@
 
 test_expect_success 'grep --no-index does not "diagnose" revs' '
 	test_must_fail git grep --no-index o :1:hello.c 2>err &&
-	test_i18ngrep ! -i "did you mean" err
+	test_grep ! -i "did you mean" err
 '
 
 cat >expected <<EOF
diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh
index 1dd0714..fe38d88 100755
--- a/t/t7811-grep-open.sh
+++ b/t/t7811-grep-open.sh
@@ -63,7 +63,7 @@
 
 test_expect_success 'git grep -O --cached' '
 	test_must_fail git grep --cached -O GREP_PATTERN >out 2>msg &&
-	test_i18ngrep open-files-in-pager msg
+	test_grep open-files-in-pager msg
 '
 
 test_expect_success 'git grep -O --no-index' '
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 8143817..167fe66 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -348,7 +348,7 @@
 {
 	test_expect_success "--recurse-submodules and $1 are incompatible" "
 		test_must_fail git grep -e. --recurse-submodules $1 2>actual &&
-		test_i18ngrep 'not supported with --recurse-submodules' actual
+		test_grep 'not supported with --recurse-submodules' actual
 	"
 }
 
@@ -594,4 +594,44 @@
 	)
 '
 
+test_expect_success 'check scope of core.useReplaceRefs' '
+	git init base &&
+	git init base/sub &&
+
+	echo A >base/a &&
+	echo B >base/b &&
+	echo C >base/sub/c &&
+	echo D >base/sub/d &&
+
+	git -C base/sub add c d &&
+	git -C base/sub commit -m "Add files" &&
+
+	git -C base submodule add ./sub &&
+	git -C base add a b sub &&
+	git -C base commit -m "Add files and submodule" &&
+
+	A=$(git -C base rev-parse HEAD:a) &&
+	B=$(git -C base rev-parse HEAD:b) &&
+	C=$(git -C base/sub rev-parse HEAD:c) &&
+	D=$(git -C base/sub rev-parse HEAD:d) &&
+
+	git -C base replace $A $B &&
+	git -C base/sub replace $C $D &&
+
+	test_must_fail git -C base grep --cached --recurse-submodules A &&
+	test_must_fail git -C base grep --cached --recurse-submodules C &&
+
+	git -C base config core.useReplaceRefs false &&
+	git -C base grep --recurse-submodules A &&
+	test_must_fail git -C base grep --cached --recurse-submodules C &&
+
+	git -C base/sub config core.useReplaceRefs false &&
+	git -C base grep --cached --recurse-submodules A &&
+	git -C base grep --cached --recurse-submodules C &&
+
+	git -C base config --unset core.useReplaceRefs &&
+	test_must_fail git -C base grep --cached --recurse-submodules A &&
+	git -C base grep --cached --recurse-submodules C
+'
+
 test_done
diff --git a/t/t7816-grep-binary-pattern.sh b/t/t7816-grep-binary-pattern.sh
index fdb2355..4353be5 100755
--- a/t/t7816-grep-binary-pattern.sh
+++ b/t/t7816-grep-binary-pattern.sh
@@ -26,7 +26,7 @@
 			>stderr &&
 			printf '$pattern' | q_to_nul >f &&
 			test_must_fail env LC_ALL=\"$lc_all\" git grep $extra_flags -f f $flags a 2>stderr &&
-			test_i18ngrep ! 'This is only supported with -P under PCRE v2' stderr
+			test_grep ! 'This is only supported with -P under PCRE v2' stderr
 		"
 	elif test "$matches" = P
 	then
@@ -34,7 +34,7 @@
 			>stderr &&
 			printf '$pattern' | q_to_nul >f &&
 			test_must_fail env LC_ALL=\"$lc_all\" git grep -f f $flags a 2>stderr &&
-			test_i18ngrep 'This is only supported with -P under PCRE v2' stderr
+			test_grep 'This is only supported with -P under PCRE v2' stderr
 		"
 	else
 		test_expect_success "PANIC: Test framework error. Unknown matches value $matches" 'false'
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 823331e..0943dfa 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -33,13 +33,13 @@
 
 test_expect_success 'help text' '
 	test_expect_code 129 git maintenance -h >actual &&
-	test_i18ngrep "usage: git maintenance <subcommand>" actual &&
+	test_grep "usage: git maintenance <subcommand>" actual &&
 	test_expect_code 129 git maintenance barf 2>err &&
-	test_i18ngrep "unknown subcommand: \`barf'\''" err &&
-	test_i18ngrep "usage: git maintenance" err &&
+	test_grep "unknown subcommand: \`barf'\''" err &&
+	test_grep "usage: git maintenance" err &&
 	test_expect_code 129 git maintenance 2>err &&
-	test_i18ngrep "error: need a subcommand" err &&
-	test_i18ngrep "usage: git maintenance" err
+	test_grep "error: need a subcommand" err &&
+	test_grep "usage: git maintenance" err
 '
 
 test_expect_success 'run [--auto|--quiet]' '
@@ -67,6 +67,51 @@
 	test_subcommand ! git maintenance run --auto --quiet  <false
 '
 
+test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
+	test_when_finished rm -r .config/git/config &&
+	(
+		XDG_CONFIG_HOME=.config &&
+		export XDG_CONFIG_HOME &&
+		mkdir -p $XDG_CONFIG_HOME/git &&
+		>$XDG_CONFIG_HOME/git/config &&
+		git maintenance register &&
+		git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+		pwd >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
+	test_when_finished git maintenance unregister &&
+	test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+	git maintenance register &&
+	git config --global --get maintenance.repo >actual &&
+	pwd >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
+	test_when_finished rm -r .config/git/config &&
+	(
+		XDG_CONFIG_HOME=.config &&
+		export XDG_CONFIG_HOME &&
+		mkdir -p $XDG_CONFIG_HOME/git &&
+		>$XDG_CONFIG_HOME/git/config &&
+		git maintenance register &&
+		git maintenance unregister &&
+		test_must_fail git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+		test_must_be_empty actual
+	)
+'
+
+test_expect_success 'unregister does not need XDG_CONFIG_HOME config to exist' '
+	test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+	git maintenance register &&
+	git maintenance unregister &&
+	test_must_fail git config --global --get maintenance.repo >actual &&
+	test_must_be_empty actual
+'
+
 test_expect_success 'maintenance.<task>.enabled' '
 	git config maintenance.gc.enabled false &&
 	git config maintenance.commit-graph.enabled true &&
@@ -131,12 +176,12 @@
 
 test_expect_success 'run --task=bogus' '
 	test_must_fail git maintenance run --task=bogus 2>err &&
-	test_i18ngrep "is not a valid task" err
+	test_grep "is not a valid task" err
 '
 
 test_expect_success 'run --task duplicate' '
 	test_must_fail git maintenance run --task=gc --task=gc 2>err &&
-	test_i18ngrep "cannot be selected multiple times" err
+	test_grep "cannot be selected multiple times" err
 '
 
 test_expect_success 'run --task=prefetch with no remotes' '
@@ -157,7 +202,8 @@
 	fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
 	test_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&
 	test_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&
-	test_path_is_missing .git/refs/remotes &&
+	git for-each-ref refs/remotes >actual &&
+	test_must_be_empty actual &&
 	git log prefetch/remotes/remote1/one &&
 	git log prefetch/remotes/remote2/two &&
 	git fetch --all &&
@@ -377,12 +423,12 @@
 
 test_expect_success '--auto and --schedule incompatible' '
 	test_must_fail git maintenance run --auto --schedule=daily 2>err &&
-	test_i18ngrep "at most one" err
+	test_grep "at most one" err
 '
 
 test_expect_success 'invalid --schedule value' '
 	test_must_fail git maintenance run --schedule=annually 2>err &&
-	test_i18ngrep "unrecognized --schedule" err
+	test_grep "unrecognized --schedule" err
 '
 
 test_expect_success '--schedule inheritance weekly -> daily -> hourly' '
@@ -524,6 +570,44 @@
 	git maintenance unregister --config-file ./other --force
 '
 
+test_expect_success 'register with no value for maintenance.repo' '
+	cp .git/config .git/config.orig &&
+	test_when_finished mv .git/config.orig .git/config &&
+
+	cat >>.git/config <<-\EOF &&
+	[maintenance]
+		repo
+	EOF
+	cat >expect <<-\EOF &&
+	error: missing value for '\''maintenance.repo'\''
+	EOF
+	git maintenance register 2>actual &&
+	test_cmp expect actual &&
+	git config maintenance.repo
+'
+
+test_expect_success 'unregister with no value for maintenance.repo' '
+	cp .git/config .git/config.orig &&
+	test_when_finished mv .git/config.orig .git/config &&
+
+	cat >>.git/config <<-\EOF &&
+	[maintenance]
+		repo
+	EOF
+	cat >expect <<-\EOF &&
+	error: missing value for '\''maintenance.repo'\''
+	EOF
+	test_expect_code 128 git maintenance unregister 2>actual.raw &&
+	grep ^error actual.raw >actual &&
+	test_cmp expect actual &&
+	git config maintenance.repo &&
+
+	git maintenance unregister --force 2>actual.raw &&
+	grep ^error actual.raw >actual &&
+	test_cmp expect actual &&
+	git config maintenance.repo
+'
+
 test_expect_success !MINGW 'register and unregister with regex metacharacters' '
 	META="a+b*c" &&
 	git init "$META" &&
@@ -538,15 +622,15 @@
 
 test_expect_success 'start --scheduler=<scheduler>' '
 	test_expect_code 129 git maintenance start --scheduler=foo 2>err &&
-	test_i18ngrep "unrecognized --scheduler argument" err &&
+	test_grep "unrecognized --scheduler argument" err &&
 
 	test_expect_code 129 git maintenance start --no-scheduler 2>err &&
-	test_i18ngrep "unknown option" err &&
+	test_grep "unknown option" err &&
 
 	test_expect_code 128 \
 		env GIT_TEST_MAINT_SCHEDULER="launchctl:true,schtasks:true" \
 		git maintenance start --scheduler=crontab 2>err &&
-	test_i18ngrep "fatal: crontab scheduler is not available" err
+	test_grep "fatal: crontab scheduler is not available" err
 '
 
 test_expect_success 'start from empty cron table' '
@@ -706,7 +790,15 @@
 	# start registers the repo
 	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
 
-	test_systemd_analyze_verify "systemd/user/git-maintenance@.service" &&
+	for schedule in hourly daily weekly
+	do
+		test_path_is_file "systemd/user/git-maintenance@$schedule.timer" || return 1
+	done &&
+	test_path_is_file "systemd/user/git-maintenance@.service" &&
+
+	test_systemd_analyze_verify "systemd/user/git-maintenance@hourly.service" &&
+	test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" &&
+	test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" &&
 
 	printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
 	test_cmp expect args &&
@@ -717,7 +809,10 @@
 	# stop does not unregister the repo
 	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
 
-	test_path_is_missing "systemd/user/git-maintenance@.timer" &&
+	for schedule in hourly daily weekly
+	do
+		test_path_is_missing "systemd/user/git-maintenance@$schedule.timer" || return 1
+	done &&
 	test_path_is_missing "systemd/user/git-maintenance@.service" &&
 
 	printf -- "--user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
@@ -800,4 +895,17 @@
 	)
 '
 
+test_expect_success 'failed schedule prevents config change' '
+	git init --bare failcase &&
+
+	for scheduler in crontab launchctl schtasks systemctl
+	do
+		GIT_TEST_MAINT_SCHEDULER="$scheduler:false" &&
+		export GIT_TEST_MAINT_SCHEDULER &&
+		test_must_fail \
+			git -C failcase maintenance start &&
+		test_must_fail git -C failcase config maintenance.auto || return 1
+	done
+'
+
 test_done
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 8bcd39e..7312655 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -207,7 +207,7 @@
 
 test_expect_success 'blame -L with invalid start' '
 	test_must_fail git blame -L5 tres 2>errors &&
-	test_i18ngrep "has only 2 lines" errors
+	test_grep "has only 2 lines" errors
 '
 
 test_expect_success 'blame -L with invalid end' '
diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh
index ca04242..eb64b76 100755
--- a/t/t8010-cat-file-filters.sh
+++ b/t/t8010-cat-file-filters.sh
@@ -43,7 +43,7 @@
 	sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
 	test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
 	git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
-	test uryyb = "$(cat rot13 | remove_cr)"
+	test uryyb = "$(remove_cr <rot13)"
 '
 
 test_expect_success '--path=<path> complains without --textconv/--filters' '
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index b18633d..dbfbd86 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -25,11 +25,11 @@
 
 	git blame --line-porcelain file >blame_raw &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
 	git rev-parse X >expect &&
 	test_cmp expect actual &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
 	git rev-parse X >expect &&
 	test_cmp expect actual
 '
@@ -53,11 +53,11 @@
 	test_expect_success "ignore_rev_changing_lines ($I)" '
 		git blame --line-porcelain --ignore-rev $I file >blame_raw &&
 
-		grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+		sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
 		git rev-parse A >expect &&
 		test_cmp expect actual &&
 
-		grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+		sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
 		git rev-parse B >expect &&
 		test_cmp expect actual
 	'
@@ -79,10 +79,10 @@
 	git rev-parse Y >expect &&
 	git blame --line-porcelain file --ignore-rev Y >blame_raw &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 3/s/ .*//p" blame_raw >actual &&
 	test_cmp expect actual &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 4/s/ .*//p" blame_raw >actual &&
 	test_cmp expect actual
 '
 
@@ -92,11 +92,11 @@
 	git rev-parse Y >ignore_y &&
 	git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
 	git rev-parse A >expect &&
 	test_cmp expect actual &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
 	git rev-parse B >expect &&
 	test_cmp expect actual
 '
@@ -106,11 +106,11 @@
 	git config --add blame.ignoreRevsFile ignore_x &&
 	git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
 	git rev-parse A >expect &&
 	test_cmp expect actual &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
 	git rev-parse B >expect &&
 	test_cmp expect actual
 '
@@ -121,22 +121,22 @@
 	git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
 	git rev-parse X >expect &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
 	test_cmp expect actual &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
 	test_cmp expect actual
 	'
 test_expect_success bad_files_and_revs '
 	test_must_fail git blame file --ignore-rev NOREV 2>err &&
-	test_i18ngrep "cannot find revision NOREV to ignore" err &&
+	test_grep "cannot find revision NOREV to ignore" err &&
 
 	test_must_fail git blame file --ignore-revs-file NOFILE 2>err &&
-	test_i18ngrep "could not open.*: NOFILE" err &&
+	test_grep "could not open.*: NOFILE" err &&
 
 	echo NOREV >ignore_norev &&
 	test_must_fail git blame file --ignore-revs-file ignore_norev 2>err &&
-	test_i18ngrep "invalid object name: NOREV" err
+	test_grep "invalid object name: NOREV" err
 '
 
 # For ignored revs that have added 'unblamable' lines, mark those lines with a
@@ -279,11 +279,11 @@
 	test_merge M B &&
 	git blame --line-porcelain file --ignore-rev M >blame_raw &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
 	git rev-parse B >expect &&
 	test_cmp expect actual &&
 
-	grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
+	sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 9/s/ .*//p" blame_raw >actual &&
 	git rev-parse C >expect &&
 	test_cmp expect actual
 '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 323952a..5a77100 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -12,7 +12,7 @@
 
 replace_variable_fields () {
 	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \
-		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+		-e "s/^\(Message-ID:\).*/\1 MESSAGE-ID-STRING/" \
 		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
 }
 
@@ -47,7 +47,7 @@
 
 test_expect_success $PREREQ 'Extract patches' '
 	patches=$(git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1) &&
-	threaded_patches=$(git format-patch -o threaded -s --in-reply-to="format" HEAD^1)
+	threaded_patches=$(git format-patch -o threaded --thread=shallow -s --in-reply-to="format" HEAD^1)
 '
 
 # Test no confirm early to ensure remaining tests will not hang
@@ -61,8 +61,8 @@
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$@ \
 		$patches >stdout &&
-		! grep "Send this email" stdout &&
-		>no_confirm_okay
+	! grep "Send this email" stdout &&
+	>no_confirm_okay
 }
 
 # Exit immediately to prevent hang if a no-confirm test fails
@@ -225,7 +225,7 @@
 	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 In-Reply-To: <unique-message-id@example.com>
 References: <unique-message-id@example.com>
@@ -337,13 +337,14 @@
 test_expect_success $PREREQ 'Prompting works' '
 	clean_fake_sendmail &&
 	(echo "to@example.com" &&
-	 echo ""
+	 echo "my-message-id@example.com"
 	) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
 		--smtp-server="$(pwd)/fake.sendmail" \
 		$patches \
 		2>errors &&
 		grep "^From: A U Thor <author@example.com>\$" msgtxt1 &&
-		grep "^To: to@example.com\$" msgtxt1
+		grep "^To: to@example.com\$" msgtxt1 &&
+		grep "^In-Reply-To: <my-message-id@example.com>" msgtxt1
 '
 
 test_expect_success $PREREQ,AUTOIDENT 'implicit ident is allowed' '
@@ -370,17 +371,20 @@
 		--smtp-server="$(pwd)/fake.sendmail" \
 		--to=to@example.com \
 		$patches </dev/null 2>errors &&
-	test_i18ngrep "tell me who you are" errors
+	test_grep "tell me who you are" errors
 	)
 '
 
-test_expect_success $PREREQ 'setup tocmd and cccmd scripts' '
+test_expect_success $PREREQ 'setup cmd scripts' '
 	write_script tocmd-sed <<-\EOF &&
 	sed -n -e "s/^tocmd--//p" "$1"
 	EOF
-	write_script cccmd-sed <<-\EOF
+	write_script cccmd-sed <<-\EOF &&
 	sed -n -e "s/^cccmd--//p" "$1"
 	EOF
+	write_script headercmd-sed <<-\EOF
+	sed -n -e "s/^headercmd--//p" "$1"
+	EOF
 '
 
 test_expect_success $PREREQ 'tocmd works' '
@@ -410,6 +414,70 @@
 	grep "^	cccmd@example.com" msgtxt1
 '
 
+test_expect_success $PREREQ 'headercmd works' '
+	clean_fake_sendmail &&
+	cp $patches headercmd.patch &&
+	echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--header-cmd=./headercmd-sed \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		headercmd.patch \
+		&&
+	grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--no-header-cmd works' '
+	clean_fake_sendmail &&
+	cp $patches headercmd.patch &&
+	echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--header-cmd=./headercmd-sed \
+		--no-header-cmd \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		headercmd.patch \
+		&&
+	! grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'multiline fields are correctly unfolded' '
+	clean_fake_sendmail &&
+	cp $patches headercmd.patch &&
+	write_script headercmd-multiline <<-\EOF &&
+	echo "X-Debbugs-CC: someone@example.com
+FoldedField: This is a tale
+ best told using
+ multiple lines."
+	EOF
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--header-cmd=./headercmd-multiline \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		headercmd.patch &&
+	grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1
+'
+
+# Blank lines in the middle of the output of a command are invalid.
+test_expect_success $PREREQ 'malform output reported on blank lines in command output' '
+	clean_fake_sendmail &&
+	cp $patches headercmd.patch &&
+	write_script headercmd-malformed-output <<-\EOF &&
+	echo "X-Debbugs-CC: someone@example.com
+
+SomeOtherField: someone-else@example.com"
+	EOF
+	! git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--header-cmd=./headercmd-malformed-output \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		headercmd.patch
+'
+
 test_expect_success $PREREQ 'reject long lines' '
 	z8=zzzzzzzz &&
 	z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
@@ -540,7 +608,7 @@
 	test_path_is_file my-hooks.ran &&
 	cat >expect <<-EOF &&
 	fatal: longline.patch: rejected by sendemail-validate hook
-	fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
+	fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
 	warning: no patches were sent
 	EOF
 	test_cmp expect actual
@@ -559,12 +627,68 @@
 	test_path_is_file my-hooks.ran &&
 	cat >expect <<-EOF &&
 	fatal: longline.patch: rejected by sendemail-validate hook
-	fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
+	fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
 	warning: no patches were sent
 	EOF
 	test_cmp expect actual
 '
 
+test_expect_success $PREREQ "--validate hook supports multiple addresses in arguments" '
+	hooks_path="$(pwd)/my-hooks" &&
+	test_config core.hooksPath "$hooks_path" &&
+	test_when_finished "rm my-hooks.ran" &&
+	test_must_fail git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com,abc@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		--validate \
+		longline.patch 2>actual &&
+	test_path_is_file my-hooks.ran &&
+	cat >expect <<-EOF &&
+	fatal: longline.patch: rejected by sendemail-validate hook
+	fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
+	warning: no patches were sent
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success $PREREQ "--validate hook supports header argument" '
+	write_script my-hooks/sendemail-validate <<-\EOF &&
+	if test "$#" -ge 2
+	then
+		grep "X-test-header: v1.0" "$2"
+	else
+		echo "No header arg passed"
+		exit 1
+	fi
+	EOF
+	test_config core.hooksPath "my-hooks" &&
+	rm -fr outdir &&
+	git format-patch \
+		--add-header="X-test-header: v1.0" \
+		-n HEAD^1 -o outdir &&
+	git send-email \
+		--dry-run \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		--validate \
+		outdir/000?-*.patch
+'
+
+test_expect_success $PREREQ 'clear message-id before parsing a new message' '
+	clean_fake_sendmail &&
+	echo true | write_script my-hooks/sendemail-validate &&
+	test_config core.hooksPath my-hooks &&
+	git send-email --validate --to=recipient@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		$patches $threaded_patches &&
+	id0=$(grep "^Message-ID: " $threaded_patches) &&
+	id1=$(grep "^Message-ID: " msgtxt1) &&
+	id2=$(grep "^Message-ID: " msgtxt2) &&
+	test "z$id0" = "z$id2" &&
+	test "z$id1" != "z$id2"
+'
+
 for enc in 7bit 8bit quoted-printable base64
 do
 	test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" '
@@ -617,7 +741,7 @@
 	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
 	test_cmp expect actual &&
 	# Second and subsequent messages are replies to the first one
-	sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+	sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
 	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
 	test_cmp expect actual &&
 	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
@@ -637,10 +761,10 @@
 		2>errors &&
 	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
 	test_cmp expect actual &&
-	sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+	sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
 	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
 	test_cmp expect actual &&
-	sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+	sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt2 >expect &&
 	sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
 	test_cmp expect actual
 '
@@ -713,7 +837,7 @@
 	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -759,7 +883,7 @@
 	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -796,7 +920,7 @@
 	C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -824,7 +948,7 @@
 To: to@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -860,7 +984,7 @@
 	cc-cmd@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -893,7 +1017,7 @@
 	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -926,7 +1050,7 @@
 	two@example.com
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -963,7 +1087,7 @@
 	C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -993,7 +1117,7 @@
 	C O Mitter <committer@example.com>
 Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
 MIME-Version: 1.0
 Content-Transfer-Encoding: 8bit
@@ -1478,7 +1602,7 @@
 test_expect_success $PREREQ 'setup expect' '
 cat >email-using-8bit <<\EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
 From: author@example.com
 Date: Sat, 12 Jun 2010 15:53:58 +0200
 Subject: subject goes here
@@ -1564,7 +1688,7 @@
 test_expect_success $PREREQ 'setup expect' '
 	cat >email-using-8bit <<-\EOF
 	From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-	Message-Id: <bogus-message-id@example.com>
+	Message-ID: <bogus-message-id@example.com>
 	From: author@example.com
 	Date: Sat, 12 Jun 2010 15:53:58 +0200
 	Subject: Dieser Betreff enthält auch einen Umlaut!
@@ -1593,7 +1717,7 @@
 test_expect_success $PREREQ 'setup expect' '
 	cat >email-using-8bit <<-\EOF
 	From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-	Message-Id: <bogus-message-id@example.com>
+	Message-ID: <bogus-message-id@example.com>
 	From: A U Thor <author@example.com>
 	Date: Sat, 12 Jun 2010 15:53:58 +0200
 	Content-Type: text/plain; charset=UTF-8
@@ -1674,7 +1798,7 @@
 test_expect_success $PREREQ 'setup expect' '
 	cat >email-using-qp <<-\EOF
 	From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-	Message-Id: <bogus-message-id@example.com>
+	Message-ID: <bogus-message-id@example.com>
 	From: A U Thor <author@example.com>
 	Date: Sat, 12 Jun 2010 15:53:58 +0200
 	MIME-Version: 1.0
@@ -1700,7 +1824,7 @@
 test_expect_success $PREREQ 'setup expect' "
 tr -d '\\015' | tr '%' '\\015' >email-using-crlf <<EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
 From: A U Thor <author@example.com>
 Date: Sat, 12 Jun 2010 15:53:58 +0200
 Content-Type: text/plain; charset=UTF-8
@@ -1957,7 +2081,7 @@
 		-c sendemail.aliasesfile=default-aliases \
 		-c sendemail.cloud.aliasesfile=cloud-aliases \
 		send-email -1 2>stderr &&
-	test_i18ngrep "cloud-aliases" stderr
+	test_grep "cloud-aliases" stderr
 '
 
 test_sendmail_aliases () {
@@ -2322,10 +2446,41 @@
 			--to=nobody@example.com \
 			--smtp-server="$(pwd)/../fake.sendmail" \
 			../another.patch 2>err &&
-		test_i18ngrep "rejected by sendemail-validate hook" err
+		test_grep "rejected by sendemail-validate hook" err
 	)
 '
 
+expected_file_counter_output () {
+	total=$1
+	count=0
+	while test $count -ne $total
+	do
+		count=$((count + 1)) &&
+		echo "$count/$total" || return
+	done
+}
+
+test_expect_success $PREREQ '--validate hook allows counting of messages' '
+	test_when_finished "rm -rf my-hooks.log" &&
+	test_config core.hooksPath "my-hooks" &&
+	mkdir -p my-hooks &&
+
+	write_script my-hooks/sendemail-validate <<-\EOF &&
+		num=$GIT_SENDEMAIL_FILE_COUNTER &&
+		tot=$GIT_SENDEMAIL_FILE_TOTAL &&
+		echo "$num/$tot" >>my-hooks.log || exit 1
+	EOF
+
+	>my-hooks.log &&
+	expected_file_counter_output 4 >expect &&
+	git send-email \
+		--from="Example <from@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		--validate -3 --cover-letter --force &&
+	test_cmp expect my-hooks.log
+'
+
 test_expect_success $PREREQ 'test that send-email works outside a repo' '
 	nongit git send-email \
 		--from="Example <nobody@example.com>" \
@@ -2347,7 +2502,7 @@
 		--to=nobody@example.com \
 		--smtp-server="$(pwd)/fake.sendmail" \
 		HEAD^ 2>err &&
-	test_i18ngrep "found configuration options for '"'"sendmail"'"'" err
+	test_grep "found configuration options for '"'"sendmail"'"'" err
 '
 
 test_expect_success $PREREQ 'test that sendmail config rejection is specific' '
@@ -2369,4 +2524,45 @@
 		HEAD^
 '
 
+test_expect_success $PREREQ '--compose handles lowercase headers' '
+	write_script fake-editor <<-\EOF &&
+	sed "s/^From:.*/from: edited-from@example.com/i" "$1" >"$1.tmp" &&
+	mv "$1.tmp" "$1"
+	EOF
+	clean_fake_sendmail &&
+	git send-email \
+		--compose \
+		--from="Example <from@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		HEAD^ &&
+	grep "From: edited-from@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--compose handles to headers' '
+	write_script fake-editor <<-\EOF &&
+	sed "s/^To: .*/&, edited-to@example.com/" <"$1" >"$1.tmp" &&
+	echo this is the body >>"$1.tmp" &&
+	mv "$1.tmp" "$1"
+	EOF
+	clean_fake_sendmail &&
+	GIT_SEND_EMAIL_NOTTY=1 \
+	git send-email \
+		--compose \
+		--from="Example <from@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		HEAD^ &&
+	# Check both that the cover letter used our modified "to" line,
+	# but also that it was picked up for the patch.
+	q_to_tab >expect <<-\EOF &&
+	To: nobody@example.com,
+	Qedited-to@example.com
+	EOF
+	grep -A1 "^To:" msgtxt1 >msgtxt1.to &&
+	test_cmp expect msgtxt1.to &&
+	grep -A1 "^To:" msgtxt2 >msgtxt2.to &&
+	test_cmp expect msgtxt2.to
+'
+
 test_done
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index 6d3dbde..d5b98e6 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='git column'
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -195,4 +196,15 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'padding must be non-negative' '
+	cat >input <<\EOF &&
+1 2 3 4 5 6
+EOF
+	cat >expected <<\EOF &&
+fatal: --padding must be non-negative
+EOF
+	test_must_fail git column --mode=column --padding=-1 <input >actual 2>&1 &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t9004-example.sh b/t/t9004-example.sh
index 7e8894a..590aab0 100755
--- a/t/t9004-example.sh
+++ b/t/t9004-example.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check that example code compiles and runs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'decorate' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3..af28b01 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -21,7 +21,7 @@
 '
 
 test_expect_success \
-    'initialize git svn' '
+	'initialize git svn' '
 	mkdir import &&
 	(
 		cd import &&
@@ -38,9 +38,9 @@
 	rm -rf import &&
 	git svn init "$svnrepo"'
 
-test_expect_success \
-    'import an SVN revision into git' \
-    'git svn fetch'
+test_expect_success 'import an SVN revision into git' '
+	git svn fetch
+'
 
 test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
@@ -233,27 +233,26 @@
 '
 
 test_expect_success 'exit if remote refs are ambigious' '
-        git config --add svn-remote.svn.fetch \
+	git config --add svn-remote.svn.fetch \
 		bar:refs/remotes/git-svn &&
 	test_must_fail git svn migrate
 '
 
 test_expect_success 'exit if init-ing a would clobber a URL' '
-        svnadmin create "${PWD}/svnrepo2" &&
-        svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
-        git config --unset svn-remote.svn.fetch \
+	svnadmin create "${PWD}/svnrepo2" &&
+	svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
+	git config --unset svn-remote.svn.fetch \
 		"^bar:refs/remotes/git-svn$" &&
 	test_must_fail git svn init "${svnrepo}2/bar"
         '
 
-test_expect_success \
-  'init allows us to connect to another directory in the same repo' '
-        git svn init --minimize-url -i bar "$svnrepo/bar" &&
-        git config --get svn-remote.svn.fetch \
-                              "^bar:refs/remotes/bar$" &&
-        git config --get svn-remote.svn.fetch \
-			      "^:refs/remotes/git-svn$"
-        '
+test_expect_success 'init allows us to connect to another directory in the same repo' '
+	git svn init --minimize-url -i bar "$svnrepo/bar" &&
+	git config --get svn-remote.svn.fetch \
+		"^bar:refs/remotes/bar$" &&
+	git config --get svn-remote.svn.fetch \
+		"^:refs/remotes/git-svn$"
+'
 
 test_expect_success 'dcommit $rev does not clobber current branch' '
 	git svn fetch -i bar &&
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 85d7358..b5845e2 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -41,51 +41,51 @@
 	'
 
 test_expect_success 'init and fetch from one svn-remote' '
-        git config svn-remote.svn.url "$svnrepo" &&
-        git config --add svn-remote.svn.fetch \
-          trunk:refs/remotes/svn/trunk &&
-        git config --add svn-remote.svn.fetch \
-          thunk:refs/remotes/svn/thunk &&
-        git svn fetch -i svn/thunk &&
+	git config svn-remote.svn.url "$svnrepo" &&
+	git config --add svn-remote.svn.fetch \
+		trunk:refs/remotes/svn/trunk &&
+	git config --add svn-remote.svn.fetch \
+		thunk:refs/remotes/svn/thunk &&
+	git svn fetch -i svn/thunk &&
 	test "$(git rev-parse --verify refs/remotes/svn/trunk)" \
-	   = "$(git rev-parse --verify refs/remotes/svn/thunk~1)" &&
+		= "$(git rev-parse --verify refs/remotes/svn/thunk~1)" &&
 	git cat-file blob refs/remotes/svn/thunk:readme >actual &&
 	test "$(sed -n -e "3p" actual)" = goodbye
-        '
+'
 
 test_expect_success 'follow deleted parent' '
-        (svn_cmd cp -m "resurrecting trunk as junk" \
-               "$svnrepo"/trunk@2 "$svnrepo"/junk ||
-         svn cp -m "resurrecting trunk as junk" \
-               -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
-        git config --add svn-remote.svn.fetch \
-          junk:refs/remotes/svn/junk &&
-        git svn fetch -i svn/thunk &&
-        git svn fetch -i svn/junk &&
+	(svn_cmd cp -m "resurrecting trunk as junk" \
+		"$svnrepo"/trunk@2 "$svnrepo"/junk ||
+	 svn cp -m "resurrecting trunk as junk" \
+		-r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
+	git config --add svn-remote.svn.fetch \
+		junk:refs/remotes/svn/junk &&
+	git svn fetch -i svn/thunk &&
+	git svn fetch -i svn/junk &&
 	test -z "$(git diff svn/junk svn/trunk)" &&
 	test "$(git merge-base svn/junk svn/trunk)" \
-	   = "$(git rev-parse svn/trunk)"
-        '
+		= "$(git rev-parse svn/trunk)"
+'
 
 test_expect_success 'follow larger parent' '
-        mkdir -p import/trunk/thunk/bump/thud &&
-        echo hi > import/trunk/thunk/bump/thud/file &&
-        svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
-        svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
-        git svn init --minimize-url -i larger \
-	  "$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
-        git svn fetch -i larger &&
+	mkdir -p import/trunk/thunk/bump/thud &&
+	echo hi > import/trunk/thunk/bump/thud/file &&
+	svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
+	svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
+	git svn init --minimize-url -i larger \
+		"$svnrepo"/larger-parent/trunk/thunk/bump/thud &&
+	git svn fetch -i larger &&
 	git svn init --minimize-url -i larger-parent \
-	  "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+		"$svnrepo"/another-larger/trunk/thunk/bump/thud &&
 	git svn fetch -i larger-parent &&
-        git rev-parse --verify refs/remotes/larger &&
-        git rev-parse --verify \
-	   refs/remotes/larger-parent &&
+	git rev-parse --verify refs/remotes/larger &&
+	git rev-parse --verify \
+		refs/remotes/larger-parent &&
 	test "$(git merge-base \
 		 refs/remotes/larger-parent \
 		 refs/remotes/larger)" = \
-	     "$(git rev-parse refs/remotes/larger)"
-        '
+		"$(git rev-parse refs/remotes/larger)"
+'
 
 test_expect_success 'follow higher-level parent' '
 	svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 32317d6..e06538b 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -27,7 +27,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
 #
 EOF
 }
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index 62de819..3b038c3 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -17,32 +17,32 @@
 test_expect_success 'basic clone' '
 	test ! -d trunk &&
 	git svn clone "$svnrepo"/project/trunk &&
-	test -d trunk/.git/svn &&
-	test -e trunk/foo &&
+	test_path_is_dir trunk/.git/svn &&
+	test_path_exists trunk/foo &&
 	rm -rf trunk
 	'
 
 test_expect_success 'clone to target directory' '
 	test ! -d target &&
 	git svn clone "$svnrepo"/project/trunk target &&
-	test -d target/.git/svn &&
-	test -e target/foo &&
+	test_path_is_dir target/.git/svn &&
+	test_path_exists target/foo &&
 	rm -rf target
 	'
 
 test_expect_success 'clone with --stdlayout' '
 	test ! -d project &&
 	git svn clone -s "$svnrepo"/project &&
-	test -d project/.git/svn &&
-	test -e project/foo &&
+	test_path_is_dir project/.git/svn &&
+	test_path_exists project/foo &&
 	rm -rf project
 	'
 
 test_expect_success 'clone to target directory with --stdlayout' '
 	test ! -d target &&
 	git svn clone -s "$svnrepo"/project target &&
-	test -d target/.git/svn &&
-	test -e target/foo &&
+	test_path_is_dir target/.git/svn &&
+	test_path_exists target/foo &&
 	rm -rf target
 	'
 
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index a159ff9..d3261e3 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -38,7 +38,7 @@
 # SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
 # Look at what SVN wound up naming the branch and use that.
 # Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
 
 test_expect_success 'test clone with funky branch names' '
 	git svn clone -s "$svnrepo/pr ject" project &&
diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
index d8d5362..8ca2467 100755
--- a/t/t9133-git-svn-nested-git-repo.sh
+++ b/t/t9133-git-svn-nested-git-repo.sh
@@ -11,7 +11,7 @@
 	(
 		cd s &&
 		git init &&
-		test -f .git/HEAD &&
+		git symbolic-ref HEAD &&
 		> .git/a &&
 		echo a > a &&
 		svn_cmd add .git a &&
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 09606f1..926ac81 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -20,11 +20,7 @@
 		cd cloned &&
 		for i in a b c d d/e d/e/f "weird file name"
 		do
-			if ! test -d "$i"
-			then
-				echo >&2 "$i does not exist" &&
-				exit 1
-			fi
+			test_path_is_dir "$i" || exit 1
 		done
 	)
 '
@@ -37,11 +33,7 @@
 		git svn fetch &&
 		for i in a b c d d/e d/e/f "weird file name"
 		do
-			if test -d "$i"
-			then
-				echo >&2 "$i exists" &&
-				exit 1
-			fi
+			test_path_is_missing "$i" || exit 1
 		done
 	)
 '
@@ -52,7 +44,7 @@
 
 test_expect_success 'git svn rebase creates empty directory' '
 	( cd cloned && git svn rebase ) &&
-	test -d cloned/"! !"
+	test_path_is_dir cloned/"! !"
 '
 
 test_expect_success 'git svn mkdirs recreates empty directories' '
@@ -62,11 +54,7 @@
 		git svn mkdirs &&
 		for i in a b c d d/e d/e/f "weird file name" "! !"
 		do
-			if ! test -d "$i"
-			then
-				echo >&2 "$i does not exist" &&
-				exit 1
-			fi
+			test_path_is_dir "$i" || exit 1
 		done
 	)
 '
@@ -78,25 +66,13 @@
 		git svn mkdirs -r7 &&
 		for i in a b c d d/e d/e/f "weird file name"
 		do
-			if ! test -d "$i"
-			then
-				echo >&2 "$i does not exist" &&
-				exit 1
-			fi
+			test_path_is_dir "$i" || exit 1
 		done &&
 
-		if test -d "! !"
-		then
-			echo >&2 "$i should not exist" &&
-			exit 1
-		fi &&
+		test_path_is_missing "! !" || exit 1 &&
 
 		git svn mkdirs -r8 &&
-		if ! test -d "! !"
-		then
-			echo >&2 "$i not exist" &&
-			exit 1
-		fi
+		test_path_is_dir "! !" || exit 1
 	)
 '
 
@@ -114,11 +90,7 @@
 		cd trunk &&
 		for i in a "weird file name"
 		do
-			if ! test -d "$i"
-			then
-				echo >&2 "$i does not exist" &&
-				exit 1
-			fi
+			test_path_is_dir "$i" || exit 1
 		done
 	)
 '
@@ -129,7 +101,7 @@
 
 test_expect_success 'removed top-level directory does not exist' '
 	git svn clone "$svnrepo" removed &&
-	test ! -e removed/d
+	test_path_is_missing removed/d
 
 '
 unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
@@ -143,15 +115,11 @@
 			svn_cmd mkdir -m gz "$svnrepo"/gz &&
 			git reset --hard $(git rev-list HEAD | tail -1) &&
 			git svn rebase &&
-			test -f "$unhandled".gz &&
-			test -f "$unhandled" &&
+			test_path_is_file "$unhandled".gz &&
+			test_path_is_file "$unhandled" &&
 			for i in a b c "weird file name" gz "! !"
 			do
-				if ! test -d "$i"
-				then
-					echo >&2 "$i does not exist" &&
-					exit 1
-				fi
+				test_path_is_dir "$i" || exit 1
 			done
 		fi
 	)
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index c8e6c07..d1dec89 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -46,6 +46,14 @@
 			"passed to setup_hook" >&2 ; return 1; }
 	echo "cnt=$skip_revs" > "$hook_type-counter"
 	rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks
+
+	# Subversion hooks run with an empty environment by default. We thus
+	# need to propagate PATH so that we can find executables.
+	cat >"$rawsvnrepo/conf/hooks-env" <<-EOF
+	[default]
+	PATH = ${PATH}
+	EOF
+
 	hook="$rawsvnrepo/hooks/$hook_type"
 	cat > "$hook" <<- 'EOF1'
 		#!/bin/sh
@@ -63,7 +71,6 @@
 	if [ "$hook_type" = "pre-commit" ]; then
 		echo "echo 'commit disallowed' >&2; exit 1" >>"$hook"
 	else
-		echo "PATH=\"$PATH\"; export PATH" >>"$hook"
 		echo "svnconf=\"$svnconf\"" >>"$hook"
 		cat >>"$hook" <<- 'EOF2'
 			cd work-auto-commits.svn
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index c5946cb..a44eabf 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -50,56 +50,56 @@
 	fi
 }
 
-test_expect_success \
-    'New file' \
-    'mkdir A B C D E F &&
-     echo hello1 >A/newfile1.txt &&
-     echo hello2 >B/newfile2.txt &&
-     cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
-     cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
-     git add A/newfile1.txt &&
-     git add B/newfile2.txt &&
-     git add C/newfile3.png &&
-     git add D/newfile4.png &&
-     git commit -a -m "Test: New file" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "newfile1.txt/1.1/" &&
-     check_entries B "newfile2.txt/1.1/" &&
-     check_entries C "newfile3.png/1.1/-kb" &&
-     check_entries D "newfile4.png/1.1/-kb" &&
-     test_cmp A/newfile1.txt ../A/newfile1.txt &&
-     test_cmp B/newfile2.txt ../B/newfile2.txt &&
-     test_cmp C/newfile3.png ../C/newfile3.png &&
-     test_cmp D/newfile4.png ../D/newfile4.png
-     )'
+test_expect_success 'New file' '
+	mkdir A B C D E F &&
+	echo hello1 >A/newfile1.txt &&
+	echo hello2 >B/newfile2.txt &&
+	cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
+	cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
+	git add A/newfile1.txt &&
+	git add B/newfile2.txt &&
+	git add C/newfile3.png &&
+	git add D/newfile4.png &&
+	git commit -a -m "Test: New file" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c $id &&
+	check_entries A "newfile1.txt/1.1/" &&
+	check_entries B "newfile2.txt/1.1/" &&
+	check_entries C "newfile3.png/1.1/-kb" &&
+	check_entries D "newfile4.png/1.1/-kb" &&
+	test_cmp A/newfile1.txt ../A/newfile1.txt &&
+	test_cmp B/newfile2.txt ../B/newfile2.txt &&
+	test_cmp C/newfile3.png ../C/newfile3.png &&
+	test_cmp D/newfile4.png ../D/newfile4.png
+	)
+'
 
-test_expect_success \
-    'Remove two files, add two and update two' \
-    'echo Hello1 >>A/newfile1.txt &&
-     rm -f B/newfile2.txt &&
-     rm -f C/newfile3.png &&
-     echo Hello5  >E/newfile5.txt &&
-     cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
-     cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
-     git add E/newfile5.txt &&
-     git add F/newfile6.png &&
-     git commit -a -m "Test: Remove, add and update" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "newfile1.txt/1.2/" &&
-     check_entries B "" &&
-     check_entries C "" &&
-     check_entries D "newfile4.png/1.2/-kb" &&
-     check_entries E "newfile5.txt/1.1/" &&
-     check_entries F "newfile6.png/1.1/-kb" &&
-     test_cmp A/newfile1.txt ../A/newfile1.txt &&
-     test_cmp D/newfile4.png ../D/newfile4.png &&
-     test_cmp E/newfile5.txt ../E/newfile5.txt &&
-     test_cmp F/newfile6.png ../F/newfile6.png
-     )'
+test_expect_success 'Remove two files, add two and update two' '
+	echo Hello1 >>A/newfile1.txt &&
+	rm -f B/newfile2.txt &&
+	rm -f C/newfile3.png &&
+	echo Hello5  >E/newfile5.txt &&
+	cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
+	cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
+	git add E/newfile5.txt &&
+	git add F/newfile6.png &&
+	git commit -a -m "Test: Remove, add and update" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c $id &&
+	check_entries A "newfile1.txt/1.2/" &&
+	check_entries B "" &&
+	check_entries C "" &&
+	check_entries D "newfile4.png/1.2/-kb" &&
+	check_entries E "newfile5.txt/1.1/" &&
+	check_entries F "newfile6.png/1.1/-kb" &&
+	test_cmp A/newfile1.txt ../A/newfile1.txt &&
+	test_cmp D/newfile4.png ../D/newfile4.png &&
+	test_cmp E/newfile5.txt ../E/newfile5.txt &&
+	test_cmp F/newfile6.png ../F/newfile6.png
+	)
+'
 
 # Should fail (but only on the git cvsexportcommit stage)
 test_expect_success \
@@ -129,67 +129,67 @@
 
 # This test is here because a patch for only binary files will
 # fail with gnu patch, so cvsexportcommit must handle that.
-test_expect_success \
-    'Remove only binary files' \
-    'git reset --hard HEAD^^ &&
-     rm -f D/newfile4.png &&
-     git commit -a -m "test: remove only a binary file" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "newfile1.txt/1.2/" &&
-     check_entries B "" &&
-     check_entries C "" &&
-     check_entries D "" &&
-     check_entries E "newfile5.txt/1.1/" &&
-     check_entries F "newfile6.png/1.1/-kb" &&
-     test_cmp A/newfile1.txt ../A/newfile1.txt &&
-     test_cmp E/newfile5.txt ../E/newfile5.txt &&
-     test_cmp F/newfile6.png ../F/newfile6.png
-     )'
+test_expect_success 'Remove only binary files' '
+	git reset --hard HEAD^^ &&
+	rm -f D/newfile4.png &&
+	git commit -a -m "test: remove only a binary file" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c $id &&
+	check_entries A "newfile1.txt/1.2/" &&
+	check_entries B "" &&
+	check_entries C "" &&
+	check_entries D "" &&
+	check_entries E "newfile5.txt/1.1/" &&
+	check_entries F "newfile6.png/1.1/-kb" &&
+	test_cmp A/newfile1.txt ../A/newfile1.txt &&
+	test_cmp E/newfile5.txt ../E/newfile5.txt &&
+	test_cmp F/newfile6.png ../F/newfile6.png
+	)
+'
 
-test_expect_success \
-    'Remove only a text file' \
-    'rm -f A/newfile1.txt &&
-     git commit -a -m "test: remove only a binary file" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     git cvsexportcommit -c $id &&
-     check_entries A "" &&
-     check_entries B "" &&
-     check_entries C "" &&
-     check_entries D "" &&
-     check_entries E "newfile5.txt/1.1/" &&
-     check_entries F "newfile6.png/1.1/-kb" &&
-     test_cmp E/newfile5.txt ../E/newfile5.txt &&
-     test_cmp F/newfile6.png ../F/newfile6.png
-     )'
+test_expect_success 'Remove only a text file' '
+	rm -f A/newfile1.txt &&
+	git commit -a -m "test: remove only a binary file" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c $id &&
+	check_entries A "" &&
+	check_entries B "" &&
+	check_entries C "" &&
+	check_entries D "" &&
+	check_entries E "newfile5.txt/1.1/" &&
+	check_entries F "newfile6.png/1.1/-kb" &&
+	test_cmp E/newfile5.txt ../E/newfile5.txt &&
+	test_cmp F/newfile6.png ../F/newfile6.png
+	)
+'
 
-test_expect_success \
-     'New file with spaces in file name' \
-     'mkdir "G g" &&
-      echo ok then >"G g/with spaces.txt" &&
-      git add "G g/with spaces.txt" && \
-      cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
-      git add "G g/with spaces.png" &&
-      git commit -a -m "With spaces" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -c $id &&
-      check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
-      )'
+test_expect_success 'New file with spaces in file name' '
+	mkdir "G g" &&
+	echo ok then >"G g/with spaces.txt" &&
+	git add "G g/with spaces.txt" && \
+	cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
+	git add "G g/with spaces.png" &&
+	git commit -a -m "With spaces" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c $id &&
+	check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
+	)
+'
 
-test_expect_success \
-     'Update file with spaces in file name' \
-     'echo Ok then >>"G g/with spaces.txt" &&
-      cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
-      git add "G g/with spaces.png" &&
-      git commit -a -m "Update with spaces" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -c $id &&
-      check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
-      )'
+test_expect_success 'Update file with spaces in file name' '
+	echo Ok then >>"G g/with spaces.txt" &&
+	cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
+	git add "G g/with spaces.png" &&
+	git commit -a -m "Update with spaces" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c $id &&
+	check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
+	)
+'
 
 # Some filesystems mangle pathnames with UTF-8 characters --
 # check and skip
@@ -202,68 +202,68 @@
 then
 
 # This test contains UTF-8 characters
-test_expect_success !MINGW \
-     'File with non-ascii file name' \
-     'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
-      echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
-      git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
-      git commit -a -m "Går det så går det" && \
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -v -c $id &&
-      check_entries \
-      "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
-      "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
-      )'
+test_expect_success !MINGW 'File with non-ascii file name' '
+	mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
+	echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+	git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+	cp "$TEST_DIRECTORY"/test-binary-1.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+	git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+	git commit -a -m "Går det så går det" && \
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -v -c $id &&
+	check_entries \
+	"Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
+	"gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
+	)
+'
 
 fi
 
 rm -fr tst
 
-test_expect_success \
-     'Mismatching patch should fail' \
-     'date >>"E/newfile5.txt" &&
-      git add "E/newfile5.txt" &&
-      git commit -a -m "Update one" &&
-      date >>"E/newfile5.txt" &&
-      git add "E/newfile5.txt" &&
-      git commit -a -m "Update two" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$CVSWORK" &&
-      test_must_fail git cvsexportcommit -c $id
-      )'
+test_expect_success 'Mismatching patch should fail' '
+	date >>"E/newfile5.txt" &&
+	git add "E/newfile5.txt" &&
+	git commit -a -m "Update one" &&
+	date >>"E/newfile5.txt" &&
+	git add "E/newfile5.txt" &&
+	git commit -a -m "Update two" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$CVSWORK" &&
+	test_must_fail git cvsexportcommit -c $id
+	)
+'
 
-test_expect_success FILEMODE \
-     'Retain execute bit' \
-     'mkdir G &&
-      echo executeon >G/on &&
-      chmod +x G/on &&
-      echo executeoff >G/off &&
-      git add G/on &&
-      git add G/off &&
-      git commit -a -m "Execute test" &&
-      (cd "$CVSWORK" &&
-      git cvsexportcommit -c HEAD &&
-      test -x G/on &&
-      ! test -x G/off
-      )'
+test_expect_success FILEMODE 'Retain execute bit' '
+	mkdir G &&
+	echo executeon >G/on &&
+	chmod +x G/on &&
+	echo executeoff >G/off &&
+	git add G/on &&
+	git add G/off &&
+	git commit -a -m "Execute test" &&
+	(cd "$CVSWORK" &&
+	git cvsexportcommit -c HEAD &&
+	test -x G/on &&
+	! test -x G/off
+	)
+'
 
 test_expect_success '-w option should work with relative GIT_DIR' '
-      mkdir W &&
-      echo foobar >W/file1.txt &&
-      echo bazzle >W/file2.txt &&
-      git add W/file1.txt &&
-      git add W/file2.txt &&
-      git commit -m "More updates" &&
-      id=$(git rev-list --max-count=1 HEAD) &&
-      (cd "$GIT_DIR" &&
-      GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
-      check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
-      test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
-      test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
-      )
+	mkdir W &&
+	echo foobar >W/file1.txt &&
+	echo bazzle >W/file2.txt &&
+	git add W/file1.txt &&
+	git add W/file2.txt &&
+	git commit -m "More updates" &&
+	id=$(git rev-list --max-count=1 HEAD) &&
+	(cd "$GIT_DIR" &&
+	GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
+	check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
+	test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
+	test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
+	)
 '
 
 test_expect_success 'check files before directories' '
@@ -290,21 +290,20 @@
 '
 
 test_expect_success 're-commit a removed filename which remains in CVS attic' '
+	(cd "$CVSWORK" &&
+	echo >attic_gremlin &&
+	cvs -Q add attic_gremlin &&
+	cvs -Q ci -m "added attic_gremlin" &&
+	rm attic_gremlin &&
+	cvs -Q rm attic_gremlin &&
+	cvs -Q ci -m "removed attic_gremlin") &&
 
-    (cd "$CVSWORK" &&
-     echo >attic_gremlin &&
-     cvs -Q add attic_gremlin &&
-     cvs -Q ci -m "added attic_gremlin" &&
-     rm attic_gremlin &&
-     cvs -Q rm attic_gremlin &&
-     cvs -Q ci -m "removed attic_gremlin") &&
-
-    echo > attic_gremlin &&
-    git add attic_gremlin &&
-    git commit -m "Added attic_gremlin" &&
+	echo > attic_gremlin &&
+	git add attic_gremlin &&
+	git commit -m "Added attic_gremlin" &&
 	git cvsexportcommit -w "$CVSWORK" -c HEAD &&
-    (cd "$CVSWORK" && cvs -Q update -d) &&
-    test -f "$CVSWORK/attic_gremlin"
+	(cd "$CVSWORK" && cvs -Q update -d) &&
+	test -f "$CVSWORK/attic_gremlin"
 '
 
 # the state of the CVS sandbox may be indeterminate for ' space'
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 4432a30..428339e 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -154,7 +154,14 @@
 		test_cmp expect actual &&
 
 		test_path_is_missing 1/2 &&
-		test_must_fail git rev-list --missing=print $second &&
+
+		# This relies on the fact that the presence of "--missing"
+		# on the command line forces lazy fetching off before
+		# "$second^{blob}" gets parsed.  Without "^{blob}", a
+		# bare object name "$second" is taken into the queue and
+		# the command may not fail with a fixed "rev-list --missing".
+		test_must_fail git rev-list --missing=print "$second^{blob}" -- &&
+
 		git rev-list $second &&
 		git cat-file blob $second >actual &&
 		echo "second" >expect &&
diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh
index 872ad1c..7869f45 100755
--- a/t/t9211-scalar-clone.sh
+++ b/t/t9211-scalar-clone.sh
@@ -180,4 +180,16 @@
 	grep "could not turn on maintenance" err
 '
 
+test_expect_success '`scalar clone --no-src`' '
+	scalar clone --src "file://$(pwd)/to-clone" with-src &&
+	scalar clone --no-src "file://$(pwd)/to-clone" without-src &&
+
+	test_path_is_dir with-src/src &&
+	test_path_is_missing without-src/src &&
+
+	(cd with-src/src && ls ?*) >with &&
+	(cd without-src && ls ?*) >without &&
+	test_cmp with without
+'
+
 test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index aa55b41..60e30fe 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -388,9 +388,7 @@
 
 	INPUT_END
 
-	test_when_finished "rm -f .git/TEMP_TAG
-		git gc
-		git prune" &&
+	test_when_finished "rm -f .git/TEMP_TAG && git gc --prune=now" &&
 	git fast-import <input &&
 	test $(test-tool ref-store main resolve-ref TEMP_TAG 0 | cut -f1 -d " " ) != "$ZERO_OID" &&
 	test $(git rev-parse main) = $(git rev-parse TEMP_TAG^)
@@ -406,8 +404,7 @@
 	INPUT_END
 
 	test_when_finished "git update-ref -d refs/heads/empty-committer-1
-		git gc
-		git prune" &&
+		git gc --prune=now" &&
 	git fast-import <input &&
 	out=$(git fsck) &&
 	echo "$out" &&
@@ -452,8 +449,7 @@
 	INPUT_END
 
 	test_when_finished "git update-ref -d refs/heads/empty-committer-2
-		git gc
-		git prune" &&
+		git gc --prune=now" &&
 	git fast-import <input &&
 	out=$(git fsck) &&
 	echo "$out" &&
@@ -990,7 +986,7 @@
 	test_when_finished "git update-ref -d refs/heads/L2" &&
 	git fast-import <input &&
 	git ls-tree L2 g/b/ >tmp &&
-	cat tmp | cut -f 2 >actual &&
+	cut -f 2 <tmp >actual &&
 	test_cmp expect actual &&
 	git fsck $(git rev-parse L2)
 '
@@ -1778,8 +1774,7 @@
 	INPUT_END
 
 	git branch -D sub &&
-	git gc &&
-	git prune &&
+	git gc --prune=now &&
 	git fast-import <input &&
 	test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)
 '
@@ -2012,12 +2007,11 @@
 '
 
 test_expect_success 'Q: verify first notes tree' '
-	cat >expect.unsorted <<-EOF &&
+	sort >expect <<-EOF &&
 	100644 blob $commit1
 	100644 blob $commit2
 	100644 blob $commit3
 	EOF
-	cat expect.unsorted | sort >expect &&
 	git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
 	test_cmp expect actual
 '
@@ -2053,12 +2047,11 @@
 '
 
 test_expect_success 'Q: verify second notes tree' '
-	cat >expect.unsorted <<-EOF &&
+	sort >expect <<-EOF &&
 	100644 blob $commit1
 	100644 blob $commit2
 	100644 blob $commit3
 	EOF
-	cat expect.unsorted | sort >expect &&
 	git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
 	test_cmp expect actual
 '
@@ -2093,10 +2086,9 @@
 '
 
 test_expect_success 'Q: verify third notes tree' '
-	cat >expect.unsorted <<-EOF &&
+	sort >expect <<-EOF &&
 	100644 blob $commit1
 	EOF
-	cat expect.unsorted | sort >expect &&
 	git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
 	test_cmp expect actual
 '
@@ -2120,10 +2112,9 @@
 '
 
 test_expect_success 'Q: verify fourth notes tree' '
-	cat >expect.unsorted <<-EOF &&
+	sort >expect <<-EOF &&
 	100644 blob $commit2
 	EOF
-	cat expect.unsorted | sort >expect &&
 	git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
 	test_cmp expect actual
 '
@@ -2884,7 +2875,7 @@
 	COMMIT
 	M 100644 :403x hello.c
 	EOF
-	test_i18ngrep "space after mark" err
+	test_grep "space after mark" err
 '
 
 # inline is misspelled; fast-import thinks it is some unknown dataref
@@ -2900,7 +2891,7 @@
 	inline
 	BLOB
 	EOF
-	test_i18ngrep "nvalid dataref" err
+	test_grep "nvalid dataref" err
 '
 
 test_expect_success 'S: filemodify with garbage after sha1 must fail' '
@@ -2913,7 +2904,7 @@
 	COMMIT
 	M 100644 ${sha1}x hello.c
 	EOF
-	test_i18ngrep "space after SHA1" err
+	test_grep "space after SHA1" err
 '
 
 #
@@ -2928,7 +2919,7 @@
 	COMMIT
 	N :202x :302
 	EOF
-	test_i18ngrep "space after mark" err
+	test_grep "space after mark" err
 '
 
 test_expect_success 'S: notemodify with garbage after inline dataref must fail' '
@@ -2943,7 +2934,7 @@
 	note blob
 	BLOB
 	EOF
-	test_i18ngrep "nvalid dataref" err
+	test_grep "nvalid dataref" err
 '
 
 test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
@@ -2956,7 +2947,7 @@
 	COMMIT
 	N ${sha1}x :302
 	EOF
-	test_i18ngrep "space after SHA1" err
+	test_grep "space after SHA1" err
 '
 
 #
@@ -2971,7 +2962,7 @@
 	COMMIT
 	N :202 :302x
 	EOF
-	test_i18ngrep "after mark" err
+	test_grep "after mark" err
 '
 
 #
@@ -3004,7 +2995,7 @@
 	EOF
 
 	# now evaluate the error
-	test_i18ngrep "after mark" err
+	test_grep "after mark" err
 '
 
 
@@ -3023,7 +3014,7 @@
 	merge :303x
 	M 100644 :403 hello.c
 	EOF
-	test_i18ngrep "after mark" err
+	test_grep "after mark" err
 '
 
 #
@@ -3038,7 +3029,7 @@
 	tag S
 	TAG
 	EOF
-	test_i18ngrep "after mark" err
+	test_grep "after mark" err
 '
 
 #
@@ -3048,7 +3039,7 @@
 	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
 	cat-blob :403x
 	EOF
-	test_i18ngrep "after mark" err
+	test_grep "after mark" err
 '
 
 #
@@ -3058,7 +3049,7 @@
 	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
 	ls :302x hello.c
 	EOF
-	test_i18ngrep "space after mark" err
+	test_grep "space after mark" err
 '
 
 test_expect_success 'S: ls with garbage after sha1 must fail' '
@@ -3066,7 +3057,7 @@
 	test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
 	ls ${sha1}x hello.c
 	EOF
-	test_i18ngrep "space after tree-ish" err
+	test_grep "space after tree-ish" err
 '
 
 ###
diff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh
index a98ef03..410a871 100755
--- a/t/t9304-fast-import-marks.sh
+++ b/t/t9304-fast-import-marks.sh
@@ -49,4 +49,33 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'paths adjusted for relative subdir' '
+	git init deep-dst &&
+	mkdir deep-dst/subdir &&
+	>deep-dst/subdir/empty-marks &&
+	git -C deep-dst/subdir fast-import \
+		--rewrite-submodules-from=sub:../../from \
+		--rewrite-submodules-to=sub:../../to \
+		--import-marks=empty-marks \
+		--export-marks=exported-marks \
+		--export-pack-edges=exported-edges \
+		<dump &&
+	# we do not bother checking resulting repo; we just care that nothing
+	# complained about failing to open files for reading, and that files
+	# for writing were created in the expected spot
+	test_path_is_file deep-dst/subdir/exported-marks &&
+	test_path_is_file deep-dst/subdir/exported-edges
+'
+
+test_expect_success 'relative marks are not affected by subdir' '
+	git init deep-relative &&
+	mkdir deep-relative/subdir &&
+	git -C deep-relative/subdir fast-import \
+		--relative-marks \
+		--export-marks=exported-marks \
+		<dump &&
+	test_path_is_missing deep-relative/subdir/exported-marks &&
+	test_path_is_file deep-relative/.git/info/fast-import/exported-marks
+'
+
 test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 26c25c0..1eb035e 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -236,7 +236,7 @@
 
 test_expect_success 'set up faked signed tag' '
 
-	cat signed-tag-import | git fast-import
+	git fast-import <signed-tag-import
 
 '
 
@@ -537,7 +537,7 @@
 
 test_expect_success 'set-up a few more tags for tag export tests' '
 	git checkout -f main &&
-	HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
+	HEAD_TREE=$(git show -s --pretty=raw HEAD | sed -n "/tree/s/tree //p") &&
 	git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
 	git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
 	git tag    tag-obj_tag     -m "tagging a tag" tree_tag-obj &&
@@ -791,4 +791,14 @@
 	)
 '
 
+test_expect_success 'fast-export handles --end-of-options' '
+	git update-ref refs/heads/nodash HEAD &&
+	git update-ref refs/heads/--dashes HEAD &&
+	git fast-export --end-of-options nodash >expect &&
+	git fast-export --end-of-options --dashes >actual.raw &&
+	# fix up lines which mention the ref for comparison
+	sed s/--dashes/nodash/ <actual.raw >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
index 77047e2..156a647 100755
--- a/t/t9351-fast-export-anonymize.sh
+++ b/t/t9351-fast-export-anonymize.sh
@@ -25,6 +25,7 @@
 test_expect_success 'export anonymized stream' '
 	git fast-export --anonymize --all \
 		--anonymize-map=retain-me \
+		--anonymize-map=xyzzy:should-not-appear \
 		--anonymize-map=xyzzy:custom-name \
 		--anonymize-map=other \
 		>stream
@@ -41,6 +42,7 @@
 
 test_expect_success 'stream contains user-specified names' '
 	grep retain-me stream &&
+	! grep should-not-appear stream &&
 	grep custom-name stream
 '
 
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 379b19f..e499c7f 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -66,10 +66,11 @@
 
 # note that cvs doesn't accept absolute pathnames
 # as argument to co -d
-test_expect_success 'basic checkout' \
-  'GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
-   test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
+test_expect_success 'basic checkout' '
+	GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
+	test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
+	test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"
+'
 
 #------------------------
 # PSERVER AUTHENTICATION
@@ -115,35 +116,40 @@
 END VERIFICATION REQUEST
 EOF
 
-test_expect_success 'pserver authentication' \
-  'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication' '
+	git-cvsserver pserver <request-anonymous >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'pserver authentication failure (non-anonymous user)' \
-  'if cat request-git | git-cvsserver pserver >log 2>&1
-   then
-       false
-   else
-       true
-   fi &&
-   sed -ne \$p log | grep "^I HATE YOU\$"'
+test_expect_success 'pserver authentication failure (non-anonymous user)' '
+	if git-cvsserver pserver <request-git >log 2>&1
+	then
+	    false
+	else
+	    true
+	fi &&
+	sed -ne \$p log | grep "^I HATE YOU\$"
+'
 
-test_expect_success 'pserver authentication success (non-anonymous user with password)' \
-  'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication success (non-anonymous user with password)' '
+	git-cvsserver pserver <login-git-ok >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'pserver authentication (login)' \
-  'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'pserver authentication (login)' '
+	git-cvsserver pserver <login-anonymous >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
-  'if cat login-git | git-cvsserver pserver >log 2>&1
-   then
-       false
-   else
-       true
-   fi &&
-   sed -ne \$p log | grep "^I HATE YOU\$"'
+test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
+	if git-cvsserver pserver <login-git >log 2>&1
+	then
+	    false
+	else
+	    true
+	fi &&
+	sed -ne \$p log | grep "^I HATE YOU\$"
+'
 
 
 # misuse pserver authentication for testing of req_Root
@@ -165,36 +171,38 @@
 Root $WORKDIR
 EOF
 
-test_expect_success 'req_Root failure (relative pathname)' \
-  'if cat request-relative | git-cvsserver pserver >log 2>&1
-   then
-       echo unexpected success
-       false
-   else
-       true
-   fi &&
-   tail log | grep "^error 1 Root must be an absolute pathname$"'
-
-test_expect_success 'req_Root failure (conflicting roots)' \
-  'cat request-conflict | git-cvsserver pserver >log 2>&1 &&
-   tail log | grep "^error 1 Conflicting roots specified$"'
-
-test_expect_success 'req_Root (strict paths)' \
-  'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
-
-test_expect_success 'req_Root failure (strict-paths)' '
-    ! cat request-anonymous |
-    git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+test_expect_success 'req_Root failure (relative pathname)' '
+	if git-cvsserver pserver <request-relative >log 2>&1
+	then
+		echo unexpected success
+		false
+	else
+		true
+	fi &&
+	tail log | grep "^error 1 Root must be an absolute pathname$"
 '
 
-test_expect_success 'req_Root (w/o strict-paths)' \
-  'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root failure (conflicting roots)' '
+	git-cvsserver pserver <request-conflict >log 2>&1 &&
+	tail log | grep "^error 1 Conflicting roots specified$"
+'
+
+test_expect_success 'req_Root (strict paths)' '
+	git-cvsserver --strict-paths pserver "$SERVERDIR" <request-anonymous >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
+
+test_expect_success 'req_Root failure (strict-paths)' '
+	! git-cvsserver --strict-paths pserver "$WORKDIR" <request-anonymous >log 2>&1
+'
+
+test_expect_success 'req_Root (w/o strict-paths)' '
+	git-cvsserver pserver "$WORKDIR/" <request-anonymous >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
-    ! cat request-anonymous |
-    git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+	! git-cvsserver pserver "$WORKDIR/gitcvs" <request-anonymous >log 2>&1
 '
 
 cat >request-base  <<EOF
@@ -206,27 +214,29 @@
 Root /gitcvs.git
 EOF
 
-test_expect_success 'req_Root (base-path)' \
-  'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (base-path)' '
+	git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 test_expect_success 'req_Root failure (base-path)' '
-    ! cat request-anonymous |
-    git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+	! git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" <request-anonymous >log 2>&1
 '
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
-test_expect_success 'req_Root (export-all)' \
-  'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (export-all)' '
+	git-cvsserver --export-all pserver "$WORKDIR" <request-anonymous >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
-test_expect_success 'req_Root failure (export-all w/o directory list)' \
-  '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+test_expect_success 'req_Root failure (export-all w/o directory list)' '
+	! (git-cvsserver --export-all pserver <request-anonymous >log 2>&1 || false)'
 
-test_expect_success 'req_Root (everything together)' \
-  'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU\$"'
+test_expect_success 'req_Root (everything together)' '
+	git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
+	sed -ne \$p log | grep "^I LOVE YOU\$"
+'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
 
@@ -247,45 +257,49 @@
    test ! -d cvswork2'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.ext.enabled = true' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
-   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
-   test_cmp cvswork cvswork2'
+test_expect_success 'gitcvs.ext.enabled = true' '
+	GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+	GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+	GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+	test_cmp cvswork cvswork2
+'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.ext.enabled = false' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
-   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
-   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
-   then
-     echo unexpected cvs success
-     false
-   else
-     true
-   fi &&
-   grep "GITCVS emulation disabled" cvs.log &&
-   test ! -d cvswork2'
+test_expect_success 'gitcvs.ext.enabled = false' '
+	GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
+	GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+	if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
+	then
+		echo unexpected cvs success
+		false
+	else
+		true
+	fi &&
+	grep "GITCVS emulation disabled" cvs.log &&
+	test ! -d cvswork2
+'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.dbname' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
-   GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
-   test_cmp cvswork cvswork2 &&
-   test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
-   cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"'
+test_expect_success 'gitcvs.dbname' '
+	GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+	GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
+	GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+	test_cmp cvswork cvswork2 &&
+	test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
+	cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"
+'
 
 rm -fr cvswork2
-test_expect_success 'gitcvs.ext.dbname' \
-  'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
-   GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
-   GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
-   test_cmp cvswork cvswork2 &&
-   test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
-   test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
-   cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"'
+test_expect_success 'gitcvs.ext.dbname' '
+	GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+	GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+	GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+	GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
+	test_cmp cvswork cvswork2 &&
+	test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
+	test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
+	cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"
+'
 
 
 #------------
@@ -299,109 +313,115 @@
 GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
 exit 1
 
-test_expect_success 'cvs update (create new file)' \
-  'echo testfile1 >testfile1 &&
-   git add testfile1 &&
-   git commit -q -m "Add testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
-   test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (create new file)' '
+	echo testfile1 >testfile1 &&
+	git add testfile1 &&
+	git commit -q -m "Add testfile1" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
+	test_cmp testfile1 ../testfile1
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (update existing file)' \
-  'echo line 2 >>testfile1 &&
-   git add testfile1 &&
-   git commit -q -m "Append to testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
-   test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (update existing file)' '
+	echo line 2 >>testfile1 &&
+	git add testfile1 &&
+	git commit -q -m "Append to testfile1" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
+	test_cmp testfile1 ../testfile1
+'
 
 cd "$WORKDIR"
 #TODO: cvsserver doesn't support update w/o -d
 test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" '
-   mkdir test &&
-   echo >test/empty &&
-   git add test &&
-   git commit -q -m "Single Subdirectory" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test ! -d test
+	mkdir test &&
+	echo >test/empty &&
+	git add test &&
+	git commit -q -m "Single Subdirectory" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test ! -d test
 '
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (subdirectories)' \
-  '(for dir in A A/B A/B/C A/D E; do
-      mkdir $dir &&
-      echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")"  &&
-      git add $dir || exit 1
-   done) &&
-   git commit -q -m "deep sub directory structure" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update -d &&
-   (for dir in A A/B A/B/C A/D E; do
-      filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
-      if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
-	test_cmp "$dir/$filename" "../$dir/$filename"; then
-        :
-      else
-	exit 1
-      fi
-    done)'
+test_expect_success 'cvs update (subdirectories)' '
+	(for dir in A A/B A/B/C A/D E; do
+		mkdir $dir &&
+		echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")"  &&
+		git add $dir || exit 1
+	done) &&
+	git commit -q -m "deep sub directory structure" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update -d &&
+	(for dir in A A/B A/B/C A/D E; do
+		filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
+		if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
+			test_cmp "$dir/$filename" "../$dir/$filename"; then
+		:
+		else
+			exit 1
+		fi
+	done)
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (delete file)' \
-  'git rm testfile1 &&
-   git commit -q -m "Remove testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test -z "$(grep testfile1 CVS/Entries)" &&
-   test ! -f testfile1'
+test_expect_success 'cvs update (delete file)' '
+	git rm testfile1 &&
+	git commit -q -m "Remove testfile1" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test -z "$(grep testfile1 CVS/Entries)" &&
+	test ! -f testfile1
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (re-add deleted file)' \
-  'echo readded testfile >testfile1 &&
-   git add testfile1 &&
-   git commit -q -m "Re-Add testfile1" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
-   test_cmp testfile1 ../testfile1'
+test_expect_success 'cvs update (re-add deleted file)' '
+	echo readded testfile >testfile1 &&
+	git add testfile1 &&
+	git commit -q -m "Re-Add testfile1" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
+	test_cmp testfile1 ../testfile1
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (merge)' \
-  'echo Line 0 >expected &&
-   for i in 1 2 3 4 5 6 7
-   do
-     echo Line $i >>merge &&
-     echo Line $i >>expected || return 1
-   done &&
-   echo Line 8 >>expected &&
-   git add merge &&
-   git commit -q -m "Merge test (pre-merge)" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
-   test_cmp merge ../merge &&
-   ( echo Line 0 && cat merge ) >merge.tmp &&
-   mv merge.tmp merge &&
-   cd "$WORKDIR" &&
-   echo Line 8 >>merge &&
-   git add merge &&
-   git commit -q -m "Merge test (merge)" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   sleep 1 && touch merge &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test_cmp merge ../expected'
+test_expect_success 'cvs update (merge)' '
+	echo Line 0 >expected &&
+	for i in 1 2 3 4 5 6 7
+	do
+		echo Line $i >>merge &&
+		echo Line $i >>expected || return 1
+	done &&
+	echo Line 8 >>expected &&
+	git add merge &&
+	git commit -q -m "Merge test (pre-merge)" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
+	test_cmp merge ../merge &&
+	( echo Line 0 && cat merge ) >merge.tmp &&
+	mv merge.tmp merge &&
+	cd "$WORKDIR" &&
+	echo Line 8 >>merge &&
+	git add merge &&
+	git commit -q -m "Merge test (merge)" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	sleep 1 && touch merge &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test_cmp merge ../expected
+'
 
 cd "$WORKDIR"
 
@@ -418,55 +438,58 @@
   echo Line $i >>expected.C
 done
 
-test_expect_success 'cvs update (conflict merge)' \
-  '( echo LINE 0 && cat merge ) >merge.tmp &&
-   mv merge.tmp merge &&
-   git add merge &&
-   git commit -q -m "Merge test (conflict)" &&
-   git push gitcvs.git >/dev/null &&
-   cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update &&
-   test_cmp merge ../expected.C'
+test_expect_success 'cvs update (conflict merge)' '
+	( echo LINE 0 && cat merge ) >merge.tmp &&
+	mv merge.tmp merge &&
+	git add merge &&
+	git commit -q -m "Merge test (conflict)" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test_cmp merge ../expected.C
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (-C)' \
-  'cd cvswork &&
-   GIT_CONFIG="$git_config" cvs -Q update -C &&
-   test_cmp merge ../merge'
+test_expect_success 'cvs update (-C)' '
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs -Q update -C &&
+	test_cmp merge ../merge
+'
 
 cd "$WORKDIR"
-test_expect_success 'cvs update (merge no-op)' \
-   'echo Line 9 >>merge &&
-    cp merge cvswork/merge &&
-    git add merge &&
-    git commit -q -m "Merge test (no-op)" &&
-    git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    sleep 1 && touch merge &&
-    GIT_CONFIG="$git_config" cvs -Q update &&
-    test_cmp merge ../merge'
+test_expect_success 'cvs update (merge no-op)' '
+	echo Line 9 >>merge &&
+	cp merge cvswork/merge &&
+	git add merge &&
+	git commit -q -m "Merge test (no-op)" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	sleep 1 && touch merge &&
+	GIT_CONFIG="$git_config" cvs -Q update &&
+	test_cmp merge ../merge
+'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (-p)' '
-    touch really-empty &&
-    echo Line 1 > no-lf &&
-    printf "Line 2" >> no-lf &&
-    git add really-empty no-lf &&
-    git commit -q -m "Update -p test" &&
-    git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs update &&
-    for i in merge no-lf empty really-empty; do
-	GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out &&
-	test_cmp $i.out ../$i || return 1
-    done
+	touch really-empty &&
+	echo Line 1 > no-lf &&
+	printf "Line 2" >> no-lf &&
+	git add really-empty no-lf &&
+	git commit -q -m "Update -p test" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs update &&
+	for i in merge no-lf empty really-empty; do
+		GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out &&
+		test_cmp $i.out ../$i || return 1
+	done
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (module list supports packed refs)' '
-    GIT_DIR="$SERVERDIR" git pack-refs --all &&
-    GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
-    grep "cvs update: New directory \`main'\''" < out
+	GIT_DIR="$SERVERDIR" git pack-refs --all &&
+	GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
+	grep "cvs update: New directory \`main'\''" < out
 '
 
 #------------
@@ -475,30 +498,30 @@
 
 cd "$WORKDIR"
 test_expect_success 'cvs status' '
-    mkdir status.dir &&
-    echo Line > status.dir/status.file &&
-    echo Line > status.file &&
-    git add status.dir status.file &&
-    git commit -q -m "Status test" &&
-    git push gitcvs.git >/dev/null &&
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs update &&
-    GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
-    test_line_count = 2 ../out
+	mkdir status.dir &&
+	echo Line > status.dir/status.file &&
+	echo Line > status.file &&
+	git add status.dir status.file &&
+	git commit -q -m "Status test" &&
+	git push gitcvs.git >/dev/null &&
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs update &&
+	GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
+	test_line_count = 2 ../out
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status (nonrecursive)' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
-    test_line_count = 1 ../out
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
+	test_line_count = 1 ../out
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status (no subdirs in header)' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
-    ! grep / <../out
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
+	! grep / <../out
 '
 
 #------------
@@ -507,9 +530,9 @@
 
 cd "$WORKDIR"
 test_expect_success 'cvs co -c (shows module database)' '
-    GIT_CONFIG="$git_config" cvs co -c > out &&
-    grep "^main[	 ][ 	]*main$" <out &&
-    ! grep -v "^main[	 ][ 	]*main$" <out
+	GIT_CONFIG="$git_config" cvs co -c > out &&
+	grep "^main[	 ][ 	]*main$" <out &&
+	! grep -v "^main[	 ][ 	]*main$" <out
 '
 
 #------------
@@ -575,11 +598,11 @@
 
 cd "$WORKDIR"
 test_expect_success 'cvs log' '
-    cd cvswork &&
-    test x"$expectStat" = x"0" &&
-    GIT_CONFIG="$git_config" cvs log merge >../out &&
-    sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
-    test_cmp ../expect ../actual
+	cd cvswork &&
+	test x"$expectStat" = x"0" &&
+	GIT_CONFIG="$git_config" cvs log merge >../out &&
+sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual &&
+	test_cmp ../expect ../actual
 '
 
 #------------
@@ -588,11 +611,11 @@
 
 cd "$WORKDIR"
 test_expect_success 'cvs annotate' '
-    cd cvswork &&
-    GIT_CONFIG="$git_config" cvs annotate merge >../out &&
-    sed -e "s/ .*//" ../out >../actual &&
-    printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
-    test_cmp ../expect ../actual
+	cd cvswork &&
+	GIT_CONFIG="$git_config" cvs annotate merge >../out &&
+	sed -e "s/ .*//" ../out >../actual &&
+	printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
+	test_cmp ../expect ../actual
 '
 
 #------------
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 0333065..7679780 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -627,6 +627,7 @@
 test_expect_success 'setup' '
 	version=$(git config core.repositoryformatversion) &&
 	algo=$(test_might_fail git config extensions.objectformat) &&
+	refstorage=$(test_might_fail git config extensions.refstorage) &&
 	cat >.git/config <<-\EOF &&
 	# testing noval and alternate separator
 	[gitweb]
@@ -637,6 +638,10 @@
 	if test -n "$algo"
 	then
 		git config extensions.objectformat "$algo"
+	fi &&
+	if test -n "$refstorage"
+	then
+		git config extensions.refstorage "$refstorage"
 	fi
 '
 
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index 6d75370..d8e8548 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use lib (split(/:/, $ENV{GITPERLLIB}));
 
-use 5.008;
+use 5.008001;
 use warnings;
 use strict;
 
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index dc88d0e..53af8e3 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -54,7 +54,7 @@
 	(
 		cd "$git" &&
 		test_must_fail git p4 sync 2>errs &&
-		test_i18ngrep "Perhaps you never did" errs
+		test_grep "Perhaps you never did" errs
 	)
 '
 
@@ -86,7 +86,7 @@
 		test_commit head &&
 		git p4 sync --branch=depot //depot@all &&
 		git p4 sync --branch=refs/remotes/p4/depot >out &&
-		test_i18ngrep "No changes to import!" out
+		test_grep "No changes to import!" out
 	)
 '
 
@@ -101,7 +101,7 @@
 		test_commit head &&
 		git p4 sync --branch=branch1 //depot@all &&
 		git p4 sync --branch=p4/branch1 >out &&
-		test_i18ngrep "No changes to import!" out
+		test_grep "No changes to import!" out
 	)
 '
 
@@ -116,7 +116,7 @@
 		test_commit head &&
 		git p4 sync --branch=p4/some/path //depot@all &&
 		git p4 sync --branch=some/path >out &&
-		test_i18ngrep "No changes to import!" out
+		test_grep "No changes to import!" out
 	)
 '
 
@@ -131,7 +131,7 @@
 		test_commit head &&
 		git p4 sync --branch=refs/remotes/someremote/depot //depot@all &&
 		git p4 sync --branch=refs/remotes/someremote/depot >out &&
-		test_i18ngrep "No changes to import!" out
+		test_grep "No changes to import!" out
 	)
 '
 
@@ -143,7 +143,7 @@
 		test_commit head &&
 		git p4 sync --branch=depot //depot@all &&
 		test_must_fail git p4 sync --branch=depot2 2>errs &&
-		test_i18ngrep "Perhaps you never did" errs
+		test_grep "Perhaps you never did" errs
 	)
 '
 
@@ -155,7 +155,7 @@
 		test_commit head &&
 		git p4 sync --branch=depot //depot@all &&
 		test_must_fail git p4 sync --branch=refs/heads/master 2>errs &&
-		test_i18ngrep "Perhaps you never did" errs
+		test_grep "Perhaps you never did" errs
 	)
 '
 
@@ -290,7 +290,7 @@
 		export PATH &&
 		test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
 	) &&
-	test_i18ngrep ! Traceback errs
+	test_grep ! Traceback errs
 '
 
 # Hide a file from p4d, make sure we catch its complaint.  This won't fail in
@@ -301,7 +301,7 @@
 	mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden &&
 	test_when_finished cleanup_git &&
 	test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err &&
-	test_i18ngrep "Error from p4 print" err
+	test_grep "Error from p4 print" err
 '
 
 test_expect_success 'clone --bare should make a bare repository' '
@@ -330,7 +330,7 @@
 	test_when_finished cleanup_git &&
 	(
 		cd "$git" &&
-		gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+		gittime=$(git show -s --pretty=format:%at HEAD) &&
 		echo $p4time $gittime &&
 		test $p4time = $gittime
 	)
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 759a14f..c598011 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -135,7 +135,7 @@
 	(
 		cd "$git" &&
 		git p4 sync --branch=depot/branch2 >out &&
-		test_i18ngrep "No changes to import!" out
+		test_grep "No changes to import!" out
 	)
 '
 
@@ -466,7 +466,7 @@
 	)
 '
 
-# From a report in http://stackoverflow.com/questions/11893688
+# From a report in https://stackoverflow.com/questions/11893688
 # where --use-client-spec caused branch prefixes not to be removed;
 # every file in git appeared into a subdirectory of the branch name.
 test_expect_success 'use-client-spec detect-branches setup' '
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index 2a6ee2a..bb236cd 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -175,7 +175,7 @@
 		cp k-text-k k-text-ko &&
 		p4 add -t text+ko k-text-ko &&
 
-		cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+		iconv -f ascii -t utf-16 <k-text-k >k-utf16-k &&
 		p4 add -t utf16+k k-utf16-k &&
 
 		cp k-utf16-k k-utf16-ko &&
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index 7d4109f..6ae7ced 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -75,7 +75,7 @@
 		test_commit "dry-run1" &&
 		test_commit "dry-run2" &&
 		git p4 submit --dry-run >out &&
-		test_i18ngrep "Would apply" out
+		test_grep "Would apply" out
 	) &&
 	(
 		cd "$cli" &&
@@ -99,7 +99,7 @@
 		git commit -m "dry-run2" dry-run2 &&
 		git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ &&
 		git p4 submit --dry-run --export-labels >out &&
-		test_i18ngrep "Would create p4 label" out
+		test_grep "Would create p4 label" out
 	) &&
 	(
 		cd "$cli" &&
@@ -418,7 +418,7 @@
 			marshal_dump job0 <change &&
 			marshal_dump job1 <change
 		) | sort >jobs &&
-		cat jobname1 jobname2 | sort >expected &&
+		sort jobname1 jobname2 >expected &&
 		test_cmp expected jobs
 	)
 '
@@ -443,7 +443,7 @@
 		# build a job
 		make_job $(cat jobname) &&
 		test_must_fail git p4 submit 2>err &&
-		test_i18ngrep "Unknown field name" err
+		test_grep "Unknown field name" err
 	) &&
 	(
 		cd "$cli" &&
@@ -461,9 +461,9 @@
 		git add prep-only-add &&
 		git commit -m "prep only add" &&
 		git p4 submit --prepare-p4-only >out &&
-		test_i18ngrep "prepared for submission" out &&
-		test_i18ngrep "must be deleted" out &&
-		test_i18ngrep ! "everything below this line is just the diff" out
+		test_grep "prepared for submission" out &&
+		test_grep "must be deleted" out &&
+		test_grep ! "everything below this line is just the diff" out
 	) &&
 	(
 		cd "$cli" &&
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 0ca9937..c766fd1 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -35,7 +35,7 @@
 		git add file1 &&
 		git commit -m "line3 in file1 will conflict" &&
 		test_expect_code 1 git p4 submit >out &&
-		test_i18ngrep "No commits applied" out
+		test_grep "No commits applied" out
 	)
 '
 
@@ -58,7 +58,7 @@
 		git add file1 &&
 		git commit -m "line4 in file1 will conflict" &&
 		test_expect_code 1 git p4 submit >out &&
-		test_i18ngrep "Applied only the commits" out
+		test_grep "Applied only the commits" out
 	)
 '
 
@@ -81,7 +81,7 @@
 		# but this commit is okay
 		test_commit "okay_commit_after_skip" &&
 		echo s | test_expect_code 1 git p4 submit >out &&
-		test_i18ngrep "Applied only the commits" out
+		test_grep "Applied only the commits" out
 	)
 '
 
@@ -104,7 +104,7 @@
 		# but this commit is okay
 		test_commit "okay_commit_after_quit" &&
 		echo q | test_expect_code 1 git p4 submit >out &&
-		test_i18ngrep "No commits applied" out
+		test_grep "No commits applied" out
 	)
 '
 
@@ -144,7 +144,7 @@
 		# but this commit is okay
 		test_commit "okay_commit_after_auto_skip" &&
 		test_expect_code 1 git p4 submit --conflict=skip >out &&
-		test_i18ngrep "Applied only the commits" out
+		test_grep "Applied only the commits" out
 	)
 '
 
@@ -167,7 +167,7 @@
 		# but this commit is okay
 		test_commit "okay_commit_after_auto_quit" &&
 		test_expect_code 1 git p4 submit --conflict=quit >out &&
-		test_i18ngrep "No commits applied" out
+		test_grep "No commits applied" out
 	)
 '
 
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
index 9328410..5e904ac 100755
--- a/t/t9816-git-p4-locked.sh
+++ b/t/t9816-git-p4-locked.sh
@@ -9,7 +9,7 @@
 '
 
 # See
-# http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1088563
+# https://web.archive.org/web/20150602090517/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html#superuser.basic.typemap_locking
 # for suggestions on how to configure "sitewide pessimistic locking"
 # where only one person can have a file open for edit at a time.
 test_expect_success 'init depot' '
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
index a28dbbd..80c8c31 100755
--- a/t/t9824-git-p4-git-lfs.sh
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -17,8 +17,8 @@
 	sed -n '2,2 p' "$FILE" | grep "^oid " &&
 	sed -n '3,3 p' "$FILE" | grep "^size " &&
 	test_line_count = 3 "$FILE" &&
-	cat "$FILE" | grep "size $SIZE" &&
-	HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+	grep "size $SIZE" "$FILE" &&
+	HASH=$(sed -ne "/oid sha256:/s/oid sha256://gp" "$FILE") &&
 	LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
 	echo $EXPECTED_CONTENT >expect &&
 	test_path_is_file "$FILE" &&
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index d6c0478..569cf23 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -5,6 +5,17 @@
 
 test_description='test bash completion'
 
+# The Bash completion scripts must not print anything to either stdout or
+# stderr, which we try to verify. When tracing is enabled without support for
+# BASH_XTRACEFD this assertion will fail, so we have to mark the test as
+# untraceable with such ancient Bash versions.
+test_untraceable=UnfortunatelyYes
+
+# Override environment and always use master for the default initial branch
+# name for these tests, so that rev completion candidates are as expected.
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-bash.sh
 
 complete ()
@@ -87,9 +98,11 @@
 	else
 		sed -e 's/Z$//' |sort >expected
 	fi &&
-	run_completion "$1" &&
+	run_completion "$1" >"$TRASH_DIRECTORY"/bash-completion-output 2>&1 &&
 	sort out >out_sorted &&
-	test_cmp expected out_sorted
+	test_cmp expected out_sorted &&
+	test_must_be_empty "$TRASH_DIRECTORY"/bash-completion-output &&
+	rm "$TRASH_DIRECTORY"/bash-completion-output
 }
 
 # Test __gitcomp.
@@ -405,40 +418,40 @@
 
 test_expect_success '__git_dequote - plain unquoted word' '
 	__git_dequote unquoted-word &&
-	verbose test unquoted-word = "$dequoted_word"
+	test unquoted-word = "$dequoted_word"
 '
 
 # input:    b\a\c\k\'\\\"s\l\a\s\h\es
 # expected: back'\"slashes
 test_expect_success '__git_dequote - backslash escaped' '
 	__git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" &&
-	verbose test "back'\''\\\"slashes" = "$dequoted_word"
+	test "back'\''\\\"slashes" = "$dequoted_word"
 '
 
 # input:    sin'gle\' '"quo'ted
 # expected: single\ "quoted
 test_expect_success '__git_dequote - single quoted' '
 	__git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" &&
-	verbose test '\''single\ "quoted'\'' = "$dequoted_word"
+	test '\''single\ "quoted'\'' = "$dequoted_word"
 '
 
 # input:    dou"ble\\" "\"\quot"ed
 # expected: double\ "\quoted
 test_expect_success '__git_dequote - double quoted' '
 	__git_dequote '\''dou"ble\\" "\"\quot"ed'\'' &&
-	verbose test '\''double\ "\quoted'\'' = "$dequoted_word"
+	test '\''double\ "\quoted'\'' = "$dequoted_word"
 '
 
 # input: 'open single quote
 test_expect_success '__git_dequote - open single quote' '
 	__git_dequote "'\''open single quote" &&
-	verbose test "open single quote" = "$dequoted_word"
+	test "open single quote" = "$dequoted_word"
 '
 
 # input: "open double quote
 test_expect_success '__git_dequote - open double quote' '
 	__git_dequote "\"open double quote" &&
-	verbose test "open double quote" = "$dequoted_word"
+	test "open double quote" = "$dequoted_word"
 '
 
 
@@ -616,7 +629,7 @@
 	test_when_finished "git remote remove remote_2" &&
 	git remote add remote_2 git://remote_2 &&
 	(
-		verbose __git_is_configured_remote remote_2 &&
+		__git_is_configured_remote remote_2 &&
 		test_must_fail __git_is_configured_remote non-existent
 	)
 '
@@ -1250,6 +1263,29 @@
 	test_cmp expected out
 '
 
+test_expect_success '__git_complete_worktree_paths' '
+	test_when_finished "git worktree remove other_wt" &&
+	git worktree add --orphan other_wt &&
+	run_completion "git worktree remove " &&
+	grep other_wt out
+'
+
+test_expect_success '__git_complete_worktree_paths - not a git repository' '
+	(
+		cd non-repo &&
+		GIT_CEILING_DIRECTORIES="$ROOT" &&
+		export GIT_CEILING_DIRECTORIES &&
+		test_completion "git worktree remove " ""
+	)
+'
+
+test_expect_success '__git_complete_worktree_paths with -C' '
+	test_when_finished "git -C otherrepo worktree remove otherrepo_wt" &&
+	git -C otherrepo worktree add --orphan otherrepo_wt &&
+	run_completion "git -C otherrepo worktree remove " &&
+	grep otherrepo_wt out
+'
+
 test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
 	test_completion "git switch " <<-\EOF
 	branch-in-other Z
@@ -1259,6 +1295,142 @@
 	EOF
 '
 
+test_expect_success 'git bisect - when not bisecting, complete only replay and start subcommands' '
+	test_completion "git bisect " <<-\EOF
+	replay Z
+	start Z
+	EOF
+'
+
+test_expect_success 'git bisect - complete options to start subcommand' '
+	test_completion "git bisect start --" <<-\EOF
+	--term-new Z
+	--term-bad Z
+	--term-old Z
+	--term-good Z
+	--no-checkout Z
+	--first-parent Z
+	EOF
+'
+
+test_expect_success 'setup for git-bisect tests requiring a repo' '
+	git init git-bisect &&
+	(
+		cd git-bisect &&
+		echo "initial contents" >file &&
+		git add file &&
+		git commit -am "Initial commit" &&
+		git tag initial &&
+		echo "new line" >>file &&
+		git commit -am "First change" &&
+		echo "another new line" >>file &&
+		git commit -am "Second change" &&
+		git tag final
+	)
+'
+
+test_expect_success 'git bisect - start subcommand arguments before double-dash are completed as revs' '
+	(
+		cd git-bisect &&
+		test_completion "git bisect start " <<-\EOF
+		HEAD Z
+		final Z
+		initial Z
+		master Z
+		EOF
+	)
+'
+
+# Note that these arguments are <pathspec>s, which in practice the fallback
+# completion (not the git completion) later ends up completing as paths.
+test_expect_success 'git bisect - start subcommand arguments after double-dash are not completed' '
+	(
+		cd git-bisect &&
+		test_completion "git bisect start final initial -- " ""
+	)
+'
+
+test_expect_success 'setup for git-bisect tests requiring ongoing bisection' '
+	(
+		cd git-bisect &&
+		git bisect start --term-new=custom_new --term-old=custom_old final initial
+	)
+'
+
+test_expect_success 'git-bisect - when bisecting all subcommands are candidates' '
+	(
+		cd git-bisect &&
+		test_completion "git bisect " <<-\EOF
+		start Z
+		bad Z
+		custom_new Z
+		custom_old Z
+		new Z
+		good Z
+		old Z
+		terms Z
+		skip Z
+		reset Z
+		visualize Z
+		replay Z
+		log Z
+		run Z
+		help Z
+		EOF
+	)
+'
+
+test_expect_success 'git-bisect - options to terms subcommand are candidates' '
+	(
+		cd git-bisect &&
+		test_completion "git bisect terms --" <<-\EOF
+		--term-bad Z
+		--term-good Z
+		--term-new Z
+		--term-old Z
+		EOF
+	)
+'
+
+test_expect_success 'git-bisect - git-log options to visualize subcommand are candidates' '
+	(
+		cd git-bisect &&
+		# The completion used for git-log and here does not complete
+		# every git-log option, so rather than hope to stay in sync
+		# with exactly what it does we will just spot-test here.
+		test_completion "git bisect visualize --sta" <<-\EOF &&
+		--stat Z
+		EOF
+		test_completion "git bisect visualize --summar" <<-\EOF
+		--summary Z
+		EOF
+	)
+'
+
+test_expect_success 'git-bisect - view subcommand is not a candidate' '
+	(
+		cd git-bisect &&
+		test_completion "git bisect vi" <<-\EOF
+		visualize Z
+		EOF
+	)
+'
+
+test_expect_success 'git-bisect - existing view subcommand is recognized and enables completion of git-log options' '
+	(
+		cd git-bisect &&
+		# The completion used for git-log and here does not complete
+		# every git-log option, so rather than hope to stay in sync
+		# with exactly what it does we will just spot-test here.
+		test_completion "git bisect view --sta" <<-\EOF &&
+		--stat Z
+		EOF
+		test_completion "git bisect view --summar" <<-\EOF
+		--summary Z
+		EOF
+	)
+'
+
 test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
 	test_completion "git checkout " <<-\EOF
 	HEAD Z
@@ -1571,7 +1743,7 @@
 	)
 '
 
-test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
+test_expect_success 'non-cone mode sparse-checkout gives rooted paths' '
 	# reset sparse-checkout repo to non-cone mode
 	git -C sparse-checkout sparse-checkout disable &&
 	git -C sparse-checkout sparse-checkout set --no-cone &&
@@ -1581,7 +1753,12 @@
 		# expected to be empty since we have not configured
 		# custom completion for non-cone mode
 		test_completion "git sparse-checkout set f" <<-\EOF
-
+		/folder1/0/1/t.txt Z
+		/folder1/expected Z
+		/folder1/out Z
+		/folder1/out_sorted Z
+		/folder2/0/t.txt Z
+		/folder3/t.txt Z
 		EOF
 	)
 '
@@ -1622,14 +1799,22 @@
 '
 
 test_expect_success 'git switch - with --track, complete only remote branches' '
-	test_completion "git switch --track " <<-\EOF
+	test_completion "git switch --track " <<-\EOF &&
+	other/branch-in-other Z
+	other/main-in-other Z
+	EOF
+	test_completion "git switch -t " <<-\EOF
 	other/branch-in-other Z
 	other/main-in-other Z
 	EOF
 '
 
 test_expect_success 'git checkout - with --track, complete only remote branches' '
-	test_completion "git checkout --track " <<-\EOF
+	test_completion "git checkout --track " <<-\EOF &&
+	other/branch-in-other Z
+	other/main-in-other Z
+	EOF
+	test_completion "git checkout -t " <<-\EOF
 	other/branch-in-other Z
 	other/main-in-other Z
 	EOF
@@ -1912,6 +2097,14 @@
 	EOF
 '
 
+test_expect_success 'git restore completes modified files' '
+	test_commit A a.file &&
+	echo B >a.file &&
+	test_completion "git restore a." <<-\EOF
+	a.file
+	EOF
+'
+
 test_expect_success 'teardown after ref completion' '
 	git branch -d matching-branch &&
 	git tag -d matching-tag &&
@@ -2456,6 +2649,24 @@
 	EOF
 '
 
+test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd> ; ... }' '
+	test_config alias.co "!f() { : checkout ; if ... } f" &&
+	test_completion "git co m" <<-\EOF
+	main Z
+	mybranch Z
+	mytag Z
+	EOF
+'
+
+test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd>; ... }' '
+	test_config alias.co "!f() { : checkout; if ... } f" &&
+	test_completion "git co m" <<-\EOF
+	main Z
+	mybranch Z
+	mytag Z
+	EOF
+'
+
 test_expect_success 'completion without explicit _git_xxx function' '
 	test_completion "git version --" <<-\EOF
 	--build-options Z
@@ -2536,6 +2747,35 @@
 	EOF
 '
 
+test_expect_success 'setup for git config submodule tests' '
+	test_create_repo sub &&
+	test_commit -C sub initial &&
+	git submodule add ./sub
+'
+
+test_expect_success 'git config - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
+	test_completion "git config submodule." <<-\EOF
+	submodule.active Z
+	submodule.alternateErrorStrategy Z
+	submodule.alternateLocation Z
+	submodule.fetchJobs Z
+	submodule.propagateBranches Z
+	submodule.recurse Z
+	submodule.sub.Z
+	EOF
+'
+
+test_expect_success 'git config - variable name - __git_compute_second_level_config_vars_for_section' '
+	test_completion "git config submodule.sub." <<-\EOF
+	submodule.sub.url Z
+	submodule.sub.update Z
+	submodule.sub.branch Z
+	submodule.sub.fetchRecurseSubmodules Z
+	submodule.sub.ignore Z
+	submodule.sub.active Z
+	EOF
+'
+
 test_expect_success 'git config - value' '
 	test_completion "git config color.pager " <<-\EOF
 	false Z
@@ -2587,6 +2827,20 @@
 	EOF
 '
 
+test_expect_success 'git reflog show' '
+	test_when_finished "git checkout - && git branch -d shown" &&
+	git checkout -b shown &&
+	test_completion "git reflog sho" <<-\EOF &&
+	show Z
+	shown Z
+	EOF
+	test_completion "git reflog show sho" "shown " &&
+	test_completion "git reflog shown sho" "shown " &&
+	test_completion "git reflog --unt" "--until=" &&
+	test_completion "git reflog show --unt" "--until=" &&
+	test_completion "git reflog shown --unt" "--until="
+'
+
 test_expect_success 'options with value' '
 	test_completion "git merge -X diff-algorithm=" <<-\EOF
 
@@ -2596,30 +2850,30 @@
 test_expect_success 'sourcing the completion script clears cached commands' '
 	(
 		__git_compute_all_commands &&
-		verbose test -n "$__git_all_commands" &&
+		test -n "$__git_all_commands" &&
 		. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
-		verbose test -z "$__git_all_commands"
+		test -z "$__git_all_commands"
 	)
 '
 
 test_expect_success 'sourcing the completion script clears cached merge strategies' '
 	(
 		__git_compute_merge_strategies &&
-		verbose test -n "$__git_merge_strategies" &&
+		test -n "$__git_merge_strategies" &&
 		. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
-		verbose test -z "$__git_merge_strategies"
+		test -z "$__git_merge_strategies"
 	)
 '
 
 test_expect_success 'sourcing the completion script clears cached --options' '
 	(
 		__gitcomp_builtin checkout &&
-		verbose test -n "$__gitcomp_builtin_checkout" &&
+		test -n "$__gitcomp_builtin_checkout" &&
 		__gitcomp_builtin notes_edit &&
-		verbose test -n "$__gitcomp_builtin_notes_edit" &&
+		test -n "$__gitcomp_builtin_notes_edit" &&
 		. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
-		verbose test -z "$__gitcomp_builtin_checkout" &&
-		verbose test -z "$__gitcomp_builtin_notes_edit"
+		test -z "$__gitcomp_builtin_checkout" &&
+		test -z "$__gitcomp_builtin_notes_edit"
 	)
 '
 
@@ -2689,4 +2943,31 @@
 	test_must_fail __git_complete ga missing
 '
 
+test_expect_success '__git_pseudoref_exists' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		sane_unset __git_repo_path &&
+
+		# HEAD should exist, even if it points to an unborn branch.
+		__git_pseudoref_exists HEAD >output 2>&1 &&
+		test_must_be_empty output &&
+
+		# HEAD points to an existing branch, so it should exist.
+		test_commit A &&
+		__git_pseudoref_exists HEAD >output 2>&1 &&
+		test_must_be_empty output &&
+
+		# CHERRY_PICK_HEAD does not exist, so the existence check should fail.
+		! __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+		test_must_be_empty output &&
+
+		# CHERRY_PICK_HEAD points to a commit, so it should exist.
+		git update-ref CHERRY_PICK_HEAD A &&
+		__git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+		test_must_be_empty output
+	)
+'
+
 test_done
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index d459fae..d667dda 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -13,10 +13,10 @@
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
-c_red='\\[\\e[31m\\]'
-c_green='\\[\\e[32m\\]'
-c_lblue='\\[\\e[1;34m\\]'
-c_clear='\\[\\e[0m\\]'
+c_red='\001\e[31m\002'
+c_green='\001\e[32m\002'
+c_lblue='\001\e[1;34m\002'
+c_clear='\001\e[0m\002'
 
 test_expect_success 'setup for prompt tests' '
 	git init otherrepo &&
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 999d46f..2eccf10 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -14,7 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # The semantics of the editor variables are that of invoking
 # sh -c "$EDITOR \"$@\"" files ...
@@ -251,6 +251,61 @@
 	done
 }
 
+# Usage: test_ref_exists [options] <ref>
+#
+#   -C <dir>:
+#      Run all git commands in directory <dir>
+#
+# This helper function checks whether a reference exists. Symrefs or object IDs
+# will not be resolved. Can be used to check references with bad names.
+test_ref_exists () {
+	local indir=
+
+	while test $# != 0
+	do
+		case "$1" in
+		-C)
+			indir="$2"
+			shift
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done &&
+
+	indir=${indir:+"$indir"/} &&
+
+	if test "$#" != 1
+	then
+		BUG "expected exactly one reference"
+	fi &&
+
+	git ${indir:+ -C "$indir"} show-ref --exists "$1"
+}
+
+# Behaves the same as test_ref_exists, except that it checks for the absence of
+# a reference. This is preferable to `! test_ref_exists` as this function is
+# able to distinguish actually-missing references from other, generic errors.
+test_ref_missing () {
+	test_ref_exists "$@"
+	case "$?" in
+	2)
+		# This is the good case.
+		return 0
+		;;
+	0)
+		echo >&4 "test_ref_missing: reference exists"
+		return 1
+		;;
+	*)
+		echo >&4 "test_ref_missing: generic error"
+		return 1
+		;;
+	esac
+}
+
 # Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]
 #   -C <dir>:
 #	Run all git commands in directory <dir>
@@ -542,8 +597,17 @@
 		config_dir=$1
 		shift
 	fi
-	test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
-	git ${config_dir:+-C "$config_dir"} config "$@"
+
+	# If --worktree is provided, use it to configure/unconfigure
+	is_worktree=
+	if test "$1" = --worktree
+	then
+		is_worktree=1
+		shift
+	fi
+
+	test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} ${is_worktree:+--worktree} '$1'" &&
+	git ${config_dir:+-C "$config_dir"} config ${is_worktree:+--worktree} "$@"
 }
 
 test_config_global () {
@@ -901,6 +965,15 @@
 	fi
 }
 
+test_path_is_executable () {
+	test "$#" -ne 1 && BUG "1 param"
+	if ! test -x "$1"
+	then
+		echo "$1 is not executable"
+		false
+	fi
+}
+
 # Check if the directory exists and is empty as expected, barf otherwise.
 test_dir_is_empty () {
 	test "$#" -ne 1 && BUG "1 param"
@@ -1190,19 +1263,20 @@
 	cmp "$@"
 }
 
-# Wrapper for grep which used to be used for
-# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
-# in-flight changes. Should not be used and will be removed soon.
 test_i18ngrep () {
+	BUG "do not use test_i18ngrep---use test_grep instead"
+}
+
+test_grep () {
 	eval "last_arg=\${$#}"
 
 	test -f "$last_arg" ||
-	BUG "test_i18ngrep requires a file to read as the last parameter"
+	BUG "test_grep requires a file to read as the last parameter"
 
 	if test $# -lt 2 ||
 	   { test "x!" = "x$1" && test $# -lt 3 ; }
 	then
-		BUG "too few parameters to test_i18ngrep"
+		BUG "too few parameters to test_grep"
 	fi
 
 	if test "x!" = "x$1"
@@ -1227,15 +1301,6 @@
 	return 1
 }
 
-# Call any command "$@" but be more verbose about its
-# failure. This is handy for commands like "test" which do
-# not output anything when they fail.
-verbose () {
-	"$@" && return 0
-	echo >&4 "command failed: $(git rev-parse --sq-quote "$@")"
-	return 1
-}
-
 # Check if the file expected to be empty is indeed empty, and barfs
 # otherwise.
 
@@ -1282,6 +1347,39 @@
 	fi
 }
 
+# Tests that a commit message matches the expected text
+#
+# Usage: test_commit_message <rev> [-m <msg> | <file>]
+#
+# When using "-m" <msg> will have a line feed appended. If the second
+# argument is omitted then the expected message is read from stdin.
+
+test_commit_message () {
+	local msg_file=expect.msg
+
+	case $# in
+	3)
+		if test "$2" = "-m"
+		then
+			printf "%s\n" "$3" >"$msg_file"
+		else
+			BUG "Usage: test_commit_message <rev> [-m <message> | <file>]"
+		fi
+		;;
+	2)
+		msg_file="$2"
+		;;
+	1)
+		cat >"$msg_file"
+		;;
+	*)
+		BUG "Usage: test_commit_message <rev> [-m <message> | <file>]"
+		;;
+	esac
+	git show --no-patch --pretty=format:%B "$1" -- >actual.msg &&
+	test_cmp "$msg_file" actual.msg
+}
+
 # Compare paths respecting core.ignoreCase
 test_cmp_fspath () {
 	if test "x$1" = "x$2"
@@ -1557,7 +1655,21 @@
 
 # Detect the hash algorithm in use.
 test_detect_hash () {
-	test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
+	case "$GIT_TEST_DEFAULT_HASH" in
+	"sha256")
+	    test_hash_algo=sha256
+	    test_compat_hash_algo=sha1
+	    ;;
+	*)
+	    test_hash_algo=sha1
+	    test_compat_hash_algo=sha256
+	    ;;
+	esac
+}
+
+# Detect the hash algorithm in use.
+test_detect_ref_format () {
+	echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
 }
 
 # Load common hash metadata and common placeholder object IDs for use with
@@ -1609,6 +1721,12 @@
 	local algo="${test_hash_algo}" &&
 
 	case "$1" in
+	--hash=storage)
+		algo="$test_hash_algo" &&
+		shift;;
+	--hash=compat)
+		algo="$test_compat_hash_algo" &&
+		shift;;
 	--hash=*)
 		algo="${1#--hash=}" &&
 		shift;;
@@ -1775,6 +1893,20 @@
 	return 0
 }
 
+# Check that the given data fragment was included as part of the
+# trace2-format trace on stdin.
+#
+#	test_trace2_data <category> <key> <value>
+#
+# For example, to look for trace2_data_intmax("pack-objects", repo,
+# "reused", N) in an invocation of "git pack-objects", run:
+#
+#	GIT_TRACE2_EVENT="$(pwd)/trace.txt" git pack-objects ... &&
+#	test_trace2_data pack-objects reused N <trace2.txt
+test_trace2_data () {
+	grep -e '"category":"'"$1"'","key":"'"$2"'","value":"'"$3"'"'
+}
+
 # Given a GIT_TRACE2_EVENT log over stdin, writes to stdout a list of URLs
 # sent to git-remote-https child processes.
 test_remote_https_urls() {
diff --git a/t/test-lib-github-workflow-markup.sh b/t/test-lib-github-workflow-markup.sh
index 9c5339c..33405c9 100644
--- a/t/test-lib-github-workflow-markup.sh
+++ b/t/test-lib-github-workflow-markup.sh
@@ -14,7 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 #
 # The idea is for `test-lib.sh` to source this file when run in GitHub
 # workflows; these functions will then override (empty) functions
@@ -42,8 +42,8 @@
 	fixed)
 		echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1"
 		;;
-	ok)
-		# Exit without printing the "ok" tests
+	ok|broken)
+		# Exit without printing the "ok" or ""broken" tests
 		return
 		;;
 	esac
diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh
index 79c31c7..76cbbd3 100644
--- a/t/test-lib-junit.sh
+++ b/t/test-lib-junit.sh
@@ -15,7 +15,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 #
 # The idea is for `test-lib.sh` to source this file when the user asks
 # for JUnit XML; these functions will then override (empty) functions
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 62136ca..79d3e0e 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -13,7 +13,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
@@ -89,6 +89,9 @@
 prepend_var LSAN_OPTIONS : fast_unwind_on_malloc=0
 export LSAN_OPTIONS
 
+prepend_var UBSAN_OPTIONS : $GIT_SAN_OPTIONS
+export UBSAN_OPTIONS
+
 if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 then
 	echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).'
@@ -334,6 +337,7 @@
 	find "$TEST_RESULTS_SAN_DIR" \
 		-type f \
 		-name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null |
+	xargs grep -lv "Unable to get registers from thread" |
 	wc -l
 }
 
@@ -538,6 +542,8 @@
 
 GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
 export GIT_DEFAULT_HASH
+GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+export GIT_DEFAULT_REF_FORMAT
 GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
 export GIT_TEST_MERGE_ALGORITHM
 
@@ -1041,10 +1047,7 @@
 # (and we want to make sure we run any cleanup like
 # "set +x").
 test_eval_inner_ () {
-	# Do not add anything extra (including LF) after '$*'
-	eval "
-		want_trace && trace_level_=$(($trace_level_+1)) && set -x
-		$*"
+	eval "$*"
 }
 
 test_eval_ () {
@@ -1069,7 +1072,10 @@
 	#     be _inside_ the block to avoid polluting the "set -x" output
 	#
 
-	test_eval_inner_ "$@" </dev/null >&3 2>&4
+	# Do not add anything extra (including LF) after '$*'
+	test_eval_inner_ </dev/null >&3 2>&4 "
+		want_trace && trace_level_=$(($trace_level_+1)) && set -x
+		$*"
 	{
 		test_eval_ret_=$?
 		if want_trace
@@ -1086,22 +1092,22 @@
 	return $test_eval_ret_
 }
 
+fail_117 () {
+	return 117
+}
+
 test_run_ () {
 	test_cleanup=:
 	expecting_failure=$2
 
 	if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
-		# turn off tracing for this test-eval, as it simply creates
-		# confusing noise in the "-x" output
-		trace_tmp=$trace
-		trace=
 		# 117 is magic because it is unlikely to match the exit
 		# code of other programs
-		if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+		test_eval_inner_ "fail_117 && $1" </dev/null >&3 2>&4
+		if test $? != 117
 		then
-			BUG "broken &&-chain or run-away HERE-DOC: $1"
+			BUG "broken &&-chain: $1"
 		fi
-		trace=$trace_tmp
 	fi
 
 	setup_malloc_check
@@ -1291,6 +1297,11 @@
 		EOF
 	fi
 
+	if test -z "$passes_sanitize_leak" && test_bool_env TEST_PASSES_SANITIZE_LEAK false
+	then
+		BAIL_OUT "Please, set TEST_PASSES_SANITIZE_LEAK before sourcing test-lib.sh"
+	fi
+
 	if test "$test_fixed" != 0
 	then
 		say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
@@ -1593,7 +1604,8 @@
 	BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_SANITIZE_LEAK_LOG=true"
 fi
 
-if test "${GIT_TEST_CHAIN_LINT:-1}" != 0
+if test "${GIT_TEST_CHAIN_LINT:-1}" != 0 &&
+   test "${GIT_TEST_EXT_CHAIN_LINT:-1}" != 0
 then
 	"$PERL_PATH" "$TEST_DIRECTORY/chainlint.pl" "$0" ||
 		BUG "lint error (see '?!...!? annotations above)"
@@ -1740,7 +1752,16 @@
 	;;
 esac
 
-test_set_prereq REFFILES
+case "$GIT_DEFAULT_REF_FORMAT" in
+files)
+	test_set_prereq REFFILES;;
+reftable)
+	test_set_prereq REFTABLE;;
+*)
+	echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
+	exit 1
+	;;
+esac
 
 ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_CURL" && test_set_prereq LIBCURL
@@ -1931,12 +1952,17 @@
 	esac
 '
 
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+	test_have_prereq SHA1,REFFILES
+'
+
 # Ensure that no test accidentally triggers a Git command
 # that runs the actual maintenance scheduler, affecting a user's
 # system permanently.
 # Tests that verify the scheduler integration must set this locally
 # to avoid errors.
 GIT_TEST_MAINT_SCHEDULER="none:exit 1"
+export GIT_TEST_MAINT_SCHEDULER
 
 # Does this platform support `git fsmonitor--daemon`
 #
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
index 1bcf01a..3810e9b 100755
--- a/t/test-terminal.perl
+++ b/t/test-terminal.perl
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use IO::Pty;
diff --git a/t/unit-tests/.gitignore b/t/unit-tests/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/t/unit-tests/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/t/unit-tests/t-basic.c b/t/unit-tests/t-basic.c
new file mode 100644
index 0000000..fda1ae5
--- /dev/null
+++ b/t/unit-tests/t-basic.c
@@ -0,0 +1,95 @@
+#include "test-lib.h"
+
+/*
+ * The purpose of this "unit test" is to verify a few invariants of the unit
+ * test framework itself, as well as to provide examples of output from actually
+ * failing tests. As such, it is intended that this test fails, and thus it
+ * should not be run as part of `make unit-tests`. Instead, we verify it behaves
+ * as expected in the integration test t0080-unit-test-output.sh
+ */
+
+/* Used to store the return value of check_int(). */
+static int check_res;
+
+/* Used to store the return value of TEST(). */
+static int test_res;
+
+static void t_res(int expect)
+{
+	check_int(check_res, ==, expect);
+	check_int(test_res, ==, expect);
+}
+
+static void t_todo(int x)
+{
+	check_res = TEST_TODO(check(x));
+}
+
+static void t_skip(void)
+{
+	check(0);
+	test_skip("missing prerequisite");
+	check(1);
+}
+
+static int do_skip(void)
+{
+	test_skip("missing prerequisite");
+	return 1;
+}
+
+static void t_skip_todo(void)
+{
+	check_res = TEST_TODO(do_skip());
+}
+
+static void t_todo_after_fail(void)
+{
+	check(0);
+	TEST_TODO(check(0));
+}
+
+static void t_fail_after_todo(void)
+{
+	check(1);
+	TEST_TODO(check(0));
+	check(0);
+}
+
+static void t_messages(void)
+{
+	check_str("\thello\\", "there\"\n");
+	check_str("NULL", NULL);
+	check_char('a', ==, '\n');
+	check_char('\\', ==, '\'');
+}
+
+static void t_empty(void)
+{
+	; /* empty */
+}
+
+int cmd_main(int argc, const char **argv)
+{
+	test_res = TEST(check_res = check_int(1, ==, 1), "passing test");
+	TEST(t_res(1), "passing test and assertion return 1");
+	test_res = TEST(check_res = check_int(1, ==, 2), "failing test");
+	TEST(t_res(0), "failing test and assertion return 0");
+	test_res = TEST(t_todo(0), "passing TEST_TODO()");
+	TEST(t_res(1), "passing TEST_TODO() returns 1");
+	test_res = TEST(t_todo(1), "failing TEST_TODO()");
+	TEST(t_res(0), "failing TEST_TODO() returns 0");
+	test_res = TEST(t_skip(), "test_skip()");
+	TEST(check_int(test_res, ==, 1), "skipped test returns 1");
+	test_res = TEST(t_skip_todo(), "test_skip() inside TEST_TODO()");
+	TEST(t_res(1), "test_skip() inside TEST_TODO() returns 1");
+	test_res = TEST(t_todo_after_fail(), "TEST_TODO() after failing check");
+	TEST(check_int(test_res, ==, 0), "TEST_TODO() after failing check returns 0");
+	test_res = TEST(t_fail_after_todo(), "failing check after TEST_TODO()");
+	TEST(check_int(test_res, ==, 0), "failing check after TEST_TODO() returns 0");
+	TEST(t_messages(), "messages from failing string and char comparison");
+	test_res = TEST(t_empty(), "test with no checks");
+	TEST(check_int(test_res, ==, 0), "test with no checks returns 0");
+
+	return test_done();
+}
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
new file mode 100644
index 0000000..d6ac1fe
--- /dev/null
+++ b/t/unit-tests/t-ctype.c
@@ -0,0 +1,53 @@
+#include "test-lib.h"
+
+#define TEST_CHAR_CLASS(class, string) do { \
+	size_t len = ARRAY_SIZE(string) - 1 + \
+		BUILD_ASSERT_OR_ZERO(ARRAY_SIZE(string) > 0) + \
+		BUILD_ASSERT_OR_ZERO(sizeof(string[0]) == sizeof(char)); \
+	int skip = test__run_begin(); \
+	if (!skip) { \
+		for (int i = 0; i < 256; i++) { \
+			if (!check_int(class(i), ==, !!memchr(string, i, len)))\
+				test_msg("      i: 0x%02x", i); \
+		} \
+		check(!class(EOF)); \
+	} \
+	test__run_end(!skip, TEST_LOCATION(), #class " works"); \
+} while (0)
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+	"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+	"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+	"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+	"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+	"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+	"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+	"\x7f"
+
+int cmd_main(int argc, const char **argv) {
+	TEST_CHAR_CLASS(isspace, " \n\r\t");
+	TEST_CHAR_CLASS(isdigit, DIGIT);
+	TEST_CHAR_CLASS(isalpha, LOWER UPPER);
+	TEST_CHAR_CLASS(isalnum, LOWER UPPER DIGIT);
+	TEST_CHAR_CLASS(is_glob_special, "*?[\\");
+	TEST_CHAR_CLASS(is_regex_special, "$()*+.?[\\^{|");
+	TEST_CHAR_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+	TEST_CHAR_CLASS(isascii, ASCII);
+	TEST_CHAR_CLASS(islower, LOWER);
+	TEST_CHAR_CLASS(isupper, UPPER);
+	TEST_CHAR_CLASS(iscntrl, CNTRL);
+	TEST_CHAR_CLASS(ispunct, PUNCT);
+	TEST_CHAR_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+	TEST_CHAR_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
+
+	return test_done();
+}
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
new file mode 100644
index 0000000..a0d57df
--- /dev/null
+++ b/t/unit-tests/t-mem-pool.c
@@ -0,0 +1,31 @@
+#include "test-lib.h"
+#include "mem-pool.h"
+
+static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
+{
+	struct mem_pool pool = { .block_alloc = block_alloc };
+	f(&pool);
+	mem_pool_discard(&pool, 0);
+}
+
+static void t_calloc_100(struct mem_pool *pool)
+{
+	size_t size = 100;
+	char *buffer = mem_pool_calloc(pool, 1, size);
+	for (size_t i = 0; i < size; i++)
+		check_int(buffer[i], ==, 0);
+	if (!check(pool->mp_block != NULL))
+		return;
+	check(pool->mp_block->next_free != NULL);
+	check(pool->mp_block->end != NULL);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+	TEST(setup_static(t_calloc_100, 1024 * 1024),
+	     "mem_pool_calloc returns 100 zeroed bytes with big block");
+	TEST(setup_static(t_calloc_100, 1),
+	     "mem_pool_calloc returns 100 zeroed bytes with tiny block");
+
+	return test_done();
+}
diff --git a/t/unit-tests/t-prio-queue.c b/t/unit-tests/t-prio-queue.c
new file mode 100644
index 0000000..7a4e578
--- /dev/null
+++ b/t/unit-tests/t-prio-queue.c
@@ -0,0 +1,91 @@
+#include "test-lib.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data UNUSED)
+{
+	const int *a = va, *b = vb;
+	return *a - *b;
+}
+
+
+#define MISSING  -1
+#define DUMP	 -2
+#define STACK	 -3
+#define GET	 -4
+#define REVERSE  -5
+
+static int show(int *v)
+{
+	return v ? *v : MISSING;
+}
+
+static void test_prio_queue(int *input, size_t input_size,
+			    int *result, size_t result_size)
+{
+	struct prio_queue pq = { intcmp };
+	int j = 0;
+
+	for (int i = 0; i < input_size; i++) {
+		void *peek, *get;
+		switch(input[i]) {
+		case GET:
+			peek = prio_queue_peek(&pq);
+			get = prio_queue_get(&pq);
+			if (!check(peek == get))
+				return;
+			if (!check_uint(j, <, result_size))
+				break;
+			if (!check_int(result[j], ==, show(get)))
+				test_msg("      j: %d", j);
+			j++;
+			break;
+		case DUMP:
+			while ((peek = prio_queue_peek(&pq))) {
+				get = prio_queue_get(&pq);
+				if (!check(peek == get))
+					return;
+				if (!check_uint(j, <, result_size))
+					break;
+				if (!check_int(result[j], ==, show(get)))
+					test_msg("      j: %d", j);
+				j++;
+			}
+			break;
+		case STACK:
+			pq.compare = NULL;
+			break;
+		case REVERSE:
+			prio_queue_reverse(&pq);
+			break;
+		default:
+			prio_queue_put(&pq, &input[i]);
+			break;
+		}
+	}
+	check_uint(j, ==, result_size);
+	clear_prio_queue(&pq);
+}
+
+#define TEST_INPUT(input, result) \
+	test_prio_queue(input, ARRAY_SIZE(input), result, ARRAY_SIZE(result))
+
+int cmd_main(int argc, const char **argv)
+{
+	TEST(TEST_INPUT(((int []){ 2, 6, 3, 10, 9, 5, 7, 4, 5, 8, 1, DUMP }),
+			((int []){ 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10 })),
+	     "prio-queue works for basic input");
+	TEST(TEST_INPUT(((int []){ 6, 2, 4, GET, 5, 3, GET, GET, 1, DUMP }),
+			((int []){ 2, 3, 4, 1, 5, 6 })),
+	     "prio-queue works for mixed put & get commands");
+	TEST(TEST_INPUT(((int []){ 1, 2, GET, GET, GET, 1, 2, GET, GET, GET }),
+			((int []){ 1, 2, MISSING, 1, 2, MISSING })),
+	     "prio-queue works when queue is empty");
+	TEST(TEST_INPUT(((int []){ STACK, 8, 1, 5, 4, 6, 2, 3, DUMP }),
+			((int []){ 3, 2, 6, 4, 5, 1, 8 })),
+	     "prio-queue works when used as a LIFO stack");
+	TEST(TEST_INPUT(((int []){ STACK, 1, 2, 3, 4, 5, 6, REVERSE, DUMP }),
+			((int []){ 1, 2, 3, 4, 5, 6 })),
+	     "prio-queue works when LIFO stack is reversed");
+
+	return test_done();
+}
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
new file mode 100644
index 0000000..de434a4
--- /dev/null
+++ b/t/unit-tests/t-strbuf.c
@@ -0,0 +1,120 @@
+#include "test-lib.h"
+#include "strbuf.h"
+
+/* wrapper that supplies tests with an empty, initialized strbuf */
+static void setup(void (*f)(struct strbuf*, void*), void *data)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	f(&buf, data);
+	strbuf_release(&buf);
+	check_uint(buf.len, ==, 0);
+	check_uint(buf.alloc, ==, 0);
+}
+
+/* wrapper that supplies tests with a populated, initialized strbuf */
+static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	strbuf_addstr(&buf, init_str);
+	check_uint(buf.len, ==, strlen(init_str));
+	f(&buf, data);
+	strbuf_release(&buf);
+	check_uint(buf.len, ==, 0);
+	check_uint(buf.alloc, ==, 0);
+}
+
+static int assert_sane_strbuf(struct strbuf *buf)
+{
+	/* Initialized strbufs should always have a non-NULL buffer */
+	if (!check(!!buf->buf))
+		return 0;
+	/* Buffers should always be NUL-terminated */
+	if (!check_char(buf->buf[buf->len], ==, '\0'))
+		return 0;
+	/*
+	 * Freshly-initialized strbufs may not have a dynamically allocated
+	 * buffer
+	 */
+	if (buf->len == 0 && buf->alloc == 0)
+		return 1;
+	/* alloc must be at least one byte larger than len */
+	return check_uint(buf->len, <, buf->alloc);
+}
+
+static void t_static_init(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	check_uint(buf.len, ==, 0);
+	check_uint(buf.alloc, ==, 0);
+	check_char(buf.buf[0], ==, '\0');
+}
+
+static void t_dynamic_init(void)
+{
+	struct strbuf buf;
+
+	strbuf_init(&buf, 1024);
+	check(assert_sane_strbuf(&buf));
+	check_uint(buf.len, ==, 0);
+	check_uint(buf.alloc, >=, 1024);
+	check_char(buf.buf[0], ==, '\0');
+	strbuf_release(&buf);
+}
+
+static void t_addch(struct strbuf *buf, void *data)
+{
+	const char *p_ch = data;
+	const char ch = *p_ch;
+	size_t orig_alloc = buf->alloc;
+	size_t orig_len = buf->len;
+
+	if (!check(assert_sane_strbuf(buf)))
+		return;
+	strbuf_addch(buf, ch);
+	if (!check(assert_sane_strbuf(buf)))
+		return;
+	if (!(check_uint(buf->len, ==, orig_len + 1) &&
+	      check_uint(buf->alloc, >=, orig_alloc)))
+		return; /* avoid de-referencing buf->buf */
+	check_char(buf->buf[buf->len - 1], ==, ch);
+	check_char(buf->buf[buf->len], ==, '\0');
+}
+
+static void t_addstr(struct strbuf *buf, void *data)
+{
+	const char *text = data;
+	size_t len = strlen(text);
+	size_t orig_alloc = buf->alloc;
+	size_t orig_len = buf->len;
+
+	if (!check(assert_sane_strbuf(buf)))
+		return;
+	strbuf_addstr(buf, text);
+	if (!check(assert_sane_strbuf(buf)))
+		return;
+	if (!(check_uint(buf->len, ==, orig_len + len) &&
+	      check_uint(buf->alloc, >=, orig_alloc) &&
+	      check_uint(buf->alloc, >, orig_len + len) &&
+	      check_char(buf->buf[orig_len + len], ==, '\0')))
+	    return;
+	check_str(buf->buf + orig_len, text);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+	if (!TEST(t_static_init(), "static initialization works"))
+		test_skip_all("STRBUF_INIT is broken");
+	TEST(t_dynamic_init(), "dynamic initialization works");
+	TEST(setup(t_addch, "a"), "strbuf_addch adds char");
+	TEST(setup(t_addch, ""), "strbuf_addch adds NUL char");
+	TEST(setup_populated(t_addch, "initial value", "a"),
+	     "strbuf_addch appends to initial value");
+	TEST(setup(t_addstr, "hello there"), "strbuf_addstr adds string");
+	TEST(setup_populated(t_addstr, "initial value", "hello there"),
+	     "strbuf_addstr appends string to initial value");
+
+	return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
new file mode 100644
index 0000000..66d6980
--- /dev/null
+++ b/t/unit-tests/test-lib.c
@@ -0,0 +1,407 @@
+#include "test-lib.h"
+
+enum result {
+	RESULT_NONE,
+	RESULT_FAILURE,
+	RESULT_SKIP,
+	RESULT_SUCCESS,
+	RESULT_TODO
+};
+
+static struct {
+	enum result result;
+	int count;
+	unsigned failed :1;
+	unsigned lazy_plan :1;
+	unsigned running :1;
+	unsigned skip_all :1;
+	unsigned todo :1;
+} ctx = {
+	.lazy_plan = 1,
+	.result = RESULT_NONE,
+};
+
+/*
+ * Visual C interpolates the absolute Windows path for `__FILE__`,
+ * but we want to see relative paths, as verified by t0080.
+ * There are other compilers that do the same, and are not for
+ * Windows.
+ */
+#include "dir.h"
+
+static const char *make_relative(const char *location)
+{
+	static char prefix[] = __FILE__, buf[PATH_MAX], *p;
+	static size_t prefix_len;
+	static int need_bs_to_fs = -1;
+
+	/* one-time preparation */
+	if (need_bs_to_fs < 0) {
+		size_t len = strlen(prefix);
+		char needle[] = "t\\unit-tests\\test-lib.c";
+		size_t needle_len = strlen(needle);
+
+		if (len < needle_len)
+			die("unexpected prefix '%s'", prefix);
+
+		/*
+		 * The path could be relative (t/unit-tests/test-lib.c)
+		 * or full (/home/user/git/t/unit-tests/test-lib.c).
+		 * Check the slash between "t" and "unit-tests".
+		 */
+		prefix_len = len - needle_len;
+		if (prefix[prefix_len + 1] == '/') {
+			/* Oh, we're not Windows */
+			for (size_t i = 0; i < needle_len; i++)
+				if (needle[i] == '\\')
+					needle[i] = '/';
+			need_bs_to_fs = 0;
+		} else {
+			need_bs_to_fs = 1;
+		}
+
+		/*
+		 * prefix_len == 0 if the compiler gives paths relative
+		 * to the root of the working tree.  Otherwise, we want
+		 * to see that we did find the needle[] at a directory
+		 * boundary.  Again we rely on that needle[] begins with
+		 * "t" followed by the directory separator.
+		 */
+		if (fspathcmp(needle, prefix + prefix_len) ||
+		    (prefix_len && prefix[prefix_len - 1] != needle[1]))
+			die("unexpected suffix of '%s'", prefix);
+	}
+
+	/*
+	 * Does it not start with the expected prefix?
+	 * Return it as-is without making it worse.
+	 */
+	if (prefix_len && fspathncmp(location, prefix, prefix_len))
+		return location;
+
+	/*
+	 * If we do not need to munge directory separator, we can return
+	 * the substring at the tail of the location.
+	 */
+	if (!need_bs_to_fs)
+		return location + prefix_len;
+
+	/* convert backslashes to forward slashes */
+	strlcpy(buf, location + prefix_len, sizeof(buf));
+	for (p = buf; *p; p++)
+		if (*p == '\\')
+			*p = '/';
+	return buf;
+}
+
+static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
+{
+	fflush(stderr);
+	if (prefix)
+		fprintf(stdout, "%s", prefix);
+	vprintf(format, ap); /* TODO: handle newlines */
+	putc('\n', stdout);
+	fflush(stdout);
+}
+
+void test_msg(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	msg_with_prefix("# ", format, ap);
+	va_end(ap);
+}
+
+void test_plan(int count)
+{
+	assert(!ctx.running);
+
+	fflush(stderr);
+	printf("1..%d\n", count);
+	fflush(stdout);
+	ctx.lazy_plan = 0;
+}
+
+int test_done(void)
+{
+	assert(!ctx.running);
+
+	if (ctx.lazy_plan)
+		test_plan(ctx.count);
+
+	return ctx.failed;
+}
+
+void test_skip(const char *format, ...)
+{
+	va_list ap;
+
+	assert(ctx.running);
+
+	ctx.result = RESULT_SKIP;
+	va_start(ap, format);
+	if (format)
+		msg_with_prefix("# skipping test - ", format, ap);
+	va_end(ap);
+}
+
+void test_skip_all(const char *format, ...)
+{
+	va_list ap;
+	const char *prefix;
+
+	if (!ctx.count && ctx.lazy_plan) {
+		/* We have not printed a test plan yet */
+		prefix = "1..0 # SKIP ";
+		ctx.lazy_plan = 0;
+	} else {
+		/* We have already printed a test plan */
+		prefix = "Bail out! # ";
+		ctx.failed = 1;
+	}
+	ctx.skip_all = 1;
+	ctx.result = RESULT_SKIP;
+	va_start(ap, format);
+	msg_with_prefix(prefix, format, ap);
+	va_end(ap);
+}
+
+int test__run_begin(void)
+{
+	assert(!ctx.running);
+
+	ctx.count++;
+	ctx.result = RESULT_NONE;
+	ctx.running = 1;
+
+	return ctx.skip_all;
+}
+
+static void print_description(const char *format, va_list ap)
+{
+	if (format) {
+		fputs(" - ", stdout);
+		vprintf(format, ap);
+	}
+}
+
+int test__run_end(int was_run UNUSED, const char *location, const char *format, ...)
+{
+	va_list ap;
+
+	assert(ctx.running);
+	assert(!ctx.todo);
+
+	fflush(stderr);
+	va_start(ap, format);
+	if (!ctx.skip_all) {
+		switch (ctx.result) {
+		case RESULT_SUCCESS:
+			printf("ok %d", ctx.count);
+			print_description(format, ap);
+			break;
+
+		case RESULT_FAILURE:
+			printf("not ok %d", ctx.count);
+			print_description(format, ap);
+			break;
+
+		case RESULT_TODO:
+			printf("not ok %d", ctx.count);
+			print_description(format, ap);
+			printf(" # TODO");
+			break;
+
+		case RESULT_SKIP:
+			printf("ok %d", ctx.count);
+			print_description(format, ap);
+			printf(" # SKIP");
+			break;
+
+		case RESULT_NONE:
+			test_msg("BUG: test has no checks at %s",
+				 make_relative(location));
+			printf("not ok %d", ctx.count);
+			print_description(format, ap);
+			ctx.result = RESULT_FAILURE;
+			break;
+		}
+	}
+	va_end(ap);
+	ctx.running = 0;
+	if (ctx.skip_all)
+		return 1;
+	putc('\n', stdout);
+	fflush(stdout);
+	ctx.failed |= ctx.result == RESULT_FAILURE;
+
+	return ctx.result != RESULT_FAILURE;
+}
+
+static void test_fail(void)
+{
+	assert(ctx.result != RESULT_SKIP);
+
+	ctx.result = RESULT_FAILURE;
+}
+
+static void test_pass(void)
+{
+	assert(ctx.result != RESULT_SKIP);
+
+	if (ctx.result == RESULT_NONE)
+		ctx.result = RESULT_SUCCESS;
+}
+
+static void test_todo(void)
+{
+	assert(ctx.result != RESULT_SKIP);
+
+	if (ctx.result != RESULT_FAILURE)
+		ctx.result = RESULT_TODO;
+}
+
+int test_assert(const char *location, const char *check, int ok)
+{
+	assert(ctx.running);
+
+	if (ctx.result == RESULT_SKIP) {
+		test_msg("skipping check '%s' at %s", check,
+			 make_relative(location));
+		return 1;
+	}
+	if (!ctx.todo) {
+		if (ok) {
+			test_pass();
+		} else {
+			test_msg("check \"%s\" failed at %s", check,
+				 make_relative(location));
+			test_fail();
+		}
+	}
+
+	return !!ok;
+}
+
+void test__todo_begin(void)
+{
+	assert(ctx.running);
+	assert(!ctx.todo);
+
+	ctx.todo = 1;
+}
+
+int test__todo_end(const char *location, const char *check, int res)
+{
+	assert(ctx.running);
+	assert(ctx.todo);
+
+	ctx.todo = 0;
+	if (ctx.result == RESULT_SKIP)
+		return 1;
+	if (res) {
+		test_msg("todo check '%s' succeeded at %s", check,
+			 make_relative(location));
+		test_fail();
+	} else {
+		test_todo();
+	}
+
+	return !res;
+}
+
+int check_bool_loc(const char *loc, const char *check, int ok)
+{
+	return test_assert(loc, check, ok);
+}
+
+union test__tmp test__tmp[2];
+
+int check_int_loc(const char *loc, const char *check, int ok,
+		  intmax_t a, intmax_t b)
+{
+	int ret = test_assert(loc, check, ok);
+
+	if (!ret) {
+		test_msg("   left: %"PRIdMAX, a);
+		test_msg("  right: %"PRIdMAX, b);
+	}
+
+	return ret;
+}
+
+int check_uint_loc(const char *loc, const char *check, int ok,
+		   uintmax_t a, uintmax_t b)
+{
+	int ret = test_assert(loc, check, ok);
+
+	if (!ret) {
+		test_msg("   left: %"PRIuMAX, a);
+		test_msg("  right: %"PRIuMAX, b);
+	}
+
+	return ret;
+}
+
+static void print_one_char(char ch, char quote)
+{
+	if ((unsigned char)ch < 0x20u || ch == 0x7f) {
+		/* TODO: improve handling of \a, \b, \f ... */
+		printf("\\%03o", (unsigned char)ch);
+	} else {
+		if (ch == '\\' || ch == quote)
+			putc('\\', stdout);
+		putc(ch, stdout);
+	}
+}
+
+static void print_char(const char *prefix, char ch)
+{
+	printf("# %s: '", prefix);
+	print_one_char(ch, '\'');
+	fputs("'\n", stdout);
+}
+
+int check_char_loc(const char *loc, const char *check, int ok, char a, char b)
+{
+	int ret = test_assert(loc, check, ok);
+
+	if (!ret) {
+		fflush(stderr);
+		print_char("   left", a);
+		print_char("  right", b);
+		fflush(stdout);
+	}
+
+	return ret;
+}
+
+static void print_str(const char *prefix, const char *str)
+{
+	printf("# %s: ", prefix);
+	if (!str) {
+		fputs("NULL\n", stdout);
+	} else {
+		putc('"', stdout);
+		while (*str)
+			print_one_char(*str++, '"');
+		fputs("\"\n", stdout);
+	}
+}
+
+int check_str_loc(const char *loc, const char *check,
+		  const char *a, const char *b)
+{
+	int ok = (!a && !b) || (a && b && !strcmp(a, b));
+	int ret = test_assert(loc, check, ok);
+
+	if (!ret) {
+		fflush(stderr);
+		print_str("   left", a);
+		print_str("  right", b);
+		fflush(stdout);
+	}
+
+	return ret;
+}
diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h
new file mode 100644
index 0000000..a8f07ae
--- /dev/null
+++ b/t/unit-tests/test-lib.h
@@ -0,0 +1,149 @@
+#ifndef TEST_LIB_H
+#define TEST_LIB_H
+
+#include "git-compat-util.h"
+
+/*
+ * Run a test function, returns 1 if the test succeeds, 0 if it
+ * fails. If test_skip_all() has been called then the test will not be
+ * run. The description for each test should be unique. For example:
+ *
+ *  TEST(test_something(arg1, arg2), "something %d %d", arg1, arg2)
+ */
+#define TEST(t, ...)					\
+	test__run_end(test__run_begin() ? 0 : (t, 1),	\
+		      TEST_LOCATION(),  __VA_ARGS__)
+
+/*
+ * Print a test plan, should be called before any tests. If the number
+ * of tests is not known in advance test_done() will automatically
+ * print a plan at the end of the test program.
+ */
+void test_plan(int count);
+
+/*
+ * test_done() must be called at the end of main(). It will print the
+ * plan if plan() was not called at the beginning of the test program
+ * and returns the exit code for the test program.
+ */
+int test_done(void);
+
+/* Skip the current test. */
+__attribute__((format (printf, 1, 2)))
+void test_skip(const char *format, ...);
+
+/* Skip all remaining tests. */
+__attribute__((format (printf, 1, 2)))
+void test_skip_all(const char *format, ...);
+
+/* Print a diagnostic message to stdout. */
+__attribute__((format (printf, 1, 2)))
+void test_msg(const char *format, ...);
+
+/*
+ * Test checks are built around test_assert(). checks return 1 on
+ * success, 0 on failure. If any check fails then the test will fail. To
+ * create a custom check define a function that wraps test_assert() and
+ * a macro to wrap that function to provide a source location and
+ * stringified arguments. Custom checks that take pointer arguments
+ * should be careful to check that they are non-NULL before
+ * dereferencing them. For example:
+ *
+ *  static int check_oid_loc(const char *loc, const char *check,
+ *			     struct object_id *a, struct object_id *b)
+ *  {
+ *	    int res = test_assert(loc, check, a && b && oideq(a, b));
+ *
+ *	    if (!res) {
+ *		    test_msg("   left: %s", a ? oid_to_hex(a) : "NULL";
+ *		    test_msg("  right: %s", b ? oid_to_hex(a) : "NULL";
+ *
+ *	    }
+ *	    return res;
+ *  }
+ *
+ *  #define check_oid(a, b) \
+ *	    check_oid_loc(TEST_LOCATION(), "oideq("#a", "#b")", a, b)
+ */
+int test_assert(const char *location, const char *check, int ok);
+
+/* Helper macro to pass the location to checks */
+#define TEST_LOCATION() TEST__MAKE_LOCATION(__LINE__)
+
+/* Check a boolean condition. */
+#define check(x)				\
+	check_bool_loc(TEST_LOCATION(), #x, x)
+int check_bool_loc(const char *loc, const char *check, int ok);
+
+/*
+ * Compare two integers. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_int(a, op, b)						\
+	(test__tmp[0].i = (a), test__tmp[1].i = (b),			\
+	 check_int_loc(TEST_LOCATION(), #a" "#op" "#b,			\
+		       test__tmp[0].i op test__tmp[1].i,		\
+		       test__tmp[0].i, test__tmp[1].i))
+int check_int_loc(const char *loc, const char *check, int ok,
+		  intmax_t a, intmax_t b);
+
+/*
+ * Compare two unsigned integers. Prints a message with the two values
+ * if the comparison fails. NB this is not thread safe.
+ */
+#define check_uint(a, op, b)						\
+	(test__tmp[0].u = (a), test__tmp[1].u = (b),			\
+	 check_uint_loc(TEST_LOCATION(), #a" "#op" "#b,			\
+			test__tmp[0].u op test__tmp[1].u,		\
+			test__tmp[0].u, test__tmp[1].u))
+int check_uint_loc(const char *loc, const char *check, int ok,
+		   uintmax_t a, uintmax_t b);
+
+/*
+ * Compare two chars. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_char(a, op, b)						\
+	(test__tmp[0].c = (a), test__tmp[1].c = (b),			\
+	 check_char_loc(TEST_LOCATION(), #a" "#op" "#b,			\
+			test__tmp[0].c op test__tmp[1].c,		\
+			test__tmp[0].c, test__tmp[1].c))
+int check_char_loc(const char *loc, const char *check, int ok,
+		   char a, char b);
+
+/* Check whether two strings are equal. */
+#define check_str(a, b)							\
+	check_str_loc(TEST_LOCATION(), "!strcmp("#a", "#b")", a, b)
+int check_str_loc(const char *loc, const char *check,
+		  const char *a, const char *b);
+
+/*
+ * Wrap a check that is known to fail. If the check succeeds then the
+ * test will fail. Returns 1 if the check fails, 0 if it
+ * succeeds. For example:
+ *
+ *  TEST_TODO(check(0));
+ */
+#define TEST_TODO(check) \
+	(test__todo_begin(), test__todo_end(TEST_LOCATION(), #check, check))
+
+/* Private helpers */
+
+#define TEST__STR(x) #x
+#define TEST__MAKE_LOCATION(line) __FILE__ ":" TEST__STR(line)
+
+union test__tmp {
+	intmax_t i;
+	uintmax_t u;
+	char c;
+};
+
+extern union test__tmp test__tmp[2];
+
+int test__run_begin(void);
+__attribute__((format (printf, 3, 4)))
+int test__run_end(int, const char *, const char *, ...);
+void test__todo_begin(void);
+int test__todo_end(const char *, const char *, int);
+
+#endif /* TEST_LIB_H */
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
index 669ebaf..3c8ee19 100755
--- a/t/valgrind/valgrind.sh
+++ b/t/valgrind/valgrind.sh
@@ -23,7 +23,7 @@
 	VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
 	VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
 	test 3 -gt "$VALGRIND_MAJOR" ||
-	test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+	{ test 3 -eq "$VALGRIND_MAJOR" && test 4 -gt "$VALGRIND_MINOR"; } ||
 	TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes"
 	;;
 *)
diff --git a/tag.c b/tag.c
index dfbcd7f..fc3834d 100644
--- a/tag.c
+++ b/tag.c
@@ -1,11 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "environment.h"
 #include "tag.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "commit.h"
 #include "tree.h"
 #include "blob.h"
 #include "alloc.h"
 #include "gpg-interface.h"
+#include "hex.h"
 #include "packfile.h"
 
 const char *tag_type = "tag";
@@ -51,15 +54,15 @@
 		return error("%s: cannot verify a non-tag object of type %s.",
 				name_to_report ?
 				name_to_report :
-				find_unique_abbrev(oid, DEFAULT_ABBREV),
+				repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV),
 				type_name(type));
 
-	buf = read_object_file(oid, &type, &size);
+	buf = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!buf)
 		return error("%s: unable to read file.",
 				name_to_report ?
 				name_to_report :
-				find_unique_abbrev(oid, DEFAULT_ABBREV));
+				repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
 
 	ret = run_gpg_verify(buf, size, flags);
 
@@ -216,7 +219,8 @@
 
 	if (item->object.parsed)
 		return 0;
-	data = read_object_file(&item->object.oid, &type, &size);
+	data = repo_read_object_file(the_repository, &item->object.oid, &type,
+				     &size);
 	if (!data)
 		return error("Could not read %s",
 			     oid_to_hex(&item->object.oid));
diff --git a/tempfile.c b/tempfile.c
index e27048f..ed88cf8 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -42,21 +42,25 @@
  * file created by its parent.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "path.h"
 #include "tempfile.h"
 #include "sigchain.h"
 
 static VOLATILE_LIST_HEAD(tempfile_list);
 
-static void remove_template_directory(struct tempfile *tempfile,
+static int remove_template_directory(struct tempfile *tempfile,
 				      int in_signal_handler)
 {
 	if (tempfile->directory) {
 		if (in_signal_handler)
-			rmdir(tempfile->directory);
+			return rmdir(tempfile->directory);
 		else
-			rmdir_or_warn(tempfile->directory);
+			return rmdir_or_warn(tempfile->directory);
 	}
+
+	return 0;
 }
 
 static void remove_tempfiles(int in_signal_handler)
@@ -351,16 +355,19 @@
 	return 0;
 }
 
-void delete_tempfile(struct tempfile **tempfile_p)
+int delete_tempfile(struct tempfile **tempfile_p)
 {
 	struct tempfile *tempfile = *tempfile_p;
+	int err = 0;
 
 	if (!is_tempfile_active(tempfile))
-		return;
+		return 0;
 
-	close_tempfile_gently(tempfile);
-	unlink_or_warn(tempfile->filename.buf);
-	remove_template_directory(tempfile, 0);
+	err |= close_tempfile_gently(tempfile);
+	err |= unlink_or_warn(tempfile->filename.buf);
+	err |= remove_template_directory(tempfile, 0);
 	deactivate_tempfile(tempfile);
 	*tempfile_p = NULL;
+
+	return err ? -1 : 0;
 }
diff --git a/tempfile.h b/tempfile.h
index d0413af..2d2ae5b 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -269,7 +269,7 @@
  * `delete_tempfile()` for a `tempfile` object that has already been
  * deleted or renamed.
  */
-void delete_tempfile(struct tempfile **tempfile_p);
+int delete_tempfile(struct tempfile **tempfile_p);
 
 /*
  * Close the file descriptor and/or file pointer if they are still
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
index e144712..29ed5ee 100755
--- a/templates/hooks--pre-commit.sample
+++ b/templates/hooks--pre-commit.sample
@@ -28,7 +28,7 @@
 	# Note that the use of brackets around a tr range is ok here, (it's
 	# even required, for portability to Solaris 10's /usr/bin/tr), since
 	# the square bracket bytes happen to fall in the designated range.
-	test $(git diff --cached --name-only --diff-filter=A -z $against |
+	test $(git diff-index --cached --name-only --diff-filter=A -z $against |
 	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
 	cat <<\EOF
diff --git a/templates/hooks--sendemail-validate.sample b/templates/hooks--sendemail-validate.sample
new file mode 100755
index 0000000..640bcf8
--- /dev/null
+++ b/templates/hooks--sendemail-validate.sample
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# An example hook script to validate a patch (and/or patch series) before
+# sending it via email.
+#
+# The hook should exit with non-zero status after issuing an appropriate
+# message if it wants to prevent the email(s) from being sent.
+#
+# To enable this hook, rename this file to "sendemail-validate".
+#
+# By default, it will only check that the patch(es) can be applied on top of
+# the default upstream branch without conflicts in a secondary worktree. After
+# validation (successful or not) of the last patch of a series, the worktree
+# will be deleted.
+#
+# The following config variables can be set to change the default remote and
+# remote ref that are used to apply the patches against:
+#
+#   sendemail.validateRemote (default: origin)
+#   sendemail.validateRemoteRef (default: HEAD)
+#
+# Replace the TODO placeholders with appropriate checks according to your
+# needs.
+
+validate_cover_letter () {
+	file="$1"
+	# TODO: Replace with appropriate checks (e.g. spell checking).
+	true
+}
+
+validate_patch () {
+	file="$1"
+	# Ensure that the patch applies without conflicts.
+	git am -3 "$file" || return
+	# TODO: Replace with appropriate checks for this patch
+	# (e.g. checkpatch.pl).
+	true
+}
+
+validate_series () {
+	# TODO: Replace with appropriate checks for the whole series
+	# (e.g. quick build, coding style checks, etc.).
+	true
+}
+
+# main -------------------------------------------------------------------------
+
+if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
+then
+	remote=$(git config --default origin --get sendemail.validateRemote) &&
+	ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
+	worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
+	git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
+	git config --replace-all sendemail.validateWorktree "$worktree"
+else
+	worktree=$(git config --get sendemail.validateWorktree)
+fi || {
+	echo "sendemail-validate: error: failed to prepare worktree" >&2
+	exit 1
+}
+
+unset GIT_DIR GIT_WORK_TREE
+cd "$worktree" &&
+
+if grep -q "^diff --git " "$1"
+then
+	validate_patch "$1"
+else
+	validate_cover_letter "$1"
+fi &&
+
+if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL"
+then
+	git config --unset-all sendemail.validateWorktree &&
+	trap 'git worktree remove -ff "$worktree"' EXIT &&
+	validate_series
+fi
diff --git a/thread-utils.c b/thread-utils.c
index 5329845..1f89ffa 100644
--- a/thread-utils.c
+++ b/thread-utils.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "thread-utils.h"
 
 #if defined(hpux) || defined(__hpux) || defined(_hpux)
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 2a2012e..3509258 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -1,13 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tmp-objdir.h"
+#include "abspath.h"
 #include "chdir-notify.h"
 #include "dir.h"
-#include "sigchain.h"
+#include "environment.h"
+#include "object-file.h"
+#include "path.h"
 #include "string-list.h"
 #include "strbuf.h"
 #include "strvec.h"
 #include "quote.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 
 struct tmp_objdir {
 	struct strbuf path;
diff --git a/trace.c b/trace.c
index efa4e2d..8669ddf 100644
--- a/trace.c
+++ b/trace.c
@@ -18,11 +18,15 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *  along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
 #include "quote.h"
+#include "setup.h"
+#include "trace.h"
 
 struct trace_key trace_default_key = { "GIT_TRACE", 0, 0, 0 };
 struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
diff --git a/trace.h b/trace.h
index b6e35b9..d304d55 100644
--- a/trace.h
+++ b/trace.h
@@ -1,7 +1,6 @@
 #ifndef TRACE_H
 #define TRACE_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
 
 /**
diff --git a/trace2.c b/trace2.c
index 279bddf..f894532 100644
--- a/trace2.c
+++ b/trace2.c
@@ -1,11 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
-#include "json-writer.h"
-#include "quote.h"
+#include "repository.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "thread-utils.h"
-#include "version.h"
+#include "trace.h"
+#include "trace2.h"
 #include "trace2/tr2_cfg.h"
 #include "trace2/tr2_cmd_name.h"
 #include "trace2/tr2_ctr.h"
@@ -17,6 +17,7 @@
 #include "trace2/tr2_tmr.h"
 
 static int trace2_enabled;
+static int trace2_redact = 1;
 
 static int tr2_next_child_id; /* modify under lock */
 static int tr2_next_exec_id; /* modify under lock */
@@ -224,6 +225,8 @@
 	if (!tr2_tgt_want_builtins())
 		return;
 	trace2_enabled = 1;
+	if (!git_env_bool("GIT_TRACE2_REDACT", 1))
+		trace2_redact = 0;
 
 	tr2_sid_get();
 
@@ -244,12 +247,93 @@
 	return trace2_enabled;
 }
 
+/*
+ * Redacts an argument, i.e. ensures that no password in
+ * https://user:password@host/-style URLs is logged.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Returns a pointer that needs to be `free()`d otherwise.
+ */
+static const char *redact_arg(const char *arg)
+{
+	const char *p, *colon;
+	size_t at;
+
+	if (!trace2_redact ||
+	    (!skip_prefix(arg, "https://", &p) &&
+	     !skip_prefix(arg, "http://", &p)))
+		return arg;
+
+	at = strcspn(p, "@/");
+	if (p[at] != '@')
+		return arg;
+
+	colon = memchr(p, ':', at);
+	if (!colon)
+		return arg;
+
+	return xstrfmt("%.*s:<REDACTED>%s", (int)(colon - arg), arg, p + at);
+}
+
+/*
+ * Redacts arguments in an argument list.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Otherwise, returns a new array that needs to be released
+ * via `free_redacted_argv()`.
+ */
+static const char **redact_argv(const char **argv)
+{
+	int i, j;
+	const char *redacted = NULL;
+	const char **ret;
+
+	if (!trace2_redact)
+		return argv;
+
+	for (i = 0; argv[i]; i++)
+		if ((redacted = redact_arg(argv[i])) != argv[i])
+			break;
+
+	if (!argv[i])
+		return argv;
+
+	for (j = 0; argv[j]; j++)
+		; /* keep counting */
+
+	ALLOC_ARRAY(ret, j + 1);
+	ret[j] = NULL;
+
+	for (j = 0; j < i; j++)
+		ret[j] = argv[j];
+	ret[i] = redacted;
+	for (++i; argv[i]; i++) {
+		redacted = redact_arg(argv[i]);
+		ret[i] = redacted ? redacted : argv[i];
+	}
+
+	return ret;
+}
+
+static void free_redacted_argv(const char **redacted, const char **argv)
+{
+	int i;
+
+	if (redacted != argv) {
+		for (i = 0; argv[i]; i++)
+			if (redacted[i] != argv[i])
+				free((void *)redacted[i]);
+		free((void *)redacted);
+	}
+}
+
 void trace2_cmd_start_fl(const char *file, int line, const char **argv)
 {
 	struct tr2_tgt *tgt_j;
 	int j;
 	uint64_t us_now;
 	uint64_t us_elapsed_absolute;
+	const char **redacted;
 
 	if (!trace2_enabled)
 		return;
@@ -257,10 +341,14 @@
 	us_now = getnanotime() / 1000;
 	us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 
+	redacted = redact_argv(argv);
+
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_start_fl)
 			tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
-					    argv);
+					    redacted);
+
+	free_redacted_argv(redacted, argv);
 }
 
 void trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -273,7 +361,6 @@
 	if (!trace2_enabled)
 		return;
 
-	trace_git_fsync_stats();
 	trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
 
 	tr2main_exit_code = code;
@@ -346,6 +433,9 @@
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_command_name_fl)
 			tgt_j->pfn_command_name_fl(file, line, name, hierarchy);
+
+	trace2_cmd_list_config();
+	trace2_cmd_list_env_vars();
 }
 
 void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
@@ -377,17 +467,29 @@
 
 void trace2_cmd_list_config_fl(const char *file, int line)
 {
+	static int emitted = 0;
+
 	if (!trace2_enabled)
 		return;
 
+	if (emitted)
+		return;
+	emitted = 1;
+
 	tr2_cfg_list_config_fl(file, line);
 }
 
 void trace2_cmd_list_env_vars_fl(const char *file, int line)
 {
+	static int emitted = 0;
+
 	if (!trace2_enabled)
 		return;
 
+	if (emitted)
+		return;
+	emitted = 1;
+
 	tr2_list_env_vars_fl(file, line);
 }
 
@@ -407,6 +509,7 @@
 	int j;
 	uint64_t us_now;
 	uint64_t us_elapsed_absolute;
+	const char **orig_argv = cmd->args.v;
 
 	if (!trace2_enabled)
 		return;
@@ -417,10 +520,24 @@
 	cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id);
 	cmd->trace2_child_us_start = us_now;
 
+	/*
+	 * The `pfn_child_start_fl` API takes a `struct child_process`
+	 * rather than a simple `argv` for the child because some
+	 * targets make use of the additional context bits/values. So
+	 * temporarily replace the original argv (inside the `strvec`)
+	 * with a possibly redacted version.
+	 */
+	cmd->args.v = redact_argv(orig_argv);
+
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_child_start_fl)
 			tgt_j->pfn_child_start_fl(file, line,
 						  us_elapsed_absolute, cmd);
+
+	if (cmd->args.v != orig_argv) {
+		free_redacted_argv(cmd->args.v, orig_argv);
+		cmd->args.v = orig_argv;
+	}
 }
 
 void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
@@ -491,6 +608,7 @@
 	int exec_id;
 	uint64_t us_now;
 	uint64_t us_elapsed_absolute;
+	const char **redacted;
 
 	if (!trace2_enabled)
 		return -1;
@@ -500,10 +618,14 @@
 
 	exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
 
+	redacted = redact_argv(argv);
+
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_exec_fl)
 			tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
-					   exec_id, exe, argv);
+					   exec_id, exe, redacted);
+
+	free_redacted_argv(redacted, argv);
 
 	return exec_id;
 }
@@ -631,17 +753,23 @@
 }
 
 void trace2_def_param_fl(const char *file, int line, const char *param,
-			 const char *value)
+			 const char *value, const struct key_value_info *kvi)
 {
 	struct tr2_tgt *tgt_j;
 	int j;
+	const char *redacted;
 
 	if (!trace2_enabled)
 		return;
 
+	redacted = redact_arg(value);
+
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_param_fl)
-			tgt_j->pfn_param_fl(file, line, param, value);
+			tgt_j->pfn_param_fl(file, line, param, redacted, kvi);
+
+	if (redacted != value)
+		free((void *)redacted);
 }
 
 void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
diff --git a/trace2.h b/trace2.h
index 4ced30c..1f0669b 100644
--- a/trace2.h
+++ b/trace2.h
@@ -325,6 +325,7 @@
 
 #define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__)
 
+struct key_value_info;
 /*
  * Emits a "def_param" message containing a key/value pair.
  *
@@ -334,10 +335,10 @@
  * `core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`.
  */
 void trace2_def_param_fl(const char *file, int line, const char *param,
-			 const char *value);
+			 const char *value, const struct key_value_info *kvi);
 
-#define trace2_def_param(param, value) \
-	trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
+#define trace2_def_param(param, value, kvi) \
+	trace2_def_param_fl(__FILE__, __LINE__, (param), (value), (kvi))
 
 /*
  * Tell trace2 about a newly instantiated repo object and assign
@@ -540,7 +541,7 @@
  * elsewhere as array indexes).
  *
  * Any values added to this enum be also be added to the
- * `tr2_counter_metadata[]` in `trace2/tr2_tr2_ctr.c`.
+ * `tr2_counter_metadata[]` in `trace2/tr2_ctr.c`.
  */
 enum trace2_counter_id {
 	/*
@@ -551,6 +552,12 @@
 	TRACE2_COUNTER_ID_TEST1 = 0, /* emits summary event only */
 	TRACE2_COUNTER_ID_TEST2,     /* emits summary and thread events */
 
+	TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, /* counts number of jumps */
+
+	/* counts number of fsyncs */
+	TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY,
+	TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH,
+
 	/* Add additional counter definitions before here. */
 	TRACE2_NUMBER_OF_COUNTERS
 };
diff --git a/trace2/tr2_cfg.c b/trace2/tr2_cfg.c
index ec9ac1a..d96d908 100644
--- a/trace2/tr2_cfg.c
+++ b/trace2/tr2_cfg.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "strbuf.h"
+#include "trace2.h"
 #include "trace2/tr2_cfg.h"
 #include "trace2/tr2_sysenv.h"
+#include "wildmatch.h"
 
 static struct strbuf **tr2_cfg_patterns;
 static int tr2_cfg_count_patterns;
@@ -97,7 +100,8 @@
 /*
  * See if the given config key matches any of our patterns of interest.
  */
-static int tr2_cfg_cb(const char *key, const char *value, void *d)
+static int tr2_cfg_cb(const char *key, const char *value,
+		      const struct config_context *ctx, void *d)
 {
 	struct strbuf **s;
 	struct tr2_cfg_data *data = (struct tr2_cfg_data *)d;
@@ -106,7 +110,8 @@
 		struct strbuf *buf = *s;
 		int wm = wildmatch(buf->buf, key, WM_CASEFOLD);
 		if (wm == WM_MATCH) {
-			trace2_def_param_fl(data->file, data->line, key, value);
+			trace2_def_param_fl(data->file, data->line, key, value,
+					    ctx->kvi);
 			return 0;
 		}
 	}
@@ -124,8 +129,10 @@
 
 void tr2_list_env_vars_fl(const char *file, int line)
 {
+	struct key_value_info kvi = KVI_INIT;
 	struct strbuf **s;
 
+	kvi_from_param(&kvi);
 	if (tr2_load_env_vars() <= 0)
 		return;
 
@@ -133,15 +140,19 @@
 		struct strbuf *buf = *s;
 		const char *val = getenv(buf->buf);
 		if (val && *val)
-			trace2_def_param_fl(file, line, buf->buf, val);
+			trace2_def_param_fl(file, line, buf->buf, val, &kvi);
 	}
 }
 
 void tr2_cfg_set_fl(const char *file, int line, const char *key,
 		    const char *value)
 {
+	struct key_value_info kvi = KVI_INIT;
+	struct config_context ctx = {
+		.kvi = &kvi,
+	};
 	struct tr2_cfg_data data = { file, line };
 
 	if (tr2_cfg_load_patterns() > 0)
-		tr2_cfg_cb(key, value, &data);
+		tr2_cfg_cb(key, value, &ctx, &data);
 }
diff --git a/trace2/tr2_cmd_name.c b/trace2/tr2_cmd_name.c
index dd31320..b7b5a86 100644
--- a/trace2/tr2_cmd_name.c
+++ b/trace2/tr2_cmd_name.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
 #include "trace2/tr2_cmd_name.h"
 
 #define TR2_ENVVAR_PARENT_NAME "GIT_TRACE2_PARENT_NAME"
diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c
index 483ca7c..d3a3371 100644
--- a/trace2/tr2_ctr.c
+++ b/trace2/tr2_ctr.c
@@ -1,5 +1,4 @@
-#include "cache.h"
-#include "thread-utils.h"
+#include "git-compat-util.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 #include "trace2/tr2_ctr.h"
@@ -27,6 +26,21 @@
 		.name = "test2",
 		.want_per_thread_events = 1,
 	},
+	[TRACE2_COUNTER_ID_PACKED_REFS_JUMPS] = {
+		.category = "packed-refs",
+		.name = "jumps_made",
+		.want_per_thread_events = 0,
+	},
+	[TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY] = {
+		.category = "fsync",
+		.name = "writeout-only",
+		.want_per_thread_events = 0,
+	},
+	[TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH] = {
+		.category = "fsync",
+		.name = "hardware-flush",
+		.want_per_thread_events = 0,
+	},
 
 	/* Add additional metadata before here. */
 };
diff --git a/trace2/tr2_dst.c b/trace2/tr2_dst.c
index 8a21dd2..5be892c 100644
--- a/trace2/tr2_dst.c
+++ b/trace2/tr2_dst.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
 #include "sigchain.h"
+#include "strbuf.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
 #include "trace2/tr2_sysenv.h"
diff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c
index dc6e75e..09c4ef0 100644
--- a/trace2/tr2_sid.c
+++ b/trace2/tr2_sid.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex.h"
+#include "strbuf.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_sid.h"
 
diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c
index a380dcf..048cdd5 100644
--- a/trace2/tr2_sysenv.c
+++ b/trace2/tr2_sysenv.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "dir.h"
 #include "tr2_sysenv.h"
@@ -57,7 +57,9 @@
 };
 /* clang-format on */
 
-static int tr2_sysenv_cb(const char *key, const char *value, void *d)
+static int tr2_sysenv_cb(const char *key, const char *value,
+			 const struct config_context *ctx UNUSED,
+			 void *d UNUSED)
 {
 	int k;
 
@@ -66,6 +68,8 @@
 
 	for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
 		if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+			if (!value)
+				return config_error_nonbool(key);
 			free(tr2_sysenv_settings[k].value);
 			tr2_sysenv_settings[k].value = xstrdup(value);
 			return 0;
diff --git a/trace2/tr2_tbuf.c b/trace2/tr2_tbuf.c
index 2498482..c3b3822 100644
--- a/trace2/tr2_tbuf.c
+++ b/trace2/tr2_tbuf.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tr2_tbuf.h"
 
 void tr2_tbuf_local_time(struct tr2_tbuf *tb)
diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h
index bf8745c..1f626cf 100644
--- a/trace2/tr2_tgt.h
+++ b/trace2/tr2_tgt.h
@@ -69,8 +69,10 @@
 					   uint64_t us_elapsed_absolute,
 					   int exec_id, int code);
 
+struct key_value_info;
 typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line,
-				     const char *param, const char *value);
+				     const char *param, const char *value,
+				     const struct key_value_info *kvi);
 
 typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line,
 				    const struct repository *repo);
diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c
index 16f6332..59910a1 100644
--- a/trace2/tr2_tgt_event.c
+++ b/trace2/tr2_tgt_event.c
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "json-writer.h"
+#include "repository.h"
 #include "run-command.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
@@ -334,7 +335,7 @@
 }
 
 static void fn_child_start_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute,
+			      uint64_t us_elapsed_absolute UNUSED,
 			      const struct child_process *cmd)
 {
 	const char *event_name = "child_start";
@@ -366,7 +367,8 @@
 }
 
 static void fn_child_exit_fl(const char *file, int line,
-			     uint64_t us_elapsed_absolute, int cid, int pid,
+			     uint64_t us_elapsed_absolute UNUSED,
+			     int cid, int pid,
 			     int code, uint64_t us_elapsed_child)
 {
 	const char *event_name = "child_exit";
@@ -387,7 +389,8 @@
 }
 
 static void fn_child_ready_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute, int cid, int pid,
+			      uint64_t us_elapsed_absolute UNUSED,
+			      int cid, int pid,
 			      const char *ready, uint64_t us_elapsed_child)
 {
 	const char *event_name = "child_ready";
@@ -408,7 +411,7 @@
 }
 
 static void fn_thread_start_fl(const char *file, int line,
-			       uint64_t us_elapsed_absolute)
+			       uint64_t us_elapsed_absolute UNUSED)
 {
 	const char *event_name = "thread_start";
 	struct json_writer jw = JSON_WRITER_INIT;
@@ -422,7 +425,7 @@
 }
 
 static void fn_thread_exit_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute,
+			      uint64_t us_elapsed_absolute UNUSED,
 			      uint64_t us_elapsed_thread)
 {
 	const char *event_name = "thread_exit";
@@ -438,7 +441,8 @@
 	jw_release(&jw);
 }
 
-static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+static void fn_exec_fl(const char *file, int line,
+		       uint64_t us_elapsed_absolute UNUSED,
 		       int exec_id, const char *exe, const char **argv)
 {
 	const char *event_name = "exec";
@@ -459,8 +463,8 @@
 }
 
 static void fn_exec_result_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute, int exec_id,
-			      int code)
+			      uint64_t us_elapsed_absolute UNUSED,
+			      int exec_id, int code)
 {
 	const char *event_name = "exec_result";
 	struct json_writer jw = JSON_WRITER_INIT;
@@ -476,11 +480,11 @@
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	const char *event_name = "def_param";
 	struct json_writer jw = JSON_WRITER_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	jw_object_begin(&jw, 0);
@@ -510,7 +514,7 @@
 }
 
 static void fn_region_enter_printf_va_fl(const char *file, int line,
-					 uint64_t us_elapsed_absolute,
+					 uint64_t us_elapsed_absolute UNUSED,
 					 const char *category,
 					 const char *label,
 					 const struct repository *repo,
@@ -537,7 +541,7 @@
 }
 
 static void fn_region_leave_printf_va_fl(
-	const char *file, int line, uint64_t us_elapsed_absolute,
+	const char *file, int line, uint64_t us_elapsed_absolute UNUSED,
 	uint64_t us_elapsed_region, const char *category, const char *label,
 	const struct repository *repo, const char *fmt, va_list ap)
 {
diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c
index fbbef68..baef48a 100644
--- a/trace2/tr2_tgt_normal.c
+++ b/trace2/tr2_tgt_normal.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "repository.h"
 #include "run-command.h"
+#include "strbuf.h"
 #include "quote.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
@@ -85,7 +87,7 @@
 }
 
 static void fn_start_fl(const char *file, int line,
-			uint64_t us_elapsed_absolute, const char **argv)
+			uint64_t us_elapsed_absolute UNUSED, const char **argv)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
 
@@ -214,7 +216,7 @@
 }
 
 static void fn_child_start_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute,
+			      uint64_t us_elapsed_absolute UNUSED,
 			      const struct child_process *cmd)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
@@ -242,7 +244,8 @@
 }
 
 static void fn_child_exit_fl(const char *file, int line,
-			     uint64_t us_elapsed_absolute, int cid, int pid,
+			     uint64_t us_elapsed_absolute UNUSED,
+			     int cid, int pid,
 			     int code, uint64_t us_elapsed_child)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
@@ -255,7 +258,8 @@
 }
 
 static void fn_child_ready_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute, int cid, int pid,
+			      uint64_t us_elapsed_absolute UNUSED,
+			      int cid, int pid,
 			      const char *ready, uint64_t us_elapsed_child)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
@@ -267,7 +271,8 @@
 	strbuf_release(&buf_payload);
 }
 
-static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+static void fn_exec_fl(const char *file, int line,
+		       uint64_t us_elapsed_absolute UNUSED,
 		       int exec_id, const char *exe, const char **argv)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
@@ -283,8 +288,8 @@
 }
 
 static void fn_exec_result_fl(const char *file, int line,
-			      uint64_t us_elapsed_absolute, int exec_id,
-			      int code)
+			      uint64_t us_elapsed_absolute UNUSED,
+			      int exec_id, int code)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
 
@@ -296,10 +301,10 @@
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	strbuf_addf(&buf_payload, "def_param scope:%s %s=%s", scope_name, param,
@@ -320,7 +325,8 @@
 }
 
 static void fn_printf_va_fl(const char *file, int line,
-			    uint64_t us_elapsed_absolute, const char *fmt,
+			    uint64_t us_elapsed_absolute UNUSED,
+			    const char *fmt,
 			    va_list ap)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c
index adae803..a6f9a8a 100644
--- a/trace2/tr2_tgt_perf.c
+++ b/trace2/tr2_tgt_perf.c
@@ -1,5 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "repository.h"
 #include "run-command.h"
 #include "quote.h"
 #include "version.h"
@@ -438,12 +439,12 @@
 }
 
 static void fn_param_fl(const char *file, int line, const char *param,
-			const char *value)
+			const char *value, const struct key_value_info *kvi)
 {
 	const char *event_name = "def_param";
 	struct strbuf buf_payload = STRBUF_INIT;
 	struct strbuf scope_payload = STRBUF_INIT;
-	enum config_scope scope = current_config_scope();
+	enum config_scope scope = kvi->scope;
 	const char *scope_name = config_scope_name(scope);
 
 	strbuf_addf(&buf_payload, "%s:%s", param, value);
diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c
index 04900bb..4f75392 100644
--- a/trace2/tr2_tls.c
+++ b/trace2/tr2_tls.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
 #include "thread-utils.h"
+#include "trace.h"
 #include "trace2/tr2_tls.h"
 
 /*
diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h
index f904980..3dfe655 100644
--- a/trace2/tr2_tls.h
+++ b/trace2/tr2_tls.h
@@ -1,7 +1,6 @@
 #ifndef TR2_TLS_H
 #define TR2_TLS_H
 
-#include "strbuf.h"
 #include "trace2/tr2_ctr.h"
 #include "trace2/tr2_tmr.h"
 
diff --git a/trace2/tr2_tmr.c b/trace2/tr2_tmr.c
index 786762d..51f564b 100644
--- a/trace2/tr2_tmr.c
+++ b/trace2/tr2_tmr.c
@@ -1,8 +1,8 @@
-#include "cache.h"
-#include "thread-utils.h"
+#include "git-compat-util.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 #include "trace2/tr2_tmr.h"
+#include "trace.h"
 
 #define MY_MAX(a, b) ((a) > (b) ? (a) : (b))
 #define MY_MIN(a, b) ((a) < (b) ? (a) : (b))
diff --git a/trailer.c b/trailer.c
index 0fd5b14..dc15d85 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
 #include "string-list.h"
 #include "run-command.h"
 #include "commit.h"
-#include "tempfile.h"
 #include "trailer.h"
 #include "list.h"
 /*
@@ -143,12 +144,12 @@
 	return '\0';
 }
 
-static void print_tok_val(FILE *outfile, const char *tok, const char *val)
+static void print_tok_val(struct strbuf *out, const char *tok, const char *val)
 {
 	char c;
 
 	if (!tok) {
-		fprintf(outfile, "%s\n", val);
+		strbuf_addf(out, "%s\n", val);
 		return;
 	}
 
@@ -156,21 +157,22 @@
 	if (!c)
 		return;
 	if (strchr(separators, c))
-		fprintf(outfile, "%s%s\n", tok, val);
+		strbuf_addf(out, "%s%s\n", tok, val);
 	else
-		fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
+		strbuf_addf(out, "%s%c %s\n", tok, separators[0], val);
 }
 
-static void print_all(FILE *outfile, struct list_head *head,
-		      const struct process_trailer_options *opts)
+void format_trailers(const struct process_trailer_options *opts,
+		     struct list_head *trailers,
+		     struct strbuf *out)
 {
 	struct list_head *pos;
 	struct trailer_item *item;
-	list_for_each(pos, head) {
+	list_for_each(pos, trailers) {
 		item = list_entry(pos, struct trailer_item, list);
 		if ((!opts->trim_empty || strlen(item->value) > 0) &&
 		    (!opts->only_trailers || item->token))
-			print_tok_val(outfile, item->token, item->value);
+			print_tok_val(out, item->token, item->value);
 	}
 }
 
@@ -364,8 +366,8 @@
 	return 0;
 }
 
-static void process_trailers_lists(struct list_head *head,
-				   struct list_head *arg_head)
+void process_trailers_lists(struct list_head *head,
+			    struct list_head *arg_head)
 {
 	struct list_head *pos, *p;
 	struct arg_item *arg_tok;
@@ -479,6 +481,7 @@
 };
 
 static int git_trailer_default_config(const char *conf_key, const char *value,
+				      const struct config_context *ctx UNUSED,
 				      void *cb UNUSED)
 {
 	const char *trailer_item, *variable_name;
@@ -504,6 +507,8 @@
 				warning(_("unknown value '%s' for key '%s'"),
 					value, conf_key);
 		} else if (!strcmp(trailer_item, "separators")) {
+			if (!value)
+				return config_error_nonbool(conf_key);
 			separators = xstrdup(value);
 		}
 	}
@@ -511,6 +516,7 @@
 }
 
 static int git_trailer_config(const char *conf_key, const char *value,
+			      const struct config_context *ctx UNUSED,
 			      void *cb UNUSED)
 {
 	const char *trailer_item, *variable_name;
@@ -547,16 +553,22 @@
 	case TRAILER_KEY:
 		if (conf->key)
 			warning(_("more than one %s"), conf_key);
+		if (!value)
+			return config_error_nonbool(conf_key);
 		conf->key = xstrdup(value);
 		break;
 	case TRAILER_COMMAND:
 		if (conf->command)
 			warning(_("more than one %s"), conf_key);
+		if (!value)
+			return config_error_nonbool(conf_key);
 		conf->command = xstrdup(value);
 		break;
 	case TRAILER_CMD:
 		if (conf->cmd)
 			warning(_("more than one %s"), conf_key);
+		if (!value)
+			return config_error_nonbool(conf_key);
 		conf->cmd = xstrdup(value);
 		break;
 	case TRAILER_WHERE:
@@ -577,7 +589,7 @@
 	return 0;
 }
 
-static void ensure_configured(void)
+void trailer_config_init(void)
 {
 	if (configured)
 		return;
@@ -707,10 +719,25 @@
 	list_add_tail(&new_item->list, arg_head);
 }
 
-static void process_command_line_args(struct list_head *arg_head,
-				      struct list_head *new_trailer_head)
+void parse_trailers_from_config(struct list_head *config_head)
 {
 	struct arg_item *item;
+	struct list_head *pos;
+
+	/* Add an arg item for each configured trailer with a command */
+	list_for_each(pos, &conf_head) {
+		item = list_entry(pos, struct arg_item, list);
+		if (item->conf.command)
+			add_arg_item(config_head,
+				     xstrdup(token_from_item(item, NULL)),
+				     xstrdup(""),
+				     &item->conf, NULL);
+	}
+}
+
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+					   struct list_head *new_trailer_head)
+{
 	struct strbuf tok = STRBUF_INIT;
 	struct strbuf val = STRBUF_INIT;
 	const struct conf_info *conf;
@@ -722,16 +749,6 @@
 	 */
 	char *cl_separators = xstrfmt("=%s", separators);
 
-	/* Add an arg item for each configured trailer with a command */
-	list_for_each(pos, &conf_head) {
-		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
-			add_arg_item(arg_head,
-				     xstrdup(token_from_item(item, NULL)),
-				     xstrdup(""),
-				     &item->conf, NULL);
-	}
-
 	/* Add an arg item for each trailer on the command line */
 	list_for_each(pos, new_trailer_head) {
 		struct new_trailer_item *tr =
@@ -758,17 +775,6 @@
 	free(cl_separators);
 }
 
-static void read_input_file(struct strbuf *sb, const char *file)
-{
-	if (file) {
-		if (strbuf_read_file(sb, file, 0) < 0)
-			die_errno(_("could not read input file '%s'"), file);
-	} else {
-		if (strbuf_read(sb, fileno(stdin), 0) < 0)
-			die_errno(_("could not read from stdin"));
-	}
-}
-
 static const char *next_line(const char *str)
 {
 	const char *nl = strchrnul(str, '\n');
@@ -800,28 +806,55 @@
 }
 
 /*
- * Return the position of the start of the patch or the length of str if there
- * is no patch in the message.
+ * Find the end of the log message as an offset from the start of the input
+ * (where callers of this function are interested in looking for a trailers
+ * block in the same input). We have to consider two categories of content that
+ * can come at the end of the input which we want to ignore (because they don't
+ * belong in the log message):
+ *
+ * (1) the "patch part" which begins with a "---" divider and has patch
+ * information (like the output of git-format-patch), and
+ *
+ * (2) any trailing comment lines, blank lines like in the output of "git
+ * commit -v", or stuff below the "cut" (scissor) line.
+ *
+ * As a formula, the situation looks like this:
+ *
+ *     INPUT = LOG MESSAGE + IGNORED
+ *
+ * where IGNORED can be either of the two categories described above. It may be
+ * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
+ * contains a trailer block, but that's not the concern of this function.
  */
-static size_t find_patch_start(const char *str)
+static size_t find_end_of_log_message(const char *input, int no_divider)
 {
+	size_t end;
 	const char *s;
 
-	for (s = str; *s; s = next_line(s)) {
-		const char *v;
+	/* Assume the naive end of the input is already what we want. */
+	end = strlen(input);
 
-		if (skip_prefix(s, "---", &v) && isspace(*v))
-			return s - str;
+	/* Optionally skip over any patch part ("---" line and below). */
+	if (!no_divider) {
+		for (s = input; *s; s = next_line(s)) {
+			const char *v;
+
+			if (skip_prefix(s, "---", &v) && isspace(*v)) {
+				end = s - input;
+				break;
+			}
+		}
 	}
 
-	return s - str;
+	/* Skip over other ignorable bits. */
+	return end - ignored_log_message_bytes(input, end);
 }
 
 /*
  * Return the position of the first trailer line or len if there are no
  * trailers.
  */
-static size_t find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_block_start(const char *buf, size_t len)
 {
 	const char *s;
 	ssize_t end_of_title, l;
@@ -838,7 +871,7 @@
 
 	/* The first paragraph is the title and cannot be trailers */
 	for (s = buf; s < buf + len; s = next_line(s)) {
-		if (s[0] == comment_line_char)
+		if (starts_with_mem(s, buf + len - s, comment_line_str))
 			continue;
 		if (is_blank_line(s))
 			break;
@@ -858,7 +891,7 @@
 		const char **p;
 		ssize_t separator_pos;
 
-		if (bol[0] == comment_line_char) {
+		if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
 			non_trailer_lines += possible_continuation_lines;
 			possible_continuation_lines = 0;
 			continue;
@@ -916,12 +949,6 @@
 	return len;
 }
 
-/* Return the position of the end of the trailers. */
-static size_t find_trailer_end(const char *buf, size_t len)
-{
-	return len - ignore_non_trailer(buf, len);
-}
-
 static int ends_with_blank_line(const char *buf, size_t len)
 {
 	ssize_t ll = last_line(buf, len);
@@ -957,29 +984,25 @@
 	strbuf_release(&out);
 }
 
-static size_t process_input_file(FILE *outfile,
-				 const char *str,
-				 struct list_head *head,
-				 const struct process_trailer_options *opts)
+/*
+ * Parse trailers in "str", populating the trailer info and "head"
+ * linked list structure.
+ */
+void parse_trailers(const struct process_trailer_options *opts,
+		    struct trailer_info *info,
+		    const char *str,
+		    struct list_head *head)
 {
-	struct trailer_info info;
 	struct strbuf tok = STRBUF_INIT;
 	struct strbuf val = STRBUF_INIT;
 	size_t i;
 
-	trailer_info_get(&info, str, opts);
+	trailer_info_get(opts, str, info);
 
-	/* Print lines before the trailers as is */
-	if (!opts->only_trailers)
-		fwrite(str, 1, info.trailer_start - str, outfile);
-
-	if (!opts->only_trailers && !info.blank_line_before_trailer)
-		fprintf(outfile, "\n");
-
-	for (i = 0; i < info.trailer_nr; i++) {
+	for (i = 0; i < info->trailer_nr; i++) {
 		int separator_pos;
-		char *trailer = info.trailers[i];
-		if (trailer[0] == comment_line_char)
+		char *trailer = info->trailers[i];
+		if (starts_with(trailer, comment_line_str))
 			continue;
 		separator_pos = find_separator(trailer, separators);
 		if (separator_pos >= 1) {
@@ -998,113 +1021,34 @@
 					 strbuf_detach(&val, NULL));
 		}
 	}
-
-	trailer_info_release(&info);
-
-	return info.trailer_end - str;
 }
 
-static void free_all(struct list_head *head)
+void free_trailers(struct list_head *trailers)
 {
 	struct list_head *pos, *p;
-	list_for_each_safe(pos, p, head) {
+	list_for_each_safe(pos, p, trailers) {
 		list_del(pos);
 		free_trailer_item(list_entry(pos, struct trailer_item, list));
 	}
 }
 
-static struct tempfile *trailers_tempfile;
-
-static FILE *create_in_place_tempfile(const char *file)
+void trailer_info_get(const struct process_trailer_options *opts,
+		      const char *str,
+		      struct trailer_info *info)
 {
-	struct stat st;
-	struct strbuf filename_template = STRBUF_INIT;
-	const char *tail;
-	FILE *outfile;
-
-	if (stat(file, &st))
-		die_errno(_("could not stat %s"), file);
-	if (!S_ISREG(st.st_mode))
-		die(_("file %s is not a regular file"), file);
-	if (!(st.st_mode & S_IWUSR))
-		die(_("file %s is not writable by user"), file);
-
-	/* Create temporary file in the same directory as the original */
-	tail = strrchr(file, '/');
-	if (tail)
-		strbuf_add(&filename_template, file, tail - file + 1);
-	strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
-
-	trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
-	strbuf_release(&filename_template);
-	outfile = fdopen_tempfile(trailers_tempfile, "w");
-	if (!outfile)
-		die_errno(_("could not open temporary file"));
-
-	return outfile;
-}
-
-void process_trailers(const char *file,
-		      const struct process_trailer_options *opts,
-		      struct list_head *new_trailer_head)
-{
-	LIST_HEAD(head);
-	struct strbuf sb = STRBUF_INIT;
-	size_t trailer_end;
-	FILE *outfile = stdout;
-
-	ensure_configured();
-
-	read_input_file(&sb, file);
-
-	if (opts->in_place)
-		outfile = create_in_place_tempfile(file);
-
-	/* Print the lines before the trailers */
-	trailer_end = process_input_file(outfile, sb.buf, &head, opts);
-
-	if (!opts->only_input) {
-		LIST_HEAD(arg_head);
-		process_command_line_args(&arg_head, new_trailer_head);
-		process_trailers_lists(&head, &arg_head);
-	}
-
-	print_all(outfile, &head, opts);
-
-	free_all(&head);
-
-	/* Print the lines after the trailers as is */
-	if (!opts->only_trailers)
-		fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
-
-	if (opts->in_place)
-		if (rename_tempfile(&trailers_tempfile, file))
-			die_errno(_("could not rename temporary file to %s"), file);
-
-	strbuf_release(&sb);
-}
-
-void trailer_info_get(struct trailer_info *info, const char *str,
-		      const struct process_trailer_options *opts)
-{
-	int patch_start, trailer_end, trailer_start;
+	size_t end_of_log_message = 0, trailer_block_start = 0;
 	struct strbuf **trailer_lines, **ptr;
 	char **trailer_strings = NULL;
 	size_t nr = 0, alloc = 0;
 	char **last = NULL;
 
-	ensure_configured();
+	trailer_config_init();
 
-	if (opts->no_divider)
-		patch_start = strlen(str);
-	else
-		patch_start = find_patch_start(str);
+	end_of_log_message = find_end_of_log_message(str, opts->no_divider);
+	trailer_block_start = find_trailer_block_start(str, end_of_log_message);
 
-	trailer_end = find_trailer_end(str, patch_start);
-	trailer_start = find_trailer_start(str, trailer_end);
-
-	trailer_lines = strbuf_split_buf(str + trailer_start,
-					 trailer_end - trailer_start,
+	trailer_lines = strbuf_split_buf(str + trailer_block_start,
+					 end_of_log_message - trailer_block_start,
 					 '\n',
 					 0);
 	for (ptr = trailer_lines; *ptr; ptr++) {
@@ -1125,9 +1069,9 @@
 	strbuf_list_free(trailer_lines);
 
 	info->blank_line_before_trailer = ends_with_blank_line(str,
-							       trailer_start);
-	info->trailer_start = str + trailer_start;
-	info->trailer_end = str + trailer_end;
+							       trailer_block_start);
+	info->trailer_block_start = trailer_block_start;
+	info->trailer_block_end = end_of_log_message;
 	info->trailers = trailer_strings;
 	info->trailer_nr = nr;
 }
@@ -1140,22 +1084,13 @@
 	free(info->trailers);
 }
 
-static void format_trailer_info(struct strbuf *out,
+static void format_trailer_info(const struct process_trailer_options *opts,
 				const struct trailer_info *info,
-				const struct process_trailer_options *opts)
+				struct strbuf *out)
 {
 	size_t origlen = out->len;
 	size_t i;
 
-	/* If we want the whole block untouched, we can take the fast path. */
-	if (!opts->only_trailers && !opts->unfold && !opts->filter &&
-	    !opts->separator && !opts->key_only && !opts->value_only &&
-	    !opts->key_value_separator) {
-		strbuf_add(out, info->trailer_start,
-			   info->trailer_end - info->trailer_start);
-		return;
-	}
-
 	for (i = 0; i < info->trailer_nr; i++) {
 		char *trailer = info->trailers[i];
 		ssize_t separator_pos = find_separator(trailer, separators);
@@ -1200,13 +1135,25 @@
 
 }
 
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
-				 const struct process_trailer_options *opts)
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+				 const char *msg,
+				 struct strbuf *out)
 {
+	LIST_HEAD(trailer_objects);
 	struct trailer_info info;
 
-	trailer_info_get(&info, msg, opts);
-	format_trailer_info(out, &info, opts);
+	parse_trailers(opts, &info, msg, &trailer_objects);
+
+	/* If we want the whole block untouched, we can take the fast path. */
+	if (!opts->only_trailers && !opts->unfold && !opts->filter &&
+	    !opts->separator && !opts->key_only && !opts->value_only &&
+	    !opts->key_value_separator) {
+		strbuf_add(out, msg + info.trailer_block_start,
+			   info.trailer_block_end - info.trailer_block_start);
+	} else
+		format_trailer_info(opts, &info, out);
+
+	free_trailers(&trailer_objects);
 	trailer_info_release(&info);
 }
 
@@ -1216,14 +1163,14 @@
 	strbuf_init(&iter->key, 0);
 	strbuf_init(&iter->val, 0);
 	opts.no_divider = 1;
-	trailer_info_get(&iter->info, msg, &opts);
-	iter->cur = 0;
+	trailer_info_get(&opts, msg, &iter->internal.info);
+	iter->internal.cur = 0;
 }
 
 int trailer_iterator_advance(struct trailer_iterator *iter)
 {
-	while (iter->cur < iter->info.trailer_nr) {
-		char *trailer = iter->info.trailers[iter->cur++];
+	while (iter->internal.cur < iter->internal.info.trailer_nr) {
+		char *trailer = iter->internal.info.trailers[iter->internal.cur++];
 		int separator_pos = find_separator(trailer, separators);
 
 		if (separator_pos < 1)
@@ -1233,6 +1180,7 @@
 		strbuf_reset(&iter->val);
 		parse_trailer(&iter->key, &iter->val, NULL,
 			      trailer, separator_pos);
+		/* Always unfold values during iteration. */
 		unfold_value(&iter->val);
 		return 1;
 	}
@@ -1241,7 +1189,7 @@
 
 void trailer_iterator_release(struct trailer_iterator *iter)
 {
-	trailer_info_release(&iter->info);
+	trailer_info_release(&iter->internal.info);
 	strbuf_release(&iter->val);
 	strbuf_release(&iter->key);
 }
diff --git a/trailer.h b/trailer.h
index 795d2fc..1d106b6 100644
--- a/trailer.h
+++ b/trailer.h
@@ -32,16 +32,16 @@
 struct trailer_info {
 	/*
 	 * True if there is a blank line before the location pointed to by
-	 * trailer_start.
+	 * trailer_block_start.
 	 */
 	int blank_line_before_trailer;
 
 	/*
-	 * Pointers to the start and end of the trailer block found. If there
-	 * is no trailer block found, these 2 pointers point to the end of the
-	 * input string.
+	 * Offsets to the trailer block start and end positions in the input
+	 * string. If no trailer block is found, these are both set to the
+	 * "true" end of the input (find_end_of_log_message()).
 	 */
-	const char *trailer_start, *trailer_end;
+	size_t trailer_block_start, trailer_block_end;
 
 	/*
 	 * Array of trailers found.
@@ -81,15 +81,31 @@
 
 #define PROCESS_TRAILER_OPTIONS_INIT {0}
 
-void process_trailers(const char *file,
-		      const struct process_trailer_options *opts,
-		      struct list_head *new_trailer_head);
+void parse_trailers_from_config(struct list_head *config_head);
 
-void trailer_info_get(struct trailer_info *info, const char *str,
-		      const struct process_trailer_options *opts);
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+					   struct list_head *new_trailer_head);
+
+void process_trailers_lists(struct list_head *head,
+			    struct list_head *arg_head);
+
+void parse_trailers(const struct process_trailer_options *,
+		    struct trailer_info *,
+		    const char *str,
+		    struct list_head *head);
+
+void trailer_info_get(const struct process_trailer_options *,
+		      const char *str,
+		      struct trailer_info *);
 
 void trailer_info_release(struct trailer_info *info);
 
+void trailer_config_init(void);
+void format_trailers(const struct process_trailer_options *,
+		     struct list_head *trailers,
+		     struct strbuf *out);
+void free_trailers(struct list_head *);
+
 /*
  * Format the trailers from the commit msg "msg" into the strbuf "out".
  * Note two caveats about "opts":
@@ -101,8 +117,9 @@
  *     only the trailer block itself, even if the "only_trailers" option is not
  *     set.
  */
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
-				 const struct process_trailer_options *opts);
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+				 const char *msg,
+				 struct strbuf *out);
 
 /*
  * An interface for iterating over the trailers found in a particular commit
@@ -119,8 +136,10 @@
 	struct strbuf val;
 
 	/* private */
-	struct trailer_info info;
-	size_t cur;
+	struct {
+		struct trailer_info info;
+		size_t cur;
+	} internal;
 };
 
 /*
diff --git a/transport-helper.c b/transport-helper.c
index 3ea7c2b..8d284b2 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1,10 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "transport.h"
 #include "quote.h"
 #include "run-command.h"
 #include "commit.h"
-#include "diff.h"
-#include "revision.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "repository.h"
 #include "remote.h"
 #include "string-list.h"
 #include "thread-utils.h"
@@ -14,6 +17,7 @@
 #include "refspec.h"
 #include "transport-internal.h"
 #include "protocol.h"
+#include "packfile.h"
 
 static int debug;
 
@@ -429,6 +433,8 @@
 			warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf);
 	}
 	strbuf_release(&buf);
+
+	reprepare_packed_git(the_repository);
 	return 0;
 }
 
@@ -623,7 +629,8 @@
 		ret = run_connect(transport, &cmdbuf);
 	} else if (data->stateless_connect &&
 		   (get_protocol_version_config() == protocol_v2) &&
-		   !strcmp("git-upload-pack", name)) {
+		   (!strcmp("git-upload-pack", name) ||
+		    !strcmp("git-upload-archive", name))) {
 		strbuf_addf(&cmdbuf, "stateless-connect %s\n", name);
 		ret = run_connect(transport, &cmdbuf);
 		if (ret)
@@ -640,6 +647,7 @@
 	struct helper_data *data = transport->data;
 	const char *name;
 	const char *exec;
+	int ret;
 
 	name = for_push ? "git-receive-pack" : "git-upload-pack";
 	if (for_push)
@@ -647,7 +655,10 @@
 	else
 		exec = data->transport_options.uploadpack;
 
-	return process_connect_service(transport, name, exec);
+	ret = process_connect_service(transport, name, exec);
+	if (ret)
+		do_take_over(transport);
+	return ret;
 }
 
 static int connect_helper(struct transport *transport, const char *name,
@@ -657,14 +668,14 @@
 
 	/* Get_helper so connect is inited. */
 	get_helper(transport);
-	if (!data->connect)
-		die(_("operation not supported by protocol"));
 
 	if (!process_connect_service(transport, name, exec))
 		die(_("can't connect to subservice %s"), name);
 
 	fd[0] = data->helper->out;
 	fd[1] = data->helper->in;
+
+	do_take_over(transport);
 	return 0;
 }
 
@@ -679,10 +690,8 @@
 
 	get_helper(transport);
 
-	if (process_connect(transport, 0)) {
-		do_take_over(transport);
+	if (process_connect(transport, 0))
 		return transport->vtable->fetch_refs(transport, nr_heads, to_fetch);
-	}
 
 	/*
 	 * If we reach here, then the server, the client, and/or the transport
@@ -1069,7 +1078,7 @@
 	set_common_push_options(transport, data->name, flags);
 	if (flags & TRANSPORT_PUSH_FORCE) {
 		if (set_helper_option(transport, "force", "true") != 0)
-			warning(_("helper %s does not support 'force'"), data->name);
+			warning(_("helper %s does not support '--force'"), data->name);
 	}
 
 	helper = get_helper(transport);
@@ -1081,7 +1090,7 @@
 		struct object_id oid;
 
 		private = apply_refspecs(&data->rs, ref->name);
-		if (private && !get_oid(private, &oid)) {
+		if (private && !repo_get_oid(the_repository, private, &oid)) {
 			strbuf_addf(&buf, "^%s", private);
 			string_list_append_nodup(&revlist_args,
 						 strbuf_detach(&buf, NULL));
@@ -1139,10 +1148,8 @@
 {
 	struct helper_data *data = transport->data;
 
-	if (process_connect(transport, 1)) {
-		do_take_over(transport);
+	if (process_connect(transport, 1))
 		return transport->vtable->push_refs(transport, remote_refs, flags);
-	}
 
 	if (!remote_refs) {
 		fprintf(stderr,
@@ -1183,11 +1190,9 @@
 {
 	get_helper(transport);
 
-	if (process_connect(transport, for_push)) {
-		do_take_over(transport);
+	if (process_connect(transport, for_push))
 		return transport->vtable->get_refs_list(transport, for_push,
 							transport_options);
-	}
 
 	return get_refs_list_using_list(transport, for_push);
 }
@@ -1205,16 +1210,13 @@
 	data->get_refs_list_called = 1;
 	helper = get_helper(transport);
 
-	if (data->object_format) {
-		write_str_in_full(helper->in, "option object-format\n");
-		if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
-			exit(128);
-	}
+	if (data->object_format)
+		set_helper_option(transport, "object-format", "true");
 
 	if (data->push && for_push)
-		write_str_in_full(helper->in, "list for-push\n");
+		write_constant(helper->in, "list for-push\n");
 	else
-		write_str_in_full(helper->in, "list\n");
+		write_constant(helper->in, "list\n");
 
 	while (1) {
 		char *eov, *eon;
@@ -1271,10 +1273,8 @@
 {
 	get_helper(transport);
 
-	if (process_connect(transport, 0)) {
-		do_take_over(transport);
+	if (process_connect(transport, 0))
 		return transport->vtable->get_bundle_uri(transport);
-	}
 
 	return -1;
 }
diff --git a/transport.c b/transport.c
index 77a61a9..df518ea 100644
--- a/transport.c
+++ b/transport.c
@@ -1,5 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "config.h"
+#include "environment.h"
+#include "hex.h"
 #include "transport.h"
 #include "hook.h"
 #include "pkt-line.h"
@@ -7,9 +10,8 @@
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
-#include "walker.h"
 #include "bundle.h"
-#include "dir.h"
+#include "gettext.h"
 #include "refs.h"
 #include "refspec.h"
 #include "branch.h"
@@ -18,9 +20,10 @@
 #include "string-list.h"
 #include "oid-array.h"
 #include "sigchain.h"
+#include "trace2.h"
 #include "transport-internal.h"
 #include "protocol.h"
-#include "object-store.h"
+#include "object-name.h"
 #include "color.h"
 #include "bundle-uri.h"
 
@@ -167,7 +170,8 @@
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
-			       int nr_heads, struct ref **to_fetch)
+				  int nr_heads UNUSED,
+				  struct ref **to_fetch UNUSED)
 {
 	struct bundle_transport_data *data = transport->data;
 	struct strvec extra_index_pack_args = STRVEC_INIT;
@@ -276,8 +280,12 @@
 	}
 
 	data->conn = git_connect(data->fd, transport->url,
-				 for_push ? data->options.receivepack :
-				 data->options.uploadpack,
+				 for_push ?
+					"git-receive-pack" :
+					"git-upload-pack",
+				 for_push ?
+					data->options.receivepack :
+					data->options.uploadpack,
 				 flags);
 
 	return 0;
@@ -307,7 +315,7 @@
 	struct git_transport_data *data = transport->data;
 	struct ref *refs = NULL;
 	struct packet_reader reader;
-	int sid_len;
+	size_t sid_len;
 	const char *server_sid;
 
 	connect_setup(transport, for_push);
@@ -776,7 +784,8 @@
 static int measure_abbrev(const struct object_id *oid, int sofar)
 {
 	char hex[GIT_MAX_HEXSZ + 1];
-	int w = find_unique_abbrev_r(hex, oid, DEFAULT_ABBREV);
+	int w = repo_find_unique_abbrev_r(the_repository, hex, oid,
+					  DEFAULT_ABBREV);
 
 	return (w < sofar) ? sofar : w;
 }
@@ -911,7 +920,7 @@
 {
 	struct git_transport_data *data = transport->data;
 	data->conn = git_connect(data->fd, transport->url,
-				 executable, 0);
+				 name, executable, 0);
 	fd[0] = data->fd[0];
 	fd[1] = data->fd[1];
 	return 0;
@@ -1458,6 +1467,7 @@
 	if (porcelain && !push_ret)
 		puts("Done");
 	else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
+		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
 done:
diff --git a/transport.h b/transport.h
index 85150f5..6393cd9 100644
--- a/transport.h
+++ b/transport.h
@@ -1,7 +1,6 @@
 #ifndef TRANSPORT_H
 #define TRANSPORT_H
 
-#include "cache.h"
 #include "run-command.h"
 #include "remote.h"
 #include "list-objects-filter-options.h"
diff --git a/tree-diff.c b/tree-diff.c
index 69031d7..4610777 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -1,10 +1,26 @@
 /*
  * Helper functions for tree diff generation
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "hash.h"
 #include "tree.h"
+#include "tree-walk.h"
+#include "environment.h"
+
+/*
+ * Some mode bits are also used internally for computations.
+ *
+ * They *must* not overlap with any valid modes, and they *must* not be emitted
+ * to outside world - i.e. appear on disk or network. In other words, it's just
+ * temporary fields, which we internally use, but they have to stay in-house.
+ *
+ * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
+ *   codebase mode is `unsigned int` which is assumed to be at least 32 bits )
+ */
+
+#define S_DIFFTREE_IFXMIN_NEQ	0x80000000
 
 /*
  * internal mode marker, saying a tree entry != entry of tp[imin]
@@ -30,7 +46,8 @@
 static struct combine_diff_path *ll_diff_tree_paths(
 	struct combine_diff_path *p, const struct object_id *oid,
 	const struct object_id **parents_oid, int nparent,
-	struct strbuf *base, struct diff_options *opt);
+	struct strbuf *base, struct diff_options *opt,
+	int depth);
 static void ll_diff_tree_oid(const struct object_id *old_oid,
 			     const struct object_id *new_oid,
 			     struct strbuf *base, struct diff_options *opt);
@@ -181,7 +198,7 @@
 static struct combine_diff_path *emit_path(struct combine_diff_path *p,
 	struct strbuf *base, struct diff_options *opt, int nparent,
 	struct tree_desc *t, struct tree_desc *tp,
-	int imin)
+	int imin, int depth)
 {
 	unsigned short mode;
 	const char *path;
@@ -287,7 +304,8 @@
 
 		strbuf_add(base, path, pathlen);
 		strbuf_addch(base, '/');
-		p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
+		p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt,
+				       depth + 1);
 		FAST_ARRAY_FREE(parents_oid, nparent);
 	}
 
@@ -302,7 +320,7 @@
 
 	while (t->size) {
 		match = tree_entry_interesting(opt->repo->index, &t->entry,
-					       base, 0, &opt->pathspec);
+					       base, &opt->pathspec);
 		if (match) {
 			if (match == all_entries_not_interesting)
 				t->size = 0;
@@ -408,12 +426,16 @@
 static struct combine_diff_path *ll_diff_tree_paths(
 	struct combine_diff_path *p, const struct object_id *oid,
 	const struct object_id **parents_oid, int nparent,
-	struct strbuf *base, struct diff_options *opt)
+	struct strbuf *base, struct diff_options *opt,
+	int depth)
 {
 	struct tree_desc t, *tp;
 	void *ttree, **tptree;
 	int i;
 
+	if (depth > max_allowed_tree_depth)
+		die("exceeded maximum allowed tree depth");
+
 	FAST_ARRAY_ALLOC(tp, nparent);
 	FAST_ARRAY_ALLOC(tptree, nparent);
 
@@ -507,7 +529,7 @@
 
 			/* D += {δ(t,pi) if pi=p[imin];  "+a" if pi > p[imin]} */
 			p = emit_path(p, base, opt, nparent,
-					&t, tp, imin);
+					&t, tp, imin, depth);
 
 		skip_emit_t_tp:
 			/* t↓,  ∀ pi=p[imin]  pi↓ */
@@ -519,7 +541,7 @@
 		else if (cmp < 0) {
 			/* D += "+t" */
 			p = emit_path(p, base, opt, nparent,
-					&t, /*tp=*/NULL, -1);
+					&t, /*tp=*/NULL, -1, depth);
 
 			/* t↓ */
 			update_tree_entry(&t);
@@ -535,7 +557,7 @@
 			}
 
 			p = emit_path(p, base, opt, nparent,
-					/*t=*/NULL, tp, imin);
+					/*t=*/NULL, tp, imin, depth);
 
 		skip_emit_tp:
 			/* ∀ pi=p[imin]  pi↓ */
@@ -557,7 +579,7 @@
 	const struct object_id **parents_oid, int nparent,
 	struct strbuf *base, struct diff_options *opt)
 {
-	p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
+	p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt, 0);
 
 	/*
 	 * free pre-allocated last element, if any
diff --git a/tree-walk.c b/tree-walk.c
index 74f4d71..6565d9a 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -1,40 +1,29 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "tree-walk.h"
 #include "dir.h"
-#include "object-store.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "trace2.h"
 #include "tree.h"
 #include "pathspec.h"
 #include "json-writer.h"
-
-static const char *get_mode(const char *str, unsigned int *modep)
-{
-	unsigned char c;
-	unsigned int mode = 0;
-
-	if (*str == ' ')
-		return NULL;
-
-	while ((c = *str++) != ' ') {
-		if (c < '0' || c > '7')
-			return NULL;
-		mode = (mode << 3) + (c - '0');
-	}
-	*modep = mode;
-	return str;
-}
+#include "environment.h"
 
 static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
 {
 	const char *path;
-	unsigned int mode, len;
-	const unsigned hashsz = the_hash_algo->rawsz;
+	unsigned int len;
+	uint16_t mode;
+	const unsigned hashsz = desc->algo->rawsz;
 
 	if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
 		strbuf_addstr(err, _("too-short tree object"));
 		return -1;
 	}
 
-	path = get_mode(buf, &mode);
+	path = parse_mode(buf, &mode);
 	if (!path) {
 		strbuf_addstr(err, _("malformed mode in tree entry"));
 		return -1;
@@ -49,15 +38,19 @@
 	desc->entry.path = path;
 	desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
 	desc->entry.pathlen = len - 1;
-	oidread(&desc->entry.oid, (const unsigned char *)path + len);
+	oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
+		      desc->algo);
 
 	return 0;
 }
 
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
-				   unsigned long size, struct strbuf *err,
+static int init_tree_desc_internal(struct tree_desc *desc,
+				   const struct object_id *oid,
+				   const void *buffer, unsigned long size,
+				   struct strbuf *err,
 				   enum tree_desc_flags flags)
 {
+	desc->algo = (oid && oid->algo) ? &hash_algos[oid->algo] : the_hash_algo;
 	desc->buffer = buffer;
 	desc->size = size;
 	desc->flags = flags;
@@ -66,19 +59,21 @@
 	return 0;
 }
 
-void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+		    const void *buffer, unsigned long size)
 {
 	struct strbuf err = STRBUF_INIT;
-	if (init_tree_desc_internal(desc, buffer, size, &err, 0))
+	if (init_tree_desc_internal(desc, tree_oid, buffer, size, &err, 0))
 		die("%s", err.buf);
 	strbuf_release(&err);
 }
 
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+			  const void *buffer, unsigned long size,
 			  enum tree_desc_flags flags)
 {
 	struct strbuf err = STRBUF_INIT;
-	int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
+	int result = init_tree_desc_internal(desc, oid, buffer, size, &err, flags);
 	if (result)
 		error("%s", err.buf);
 	strbuf_release(&err);
@@ -95,9 +90,9 @@
 	if (oid) {
 		buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL);
 		if (!buf)
-			die("unable to read tree %s", oid_to_hex(oid));
+			die(_("unable to read tree (%s)"), oid_to_hex(oid));
 	}
-	init_tree_desc(desc, buf, size);
+	init_tree_desc(desc, oid, buf, size);
 	return buf;
 }
 
@@ -114,7 +109,7 @@
 static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
 {
 	const void *buf = desc->buffer;
-	const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
+	const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + desc->algo->rawsz;
 	unsigned long size = desc->size;
 	unsigned long len = end - (const unsigned char *)buf;
 
@@ -430,29 +425,32 @@
 	if (still_interesting < 0)
 		return still_interesting;
 	return tree_entry_interesting(istate, e, base,
-				      0, info->pathspec);
+				      info->pathspec);
 }
 
 int traverse_trees(struct index_state *istate,
 		   int n, struct tree_desc *t,
 		   struct traverse_info *info)
 {
-	int error = 0;
-	struct name_entry entry[MAX_TRAVERSE_TREES];
+	int ret = 0;
+	struct name_entry *entry;
 	int i;
-	struct tree_desc_x tx[ARRAY_SIZE(entry)];
+	struct tree_desc_x *tx;
 	struct strbuf base = STRBUF_INIT;
 	int interesting = 1;
 	char *traverse_path;
 
+	if (traverse_trees_cur_depth > max_allowed_tree_depth)
+		return error("exceeded maximum allowed tree depth");
+
 	traverse_trees_count++;
 	traverse_trees_cur_depth++;
 
 	if (traverse_trees_cur_depth > traverse_trees_max_depth)
 		traverse_trees_max_depth = traverse_trees_cur_depth;
 
-	if (n >= ARRAY_SIZE(entry))
-		BUG("traverse_trees() called with too many trees (%d)", n);
+	ALLOC_ARRAY(entry, n);
+	ALLOC_ARRAY(tx, n);
 
 	for (i = 0; i < n; i++) {
 		tx[i].d = t[i];
@@ -535,7 +533,7 @@
 		if (interesting) {
 			trees_used = info->fn(n, mask, dirmask, entry, info);
 			if (trees_used < 0) {
-				error = trees_used;
+				ret = trees_used;
 				if (!info->show_all_errors)
 					break;
 			}
@@ -547,12 +545,14 @@
 	}
 	for (i = 0; i < n; i++)
 		free_extended_entry(tx + i);
+	free(tx);
+	free(entry);
 	free(traverse_path);
 	info->traverse_path = NULL;
 	strbuf_release(&base);
 
 	traverse_trees_cur_depth--;
-	return error;
+	return ret;
 }
 
 struct dir_state {
@@ -623,7 +623,7 @@
 		retval = -1;
 	} else {
 		struct tree_desc t;
-		init_tree_desc(&t, tree, size);
+		init_tree_desc(&t, tree_oid, tree, size);
 		retval = find_tree_entry(r, &t, name, oid, mode);
 	}
 	free(tree);
@@ -666,7 +666,7 @@
 	struct tree_desc t;
 	int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
 
-	init_tree_desc(&t, NULL, 0UL);
+	init_tree_desc(&t, NULL, NULL, 0UL);
 	strbuf_addstr(&namebuf, name);
 	oidcpy(&current_tree_oid, tree_oid);
 
@@ -702,7 +702,7 @@
 				goto done;
 
 			/* descend */
-			init_tree_desc(&t, tree, size);
+			init_tree_desc(&t, &current_tree_oid, tree, size);
 		}
 
 		/* Handle symlinks to e.g. a//b by removing leading slashes */
@@ -736,7 +736,7 @@
 			free(parent->tree);
 			parents_nr--;
 			parent = &parents[parents_nr - 1];
-			init_tree_desc(&t, parent->tree, parent->size);
+			init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
 			strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
 			continue;
 		}
@@ -816,7 +816,7 @@
 			contents_start = contents;
 
 			parent = &parents[parents_nr - 1];
-			init_tree_desc(&t, parent->tree, parent->size);
+			init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
 			strbuf_splice(&namebuf, 0, len,
 				      contents_start, link_len);
 			if (remainder)
@@ -1011,17 +1011,17 @@
 /*
  * Is a tree entry interesting given the pathspec we have?
  *
- * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * Pre-condition: either baselen == 0 (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
 static enum interesting do_match(struct index_state *istate,
 				 const struct name_entry *entry,
-				 struct strbuf *base, int base_offset,
+				 struct strbuf *base,
 				 const struct pathspec *ps,
 				 int exclude)
 {
 	int i;
-	int pathlen, baselen = base->len - base_offset;
+	int pathlen, baselen = base->len;
 	enum interesting never_interesting = ps->has_wildcard ?
 		entry_not_interesting : all_entries_not_interesting;
 
@@ -1039,7 +1039,7 @@
 		    !(ps->magic & PATHSPEC_MAXDEPTH) ||
 		    ps->max_depth == -1)
 			return all_entries_interesting;
-		return within_depth(base->buf + base_offset, baselen,
+		return within_depth(base->buf, baselen,
 				    !!S_ISDIR(entry->mode),
 				    ps->max_depth) ?
 			entry_interesting : entry_not_interesting;
@@ -1050,7 +1050,7 @@
 	for (i = ps->nr - 1; i >= 0; i--) {
 		const struct pathspec_item *item = ps->items+i;
 		const char *match = item->match;
-		const char *base_str = base->buf + base_offset;
+		const char *base_str = base->buf;
 		int matchlen = item->len, matched = 0;
 
 		if ((!exclude &&   item->magic & PATHSPEC_EXCLUDE) ||
@@ -1143,9 +1143,9 @@
 
 		strbuf_add(base, entry->path, pathlen);
 
-		if (!git_fnmatch(item, match, base->buf + base_offset,
+		if (!git_fnmatch(item, match, base->buf,
 				 item->nowildcard_len)) {
-			strbuf_setlen(base, base_offset + baselen);
+			strbuf_setlen(base, baselen);
 			goto interesting;
 		}
 
@@ -1157,13 +1157,13 @@
 		 * be performed in the submodule itself.
 		 */
 		if (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&
-		    !ps_strncmp(item, match, base->buf + base_offset,
+		    !ps_strncmp(item, match, base->buf,
 				item->nowildcard_len)) {
-			strbuf_setlen(base, base_offset + baselen);
+			strbuf_setlen(base, baselen);
 			goto interesting;
 		}
 
-		strbuf_setlen(base, base_offset + baselen);
+		strbuf_setlen(base, baselen);
 
 		/*
 		 * Match all directories. We'll try to match files
@@ -1199,9 +1199,9 @@
 				return entry_interesting;
 
 			strbuf_add(base, entry->path, pathlen);
-			ret = match_pathspec_attrs(istate, base->buf + base_offset,
-						   base->len - base_offset, item);
-			strbuf_setlen(base, base_offset + baselen);
+			ret = match_pathspec_attrs(istate, base->buf,
+						   base->len, item);
+			strbuf_setlen(base, baselen);
 			if (!ret)
 				continue;
 		}
@@ -1213,16 +1213,16 @@
 /*
  * Is a tree entry interesting given the pathspec we have?
  *
- * Pre-condition: either baselen == base_offset (i.e. empty path)
+ * Pre-condition: either baselen == 0 (i.e. empty path)
  * or base[baselen-1] == '/' (i.e. with trailing slash).
  */
 enum interesting tree_entry_interesting(struct index_state *istate,
 					const struct name_entry *entry,
-					struct strbuf *base, int base_offset,
+					struct strbuf *base,
 					const struct pathspec *ps)
 {
 	enum interesting positive, negative;
-	positive = do_match(istate, entry, base, base_offset, ps, 0);
+	positive = do_match(istate, entry, base, ps, 0);
 
 	/*
 	 * case | entry | positive | negative | result
@@ -1259,7 +1259,7 @@
 	    positive <= entry_not_interesting) /* #1, #2, #11, #12 */
 		return positive;
 
-	negative = do_match(istate, entry, base, base_offset, ps, 1);
+	negative = do_match(istate, entry, base, ps, 1);
 
 	/* #8, #18 */
 	if (positive == all_entries_interesting &&
diff --git a/tree-walk.h b/tree-walk.h
index 6305d53..0b1067f 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -1,9 +1,10 @@
 #ifndef TREE_WALK_H
 #define TREE_WALK_H
 
-#include "cache.h"
+#include "hash-ll.h"
 
-#define MAX_TRAVERSE_TREES 8
+struct index_state;
+struct repository;
 
 /**
  * The tree walking API is used to traverse and inspect trees.
@@ -23,6 +24,7 @@
  * A semi-opaque data structure used to maintain the current state of the walk.
  */
 struct tree_desc {
+	const struct git_hash_algo *algo;
 	/*
 	 * pointer into the memory representation of the tree. It always
 	 * points at the current entry being visited.
@@ -82,9 +84,11 @@
  * size parameters are assumed to be the same as the buffer and size
  * members of `struct tree`.
  */
-void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+		    const void *buf, unsigned long size);
 
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+			  const void *buf, unsigned long size,
 			  enum tree_desc_flags flags);
 
 /*
@@ -221,7 +225,7 @@
 
 enum interesting tree_entry_interesting(struct index_state *istate,
 					const struct name_entry *,
-					struct strbuf *, int,
+					struct strbuf *,
 					const struct pathspec *ps);
 
 #endif
diff --git a/tree.c b/tree.c
index 410e3b4..7973d3f 100644
--- a/tree.c
+++ b/tree.c
@@ -1,18 +1,19 @@
-#include "cache.h"
-#include "cache-tree.h"
+#include "git-compat-util.h"
+#include "hex.h"
 #include "tree.h"
-#include "object-store.h"
-#include "blob.h"
+#include "object-name.h"
+#include "object-store-ll.h"
 #include "commit.h"
-#include "tag.h"
 #include "alloc.h"
 #include "tree-walk.h"
 #include "repository.h"
+#include "environment.h"
 
 const char *tree_type = "tree";
 
 int read_tree_at(struct repository *r,
 		 struct tree *tree, struct strbuf *base,
+		 int depth,
 		 const struct pathspec *pathspec,
 		 read_tree_fn_t fn, void *context)
 {
@@ -22,15 +23,18 @@
 	int len, oldlen = base->len;
 	enum interesting retval = entry_not_interesting;
 
+	if (depth > max_allowed_tree_depth)
+		return error("exceeded maximum allowed tree depth");
+
 	if (parse_tree(tree))
 		return -1;
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 
 	while (tree_entry(&desc, &entry)) {
 		if (retval != all_entries_interesting) {
 			retval = tree_entry_interesting(r->index, &entry,
-							base, 0, pathspec);
+							base, pathspec);
 			if (retval == all_entries_not_interesting)
 				break;
 			if (retval == entry_not_interesting)
@@ -58,7 +62,7 @@
 				    oid_to_hex(&entry.oid),
 				    base->buf, entry.path);
 
-			if (parse_commit(commit))
+			if (repo_parse_commit(r, commit))
 				die("Invalid commit %s in submodule path %s%s",
 				    oid_to_hex(&entry.oid),
 				    base->buf, entry.path);
@@ -72,7 +76,7 @@
 		strbuf_add(base, entry.path, len);
 		strbuf_addch(base, '/');
 		retval = read_tree_at(r, lookup_tree(r, &oid),
-				      base, pathspec,
+				      base, depth + 1, pathspec,
 				      fn, context);
 		strbuf_setlen(base, oldlen);
 		if (retval)
@@ -87,19 +91,77 @@
 	      read_tree_fn_t fn, void *context)
 {
 	struct strbuf sb = STRBUF_INIT;
-	int ret = read_tree_at(r, tree, &sb, pathspec, fn, context);
+	int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context);
 	strbuf_release(&sb);
 	return ret;
 }
 
-int cmp_cache_name_compare(const void *a_, const void *b_)
+int base_name_compare(const char *name1, size_t len1, int mode1,
+		      const char *name2, size_t len2, int mode2)
 {
-	const struct cache_entry *ce1, *ce2;
+	unsigned char c1, c2;
+	size_t len = len1 < len2 ? len1 : len2;
+	int cmp;
 
-	ce1 = *((const struct cache_entry **)a_);
-	ce2 = *((const struct cache_entry **)b_);
-	return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
-				  ce2->name, ce2->ce_namelen, ce_stage(ce2));
+	cmp = memcmp(name1, name2, len);
+	if (cmp)
+		return cmp;
+	c1 = name1[len];
+	c2 = name2[len];
+	if (!c1 && S_ISDIR(mode1))
+		c1 = '/';
+	if (!c2 && S_ISDIR(mode2))
+		c2 = '/';
+	return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+}
+
+/*
+ * df_name_compare() is identical to base_name_compare(), except it
+ * compares conflicting directory/file entries as equal. Note that
+ * while a directory name compares as equal to a regular file, they
+ * then individually compare _differently_ to a filename that has
+ * a dot after the basename (because '\0' < '.' < '/').
+ *
+ * This is used by routines that want to traverse the git namespace
+ * but then handle conflicting entries together when possible.
+ */
+int df_name_compare(const char *name1, size_t len1, int mode1,
+		    const char *name2, size_t len2, int mode2)
+{
+	unsigned char c1, c2;
+	size_t len = len1 < len2 ? len1 : len2;
+	int cmp;
+
+	cmp = memcmp(name1, name2, len);
+	if (cmp)
+		return cmp;
+	/* Directories and files compare equal (same length, same name) */
+	if (len1 == len2)
+		return 0;
+	c1 = name1[len];
+	if (!c1 && S_ISDIR(mode1))
+		c1 = '/';
+	c2 = name2[len];
+	if (!c2 && S_ISDIR(mode2))
+		c2 = '/';
+	if (c1 == '/' && !c2)
+		return 0;
+	if (c2 == '/' && !c1)
+		return 0;
+	return c1 - c2;
+}
+
+int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
+{
+	size_t min_len = (len1 < len2) ? len1 : len2;
+	int cmp = memcmp(name1, name2, min_len);
+	if (cmp)
+		return cmp;
+	if (len1 < len2)
+		return -1;
+	if (len1 > len2)
+		return 1;
+	return 0;
 }
 
 struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
@@ -129,7 +191,8 @@
 
 	if (item->object.parsed)
 		return 0;
-	buffer = read_object_file(&item->object.oid, &type, &size);
+	buffer = repo_read_object_file(the_repository, &item->object.oid,
+				       &type, &size);
 	if (!buffer)
 		return quiet_on_missing ? -1 :
 			error("Could not read %s",
diff --git a/tree.h b/tree.h
index 6efff00..cc6ddf5 100644
--- a/tree.h
+++ b/tree.h
@@ -3,6 +3,7 @@
 
 #include "object.h"
 
+struct pathspec;
 struct repository;
 struct strbuf;
 
@@ -28,13 +29,22 @@
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const struct object_id *oid);
 
-int cmp_cache_name_compare(const void *a_, const void *b_);
+/*
+ * Functions for comparing pathnames
+ */
+int base_name_compare(const char *name1, size_t len1, int mode1,
+		      const char *name2, size_t len2, int mode2);
+int df_name_compare(const char *name1, size_t len1, int mode1,
+		    const char *name2, size_t len2, int mode2);
+int name_compare(const char *name1, size_t len1,
+		 const char *name2, size_t len2);
 
 #define READ_TREE_RECURSIVE 1
 typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);
 
 int read_tree_at(struct repository *r,
 		 struct tree *tree, struct strbuf *base,
+		 int depth,
 		 const struct pathspec *pathspec,
 		 read_tree_fn_t fn, void *context);
 
diff --git a/unicode-width.h b/unicode-width.h
index 97c851b..be5bf8c 100644
--- a/unicode-width.h
+++ b/unicode-width.h
@@ -94,7 +94,7 @@
 { 0x0E47, 0x0E4E },
 { 0x0EB1, 0x0EB1 },
 { 0x0EB4, 0x0EBC },
-{ 0x0EC8, 0x0ECD },
+{ 0x0EC8, 0x0ECE },
 { 0x0F18, 0x0F19 },
 { 0x0F35, 0x0F35 },
 { 0x0F37, 0x0F37 },
@@ -228,6 +228,7 @@
 { 0x10AE5, 0x10AE6 },
 { 0x10D24, 0x10D27 },
 { 0x10EAB, 0x10EAC },
+{ 0x10EFD, 0x10EFF },
 { 0x10F46, 0x10F50 },
 { 0x10F82, 0x10F85 },
 { 0x11001, 0x11001 },
@@ -252,6 +253,7 @@
 { 0x11234, 0x11234 },
 { 0x11236, 0x11237 },
 { 0x1123E, 0x1123E },
+{ 0x11241, 0x11241 },
 { 0x112DF, 0x112DF },
 { 0x112E3, 0x112EA },
 { 0x11300, 0x11301 },
@@ -313,7 +315,12 @@
 { 0x11D95, 0x11D95 },
 { 0x11D97, 0x11D97 },
 { 0x11EF3, 0x11EF4 },
-{ 0x13430, 0x13438 },
+{ 0x11F00, 0x11F01 },
+{ 0x11F36, 0x11F3A },
+{ 0x11F40, 0x11F40 },
+{ 0x11F42, 0x11F42 },
+{ 0x13430, 0x13440 },
+{ 0x13447, 0x13455 },
 { 0x16AF0, 0x16AF4 },
 { 0x16B30, 0x16B36 },
 { 0x16F4F, 0x16F4F },
@@ -339,9 +346,11 @@
 { 0x1E01B, 0x1E021 },
 { 0x1E023, 0x1E024 },
 { 0x1E026, 0x1E02A },
+{ 0x1E08F, 0x1E08F },
 { 0x1E130, 0x1E136 },
 { 0x1E2AE, 0x1E2AE },
 { 0x1E2EC, 0x1E2EF },
+{ 0x1E4EC, 0x1E4EF },
 { 0x1E8D0, 0x1E8D6 },
 { 0x1E944, 0x1E94A },
 { 0xE0001, 0xE0001 },
@@ -387,14 +396,13 @@
 { 0x2E80, 0x2E99 },
 { 0x2E9B, 0x2EF3 },
 { 0x2F00, 0x2FD5 },
-{ 0x2FF0, 0x2FFB },
-{ 0x3000, 0x303E },
+{ 0x2FF0, 0x303E },
 { 0x3041, 0x3096 },
 { 0x3099, 0x30FF },
 { 0x3105, 0x312F },
 { 0x3131, 0x318E },
 { 0x3190, 0x31E3 },
-{ 0x31F0, 0x321E },
+{ 0x31EF, 0x321E },
 { 0x3220, 0x3247 },
 { 0x3250, 0x4DBF },
 { 0x4E00, 0xA48C },
@@ -417,7 +425,9 @@
 { 0x1AFF5, 0x1AFFB },
 { 0x1AFFD, 0x1AFFE },
 { 0x1B000, 0x1B122 },
+{ 0x1B132, 0x1B132 },
 { 0x1B150, 0x1B152 },
+{ 0x1B155, 0x1B155 },
 { 0x1B164, 0x1B167 },
 { 0x1B170, 0x1B2FB },
 { 0x1F004, 0x1F004 },
@@ -451,7 +461,7 @@
 { 0x1F6CC, 0x1F6CC },
 { 0x1F6D0, 0x1F6D2 },
 { 0x1F6D5, 0x1F6D7 },
-{ 0x1F6DD, 0x1F6DF },
+{ 0x1F6DC, 0x1F6DF },
 { 0x1F6EB, 0x1F6EC },
 { 0x1F6F4, 0x1F6FC },
 { 0x1F7E0, 0x1F7EB },
@@ -459,15 +469,13 @@
 { 0x1F90C, 0x1F93A },
 { 0x1F93C, 0x1F945 },
 { 0x1F947, 0x1F9FF },
-{ 0x1FA70, 0x1FA74 },
-{ 0x1FA78, 0x1FA7C },
-{ 0x1FA80, 0x1FA86 },
-{ 0x1FA90, 0x1FAAC },
-{ 0x1FAB0, 0x1FABA },
-{ 0x1FAC0, 0x1FAC5 },
-{ 0x1FAD0, 0x1FAD9 },
-{ 0x1FAE0, 0x1FAE7 },
-{ 0x1FAF0, 0x1FAF6 },
+{ 0x1FA70, 0x1FA7C },
+{ 0x1FA80, 0x1FA88 },
+{ 0x1FA90, 0x1FABD },
+{ 0x1FABF, 0x1FAC5 },
+{ 0x1FACE, 0x1FADB },
+{ 0x1FAE0, 0x1FAE8 },
+{ 0x1FAF0, 0x1FAF8 },
 { 0x20000, 0x2FFFD },
 { 0x30000, 0x3FFFD }
 };
diff --git a/unix-socket.c b/unix-socket.c
index e0be1ba..79800d8 100644
--- a/unix-socket.c
+++ b/unix-socket.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
 #include "unix-socket.h"
 
 #define DEFAULT_UNIX_STREAM_LISTEN_BACKLOG (5)
diff --git a/unix-stream-server.c b/unix-stream-server.c
index efa2a20..22ac237 100644
--- a/unix-stream-server.c
+++ b/unix-stream-server.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "lockfile.h"
 #include "unix-socket.h"
 #include "unix-stream-server.h"
diff --git a/unpack-trees.c b/unpack-trees.c
index 3d05e45..c2b20b8 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,8 +1,13 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "strvec.h"
 #include "repository.h"
-#include "config.h"
+#include "parse.h"
 #include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "name-hash.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
@@ -10,15 +15,19 @@
 #include "progress.h"
 #include "refs.h"
 #include "attr.h"
+#include "read-cache.h"
 #include "split-index.h"
 #include "sparse-index.h"
 #include "submodule.h"
 #include "submodule-config.h"
+#include "symlinks.h"
+#include "trace2.h"
 #include "fsmonitor.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "promisor-remote.h"
 #include "entry.h"
 #include "parallel-checkout.h"
+#include "setup.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -66,8 +75,8 @@
 };
 
 #define ERRORMSG(o,type) \
-	( ((o) && (o)->msgs[(type)]) \
-	  ? ((o)->msgs[(type)])      \
+	( ((o) && (o)->internal.msgs[(type)]) \
+	  ? ((o)->internal.msgs[(type)])      \
 	  : (unpack_plumbing_errors[(type)]) )
 
 static const char *super_prefixed(const char *path, const char *super_prefix)
@@ -108,10 +117,10 @@
 				  const char *cmd)
 {
 	int i;
-	const char **msgs = opts->msgs;
+	const char **msgs = opts->internal.msgs;
 	const char *msg;
 
-	strvec_init(&opts->msgs_to_free);
+	strvec_init(&opts->internal.msgs_to_free);
 
 	if (!strcmp(cmd, "checkout"))
 		msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
@@ -129,7 +138,7 @@
 			  "Please commit your changes or stash them before you %s.")
 		      : _("Your local changes to the following files would be overwritten by %s:\n%%s");
 	msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
-		strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+		strvec_pushf(&opts->internal.msgs_to_free, msg, cmd, cmd);
 
 	msgs[ERROR_NOT_UPTODATE_DIR] =
 		_("Updating the following directories would lose untracked files in them:\n%s");
@@ -153,7 +162,7 @@
 			  "Please move or remove them before you %s.")
 		      : _("The following untracked working tree files would be removed by %s:\n%%s");
 	msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] =
-		strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+		strvec_pushf(&opts->internal.msgs_to_free, msg, cmd, cmd);
 
 	if (!strcmp(cmd, "checkout"))
 		msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
@@ -171,7 +180,7 @@
 			  "Please move or remove them before you %s.")
 		      : _("The following untracked working tree files would be overwritten by %s:\n%%s");
 	msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =
-		strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+		strvec_pushf(&opts->internal.msgs_to_free, msg, cmd, cmd);
 
 	/*
 	 * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@ -189,16 +198,16 @@
 	msgs[WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN] =
 		_("The following paths were already present and thus not updated despite sparse patterns:\n%s");
 
-	opts->show_all_errors = 1;
+	opts->internal.show_all_errors = 1;
 	/* rejected paths may not have a static buffer */
-	for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
-		opts->unpack_rejects[i].strdup_strings = 1;
+	for (i = 0; i < ARRAY_SIZE(opts->internal.unpack_rejects); i++)
+		opts->internal.unpack_rejects[i].strdup_strings = 1;
 }
 
 void clear_unpack_trees_porcelain(struct unpack_trees_options *opts)
 {
-	strvec_clear(&opts->msgs_to_free);
-	memset(opts->msgs, 0, sizeof(opts->msgs));
+	strvec_clear(&opts->internal.msgs_to_free);
+	memset(opts->internal.msgs, 0, sizeof(opts->internal.msgs));
 }
 
 static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
@@ -210,7 +219,7 @@
 		set |= CE_WT_REMOVE;
 
 	ce->ce_flags = (ce->ce_flags & ~clear) | set;
-	return add_index_entry(&o->result, ce,
+	return add_index_entry(&o->internal.result, ce,
 			       ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 }
 
@@ -218,7 +227,7 @@
 		      const struct cache_entry *ce,
 		      unsigned int set, unsigned int clear)
 {
-	do_add_entry(o, dup_cache_entry(ce, &o->result), set, clear);
+	do_add_entry(o, dup_cache_entry(ce, &o->internal.result), set, clear);
 }
 
 /*
@@ -233,7 +242,7 @@
 	if (o->quiet)
 		return -1;
 
-	if (!o->show_all_errors)
+	if (!o->internal.show_all_errors)
 		return error(ERRORMSG(o, e), super_prefixed(path,
 							    o->super_prefix));
 
@@ -241,7 +250,7 @@
 	 * Otherwise, insert in a list for future display by
 	 * display_(error|warning)_msgs()
 	 */
-	string_list_append(&o->unpack_rejects[e], path);
+	string_list_append(&o->internal.unpack_rejects[e], path);
 	return -1;
 }
 
@@ -253,7 +262,7 @@
 	int e;
 	unsigned error_displayed = 0;
 	for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
-		struct string_list *rejects = &o->unpack_rejects[e];
+		struct string_list *rejects = &o->internal.unpack_rejects[e];
 
 		if (rejects->nr > 0) {
 			int i;
@@ -281,7 +290,7 @@
 	unsigned warning_displayed = 0;
 	for (e = NB_UNPACK_TREES_ERROR_TYPES + 1;
 	     e < NB_UNPACK_TREES_WARNING_TYPES; e++) {
-		struct string_list *rejects = &o->unpack_rejects[e];
+		struct string_list *rejects = &o->internal.unpack_rejects[e];
 
 		if (rejects->nr > 0) {
 			int i;
@@ -458,7 +467,7 @@
 	if (should_update_submodules())
 		load_gitmodules_file(index, &state);
 
-	if (has_promisor_remote())
+	if (repo_has_promisor_remote(the_repository))
 		/*
 		 * Prefetch the objects that are to be checked out in the loop
 		 * below.
@@ -600,13 +609,14 @@
 {
 	ce->ce_flags |= CE_UNPACKED;
 
-	if (o->cache_bottom < o->src_index->cache_nr &&
-	    o->src_index->cache[o->cache_bottom] == ce) {
-		int bottom = o->cache_bottom;
+	if (o->internal.cache_bottom < o->src_index->cache_nr &&
+	    o->src_index->cache[o->internal.cache_bottom] == ce) {
+		int bottom = o->internal.cache_bottom;
+
 		while (bottom < o->src_index->cache_nr &&
 		       o->src_index->cache[bottom]->ce_flags & CE_UNPACKED)
 			bottom++;
-		o->cache_bottom = bottom;
+		o->internal.cache_bottom = bottom;
 	}
 }
 
@@ -652,7 +662,7 @@
 static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
 {
 	const struct index_state *index = o->src_index;
-	int pos = o->cache_bottom;
+	int pos = o->internal.cache_bottom;
 
 	while (pos < index->cache_nr) {
 		struct cache_entry *ce = index->cache[pos];
@@ -711,7 +721,7 @@
 
 	if (o->diff_index_cached)
 		return;
-	o->cache_bottom = bottom;
+	o->internal.cache_bottom = bottom;
 }
 
 static int switch_cache_bottom(struct traverse_info *info)
@@ -721,13 +731,13 @@
 
 	if (o->diff_index_cached)
 		return 0;
-	ret = o->cache_bottom;
+	ret = o->internal.cache_bottom;
 	pos = find_cache_pos(info->prev, info->name, info->namelen);
 
 	if (pos < -1)
-		o->cache_bottom = -2 - pos;
+		o->internal.cache_bottom = -2 - pos;
 	else if (pos < 0)
-		o->cache_bottom = o->src_index->cache_nr;
+		o->internal.cache_bottom = o->src_index->cache_nr;
 	return ret;
 }
 
@@ -838,7 +848,7 @@
 		mark_ce_used(src[0], o);
 	}
 	free(tree_ce);
-	if (o->debug_unpack)
+	if (o->internal.debug_unpack)
 		printf("Unpacked %d entries from %s to %s using cache-tree\n",
 		       nr_entries,
 		       o->src_index->cache[pos]->name,
@@ -854,8 +864,8 @@
 	struct unpack_trees_options *o = info->data;
 	int i, ret, bottom;
 	int nr_buf = 0;
-	struct tree_desc t[MAX_UNPACK_TREES];
-	void *buf[MAX_UNPACK_TREES];
+	struct tree_desc *t;
+	void **buf;
 	struct traverse_info newinfo;
 	struct name_entry *p;
 	int nr_entries;
@@ -873,9 +883,9 @@
 		 * save and restore cache_bottom anyway to not miss
 		 * unprocessed entries before 'pos'.
 		 */
-		bottom = o->cache_bottom;
+		bottom = o->internal.cache_bottom;
 		ret = traverse_by_cache_tree(pos, nr_entries, n, info);
-		o->cache_bottom = bottom;
+		o->internal.cache_bottom = bottom;
 		return ret;
 	}
 
@@ -892,6 +902,9 @@
 	newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
 	newinfo.df_conflicts |= df_conflicts;
 
+	ALLOC_ARRAY(t, n);
+	ALLOC_ARRAY(buf, n);
+
 	/*
 	 * Fetch the tree from the ODB for each peer directory in the
 	 * n commits.
@@ -927,6 +940,8 @@
 
 	for (i = 0; i < nr_buf; i++)
 		free(buf[i]);
+	free(buf);
+	free(t);
 
 	return ret;
 }
@@ -1212,8 +1227,8 @@
 		 * cache entry from the index aware logic.
 		 */
 		src[i + o->merge] = create_ce_entry(info, names + i, stage,
-						    &o->result, o->merge,
-						    bit & dirmask);
+						    &o->internal.result,
+						    o->merge, bit & dirmask);
 	}
 
 	if (o->merge) {
@@ -1237,7 +1252,7 @@
 
 static int unpack_failed(struct unpack_trees_options *o, const char *message)
 {
-	discard_index(&o->result);
+	discard_index(&o->internal.result);
 	if (!o->quiet && !o->exiting_early) {
 		if (message)
 			return error("%s", message);
@@ -1260,7 +1275,7 @@
 	struct index_state *index = o->src_index;
 	int pfxlen = info->pathlen;
 
-	for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
+	for (pos = o->internal.cache_bottom; pos < index->cache_nr; pos++) {
 		const struct cache_entry *ce = index->cache[pos];
 		const char *ce_name, *ce_slash;
 		int cmp, ce_len;
@@ -1271,8 +1286,8 @@
 			 * we can never match it; don't check it
 			 * again.
 			 */
-			if (pos == o->cache_bottom)
-				++o->cache_bottom;
+			if (pos == o->internal.cache_bottom)
+				++o->internal.cache_bottom;
 			continue;
 		}
 		if (!ce_in_traverse_path(ce, info)) {
@@ -1450,7 +1465,7 @@
 	 */
 	if (!is_null_oid(&names[0].oid)) {
 		src[0] = create_ce_entry(info, &names[0], 0,
-					&o->result, 1,
+					&o->internal.result, 1,
 					dirmask & (1ul << 0));
 		src[0]->ce_flags |= (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
 	}
@@ -1487,7 +1502,7 @@
 	while (!p->mode)
 		p++;
 
-	if (o->debug_unpack)
+	if (o->internal.debug_unpack)
 		debug_unpack_callback(n, mask, dirmask, names, info);
 
 	/* Are we supposed to look at the index too? */
@@ -1560,7 +1575,7 @@
 				 * in 'mark_ce_used()'
 				 */
 				if (!src[0] || !S_ISSPARSEDIR(src[0]->ce_mode))
-					o->cache_bottom += matches;
+					o->internal.cache_bottom += matches;
 				return mask;
 			}
 		}
@@ -1809,7 +1824,7 @@
 	if (get_sparse_checkout_patterns(pl) < 0)
 		o->skip_sparse_checkout = 1;
 	else
-		o->pl = pl;
+		o->internal.pl = pl;
 }
 
 static void update_sparsity_for_prefix(const char *prefix,
@@ -1871,8 +1886,12 @@
 
 	if (len > MAX_UNPACK_TREES)
 		die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
-	if (o->dir)
-		BUG("o->dir is for internal use only");
+	if (o->internal.dir)
+		BUG("o->internal.dir is for internal use only");
+	if (o->internal.pl)
+		BUG("o->internal.pl is for internal use only");
+	if (o->df_conflict_entry)
+		BUG("o->df_conflict_entry is an output only field");
 
 	trace_performance_enter();
 	trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
@@ -1889,9 +1908,9 @@
 		BUG("UNPACK_RESET_OVERWRITE_UNTRACKED incompatible with preserved ignored files");
 
 	if (!o->preserve_ignored) {
-		o->dir = &dir;
-		o->dir->flags |= DIR_SHOW_IGNORED;
-		setup_standard_excludes(o->dir);
+		o->internal.dir = &dir;
+		o->internal.dir->flags |= DIR_SHOW_IGNORED;
+		setup_standard_excludes(o->internal.dir);
 	}
 
 	if (o->prefix)
@@ -1899,49 +1918,52 @@
 
 	if (!core_apply_sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
-	if (!o->skip_sparse_checkout && !o->pl) {
+	if (!o->skip_sparse_checkout) {
 		memset(&pl, 0, sizeof(pl));
 		free_pattern_list = 1;
 		populate_from_existing_patterns(o, &pl);
 	}
 
-	index_state_init(&o->result, o->src_index->repo);
-	o->result.initialized = 1;
-	o->result.timestamp.sec = o->src_index->timestamp.sec;
-	o->result.timestamp.nsec = o->src_index->timestamp.nsec;
-	o->result.version = o->src_index->version;
+	index_state_init(&o->internal.result, o->src_index->repo);
+	o->internal.result.initialized = 1;
+	o->internal.result.timestamp.sec = o->src_index->timestamp.sec;
+	o->internal.result.timestamp.nsec = o->src_index->timestamp.nsec;
+	o->internal.result.version = o->src_index->version;
 	if (!o->src_index->split_index) {
-		o->result.split_index = NULL;
+		o->internal.result.split_index = NULL;
 	} else if (o->src_index == o->dst_index) {
 		/*
 		 * o->dst_index (and thus o->src_index) will be discarded
-		 * and overwritten with o->result at the end of this function,
-		 * so just use src_index's split_index to avoid having to
-		 * create a new one.
+		 * and overwritten with o->internal.result at the end of
+		 * this function, so just use src_index's split_index to
+		 * avoid having to create a new one.
 		 */
-		o->result.split_index = o->src_index->split_index;
-		o->result.split_index->refcount++;
+		o->internal.result.split_index = o->src_index->split_index;
+		if (o->src_index->cache_changed & SPLIT_INDEX_ORDERED)
+			o->internal.result.cache_changed |= SPLIT_INDEX_ORDERED;
+		o->internal.result.split_index->refcount++;
 	} else {
-		o->result.split_index = init_split_index(&o->result);
+		o->internal.result.split_index =
+			init_split_index(&o->internal.result);
 	}
-	oidcpy(&o->result.oid, &o->src_index->oid);
-	o->merge_size = len;
+	oidcpy(&o->internal.result.oid, &o->src_index->oid);
+	o->internal.merge_size = len;
 	mark_all_ce_unused(o->src_index);
 
-	o->result.fsmonitor_last_update =
+	o->internal.result.fsmonitor_last_update =
 		xstrdup_or_null(o->src_index->fsmonitor_last_update);
-	o->result.fsmonitor_has_run_once = o->src_index->fsmonitor_has_run_once;
+	o->internal.result.fsmonitor_has_run_once = o->src_index->fsmonitor_has_run_once;
 
 	if (!o->src_index->initialized &&
 	    !repo->settings.command_requires_full_index &&
-	    is_sparse_index_allowed(&o->result, 0))
-		o->result.sparse_index = 1;
+	    is_sparse_index_allowed(&o->internal.result, 0))
+		o->internal.result.sparse_index = 1;
 
 	/*
 	 * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
 	 */
 	if (!o->skip_sparse_checkout)
-		mark_new_skip_worktree(o->pl, o->src_index, 0,
+		mark_new_skip_worktree(o->internal.pl, o->src_index, 0,
 				       CE_NEW_SKIP_WORKTREE, o->verbose_update);
 
 	if (!dfc)
@@ -1955,7 +1977,7 @@
 		setup_traverse_info(&info, prefix);
 		info.fn = unpack_callback;
 		info.data = o;
-		info.show_all_errors = o->show_all_errors;
+		info.show_all_errors = o->internal.show_all_errors;
 		info.pathspec = o->pathspec;
 
 		if (o->prefix) {
@@ -1996,7 +2018,7 @@
 	}
 	mark_all_ce_unused(o->src_index);
 
-	if (o->trivial_merges_only && o->nontrivial_merge) {
+	if (o->trivial_merges_only && o->internal.nontrivial_merge) {
 		ret = unpack_failed(o, "Merge requires file-level merging");
 		goto done;
 	}
@@ -2007,13 +2029,13 @@
 		 * If they will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
 		 * so apply_sparse_checkout() won't attempt to remove it from worktree
 		 */
-		mark_new_skip_worktree(o->pl, &o->result,
+		mark_new_skip_worktree(o->internal.pl, &o->internal.result,
 				       CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE,
 				       o->verbose_update);
 
 		ret = 0;
-		for (i = 0; i < o->result.cache_nr; i++) {
-			struct cache_entry *ce = o->result.cache[i];
+		for (i = 0; i < o->internal.result.cache_nr; i++) {
+			struct cache_entry *ce = o->internal.result.cache[i];
 
 			/*
 			 * Entries marked with CE_ADDED in merged_entry() do not have
@@ -2027,7 +2049,7 @@
 			    verify_absent(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o))
 				ret = 1;
 
-			if (apply_sparse_checkout(&o->result, ce, o))
+			if (apply_sparse_checkout(&o->internal.result, ce, o))
 				ret = 1;
 		}
 		if (ret == 1) {
@@ -2035,46 +2057,47 @@
 			 * Inability to sparsify or de-sparsify individual
 			 * paths is not an error, but just a warning.
 			 */
-			if (o->show_all_errors)
+			if (o->internal.show_all_errors)
 				display_warning_msgs(o);
 			ret = 0;
 		}
 	}
 
-	ret = check_updates(o, &o->result) ? (-2) : 0;
+	ret = check_updates(o, &o->internal.result) ? (-2) : 0;
 	if (o->dst_index) {
-		move_index_extensions(&o->result, o->src_index);
+		move_index_extensions(&o->internal.result, o->src_index);
 		if (!ret) {
 			if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
-				cache_tree_verify(the_repository, &o->result);
+				cache_tree_verify(the_repository,
+						  &o->internal.result);
 			if (!o->skip_cache_tree_update &&
-			    !cache_tree_fully_valid(o->result.cache_tree))
-				cache_tree_update(&o->result,
+			    !cache_tree_fully_valid(o->internal.result.cache_tree))
+				cache_tree_update(&o->internal.result,
 						  WRITE_TREE_SILENT |
 						  WRITE_TREE_REPAIR);
 		}
 
-		o->result.updated_workdir = 1;
+		o->internal.result.updated_workdir = 1;
 		discard_index(o->dst_index);
-		*o->dst_index = o->result;
+		*o->dst_index = o->internal.result;
 	} else {
-		discard_index(&o->result);
+		discard_index(&o->internal.result);
 	}
 	o->src_index = NULL;
 
 done:
 	if (free_pattern_list)
 		clear_pattern_list(&pl);
-	if (o->dir) {
-		dir_clear(o->dir);
-		o->dir = NULL;
+	if (o->internal.dir) {
+		dir_clear(o->internal.dir);
+		o->internal.dir = NULL;
 	}
 	trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
 	trace_performance_leave("unpack_trees");
 	return ret;
 
 return_failed:
-	if (o->show_all_errors)
+	if (o->internal.show_all_errors)
 		display_error_msgs(o);
 	mark_all_ce_unused(o->src_index);
 	ret = unpack_failed(o, NULL);
@@ -2089,16 +2112,17 @@
  *
  * CE_NEW_SKIP_WORKTREE is used internally.
  */
-enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
+enum update_sparsity_result update_sparsity(struct unpack_trees_options *o,
+					    struct pattern_list *pl)
 {
 	enum update_sparsity_result ret = UPDATE_SPARSITY_SUCCESS;
-	struct pattern_list pl;
 	int i;
 	unsigned old_show_all_errors;
 	int free_pattern_list = 0;
 
-	old_show_all_errors = o->show_all_errors;
-	o->show_all_errors = 1;
+	old_show_all_errors = o->internal.show_all_errors;
+	o->internal.show_all_errors = 1;
+	index_state_init(&o->internal.result, o->src_index->repo);
 
 	/* Sanity checks */
 	if (!o->update || o->index_only || o->skip_sparse_checkout)
@@ -2109,20 +2133,19 @@
 	trace_performance_enter();
 
 	/* If we weren't given patterns, use the recorded ones */
-	if (!o->pl) {
-		memset(&pl, 0, sizeof(pl));
+	if (!pl) {
 		free_pattern_list = 1;
-		populate_from_existing_patterns(o, &pl);
-		if (o->skip_sparse_checkout)
-			goto skip_sparse_checkout;
+		pl = xcalloc(1, sizeof(*pl));
+		populate_from_existing_patterns(o, pl);
 	}
+	o->internal.pl = pl;
 
 	/* Expand sparse directories as needed */
-	expand_index(o->src_index, o->pl);
+	expand_index(o->src_index, o->internal.pl);
 
 	/* Set NEW_SKIP_WORKTREE on existing entries. */
 	mark_all_ce_unused(o->src_index);
-	mark_new_skip_worktree(o->pl, o->src_index, 0,
+	mark_new_skip_worktree(o->internal.pl, o->src_index, 0,
 			       CE_NEW_SKIP_WORKTREE, o->verbose_update);
 
 	/* Then loop over entries and update/remove as needed */
@@ -2142,14 +2165,16 @@
 			ret = UPDATE_SPARSITY_WARNINGS;
 	}
 
-skip_sparse_checkout:
 	if (check_updates(o, o->src_index))
 		ret = UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES;
 
 	display_warning_msgs(o);
-	o->show_all_errors = old_show_all_errors;
-	if (free_pattern_list)
-		clear_pattern_list(&pl);
+	o->internal.show_all_errors = old_show_all_errors;
+	if (free_pattern_list) {
+		clear_pattern_list(pl);
+		free(pl);
+		o->internal.pl = NULL;
+	}
 	trace_performance_leave("update_sparsity");
 	return ret;
 }
@@ -2244,15 +2269,15 @@
 }
 
 /*
- * TODO: We should actually invalidate o->result, not src_index [1].
+ * TODO: We should actually invalidate o->internal.result, not src_index [1].
  * But since cache tree and untracked cache both are not copied to
- * o->result until unpacking is complete, we invalidate them on
+ * o->internal.result until unpacking is complete, we invalidate them on
  * src_index instead with the assumption that they will be copied to
  * dst_index at the end.
  *
  * [1] src_index->cache_tree is also used in unpack_callback() so if
- * we invalidate o->result, we need to update it to use
- * o->result.cache_tree as well.
+ * we invalidate o->internal.result, we need to update it to use
+ * o->internal.result.cache_tree as well.
  */
 static void invalidate_ce_path(const struct cache_entry *ce,
 			       struct unpack_trees_options *o)
@@ -2336,8 +2361,8 @@
 	pathbuf = xstrfmt("%.*s/", namelen, ce->name);
 
 	memset(&d, 0, sizeof(d));
-	if (o->dir)
-		d.exclude_per_dir = o->dir->exclude_per_dir;
+	if (o->internal.dir)
+		setup_standard_excludes(&d);
 	i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
 	dir_clear(&d);
 	free(pathbuf);
@@ -2391,8 +2416,8 @@
 	if (ignore_case && icase_exists(o, name, len, st))
 		return 0;
 
-	if (o->dir &&
-	    is_excluded(o->dir, o->src_index, name, &dtype))
+	if (o->internal.dir &&
+	    is_excluded(o->internal.dir, o->src_index, name, &dtype))
 		/*
 		 * ce->name is explicitly excluded, so it is Ok to
 		 * overwrite it.
@@ -2420,7 +2445,7 @@
 	 * delete this path, which is in a subdirectory that
 	 * is being replaced with a blob.
 	 */
-	result = index_file_exists(&o->result, name, len, 0);
+	result = index_file_exists(&o->internal.result, name, len, 0);
 	if (result) {
 		if (result->ce_flags & CE_REMOVE)
 			return 0;
@@ -2521,7 +2546,7 @@
 			struct unpack_trees_options *o)
 {
 	int update = CE_UPDATE;
-	struct cache_entry *merge = dup_cache_entry(ce, &o->result);
+	struct cache_entry *merge = dup_cache_entry(ce, &o->internal.result);
 
 	if (!old) {
 		/*
@@ -2616,7 +2641,7 @@
 	setup_traverse_info(&info, src[0]->name);
 	info.fn = unpack_sparse_callback;
 	info.data = o;
-	info.show_all_errors = o->show_all_errors;
+	info.show_all_errors = o->internal.show_all_errors;
 	info.pathspec = o->pathspec;
 
 	/* Get the tree descriptors of the sparse directory in each of the merging trees */
@@ -2834,7 +2859,7 @@
 			return -1;
 	}
 
-	o->nontrivial_merge = 1;
+	o->internal.nontrivial_merge = 1;
 
 	/* #2, #3, #4, #6, #7, #9, #10, #11. */
 	count = 0;
@@ -2875,9 +2900,9 @@
 	const struct cache_entry *oldtree = src[1];
 	const struct cache_entry *newtree = src[2];
 
-	if (o->merge_size != 2)
+	if (o->internal.merge_size != 2)
 		return error("Cannot do a twoway merge of %d trees",
-			     o->merge_size);
+			     o->internal.merge_size);
 
 	if (oldtree == o->df_conflict_entry)
 		oldtree = NULL;
@@ -2957,9 +2982,9 @@
 	const struct cache_entry *old = src[0];
 	const struct cache_entry *a = src[1];
 
-	if (o->merge_size != 1)
+	if (o->internal.merge_size != 1)
 		return error("Cannot do a bind merge of %d trees",
-			     o->merge_size);
+			     o->internal.merge_size);
 	if (a && old)
 		return o->quiet ? -1 :
 			error(ERRORMSG(o, ERROR_BIND_OVERLAP),
@@ -2983,9 +3008,9 @@
 	const struct cache_entry *old = src[0];
 	const struct cache_entry *a = src[1];
 
-	if (o->merge_size != 1)
+	if (o->internal.merge_size != 1)
 		return error("Cannot do a oneway merge of %d trees",
-			     o->merge_size);
+			     o->internal.merge_size);
 
 	if (!a || a == o->df_conflict_entry)
 		return deleted_entry(old, old, o);
@@ -3020,8 +3045,8 @@
 	const struct cache_entry *worktree = src[1];
 	const struct cache_entry *untracked = src[2];
 
-	if (o->merge_size != 2)
-		BUG("invalid merge_size: %d", o->merge_size);
+	if (o->internal.merge_size != 2)
+		BUG("invalid merge_size: %d", o->internal.merge_size);
 
 	if (worktree && untracked)
 		return error(_("worktree and untracked commit have duplicate entries: %s"),
diff --git a/unpack-trees.h b/unpack-trees.h
index 3a7b3e5..5867e26 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -1,12 +1,13 @@
 #ifndef UNPACK_TREES_H
 #define UNPACK_TREES_H
 
-#include "cache.h"
+#include "convert.h"
+#include "read-cache-ll.h"
 #include "strvec.h"
 #include "string-list.h"
 #include "tree-walk.h"
 
-#define MAX_UNPACK_TREES MAX_TRAVERSE_TREES
+#define MAX_UNPACK_TREES 8
 
 struct cache_entry;
 struct unpack_trees_options;
@@ -59,47 +60,54 @@
 		     preserve_ignored,
 		     clone,
 		     index_only,
-		     nontrivial_merge,
 		     trivial_merges_only,
 		     verbose_update,
 		     aggressive,
 		     skip_unmerged,
 		     initial_checkout,
 		     diff_index_cached,
-		     debug_unpack,
 		     skip_sparse_checkout,
 		     quiet,
 		     exiting_early,
-		     show_all_errors,
 		     dry_run,
 		     skip_cache_tree_update;
 	enum unpack_trees_reset_type reset;
 	const char *prefix;
 	const char *super_prefix;
-	int cache_bottom;
 	struct pathspec *pathspec;
 	merge_fn_t fn;
-	const char *msgs[NB_UNPACK_TREES_WARNING_TYPES];
-	struct strvec msgs_to_free;
-	/*
-	 * Store error messages in an array, each case
-	 * corresponding to a error message type
-	 */
-	struct string_list unpack_rejects[NB_UNPACK_TREES_WARNING_TYPES];
 
 	int head_idx;
-	int merge_size;
 
-	struct cache_entry *df_conflict_entry;
+	struct cache_entry *df_conflict_entry; /* output only */
 	void *unpack_data;
 
 	struct index_state *dst_index;
 	struct index_state *src_index;
-	struct index_state result;
 
-	struct pattern_list *pl; /* for internal use */
-	struct dir_struct *dir; /* for internal use only */
 	struct checkout_metadata meta;
+
+	struct unpack_trees_options_internal {
+		unsigned int nontrivial_merge,
+			     show_all_errors,
+			     debug_unpack; /* used by read-tree debugging */
+
+		int merge_size; /* used by read-tree debugging */
+		int cache_bottom;
+		const char *msgs[NB_UNPACK_TREES_WARNING_TYPES];
+		struct strvec msgs_to_free;
+
+		/*
+		 * Store error messages in an array, each case
+		 * corresponding to a error message type
+		 */
+		struct string_list unpack_rejects[NB_UNPACK_TREES_WARNING_TYPES];
+
+		struct index_state result;
+
+		struct pattern_list *pl;
+		struct dir_struct *dir;
+	} internal;
 };
 
 int unpack_trees(unsigned n, struct tree_desc *t,
@@ -112,7 +120,8 @@
 	UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES = -2
 };
 
-enum update_sparsity_result update_sparsity(struct unpack_trees_options *options);
+enum update_sparsity_result update_sparsity(struct unpack_trees_options *options,
+					    struct pattern_list *pl);
 
 int verify_uptodate(const struct cache_entry *ce,
 		    struct unpack_trees_options *o);
diff --git a/upload-pack.c b/upload-pack.c
index 551f22f..902144b 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,17 +1,18 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "sideband.h"
 #include "repository.h"
-#include "object-store.h"
-#include "tag.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
 #include "object.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "run-command.h"
 #include "connect.h"
@@ -19,14 +20,15 @@
 #include "version.h"
 #include "string-list.h"
 #include "strvec.h"
-#include "prio-queue.h"
+#include "trace2.h"
 #include "protocol.h"
-#include "quote.h"
 #include "upload-pack.h"
-#include "serve.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "write-or-die.h"
+#include "json-writer.h"
+#include "strmap.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE	(1u << 11)
@@ -60,12 +62,11 @@
 	struct string_list symref;				/* v0 only */
 	struct object_array want_obj;
 	struct object_array have_obj;
-	struct oid_array haves;					/* v2 only */
-	struct string_list wanted_refs;				/* v2 only */
-	struct string_list hidden_refs;
+	struct strmap wanted_refs;				/* v2 only */
+	struct strvec hidden_refs;
 
 	struct object_array shallows;
-	struct string_list deepen_not;
+	struct oidset deepen_not;
 	struct object_array extra_edge_obj;
 	int depth;
 	timestamp_t deepen_since;
@@ -112,19 +113,21 @@
 	unsigned done : 1;					/* v2 only */
 	unsigned allow_ref_in_want : 1;				/* v2 only */
 	unsigned allow_sideband_all : 1;			/* v2 only */
+	unsigned seen_haves : 1;				/* v2 only */
+	unsigned allow_packfile_uris : 1;			/* v2 only */
 	unsigned advertise_sid : 1;
+	unsigned sent_capabilities : 1;
 };
 
 static void upload_pack_data_init(struct upload_pack_data *data)
 {
 	struct string_list symref = STRING_LIST_INIT_DUP;
-	struct string_list wanted_refs = STRING_LIST_INIT_DUP;
-	struct string_list hidden_refs = STRING_LIST_INIT_DUP;
+	struct strmap wanted_refs = STRMAP_INIT;
+	struct strvec hidden_refs = STRVEC_INIT;
 	struct object_array want_obj = OBJECT_ARRAY_INIT;
 	struct object_array have_obj = OBJECT_ARRAY_INIT;
-	struct oid_array haves = OID_ARRAY_INIT;
 	struct object_array shallows = OBJECT_ARRAY_INIT;
-	struct string_list deepen_not = STRING_LIST_INIT_DUP;
+	struct oidset deepen_not = OID_ARRAY_INIT;
 	struct string_list uri_protocols = STRING_LIST_INIT_DUP;
 	struct object_array extra_edge_obj = OBJECT_ARRAY_INIT;
 	struct string_list allowed_filters = STRING_LIST_INIT_DUP;
@@ -135,7 +138,6 @@
 	data->hidden_refs = hidden_refs;
 	data->want_obj = want_obj;
 	data->have_obj = have_obj;
-	data->haves = haves;
 	data->shallows = shallows;
 	data->deepen_not = deepen_not;
 	data->uri_protocols = uri_protocols;
@@ -153,13 +155,12 @@
 static void upload_pack_data_clear(struct upload_pack_data *data)
 {
 	string_list_clear(&data->symref, 1);
-	string_list_clear(&data->wanted_refs, 1);
-	string_list_clear(&data->hidden_refs, 0);
+	strmap_clear(&data->wanted_refs, 1);
+	strvec_clear(&data->hidden_refs);
 	object_array_clear(&data->want_obj);
 	object_array_clear(&data->have_obj);
-	oid_array_clear(&data->haves);
 	object_array_clear(&data->shallows);
-	string_list_clear(&data->deepen_not, 0);
+	oidset_clear(&data->deepen_not);
 	object_array_clear(&data->extra_edge_obj);
 	list_objects_filter_release(&data->filter_options);
 	string_list_clear(&data->allowed_filters, 0);
@@ -461,7 +462,7 @@
 
  fail:
 	free(output_state);
-	send_client_data(3, abort_msg, sizeof(abort_msg),
+	send_client_data(3, abort_msg, strlen(abort_msg),
 			 pack_data->use_sideband);
 	die("git upload-pack: %s", abort_msg);
 }
@@ -469,7 +470,9 @@
 static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid)
 {
 	int we_knew_they_have = 0;
-	struct object *o = parse_object(the_repository, oid);
+	struct object *o = parse_object_with_flags(the_repository, oid,
+						   PARSE_OBJECT_SKIP_HASH_CHECK |
+						   PARSE_OBJECT_DISCARD_TREE);
 
 	if (!o)
 		die("oops (%s)", oid_to_hex(oid));
@@ -499,8 +502,8 @@
 {
 	if (get_oid_hex(hex, oid))
 		die("git upload-pack: expected SHA1 object, got '%s'", hex);
-	if (!has_object_file_with_flags(oid,
-					OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
+	if (!repo_has_object_file_with_flags(the_repository, oid,
+					     OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
 		return -1;
 	return do_got_oid(data, oid);
 }
@@ -526,8 +529,6 @@
 	int got_other = 0;
 	int sent_ready = 0;
 
-	save_commit_buffer = 0;
-
 	for (;;) {
 		const char *arg;
 
@@ -594,11 +595,36 @@
 	}
 }
 
+static int allow_hidden_refs(enum allow_uor allow_uor)
+{
+	if ((allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1)
+		return 1;
+	return !(allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
+}
+
+static void for_each_namespaced_ref_1(each_ref_fn fn,
+				      struct upload_pack_data *data)
+{
+	const char **excludes = NULL;
+	/*
+	 * If `data->allow_uor` allows fetching hidden refs, we need to
+	 * mark all references (including hidden ones), to check in
+	 * `is_our_ref()` below.
+	 *
+	 * Otherwise, we only care about whether each reference's object
+	 * has the OUR_REF bit set or not, so do not need to visit
+	 * hidden references.
+	 */
+	if (allow_hidden_refs(data->allow_uor))
+		excludes = hidden_refs_to_excludes(&data->hidden_refs);
+
+	for_each_namespaced_ref(excludes, fn, data);
+}
+
+
 static int is_our_ref(struct object *o, enum allow_uor allow_uor)
 {
-	int allow_hidden_ref = (allow_uor &
-				(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
-	return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
+	return o->flags & ((allow_hidden_refs(allow_uor) ? 0 : HIDDEN_REF) | OUR_REF);
 }
 
 /*
@@ -769,11 +795,12 @@
 	for (i = 0; i < data->want_obj.nr; i++) {
 		struct object *o = data->want_obj.objects[i].item;
 		if (!is_our_ref(o, data->allow_uor)) {
+			error("git upload-pack: not our ref %s",
+			      oid_to_hex(&o->oid));
 			packet_writer_error(&data->writer,
 					    "upload-pack: not our ref %s",
 					    oid_to_hex(&o->oid));
-			die("git upload-pack: not our ref %s",
-			    oid_to_hex(&o->oid));
+			exit(128);
 		}
 	}
 }
@@ -847,7 +874,7 @@
 		 * marked with OUR_REF.
 		 */
 		head_ref_namespaced(check_ref, data);
-		for_each_namespaced_ref(check_ref, data);
+		for_each_namespaced_ref_1(check_ref, data);
 
 		get_reachable_list(data, &reachable_shallows);
 		result = get_shallow_commits(&reachable_shallows,
@@ -898,12 +925,13 @@
 		strvec_push(&av, "rev-list");
 		if (data->deepen_since)
 			strvec_pushf(&av, "--max-age=%"PRItime, data->deepen_since);
-		if (data->deepen_not.nr) {
+		if (oidset_size(&data->deepen_not)) {
+			const struct object_id *oid;
+			struct oidset_iter iter;
 			strvec_push(&av, "--not");
-			for (i = 0; i < data->deepen_not.nr; i++) {
-				struct string_list_item *s = data->deepen_not.items + i;
-				strvec_push(&av, s->string);
-			}
+			oidset_iter_init(&data->deepen_not, &iter);
+			while ((oid = oidset_iter_next(&iter)))
+				strvec_push(&av, oid_to_hex(oid));
 			strvec_push(&av, "--not");
 		}
 		for (i = 0; i < data->want_obj.nr; i++) {
@@ -979,7 +1007,7 @@
 	return 0;
 }
 
-static int process_deepen_not(const char *line, struct string_list *deepen_not, int *deepen_rev_list)
+static int process_deepen_not(const char *line, struct oidset *deepen_not, int *deepen_rev_list)
 {
 	const char *arg;
 	if (skip_prefix(line, "deepen-not ", &arg)) {
@@ -987,7 +1015,7 @@
 		struct object_id oid;
 		if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
 			die("git upload-pack: ambiguous deepen-not: %s", line);
-		string_list_append(deepen_not, ref);
+		oidset_insert(deepen_not, &oid);
 		free(ref);
 		*deepen_rev_list = 1;
 		return 1;
@@ -1063,7 +1091,7 @@
 		const char *features;
 		struct object_id oid_buf;
 		const char *arg;
-		int feature_len;
+		size_t feature_len;
 
 		reset_timeout(data->timeout);
 		if (packet_reader_read(reader) != PACKET_READ_NORMAL)
@@ -1123,7 +1151,9 @@
 			free(client_sid);
 		}
 
-		o = parse_object(the_repository, &oid_buf);
+		o = parse_object_with_flags(the_repository, &oid_buf,
+					    PARSE_OBJECT_SKIP_HASH_CHECK |
+					    PARSE_OBJECT_DISCARD_TREE);
 		if (!o) {
 			packet_writer_error(&data->writer,
 					    "upload-pack: not our ref %s",
@@ -1162,7 +1192,7 @@
 
 /* return non-zero if the ref is hidden, otherwise 0 */
 static int mark_our_ref(const char *refname, const char *refname_full,
-			const struct object_id *oid, const struct string_list *hidden_refs)
+			const struct object_id *oid, const struct strvec *hidden_refs)
 {
 	struct object *o = lookup_unknown_object(the_repository, oid);
 
@@ -1199,18 +1229,17 @@
 		strbuf_addf(buf, " session-id=%s", trace2_session_id());
 }
 
-static int send_ref(const char *refname, const struct object_id *oid,
-		    int flag UNUSED, void *cb_data)
+static void write_v0_ref(struct upload_pack_data *data,
+			const char *refname, const char *refname_nons,
+			const struct object_id *oid)
 {
 	static const char *capabilities = "multi_ack thin-pack side-band"
 		" side-band-64k ofs-delta shallow deepen-since deepen-not"
 		" deepen-relative no-progress include-tag multi_ack_detailed";
-	const char *refname_nons = strip_namespace(refname);
 	struct object_id peeled;
-	struct upload_pack_data *data = cb_data;
 
 	if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
-		return 0;
+		return;
 
 	if (capabilities) {
 		struct strbuf symref_info = STRBUF_INIT;
@@ -1233,12 +1262,20 @@
 			     git_user_agent_sanitized());
 		strbuf_release(&symref_info);
 		strbuf_release(&session_id);
+		data->sent_capabilities = 1;
 	} else {
 		packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
 	}
 	capabilities = NULL;
 	if (!peel_iterated_oid(oid, &peeled))
 		packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
+	return;
+}
+
+static int send_ref(const char *refname, const struct object_id *oid,
+		    int flag UNUSED, void *cb_data)
+{
+	write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
 	return 0;
 }
 
@@ -1260,7 +1297,8 @@
 }
 
 static int parse_object_filter_config(const char *var, const char *value,
-				       struct upload_pack_data *data)
+				      const struct key_value_info *kvi,
+				      struct upload_pack_data *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 	const char *sub, *key;
@@ -1287,14 +1325,17 @@
 		}
 		string_list_insert(&data->allowed_filters, buf.buf)->util =
 			(void *)(intptr_t)1;
-		data->tree_filter_max_depth = git_config_ulong(var, value);
+		data->tree_filter_max_depth = git_config_ulong(var, value,
+							       kvi);
 	}
 
 	strbuf_release(&buf);
 	return 0;
 }
 
-static int upload_pack_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_config(const char *var, const char *value,
+			      const struct config_context *ctx,
+			      void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
 
@@ -1314,7 +1355,7 @@
 		else
 			data->allow_uor &= ~ALLOW_ANY_SHA1;
 	} else if (!strcmp("uploadpack.keepalive", var)) {
-		data->keepalive = git_config_int(var, value);
+		data->keepalive = git_config_int(var, value, ctx->kvi);
 		if (!data->keepalive)
 			data->keepalive = -1;
 	} else if (!strcmp("uploadpack.allowfilter", var)) {
@@ -1323,19 +1364,24 @@
 		data->allow_ref_in_want = git_config_bool(var, value);
 	} else if (!strcmp("uploadpack.allowsidebandall", var)) {
 		data->allow_sideband_all = git_config_bool(var, value);
+	} else if (!strcmp("uploadpack.blobpackfileuri", var)) {
+		if (value)
+			data->allow_packfile_uris = 1;
 	} else if (!strcmp("core.precomposeunicode", var)) {
 		precomposed_unicode = git_config_bool(var, value);
 	} else if (!strcmp("transfer.advertisesid", var)) {
 		data->advertise_sid = git_config_bool(var, value);
 	}
 
-	if (parse_object_filter_config(var, value, data) < 0)
+	if (parse_object_filter_config(var, value, ctx->kvi, data) < 0)
 		return -1;
 
 	return parse_hide_refs_config(var, value, "uploadpack", &data->hidden_refs);
 }
 
-static int upload_pack_protected_config(const char *var, const char *value, void *cb_data)
+static int upload_pack_protected_config(const char *var, const char *value,
+					const struct config_context *ctx UNUSED,
+					void *cb_data)
 {
 	struct upload_pack_data *data = cb_data;
 
@@ -1344,10 +1390,13 @@
 	return 0;
 }
 
-static void get_upload_pack_config(struct upload_pack_data *data)
+static void get_upload_pack_config(struct repository *r,
+				   struct upload_pack_data *data)
 {
-	git_config(upload_pack_config, data);
+	repo_config(r, upload_pack_config, data);
 	git_protected_config(upload_pack_protected_config, data);
+
+	data->allow_sideband_all |= git_env_bool("GIT_TEST_SIDEBAND_ALL", 0);
 }
 
 void upload_pack(const int advertise_refs, const int stateless_rpc,
@@ -1357,7 +1406,7 @@
 	struct upload_pack_data data;
 
 	upload_pack_data_init(&data);
-	get_upload_pack_config(&data);
+	get_upload_pack_config(the_repository, &data);
 
 	data.stateless_rpc = stateless_rpc;
 	data.timeout = timeout;
@@ -1371,7 +1420,11 @@
 		if (advertise_refs)
 			data.no_done = 1;
 		head_ref_namespaced(send_ref, &data);
-		for_each_namespaced_ref(send_ref, &data);
+		for_each_namespaced_ref_1(send_ref, &data);
+		if (!data.sent_capabilities) {
+			const char *refname = "capabilities^{}";
+			write_v0_ref(&data, refname, refname, null_oid());
+		}
 		/*
 		 * fflush stdout before calling advertise_shallow_grafts because send_ref
 		 * uses stdio.
@@ -1381,7 +1434,7 @@
 		packet_flush(1);
 	} else {
 		head_ref_namespaced(check_ref, &data);
-		for_each_namespaced_ref(check_ref, &data);
+		for_each_namespaced_ref_1(check_ref, &data);
 	}
 
 	if (!advertise_refs) {
@@ -1423,7 +1476,8 @@
 			    "expected to get oid, not '%s'", line);
 
 		o = parse_object_with_flags(the_repository, &oid,
-					    PARSE_OBJECT_SKIP_HASH_CHECK);
+					    PARSE_OBJECT_SKIP_HASH_CHECK |
+					    PARSE_OBJECT_DISCARD_TREE);
 
 		if (!o) {
 			packet_writer_error(writer,
@@ -1445,14 +1499,13 @@
 }
 
 static int parse_want_ref(struct packet_writer *writer, const char *line,
-			  struct string_list *wanted_refs,
-			  struct string_list *hidden_refs,
+			  struct strmap *wanted_refs,
+			  struct strvec *hidden_refs,
 			  struct object_array *want_obj)
 {
 	const char *refname_nons;
 	if (skip_prefix(line, "want-ref ", &refname_nons)) {
 		struct object_id oid;
-		struct string_list_item *item;
 		struct object *o = NULL;
 		struct strbuf refname = STRBUF_INIT;
 
@@ -1464,8 +1517,11 @@
 		}
 		strbuf_release(&refname);
 
-		item = string_list_append(wanted_refs, refname_nons);
-		item->util = oiddup(&oid);
+		if (strmap_put(wanted_refs, refname_nons, oiddup(&oid))) {
+			packet_writer_error(writer, "duplicate want-ref %s",
+					    refname_nons);
+			die("duplicate want-ref %s", refname_nons);
+		}
 
 		if (!starts_with(refname_nons, "refs/tags/")) {
 			struct commit *commit = lookup_commit_in_graph(the_repository, &oid);
@@ -1487,21 +1543,44 @@
 	return 0;
 }
 
-static int parse_have(const char *line, struct oid_array *haves)
+static int parse_have(const char *line, struct upload_pack_data *data)
 {
 	const char *arg;
 	if (skip_prefix(line, "have ", &arg)) {
 		struct object_id oid;
 
-		if (get_oid_hex(arg, &oid))
-			die("git upload-pack: expected SHA1 object, got '%s'", arg);
-		oid_array_append(haves, &oid);
+		got_oid(data, arg, &oid);
+		data->seen_haves = 1;
 		return 1;
 	}
 
 	return 0;
 }
 
+static void trace2_fetch_info(struct upload_pack_data *data)
+{
+	struct json_writer jw = JSON_WRITER_INIT;
+
+	jw_object_begin(&jw, 0);
+	jw_object_intmax(&jw, "haves", data->have_obj.nr);
+	jw_object_intmax(&jw, "wants", data->want_obj.nr);
+	jw_object_intmax(&jw, "want-refs", strmap_get_size(&data->wanted_refs));
+	jw_object_intmax(&jw, "depth", data->depth);
+	jw_object_intmax(&jw, "shallows", data->shallows.nr);
+	jw_object_bool(&jw, "deepen-since", data->deepen_since);
+	jw_object_intmax(&jw, "deepen-not", oidset_size(&data->deepen_not));
+	jw_object_bool(&jw, "deepen-relative", data->deepen_relative);
+	if (data->filter_options.choice)
+		jw_object_string(&jw, "filter", list_object_filter_config_name(data->filter_options.choice));
+	else
+		jw_object_null(&jw, "filter");
+	jw_end(&jw);
+
+	trace2_data_json("upload-pack", the_repository, "fetch-info", &jw);
+
+	jw_release(&jw);
+}
+
 static void process_args(struct packet_reader *request,
 			 struct upload_pack_data *data)
 {
@@ -1517,7 +1596,7 @@
 				   &data->hidden_refs, &data->want_obj))
 			continue;
 		/* process have line */
-		if (parse_have(arg, &data->haves))
+		if (parse_have(arg, data))
 			continue;
 
 		/* process args like thin-pack */
@@ -1569,14 +1648,17 @@
 			continue;
 		}
 
-		if ((git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
-		     data->allow_sideband_all) &&
+		if (data->allow_sideband_all &&
 		    !strcmp(arg, "sideband-all")) {
 			data->writer.use_sideband = 1;
 			continue;
 		}
 
-		if (skip_prefix(arg, "packfile-uris ", &p)) {
+		if (data->allow_packfile_uris &&
+		    skip_prefix(arg, "packfile-uris ", &p)) {
+			if (data->uri_protocols.nr)
+				send_err_and_die(data,
+						 "multiple packfile-uris lines forbidden");
 			string_list_split(&data->uri_protocols, p, ',', -1);
 			continue;
 		}
@@ -1590,29 +1672,12 @@
 
 	if (request->status != PACKET_READ_FLUSH)
 		die(_("expected flush after fetch arguments"));
+
+	if (trace2_is_enabled())
+		trace2_fetch_info(data);
 }
 
-static int process_haves(struct upload_pack_data *data, struct oid_array *common)
-{
-	int i;
-
-	/* Process haves */
-	for (i = 0; i < data->haves.nr; i++) {
-		const struct object_id *oid = &data->haves.oid[i];
-
-		if (!has_object_file_with_flags(oid,
-						OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
-			continue;
-
-		oid_array_append(common, oid);
-
-		do_got_oid(data, oid);
-	}
-
-	return 0;
-}
-
-static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
+static int send_acks(struct upload_pack_data *data, struct object_array *acks)
 {
 	int i;
 
@@ -1624,7 +1689,7 @@
 
 	for (i = 0; i < acks->nr; i++) {
 		packet_writer_write(&data->writer, "ACK %s\n",
-				    oid_to_hex(&acks->oid[i]));
+				    oid_to_hex(&acks->objects[i].item->oid));
 	}
 
 	if (!data->wait_for_done && ok_to_give_up(data)) {
@@ -1638,13 +1703,11 @@
 
 static int process_haves_and_send_acks(struct upload_pack_data *data)
 {
-	struct oid_array common = OID_ARRAY_INIT;
 	int ret = 0;
 
-	process_haves(data, &common);
 	if (data->done) {
 		ret = 1;
-	} else if (send_acks(data, &common)) {
+	} else if (send_acks(data, &data->have_obj)) {
 		packet_writer_delim(&data->writer);
 		ret = 1;
 	} else {
@@ -1653,24 +1716,23 @@
 		ret = 0;
 	}
 
-	oid_array_clear(&data->haves);
-	oid_array_clear(&common);
 	return ret;
 }
 
 static void send_wanted_ref_info(struct upload_pack_data *data)
 {
-	const struct string_list_item *item;
+	struct hashmap_iter iter;
+	const struct strmap_entry *e;
 
-	if (!data->wanted_refs.nr)
+	if (strmap_empty(&data->wanted_refs))
 		return;
 
 	packet_writer_write(&data->writer, "wanted-refs\n");
 
-	for_each_string_list_item(item, &data->wanted_refs) {
+	strmap_for_each_entry(&data->wanted_refs, &iter, e) {
 		packet_writer_write(&data->writer, "%s %s\n",
-				    oid_to_hex(item->util),
-				    item->string);
+				    oid_to_hex(e->value),
+				    e->key);
 	}
 
 	packet_writer_delim(&data->writer);
@@ -1708,7 +1770,7 @@
 
 	upload_pack_data_init(&data);
 	data.use_sideband = LARGE_PACKET_MAX;
-	get_upload_pack_config(&data);
+	get_upload_pack_config(r, &data);
 
 	while (state != FETCH_DONE) {
 		switch (state) {
@@ -1724,7 +1786,7 @@
 				 * they didn't want anything.
 				 */
 				state = FETCH_DONE;
-			} else if (data.haves.nr) {
+			} else if (data.seen_haves) {
 				/*
 				 * Request had 'have' lines, so lets ACK them.
 				 */
@@ -1767,41 +1829,28 @@
 int upload_pack_advertise(struct repository *r,
 			  struct strbuf *value)
 {
-	if (value) {
-		int allow_filter_value;
-		int allow_ref_in_want;
-		int allow_sideband_all_value;
-		char *str = NULL;
+	struct upload_pack_data data;
 
+	upload_pack_data_init(&data);
+	get_upload_pack_config(r, &data);
+
+	if (value) {
 		strbuf_addstr(value, "shallow wait-for-done");
 
-		if (!repo_config_get_bool(the_repository,
-					 "uploadpack.allowfilter",
-					 &allow_filter_value) &&
-		    allow_filter_value)
+		if (data.allow_filter)
 			strbuf_addstr(value, " filter");
 
-		if (!repo_config_get_bool(the_repository,
-					 "uploadpack.allowrefinwant",
-					 &allow_ref_in_want) &&
-		    allow_ref_in_want)
+		if (data.allow_ref_in_want)
 			strbuf_addstr(value, " ref-in-want");
 
-		if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
-		    (!repo_config_get_bool(the_repository,
-					   "uploadpack.allowsidebandall",
-					   &allow_sideband_all_value) &&
-		     allow_sideband_all_value))
+		if (data.allow_sideband_all)
 			strbuf_addstr(value, " sideband-all");
 
-		if (!repo_config_get_string(the_repository,
-					    "uploadpack.blobpackfileuri",
-					    &str) &&
-		    str) {
+		if (data.allow_packfile_uris)
 			strbuf_addstr(value, " packfile-uris");
-			free(str);
-		}
 	}
 
+	upload_pack_data_clear(&data);
+
 	return 1;
 }
diff --git a/url.c b/url.c
index e04bd60..282b124 100644
--- a/url.c
+++ b/url.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hex-ll.h"
+#include "strbuf.h"
 #include "url.h"
 
 int is_urlschemechar(int first_flag, int ch)
diff --git a/urlmatch.c b/urlmatch.c
index 620a648..1d0254a 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -1,4 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex-ll.h"
+#include "strbuf.h"
 #include "urlmatch.h"
 
 #define URL_ALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -548,7 +551,8 @@
 	return 0;
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb)
+int urlmatch_config_entry(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb)
 {
 	struct string_list_item *item;
 	struct urlmatch_config *collect = cb;
@@ -562,7 +566,7 @@
 
 	if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
 		if (collect->cascade_fn)
-			return collect->cascade_fn(var, value, cb);
+			return collect->cascade_fn(var, value, ctx, cb);
 		return 0; /* not interested */
 	}
 	dot = strrchr(key, '.');
@@ -606,7 +610,7 @@
 	strbuf_addstr(&synthkey, collect->section);
 	strbuf_addch(&synthkey, '.');
 	strbuf_addstr(&synthkey, key);
-	retval = collect->collect_fn(synthkey.buf, value, collect->cb);
+	retval = collect->collect_fn(synthkey.buf, value, ctx, collect->cb);
 
 	strbuf_release(&synthkey);
 	return retval;
diff --git a/urlmatch.h b/urlmatch.h
index 9f40b00..5ba85ce 100644
--- a/urlmatch.h
+++ b/urlmatch.h
@@ -2,6 +2,7 @@
 #define URL_MATCH_H
 
 #include "string-list.h"
+#include "config.h"
 
 struct url_info {
 	/* normalized url on success, must be freed, otherwise NULL */
@@ -48,8 +49,8 @@
 	const char *key;
 
 	void *cb;
-	int (*collect_fn)(const char *var, const char *value, void *cb);
-	int (*cascade_fn)(const char *var, const char *value, void *cb);
+	config_fn_t collect_fn;
+	config_fn_t cascade_fn;
 	/*
 	 * Compare the two matches, the one just discovered and the existing
 	 * best match and return a negative value if the found item is to be
@@ -70,7 +71,8 @@
 	.vars = STRING_LIST_INIT_DUP, \
 }
 
-int urlmatch_config_entry(const char *var, const char *value, void *cb);
+int urlmatch_config_entry(const char *var, const char *value,
+			  const struct config_context *ctx, void *cb);
 void urlmatch_config_release(struct urlmatch_config *config);
 
 #endif /* URL_MATCH_H */
diff --git a/usage.c b/usage.c
index 5a7c6c3..09f0ed5 100644
--- a/usage.c
+++ b/usage.c
@@ -4,7 +4,8 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "git-compat-util.h"
-#include "cache.h"
+#include "gettext.h"
+#include "trace2.h"
 
 static void vreportf(const char *prefix, const char *err, va_list params)
 {
diff --git a/userdiff.c b/userdiff.c
index 58a3d59..92ef649 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -1,7 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
 #include "userdiff.h"
 #include "attr.h"
+#include "strbuf.h"
+#include "environment.h"
 
 static struct userdiff_driver *drivers;
 static int ndrivers;
@@ -15,6 +17,7 @@
 		.cflags = REG_EXTENDED, \
 	}, \
 	.word_regex = wrx "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+", \
+	.word_regex_multi_byte = wrx "|[^[:space:]]", \
 }
 #define IPATTERN(lang, rx, wrx) { \
 	.name = lang, \
@@ -24,6 +27,7 @@
 		.cflags = REG_EXTENDED | REG_ICASE, \
 	}, \
 	.word_regex = wrx "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+", \
+	.word_regex_multi_byte = wrx "|[^[:space:]]", \
 }
 
 /*
@@ -292,7 +296,7 @@
 	 /* 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\x80-\xff]+"),
+	 "\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
 { "default", NULL, NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
@@ -320,14 +324,32 @@
 {
 	struct find_by_namelen_data *cb_data = priv;
 
-	if (!strncmp(driver->name, cb_data->name, cb_data->len) &&
-	    !driver->name[cb_data->len]) {
+	if (!xstrncmpz(driver->name, cb_data->name, cb_data->len)) {
 		cb_data->driver = driver;
 		return 1; /* tell the caller to stop iterating */
 	}
 	return 0;
 }
 
+static int regexec_supports_multi_byte_chars(void)
+{
+	static const char not_space[] = "[^[:space:]]";
+	static const char utf8_multi_byte_char[] = "\xc2\xa3";
+	regex_t re;
+	regmatch_t match;
+	static int result = -1;
+
+	if (result != -1)
+		return result;
+	if (regcomp(&re, not_space, REG_EXTENDED))
+		BUG("invalid regular expression: %s", not_space);
+	result = !regexec(&re, utf8_multi_byte_char, 1, &match, 0) &&
+		match.rm_so == 0 &&
+		match.rm_eo == strlen(utf8_multi_byte_char);
+	regfree(&re);
+	return result;
+}
+
 static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t len)
 {
 	struct find_by_namelen_data udcbdata = {
@@ -403,7 +425,13 @@
 struct userdiff_driver *userdiff_find_by_name(const char *name)
 {
 	int len = strlen(name);
-	return userdiff_find_by_namelen(name, len);
+	struct userdiff_driver *driver = userdiff_find_by_namelen(name, len);
+	if (driver && driver->word_regex_multi_byte) {
+		if (regexec_supports_multi_byte_chars())
+			driver->word_regex = driver->word_regex_multi_byte;
+		driver->word_regex_multi_byte = NULL;
+	}
+	return driver;
 }
 
 struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
@@ -415,7 +443,7 @@
 		check = attr_check_initl("diff", NULL);
 	if (!path)
 		return NULL;
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, path, check);
 
 	if (ATTR_TRUE(check->items[0].value))
 		return &driver_true;
@@ -432,7 +460,8 @@
 	if (!driver->textconv)
 		return NULL;
 
-	if (driver->textconv_want_cache && !driver->textconv_cache) {
+	if (driver->textconv_want_cache && !driver->textconv_cache &&
+	    have_git_dir()) {
 		struct notes_cache *c = xmalloc(sizeof(*c));
 		struct strbuf name = STRBUF_INIT;
 
diff --git a/userdiff.h b/userdiff.h
index 24419db..d726804 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -18,6 +18,7 @@
 	int binary;
 	struct userdiff_funcname funcname;
 	const char *word_regex;
+	const char *word_regex_multi_byte;
 	const char *textconv;
 	struct notes_cache *textconv_cache;
 	int textconv_want_cache;
diff --git a/utf8.c b/utf8.c
index 6a0dd25..6bfaefa 100644
--- a/utf8.c
+++ b/utf8.c
@@ -2,7 +2,7 @@
 #include "strbuf.h"
 #include "utf8.h"
 
-/* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
+/* This code is originally from https://www.cl.cam.ac.uk/~mgk25/ucs/ */
 
 static const char utf16_be_bom[] = {'\xFE', '\xFF'};
 static const char utf16_le_bom[] = {'\xFF', '\xFE'};
diff --git a/utf8.h b/utf8.h
index b68efef..35df760 100644
--- a/utf8.h
+++ b/utf8.h
@@ -83,7 +83,7 @@
  * BOM must not be used [1]. The same applies for the UTF-32 equivalents.
  * The function returns true if this rule is violated.
  *
- * [1] http://unicode.org/faq/utf_bom.html#bom10
+ * [1] https://unicode.org/faq/utf_bom.html#bom10
  */
 int has_prohibited_utf_bom(const char *enc, const char *data, size_t len);
 
@@ -99,8 +99,8 @@
  * Therefore, strictly requiring a BOM seems to be the safest option for
  * content in Git.
  *
- * [1] http://unicode.org/faq/utf_bom.html#gen6
- * [2] http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
+ * [1] https://unicode.org/faq/utf_bom.html#gen6
+ * [2] https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
  *     Section 3.10, D98, page 132
  * [3] https://encoding.spec.whatwg.org/#utf-16le
  */
diff --git a/versioncmp.c b/versioncmp.c
index 069ee94..45e676c 100644
--- a/versioncmp.c
+++ b/versioncmp.c
@@ -1,6 +1,8 @@
-#include "cache.h"
+#include "git-compat-util.h"
 #include "config.h"
+#include "strbuf.h"
 #include "string-list.h"
+#include "versioncmp.h"
 
 /*
  * versioncmp(): copied from string/strverscmp.c in glibc commit
@@ -160,15 +162,21 @@
 	}
 
 	if (!initialized) {
-		const struct string_list *deprecated_prereleases;
+		const char *const newk = "versionsort.suffix";
+		const char *const oldk = "versionsort.prereleasesuffix";
+		const struct string_list *newl;
+		const struct string_list *oldl;
+		int new = git_config_get_string_multi(newk, &newl);
+		int old = git_config_get_string_multi(oldk, &oldl);
+
+		if (!new && !old)
+			warning("ignoring %s because %s is set", oldk, newk);
+		if (!new)
+			prereleases = newl;
+		else if (!old)
+			prereleases = oldl;
+
 		initialized = 1;
-		prereleases = git_config_get_value_multi("versionsort.suffix");
-		deprecated_prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
-		if (prereleases) {
-			if (deprecated_prereleases)
-				warning("ignoring versionsort.prereleasesuffix because versionsort.suffix is set");
-		} else
-			prereleases = deprecated_prereleases;
 	}
 	if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
 					    &diff))
diff --git a/versioncmp.h b/versioncmp.h
new file mode 100644
index 0000000..879b510
--- /dev/null
+++ b/versioncmp.h
@@ -0,0 +1,6 @@
+#ifndef VERSIONCMP_H
+#define VERSIONCMP_H
+
+int versioncmp(const char *s1, const char *s2);
+
+#endif /* VERSIONCMP_H */
diff --git a/walker.c b/walker.c
index 99d0e0e..c0fd632 100644
--- a/walker.c
+++ b/walker.c
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "hex.h"
 #include "walker.h"
 #include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
 #include "commit.h"
+#include "strbuf.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "tag.h"
@@ -42,7 +45,7 @@
 	if (parse_tree(tree))
 		return -1;
 
-	init_tree_desc(&desc, tree->buffer, tree->size);
+	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
 	while (tree_entry(&desc, &entry)) {
 		struct object *obj = NULL;
 
@@ -79,7 +82,7 @@
 {
 	struct commit_list *parents;
 
-	if (parse_commit(commit))
+	if (repo_parse_commit(the_repository, commit))
 		return -1;
 
 	while (complete && complete->item->date >= commit->date) {
@@ -93,7 +96,7 @@
 
 	walker_say(walker, "walk %s\n", oid_to_hex(&commit->object.oid));
 
-	if (process(walker, &get_commit_tree(commit)->object))
+	if (process(walker, &repo_get_commit_tree(the_repository, commit)->object))
 		return -1;
 
 	for (parents = commit->parents; parents; parents = parents->next) {
@@ -145,7 +148,7 @@
 		return 0;
 	obj->flags |= SEEN;
 
-	if (has_object_file(&obj->oid)) {
+	if (repo_has_object_file(the_repository, &obj->oid)) {
 		/* We already have it, so we should scan it now. */
 		obj->flags |= TO_SCAN;
 	}
diff --git a/wildmatch.c b/wildmatch.c
index 7e5a7ea..8ea2914 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -9,11 +9,15 @@
 **  work differently than '*', and to fix the character-class code.
 */
 
-#include "cache.h"
+#include "git-compat-util.h"
 #include "wildmatch.h"
 
 typedef unsigned char uchar;
 
+/* Internal return values */
+#define WM_ABORT_ALL -1
+#define WM_ABORT_TO_STARSTAR -2
+
 /* What character marks an inverted character class? */
 #define NEGATE_CLASS	'!'
 #define NEGATE_CLASS2	'^'
@@ -83,12 +87,12 @@
 			continue;
 		case '*':
 			if (*++p == '*') {
-				const uchar *prev_p = p - 2;
+				const uchar *prev_p = p;
 				while (*++p == '*') {}
 				if (!(flags & WM_PATHNAME))
 					/* without WM_PATHNAME, '*' == '**' */
 					match_slash = 1;
-				else if ((prev_p < pattern || *prev_p == '/') &&
+				else if ((prev_p - pattern < 2 || *(prev_p - 2) == '/') &&
 				    (*p == '\0' || *p == '/' ||
 				     (p[0] == '\\' && p[1] == '/'))) {
 					/*
@@ -114,7 +118,7 @@
 				 * only if there are no more slash characters. */
 				if (!match_slash) {
 					if (strchr((char *)text, '/'))
-						return WM_NOMATCH;
+						return WM_ABORT_TO_STARSTAR;
 				}
 				return WM_MATCH;
 			} else if (!match_slash && *p == '/') {
@@ -125,7 +129,7 @@
 				 */
 				const char *slash = strchr((char*)text, '/');
 				if (!slash)
-					return WM_NOMATCH;
+					return WM_ABORT_ALL;
 				text = (const uchar*)slash;
 				/* the slash is consumed by the top-level for loop */
 				break;
@@ -153,8 +157,12 @@
 							break;
 						text++;
 					}
-					if (t_ch != p_ch)
-						return WM_NOMATCH;
+					if (t_ch != p_ch) {
+						if (match_slash)
+							return WM_ABORT_ALL;
+						else
+							return WM_ABORT_TO_STARSTAR;
+					}
 				}
 				if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
 					if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
@@ -274,5 +282,6 @@
 /* Match the "pattern" against the "text" string. */
 int wildmatch(const char *pattern, const char *text, unsigned int flags)
 {
-	return dowild((const uchar*)pattern, (const uchar*)text, flags);
+	int res = dowild((const uchar*)pattern, (const uchar*)text, flags);
+	return res == WM_MATCH ? WM_MATCH : WM_NOMATCH;
 }
diff --git a/wildmatch.h b/wildmatch.h
index 5993696..0c890cb 100644
--- a/wildmatch.h
+++ b/wildmatch.h
@@ -6,8 +6,6 @@
 
 #define WM_NOMATCH 1
 #define WM_MATCH 0
-#define WM_ABORT_ALL -1
-#define WM_ABORT_TO_STARSTAR -2
 
 int wildmatch(const char *pattern, const char *text, unsigned int flags);
 #endif
diff --git a/worktree.c b/worktree.c
index aa43c64..cf5eea8 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1,24 +1,34 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
 #include "repository.h"
 #include "refs.h"
+#include "setup.h"
 #include "strbuf.h"
 #include "worktree.h"
 #include "dir.h"
 #include "wt-status.h"
 #include "config.h"
 
+void free_worktree(struct worktree *worktree)
+{
+	if (!worktree)
+		return;
+	free(worktree->path);
+	free(worktree->id);
+	free(worktree->head_ref);
+	free(worktree->lock_reason);
+	free(worktree->prune_reason);
+	free(worktree);
+}
+
 void free_worktrees(struct worktree **worktrees)
 {
 	int i = 0;
-
-	for (i = 0; worktrees[i]; i++) {
-		free(worktrees[i]->path);
-		free(worktrees[i]->id);
-		free(worktrees[i]->head_ref);
-		free(worktrees[i]->lock_reason);
-		free(worktrees[i]->prune_reason);
-		free(worktrees[i]);
-	}
+	for (i = 0; worktrees[i]; i++)
+		free_worktree(worktrees[i]);
 	free (worktrees);
 }
 
@@ -46,7 +56,7 @@
 /**
  * get the main worktree
  */
-static struct worktree *get_main_worktree(void)
+static struct worktree *get_main_worktree(int skip_reading_head)
 {
 	struct worktree *worktree = NULL;
 	struct strbuf worktree_path = STRBUF_INIT;
@@ -65,11 +75,13 @@
 	 */
 	worktree->is_bare = (is_bare_repository_cfg == 1) ||
 		is_bare_repository();
-	add_head_info(worktree);
+	if (!skip_reading_head)
+		add_head_info(worktree);
 	return worktree;
 }
 
-static struct worktree *get_linked_worktree(const char *id)
+struct worktree *get_linked_worktree(const char *id,
+				     int skip_reading_head)
 {
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
@@ -88,7 +100,8 @@
 	CALLOC_ARRAY(worktree, 1);
 	worktree->path = strbuf_detach(&worktree_path, NULL);
 	worktree->id = xstrdup(id);
-	add_head_info(worktree);
+	if (!skip_reading_head)
+		add_head_info(worktree);
 
 done:
 	strbuf_release(&path);
@@ -113,7 +126,14 @@
 	free(git_dir);
 }
 
-struct worktree **get_worktrees(void)
+/*
+ * NEEDSWORK: This function exists so that we can look up metadata of a
+ * worktree without trying to access any of its internals like the refdb. It
+ * would be preferable to instead have a corruption-tolerant function for
+ * retrieving worktree metadata that could be used when the worktree is known
+ * to not be in a healthy state, e.g. when creating or repairing it.
+ */
+static struct worktree **get_worktrees_internal(int skip_reading_head)
 {
 	struct worktree **list = NULL;
 	struct strbuf path = STRBUF_INIT;
@@ -123,7 +143,7 @@
 
 	ALLOC_ARRAY(list, alloc);
 
-	list[counter++] = get_main_worktree();
+	list[counter++] = get_main_worktree(skip_reading_head);
 
 	strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
 	dir = opendir(path.buf);
@@ -132,7 +152,7 @@
 		while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
 			struct worktree *linked = NULL;
 
-			if ((linked = get_linked_worktree(d->d_name))) {
+			if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
 				ALLOC_GROW(list, counter + 1, alloc);
 				list[counter++] = linked;
 			}
@@ -146,6 +166,11 @@
 	return list;
 }
 
+struct worktree **get_worktrees(void)
+{
+	return get_worktrees_internal(0);
+}
+
 const char *get_worktree_git_dir(const struct worktree *wt)
 {
 	if (!wt)
@@ -390,9 +415,9 @@
 
 	memset(&state, 0, sizeof(state));
 	found_bisect = wt_status_check_bisect(wt, &state) &&
-		       state.branch &&
+		       state.bisecting_from &&
 		       skip_prefix(target, "refs/heads/", &target) &&
-		       !strcmp(state.branch, target);
+		       !strcmp(state.bisecting_from, target);
 	wt_status_state_free_buffers(&state);
 	return found_bisect;
 }
@@ -403,44 +428,43 @@
  * bisect). New commands that do similar things should update this
  * function as well.
  */
+int is_shared_symref(const struct worktree *wt, const char *symref,
+		     const char *target)
+{
+	const char *symref_target;
+	struct ref_store *refs;
+	int flags;
+
+	if (wt->is_bare)
+		return 0;
+
+	if (wt->is_detached && !strcmp(symref, "HEAD")) {
+		if (is_worktree_being_rebased(wt, target))
+			return 1;
+		if (is_worktree_being_bisected(wt, target))
+			return 1;
+	}
+
+	refs = get_worktree_ref_store(wt);
+	symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
+						NULL, &flags);
+	if ((flags & REF_ISSYMREF) &&
+	    symref_target && !strcmp(symref_target, target))
+		return 1;
+
+	return 0;
+}
+
 const struct worktree *find_shared_symref(struct worktree **worktrees,
 					  const char *symref,
 					  const char *target)
 {
-	const struct worktree *existing = NULL;
-	int i = 0;
 
-	for (i = 0; worktrees[i]; i++) {
-		struct worktree *wt = worktrees[i];
-		const char *symref_target;
-		struct ref_store *refs;
-		int flags;
+	for (int i = 0; worktrees[i]; i++)
+		if (is_shared_symref(worktrees[i], symref, target))
+			return worktrees[i];
 
-		if (wt->is_bare)
-			continue;
-
-		if (wt->is_detached && !strcmp(symref, "HEAD")) {
-			if (is_worktree_being_rebased(wt, target)) {
-				existing = wt;
-				break;
-			}
-			if (is_worktree_being_bisected(wt, target)) {
-				existing = wt;
-				break;
-			}
-		}
-
-		refs = get_worktree_ref_store(wt);
-		symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
-							NULL, &flags);
-		if ((flags & REF_ISSYMREF) &&
-		    symref_target && !strcmp(symref_target, target)) {
-			existing = wt;
-			break;
-		}
-	}
-
-	return existing;
+	return NULL;
 }
 
 int submodule_uses_worktrees(const char *path)
@@ -577,15 +601,17 @@
 	strbuf_release(&dotgit);
 }
 
-static void repair_noop(int iserr, const char *path, const char *msg,
-			void *cb_data)
+static void repair_noop(int iserr UNUSED,
+			const char *path UNUSED,
+			const char *msg UNUSED,
+			void *cb_data UNUSED)
 {
 	/* nothing */
 }
 
 void repair_worktrees(worktree_repair_fn fn, void *cb_data)
 {
-	struct worktree **worktrees = get_worktrees();
+	struct worktree **worktrees = get_worktrees_internal(1);
 	struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
 
 	if (!fn)
@@ -781,9 +807,9 @@
 static int move_config_setting(const char *key, const char *value,
 			       const char *from_file, const char *to_file)
 {
-	if (git_config_set_in_file_gently(to_file, key, value))
+	if (git_config_set_in_file_gently(to_file, key, NULL, value))
 		return error(_("unable to set %s in '%s'"), key, to_file);
-	if (git_config_set_in_file_gently(from_file, key, NULL))
+	if (git_config_set_in_file_gently(from_file, key, NULL, NULL))
 		return error(_("unable to unset %s in '%s'"), key, from_file);
 	return 0;
 }
@@ -801,7 +827,7 @@
 	 * If the extension is already enabled, then we can skip the
 	 * upgrade process.
 	 */
-	if (repository_format_worktree_config)
+	if (r->repository_format_worktree_config)
 		return 0;
 	if ((res = git_config_set_gently("extensions.worktreeConfig", "true")))
 		return error(_("failed to set extensions.worktreeConfig setting"));
@@ -830,7 +856,7 @@
 	 * Relocate that value to avoid breaking all worktrees with this
 	 * upgrade to worktree config.
 	 */
-	if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
+	if (!git_configset_get_value(&cs, "core.worktree", &core_worktree, NULL)) {
 		if ((res = move_config_setting("core.worktree", core_worktree,
 					       common_config_file,
 					       main_worktree_file)))
@@ -841,7 +867,7 @@
 	 * Ensure that we use worktree config for the remaining lifetime
 	 * of the current process.
 	 */
-	repository_format_worktree_config = 1;
+	r->repository_format_worktree_config = 1;
 
 cleanup:
 	git_configset_clear(&cs);
diff --git a/worktree.h b/worktree.h
index 9dcea6f..f14784a 100644
--- a/worktree.h
+++ b/worktree.h
@@ -1,7 +1,6 @@
 #ifndef WORKTREE_H
 #define WORKTREE_H
 
-#include "cache.h"
 #include "refs.h"
 
 struct strbuf;
@@ -59,6 +58,13 @@
 			       const char *arg);
 
 /*
+ * Look up the worktree corresponding to `id`, or NULL of no such worktree
+ * exists.
+ */
+struct worktree *get_linked_worktree(const char *id,
+				     int skip_reading_head);
+
+/*
  * Return the worktree corresponding to `path`, or NULL if no such worktree
  * exists.
  */
@@ -136,6 +142,11 @@
 void repair_worktree_at_path(const char *, worktree_repair_fn, void *cb_data);
 
 /*
+ * Free up the memory for a worktree.
+ */
+void free_worktree(struct worktree *);
+
+/*
  * Free up the memory for worktree(s)
  */
 void free_worktrees(struct worktree **);
@@ -150,6 +161,12 @@
 					  const char *target);
 
 /*
+ * Returns true if a symref points to a ref in a worktree.
+ */
+int is_shared_symref(const struct worktree *wt,
+		     const char *symref, const char *target);
+
+/*
  * Similar to head_ref() for all HEADs _except_ one from the current
  * worktree, which is covered by head_ref().
  */
diff --git a/wrapper.c b/wrapper.c
index 299d648..eeac374 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -1,16 +1,17 @@
 /*
  * Various trivial helper wrappers around standard functions
  */
-#include "cache.h"
-#include "config.h"
-
-static intmax_t count_fsync_writeout_only;
-static intmax_t count_fsync_hardware_flush;
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "parse.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "trace2.h"
 
 #ifdef HAVE_RTLGENRANDOM
 /* This is required to get access to RtlGenRandom. */
 #define SystemFunction036 NTAPI SystemFunction036
-#include <NTSecAPI.h>
+#include <ntsecapi.h>
 #undef SystemFunction036
 #endif
 
@@ -545,7 +546,7 @@
 {
 	switch (action) {
 	case FSYNC_WRITEOUT_ONLY:
-		count_fsync_writeout_only += 1;
+		trace2_counter_add(TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY, 1);
 
 #ifdef __APPLE__
 		/*
@@ -577,7 +578,7 @@
 		return -1;
 
 	case FSYNC_HARDWARE_FLUSH:
-		count_fsync_hardware_flush += 1;
+		trace2_counter_add(TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH, 1);
 
 		/*
 		 * On macOS, a special fcntl is required to really flush the
@@ -594,18 +595,6 @@
 	}
 }
 
-static void log_trace_fsync_if(const char *key, intmax_t value)
-{
-	if (value)
-		trace2_data_intmax("fsync", the_repository, key, value);
-}
-
-void trace_git_fsync_stats(void)
-{
-	log_trace_fsync_if("fsync/writeout-only", count_fsync_writeout_only);
-	log_trace_fsync_if("fsync/hardware-flush", count_fsync_hardware_flush);
-}
-
 static int warn_if_unremovable(const char *op, const char *file, int rc)
 {
 	int err;
@@ -641,11 +630,6 @@
 	return warn_if_unremovable("rmdir", file, rmdir(file));
 }
 
-int remove_or_warn(unsigned int mode, const char *file)
-{
-	return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
-}
-
 static int access_error_is_ok(int err, unsigned flag)
 {
 	return (is_missing_file_error(err) ||
@@ -828,3 +812,13 @@
 	return 0;
 #endif
 }
+
+uint32_t git_rand(void)
+{
+	uint32_t result;
+
+	if (csprng_bytes(&result, sizeof(result)) < 0)
+		die(_("unable to get random bytes"));
+
+	return result;
+}
diff --git a/wrapper.h b/wrapper.h
new file mode 100644
index 0000000..1b2b047
--- /dev/null
+++ b/wrapper.h
@@ -0,0 +1,143 @@
+#ifndef WRAPPER_H
+#define WRAPPER_H
+
+char *xstrdup(const char *str);
+void *xmalloc(size_t size);
+void *xmallocz(size_t size);
+void *xmallocz_gently(size_t size);
+void *xmemdupz(const void *data, size_t len);
+char *xstrndup(const char *str, size_t len);
+void *xrealloc(void *ptr, size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void xsetenv(const char *name, const char *value, int overwrite);
+void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+const char *mmap_os_err(void);
+void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int xopen(const char *path, int flags, ...);
+ssize_t xread(int fd, void *buf, size_t len);
+ssize_t xwrite(int fd, const void *buf, size_t len);
+ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
+int xdup(int fd);
+FILE *xfopen(const char *path, const char *mode);
+FILE *xfdopen(int fd, const char *mode);
+int xmkstemp(char *temp_filename);
+int xmkstemp_mode(char *temp_filename, int mode);
+char *xgetcwd(void);
+FILE *fopen_for_writing(const char *path);
+FILE *fopen_or_warn(const char *path, const char *mode);
+
+/*
+ * Like strncmp, but only return zero if s is NUL-terminated and exactly len
+ * characters long.  If it is not, consider it greater than t.
+ */
+int xstrncmpz(const char *s, const char *t, size_t len);
+
+__attribute__((format (printf, 3, 4)))
+int xsnprintf(char *dst, size_t max, const char *fmt, ...);
+
+int xgethostname(char *buf, size_t len);
+
+/* set default permissions by passing mode arguments to open(2) */
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
+int git_mkstemp_mode(char *pattern, int mode);
+
+ssize_t read_in_full(int fd, void *buf, size_t count);
+ssize_t write_in_full(int fd, const void *buf, size_t count);
+ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
+
+static inline ssize_t write_str_in_full(int fd, const char *str)
+{
+	return write_in_full(fd, str, strlen(str));
+}
+
+/**
+ * Open (and truncate) the file at path, write the contents of buf to it,
+ * and close it. Dies if any errors are encountered.
+ */
+void write_file_buf(const char *path, const char *buf, size_t len);
+
+/**
+ * Like write_file_buf(), but format the contents into a buffer first.
+ * Additionally, write_file() will append a newline if one is not already
+ * present, making it convenient to write text files:
+ *
+ *   write_file(path, "counter: %d", ctr);
+ */
+__attribute__((format (printf, 2, 3)))
+void write_file(const char *path, const char *fmt, ...);
+
+/* Return 1 if the file is empty or does not exists, 0 otherwise. */
+int is_empty_or_missing_file(const char *filename);
+
+enum fsync_action {
+	FSYNC_WRITEOUT_ONLY,
+	FSYNC_HARDWARE_FLUSH
+};
+
+/*
+ * Issues an fsync against the specified file according to the specified mode.
+ *
+ * FSYNC_WRITEOUT_ONLY attempts to use interfaces available on some operating
+ * systems to flush the OS cache without issuing a flush command to the storage
+ * controller. If those interfaces are unavailable, the function fails with
+ * ENOSYS.
+ *
+ * FSYNC_HARDWARE_FLUSH does an OS writeout and hardware flush to ensure that
+ * changes are durable. It is not expected to fail.
+ */
+int git_fsync(int fd, enum fsync_action action);
+
+/*
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success, which includes trying to unlink an object that does
+ * not exist.
+ */
+int unlink_or_warn(const char *path);
+ /*
+  * Tries to unlink file.  Returns 0 if unlink succeeded
+  * or the file already didn't exist.  Returns -1 and
+  * appends a message to err suitable for
+  * 'error("%s", err->buf)' on error.
+  */
+int unlink_or_msg(const char *file, struct strbuf *err);
+/*
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success, which includes trying to remove a directory that does
+ * not exist.
+ */
+int rmdir_or_warn(const char *path);
+
+/*
+ * Call access(2), but warn for any error except "missing file"
+ * (ENOENT or ENOTDIR).
+ */
+#define ACCESS_EACCES_OK (1U << 0)
+int access_or_warn(const char *path, int mode, unsigned flag);
+int access_or_die(const char *path, int mode, unsigned flag);
+
+/* Warn on an inaccessible file if errno indicates this is an error */
+int warn_on_fopen_errors(const char *path);
+
+/*
+ * Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
+ * may be racy. Do not use this as protection against an attacker who can
+ * simultaneously create paths.
+ */
+int open_nofollow(const char *path, int flags);
+
+void sleep_millisec(int millisec);
+
+/*
+ * Generate len bytes from the system cryptographically secure PRNG.
+ * Returns 0 on success and -1 on error, setting errno.  The inability to
+ * satisfy the full request is an error.
+ */
+int csprng_bytes(void *buf, size_t len);
+
+/*
+ * Returns a random uint32_t, uniformly distributed across all possible
+ * values.
+ */
+uint32_t git_rand(void);
+
+#endif /* WRAPPER_H */
diff --git a/write-or-die.c b/write-or-die.c
index aaa0318..01a9a51 100644
--- a/write-or-die.c
+++ b/write-or-die.c
@@ -1,6 +1,7 @@
-#include "cache.h"
-#include "config.h"
+#include "git-compat-util.h"
+#include "parse.h"
 #include "run-command.h"
+#include "write-or-die.h"
 
 /*
  * Some cases use stdio, but want to flush after the write
@@ -17,23 +18,20 @@
  */
 void maybe_flush_or_die(FILE *f, const char *desc)
 {
-	static int skip_stdout_flush = -1;
-	struct stat st;
-	char *cp;
-
 	if (f == stdout) {
-		if (skip_stdout_flush < 0) {
-			/* NEEDSWORK: make this a normal Boolean */
-			cp = getenv("GIT_FLUSH");
-			if (cp)
-				skip_stdout_flush = (atoi(cp) == 0);
-			else if ((fstat(fileno(stdout), &st) == 0) &&
-				 S_ISREG(st.st_mode))
-				skip_stdout_flush = 1;
-			else
-				skip_stdout_flush = 0;
+		static int force_flush_stdout = -1;
+
+		if (force_flush_stdout < 0) {
+			force_flush_stdout = git_env_bool("GIT_FLUSH", -1);
+			if (force_flush_stdout < 0) {
+				struct stat st;
+				if (fstat(fileno(stdout), &st))
+					force_flush_stdout = 1;
+				else
+					force_flush_stdout = !S_ISREG(st.st_mode);
+			}
 		}
-		if (skip_stdout_flush && !ferror(f))
+		if (!force_flush_stdout && !ferror(f))
 			return;
 	}
 	if (fflush(f)) {
diff --git a/write-or-die.h b/write-or-die.h
new file mode 100644
index 0000000..65a5c42
--- /dev/null
+++ b/write-or-die.h
@@ -0,0 +1,78 @@
+#ifndef WRITE_OR_DIE_H
+#define WRITE_OR_DIE_H
+
+void maybe_flush_or_die(FILE *, const char *);
+__attribute__((format (printf, 2, 3)))
+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);
+
+/*
+ * These values are used to help identify parts of a repository to fsync.
+ * FSYNC_COMPONENT_NONE identifies data that will not be a persistent part of the
+ * repository and so shouldn't be fsynced.
+ */
+enum fsync_component {
+	FSYNC_COMPONENT_NONE,
+	FSYNC_COMPONENT_LOOSE_OBJECT		= 1 << 0,
+	FSYNC_COMPONENT_PACK			= 1 << 1,
+	FSYNC_COMPONENT_PACK_METADATA		= 1 << 2,
+	FSYNC_COMPONENT_COMMIT_GRAPH		= 1 << 3,
+	FSYNC_COMPONENT_INDEX			= 1 << 4,
+	FSYNC_COMPONENT_REFERENCE		= 1 << 5,
+};
+
+#define FSYNC_COMPONENTS_OBJECTS (FSYNC_COMPONENT_LOOSE_OBJECT | \
+				  FSYNC_COMPONENT_PACK)
+
+#define FSYNC_COMPONENTS_DERIVED_METADATA (FSYNC_COMPONENT_PACK_METADATA | \
+					   FSYNC_COMPONENT_COMMIT_GRAPH)
+
+#define FSYNC_COMPONENTS_DEFAULT ((FSYNC_COMPONENTS_OBJECTS | \
+				   FSYNC_COMPONENTS_DERIVED_METADATA) & \
+				  ~FSYNC_COMPONENT_LOOSE_OBJECT)
+
+#define FSYNC_COMPONENTS_COMMITTED (FSYNC_COMPONENTS_OBJECTS | \
+				    FSYNC_COMPONENT_REFERENCE)
+
+#define FSYNC_COMPONENTS_ADDED (FSYNC_COMPONENTS_COMMITTED | \
+				FSYNC_COMPONENT_INDEX)
+
+#define FSYNC_COMPONENTS_ALL (FSYNC_COMPONENT_LOOSE_OBJECT | \
+			      FSYNC_COMPONENT_PACK | \
+			      FSYNC_COMPONENT_PACK_METADATA | \
+			      FSYNC_COMPONENT_COMMIT_GRAPH | \
+			      FSYNC_COMPONENT_INDEX | \
+			      FSYNC_COMPONENT_REFERENCE)
+
+#ifndef FSYNC_COMPONENTS_PLATFORM_DEFAULT
+#define FSYNC_COMPONENTS_PLATFORM_DEFAULT FSYNC_COMPONENTS_DEFAULT
+#endif
+
+/* IO helper functions */
+void fsync_or_die(int fd, const char *);
+int fsync_component(enum fsync_component component, int fd);
+void fsync_component_or_die(enum fsync_component component, int fd, const char *msg);
+
+/*
+ * A bitmask indicating which components of the repo should be fsynced.
+ */
+extern enum fsync_component fsync_components;
+extern int fsync_object_files;
+extern int use_fsync;
+
+enum fsync_method {
+	FSYNC_METHOD_FSYNC,
+	FSYNC_METHOD_WRITEOUT_ONLY,
+	FSYNC_METHOD_BATCH,
+};
+
+extern enum fsync_method fsync_method;
+
+static inline int batch_fsync_enabled(enum fsync_component component)
+{
+	return (fsync_components & component) && (fsync_method == FSYNC_METHOD_BATCH);
+}
+
+#endif /* WRITE_OR_DIE_H */
diff --git a/ws.c b/ws.c
index da3d0e2..9456e2f 100644
--- a/ws.c
+++ b/ws.c
@@ -3,8 +3,12 @@
  *
  * Copyright (c) 2007 Junio C Hamano
  */
-#include "cache.h"
+#include "git-compat-util.h"
 #include "attr.h"
+#include "strbuf.h"
+#include "ws.h"
+
+unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 
 static struct whitespace_rule {
 	const char *rule_name;
@@ -79,7 +83,7 @@
 	if (!attr_whitespace_rule)
 		attr_whitespace_rule = attr_check_initl("whitespace", NULL);
 
-	git_check_attr(istate, NULL, pathname, attr_whitespace_rule);
+	git_check_attr(istate, pathname, attr_whitespace_rule);
 	value = attr_whitespace_rule->items[0].value;
 	if (ATTR_TRUE(value)) {
 		/* true (whitespace) */
diff --git a/ws.h b/ws.h
new file mode 100644
index 0000000..5ba676c
--- /dev/null
+++ b/ws.h
@@ -0,0 +1,33 @@
+#ifndef WS_H
+#define WS_H
+
+struct index_state;
+struct strbuf;
+
+/*
+ * whitespace rules.
+ * used by both diff and apply
+ * last two digits are tab width
+ */
+#define WS_BLANK_AT_EOL         0100
+#define WS_SPACE_BEFORE_TAB     0200
+#define WS_INDENT_WITH_NON_TAB  0400
+#define WS_CR_AT_EOL           01000
+#define WS_BLANK_AT_EOF        02000
+#define WS_TAB_IN_INDENT       04000
+#define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
+#define WS_TAB_WIDTH_MASK        077
+/* All WS_* -- when extended, adapt diff.c emit_symbol */
+#define WS_RULE_MASK           07777
+extern unsigned whitespace_rule_cfg;
+unsigned whitespace_rule(struct index_state *, const char *);
+unsigned parse_whitespace_rule(const char *);
+unsigned ws_check(const char *line, int len, unsigned ws_rule);
+void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
+char *whitespace_error_string(unsigned ws);
+void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
+int ws_blank_line(const char *line, int len);
+#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
+
+#endif /* WS_H */
diff --git a/wt-status.c b/wt-status.c
index 3162241..bdfc23e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,9 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
 #include "wt-status.h"
 #include "object.h"
 #include "dir.h"
 #include "commit.h"
 #include "diff.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object-name.h"
+#include "path.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "quote.h"
@@ -13,7 +20,12 @@
 #include "refs.h"
 #include "submodule.h"
 #include "column.h"
+#include "read-cache.h"
+#include "setup.h"
 #include "strbuf.h"
+#include "trace.h"
+#include "trace2.h"
+#include "tree.h"
 #include "utf8.h"
 #include "worktree.h"
 #include "lockfile.h"
@@ -58,7 +70,7 @@
 	strbuf_vaddf(&sb, fmt, ap);
 	if (!sb.len) {
 		if (s->display_comment_prefix) {
-			strbuf_addch(&sb, comment_line_char);
+			strbuf_addstr(&sb, comment_line_str);
 			if (!trail)
 				strbuf_addch(&sb, ' ');
 		}
@@ -73,7 +85,7 @@
 
 		strbuf_reset(&linebuf);
 		if (at_bol && s->display_comment_prefix) {
-			strbuf_addch(&linebuf, comment_line_char);
+			strbuf_addstr(&linebuf, comment_line_str);
 			if (*line != '\n' && *line != '\t')
 				strbuf_addch(&linebuf, ' ');
 		}
@@ -663,7 +675,7 @@
 	rev.diffopt.flags.recursive = 1;
 
 	copy_pathspec(&rev.prune_data, &s->pathspec);
-	run_diff_index(&rev, 1);
+	run_diff_index(&rev, DIFF_INDEX_CACHED);
 	release_revisions(&rev);
 }
 
@@ -727,7 +739,7 @@
 			ps.max_depth = -1;
 
 			strbuf_add(&base, ce->name, ce->ce_namelen);
-			read_tree_at(istate->repo, tree, &base, &ps,
+			read_tree_at(istate->repo, tree, &base, 0, &ps,
 				     add_file_to_list, s);
 			continue;
 		}
@@ -849,6 +861,7 @@
 	FREE_AND_NULL(state->branch);
 	FREE_AND_NULL(state->onto);
 	FREE_AND_NULL(state->detached_from);
+	FREE_AND_NULL(state->bisecting_from);
 }
 
 static void wt_longstatus_print_unmerged(struct wt_status *s)
@@ -1015,7 +1028,7 @@
 	if (s->display_comment_prefix) {
 		size_t len;
 		summary_content = strbuf_detach(&summary, &len);
-		strbuf_add_commented_lines(&summary, summary_content, len);
+		strbuf_add_commented_lines(&summary, summary_content, len, comment_line_str);
 		free(summary_content);
 	}
 
@@ -1077,11 +1090,14 @@
 	const char *p;
 	struct strbuf pattern = STRBUF_INIT;
 
-	strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+	strbuf_addf(&pattern, "\n%s %s", comment_line_str, cut_line);
 	if (starts_with(s, pattern.buf + 1))
 		len = 0;
-	else if ((p = strstr(s, pattern.buf)))
-		len = p - s + 1;
+	else if ((p = strstr(s, pattern.buf))) {
+		size_t newlen = p - s + 1;
+		if (newlen < len)
+			len = newlen;
+	}
 	strbuf_release(&pattern);
 	return len;
 }
@@ -1090,16 +1106,19 @@
 {
 	const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
 
-	strbuf_commented_addf(buf, "%s", cut_line);
-	strbuf_add_commented_lines(buf, explanation, strlen(explanation));
+	strbuf_commented_addf(buf, comment_line_str, "%s", cut_line);
+	strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_str);
 }
 
-void wt_status_add_cut_line(FILE *fp)
+void wt_status_add_cut_line(struct wt_status *s)
 {
 	struct strbuf buf = STRBUF_INIT;
 
+	if (s->added_cut_line)
+		return;
+	s->added_cut_line = 1;
 	wt_status_append_cut_line(&buf);
-	fputs(buf.buf, fp);
+	fputs(buf.buf, s->fp);
 	strbuf_release(&buf);
 }
 
@@ -1130,11 +1149,12 @@
 	 * file (and even the "auto" setting won't work, since it
 	 * will have checked isatty on stdout). But we then do want
 	 * to insert the scissor line here to reliably remove the
-	 * diff before committing.
+	 * diff before committing, if we didn't already include one
+	 * before.
 	 */
 	if (s->fp != stdout) {
 		rev.diffopt.use_color = 0;
-		wt_status_add_cut_line(s->fp);
+		wt_status_add_cut_line(s);
 	}
 	if (s->verbose > 1 && s->committable) {
 		/* print_updated() printed a header, so do we */
@@ -1144,7 +1164,7 @@
 		rev.diffopt.a_prefix = "c/";
 		rev.diffopt.b_prefix = "i/";
 	} /* else use prefix as per user config */
-	run_diff_index(&rev, 1);
+	run_diff_index(&rev, DIFF_INDEX_CACHED);
 	if (s->verbose > 1 &&
 	    wt_status_check_worktree_changes(s, &dirty_submodules)) {
 		status_printf_ln(s, c,
@@ -1163,8 +1183,6 @@
 	struct strbuf sb = STRBUF_INIT;
 	const char *cp, *ep, *branch_name;
 	struct branch *branch;
-	char comment_line_string[3];
-	int i;
 	uint64_t t_begin = 0;
 
 	assert(s->branch && !s->is_initial);
@@ -1174,7 +1192,8 @@
 
 	t_begin = getnanotime();
 
-	if (!format_tracking_info(branch, &sb, s->ahead_behind_flags))
+	if (!format_tracking_info(branch, &sb, s->ahead_behind_flags,
+				  !s->commit_template))
 		return;
 
 	if (advice_enabled(ADVICE_STATUS_AHEAD_BEHIND_WARNING) &&
@@ -1188,20 +1207,15 @@
 		}
 	}
 
-	i = 0;
-	if (s->display_comment_prefix) {
-		comment_line_string[i++] = comment_line_char;
-		comment_line_string[i++] = ' ';
-	}
-	comment_line_string[i] = '\0';
-
 	for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
-				 "%s%.*s", comment_line_string,
+				 "%s%s%.*s",
+				 s->display_comment_prefix ? comment_line_str : "",
+				 s->display_comment_prefix ? " " : "",
 				 (int)(ep - cp), cp);
 	if (s->display_comment_prefix)
-		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
-				 comment_line_char);
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%s",
+				 comment_line_str);
 	else
 		fputs("\n", s->fp);
 	strbuf_release(&sb);
@@ -1282,26 +1296,32 @@
 static int split_commit_in_progress(struct wt_status *s)
 {
 	int split_in_progress = 0;
-	char *head, *orig_head, *rebase_amend, *rebase_orig_head;
+	struct object_id head_oid, orig_head_oid;
+	char *rebase_amend, *rebase_orig_head;
+	int head_flags, orig_head_flags;
 
 	if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
 	    !s->branch || strcmp(s->branch, "HEAD"))
 		return 0;
 
-	head = read_line_from_git_path("HEAD");
-	orig_head = read_line_from_git_path("ORIG_HEAD");
+	if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			  &head_oid, &head_flags) ||
+	    read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			  &orig_head_oid, &orig_head_flags))
+		return 0;
+	if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
+		return 0;
+
 	rebase_amend = read_line_from_git_path("rebase-merge/amend");
 	rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
 
-	if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+	if (!rebase_amend || !rebase_orig_head)
 		; /* fall through, no split in progress */
 	else if (!strcmp(rebase_amend, rebase_orig_head))
-		split_in_progress = !!strcmp(head, rebase_amend);
-	else if (strcmp(orig_head, rebase_orig_head))
+		split_in_progress = !!strcmp(oid_to_hex(&head_oid), rebase_amend);
+	else if (strcmp(oid_to_hex(&orig_head_oid), rebase_orig_head))
 		split_in_progress = 1;
 
-	free(head);
-	free(orig_head);
 	free(rebase_amend);
 	free(rebase_orig_head);
 
@@ -1337,7 +1357,7 @@
 		 * it after abbreviation.
 		 */
 		strbuf_trim(split[1]);
-		if (!get_oid(split[1]->buf, &oid)) {
+		if (!repo_get_oid(the_repository, split[1]->buf, &oid)) {
 			strbuf_reset(split[1]);
 			strbuf_add_unique_abbrev(split[1], &oid,
 						 DEFAULT_ABBREV);
@@ -1362,7 +1382,7 @@
 			  git_path("%s", fname));
 	}
 	while (!strbuf_getline_lf(&line, f)) {
-		if (line.len && line.buf[0] == comment_line_char)
+		if (starts_with(line.buf, comment_line_str))
 			continue;
 		strbuf_trim(&line);
 		if (!line.len)
@@ -1503,8 +1523,8 @@
 	else
 		status_printf_ln(s, color,
 			_("You are currently cherry-picking commit %s."),
-			find_unique_abbrev(&s->state.cherry_pick_head_oid,
-					   DEFAULT_ABBREV));
+			repo_find_unique_abbrev(the_repository, &s->state.cherry_pick_head_oid,
+						DEFAULT_ABBREV));
 
 	if (s->hints) {
 		if (has_unmerged(s))
@@ -1533,8 +1553,8 @@
 	else
 		status_printf_ln(s, color,
 			_("You are currently reverting commit %s."),
-			find_unique_abbrev(&s->state.revert_head_oid,
-					   DEFAULT_ABBREV));
+			repo_find_unique_abbrev(the_repository, &s->state.revert_head_oid,
+						DEFAULT_ABBREV));
 	if (s->hints) {
 		if (has_unmerged(s))
 			status_printf_ln(s, color,
@@ -1556,10 +1576,10 @@
 static void show_bisect_in_progress(struct wt_status *s,
 				    const char *color)
 {
-	if (s->state.branch)
+	if (s->state.bisecting_from)
 		status_printf_ln(s, color,
 				 _("You are currently bisecting, started from branch '%s'."),
-				 s->state.branch);
+				 s->state.bisecting_from);
 	else
 		status_printf_ln(s, color,
 				 _("You are currently bisecting."));
@@ -1664,7 +1684,8 @@
 		return;
 	}
 
-	if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref, 1) == 1 &&
+	if (repo_dwim_ref(r, cb.buf.buf, cb.buf.len, &oid, &ref,
+			  1) == 1 &&
 	    /* oid is a commit? match without further lookup */
 	    (oideq(&cb.noid, &oid) ||
 	     /* perhaps oid is a tag, try to dereference to a commit */
@@ -1676,9 +1697,9 @@
 		state->detached_from = xstrdup(from);
 	} else
 		state->detached_from =
-			xstrdup(find_unique_abbrev(&cb.noid, DEFAULT_ABBREV));
+			xstrdup(repo_find_unique_abbrev(r, &cb.noid, DEFAULT_ABBREV));
 	oidcpy(&state->detached_oid, &cb.noid);
-	state->detached_at = !get_oid("HEAD", &oid) &&
+	state->detached_at = !repo_get_oid(r, "HEAD", &oid) &&
 			     oideq(&oid, &state->detached_oid);
 
 	free(ref);
@@ -1719,7 +1740,7 @@
 
 	if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
 		state->bisect_in_progress = 1;
-		state->branch = get_branch(wt, "BISECT_START");
+		state->bisecting_from = get_branch(wt, "BISECT_START");
 		return 1;
 	}
 	return 0;
@@ -1769,21 +1790,21 @@
 	} else if (wt_status_check_rebase(NULL, state)) {
 		;		/* all set */
 	} else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
-		   !get_oid("CHERRY_PICK_HEAD", &oid)) {
+		   !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) {
 		state->cherry_pick_in_progress = 1;
 		oidcpy(&state->cherry_pick_head_oid, &oid);
 	}
 	wt_status_check_bisect(NULL, state);
 	if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
-	    !get_oid("REVERT_HEAD", &oid)) {
+	    !repo_get_oid(r, "REVERT_HEAD", &oid)) {
 		state->revert_in_progress = 1;
 		oidcpy(&state->revert_head_oid, &oid);
 	}
 	if (!sequencer_get_last_command(r, &action)) {
-		if (action == REPLAY_PICK) {
+		if (action == REPLAY_PICK && !state->cherry_pick_in_progress) {
 			state->cherry_pick_in_progress = 1;
 			oidcpy(&state->cherry_pick_head_oid, null_oid());
-		} else {
+		} else if (action == REPLAY_REVERT && !state->revert_in_progress) {
 			state->revert_in_progress = 1;
 			oidcpy(&state->revert_head_oid, null_oid());
 		}
@@ -2566,8 +2587,8 @@
 	}
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
-	result = run_diff_files(&rev_info, 0);
-	result = diff_result_code(&rev_info.diffopt, result);
+	run_diff_files(&rev_info, 0);
+	result = diff_result_code(&rev_info.diffopt);
 	release_revisions(&rev_info);
 	return result;
 }
@@ -2600,8 +2621,8 @@
 	}
 
 	diff_setup_done(&rev_info.diffopt);
-	result = run_diff_index(&rev_info, 1);
-	result = diff_result_code(&rev_info.diffopt, result);
+	run_diff_index(&rev_info, DIFF_INDEX_CACHED);
+	result = diff_result_code(&rev_info.diffopt);
 	release_revisions(&rev_info);
 	return result;
 }
@@ -2641,8 +2662,12 @@
 	}
 
 	if (err) {
-		if (hint)
+		if (hint) {
+			if (!*hint)
+				BUG("empty hint passed to require_clean_work_tree();"
+				    " use NULL instead");
 			error("%s", hint);
+		}
 		if (!gently)
 			exit(128);
 	}
diff --git a/wt-status.h b/wt-status.h
index ab9cc9d..4e377ce 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -23,7 +23,8 @@
 };
 
 enum untracked_status_type {
-	SHOW_NO_UNTRACKED_FILES,
+	SHOW_UNTRACKED_FILES_ERROR = -1,
+	SHOW_NO_UNTRACKED_FILES = 0,
 	SHOW_NORMAL_UNTRACKED_FILES,
 	SHOW_ALL_UNTRACKED_FILES
 };
@@ -94,6 +95,7 @@
 	char *branch;
 	char *onto;
 	char *detached_from;
+	char *bisecting_from;
 	struct object_id detached_oid;
 	struct object_id revert_head_oid;
 	struct object_id cherry_pick_head_oid;
@@ -129,6 +131,7 @@
 	int rename_score;
 	int rename_limit;
 	enum wt_status_format status_format;
+	unsigned char added_cut_line; /* boolean */
 	struct wt_status_state state;
 	struct object_id oid_commit; /* when not Initial */
 
@@ -146,7 +149,7 @@
 
 size_t wt_status_locate_end(const char *s, size_t len);
 void wt_status_append_cut_line(struct strbuf *buf);
-void wt_status_add_cut_line(FILE *fp);
+void wt_status_add_cut_line(struct wt_status *s);
 void wt_status_prepare(struct repository *r, struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
diff --git a/xdiff-interface.c b/xdiff-interface.c
index e87950d..16ed8ac 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,11 +1,12 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "gettext.h"
 #include "config.h"
-#include "object-store.h"
+#include "hex.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
 #include "xdiff-interface.h"
 #include "xdiff/xtypes.h"
 #include "xdiff/xdiffi.h"
-#include "xdiff/xemit.h"
-#include "xdiff/xmacros.h"
 #include "xdiff/xutils.h"
 
 struct xdiff_emit_state {
@@ -183,7 +184,7 @@
 		return;
 	}
 
-	ptr->ptr = read_object_file(oid, &type, &size);
+	ptr->ptr = repo_read_object_file(the_repository, oid, &type, &size);
 	if (!ptr->ptr || type != OBJ_BLOB)
 		die("unable to read blob object %s", oid_to_hex(oid));
 	ptr->size = size;
@@ -304,27 +305,35 @@
 	return xdl_recmatch(l1, s1, l2, s2, flags);
 }
 
+int parse_conflict_style_name(const char *value)
+{
+	if (!strcmp(value, "diff3"))
+		return XDL_MERGE_DIFF3;
+	else if (!strcmp(value, "zdiff3"))
+		return XDL_MERGE_ZEALOUS_DIFF3;
+	else if (!strcmp(value, "merge"))
+		return 0;
+	/*
+	 * Please update _git_checkout() in git-completion.bash when
+	 * you add new merge config
+	 */
+	else
+		return -1;
+}
+
 int git_xmerge_style = -1;
 
-int git_xmerge_config(const char *var, const char *value, void *cb)
+int git_xmerge_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb)
 {
 	if (!strcmp(var, "merge.conflictstyle")) {
 		if (!value)
-			die("'%s' is not a boolean", var);
-		if (!strcmp(value, "diff3"))
-			git_xmerge_style = XDL_MERGE_DIFF3;
-		else if (!strcmp(value, "zdiff3"))
-			git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
-		else if (!strcmp(value, "merge"))
-			git_xmerge_style = 0;
-		/*
-		 * Please update _git_checkout() in
-		 * git-completion.bash when you add new merge config
-		 */
-		else
-			die("unknown style '%s' given for '%s'",
-			    value, var);
+			return config_error_nonbool(var);
+		git_xmerge_style = parse_conflict_style_name(value);
+		if (git_xmerge_style == -1)
+			return error(_("unknown style '%s' given for '%s'"),
+				     value, var);
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+	return git_default_config(var, value, ctx, cb);
 }
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 4301a7e..3853716 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -1,7 +1,7 @@
 #ifndef XDIFF_INTERFACE_H
 #define XDIFF_INTERFACE_H
 
-#include "cache.h"
+#include "hash-ll.h"
 #include "xdiff/xdiff.h"
 
 /*
@@ -50,7 +50,10 @@
 
 void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
 void xdiff_clear_find_func(xdemitconf_t *xecfg);
-int git_xmerge_config(const char *var, const char *value, void *cb);
+struct config_context;
+int parse_conflict_style_name(const char *value);
+int git_xmerge_config(const char *var, const char *value,
+		      const struct config_context *ctx, void *cb);
 extern int git_xmerge_style;
 
 /*